//// MetavoxelSystem.cpp// interface/src//// Created by Andrzej Kapolka on 12/10/13.// Copyright 2013 High Fidelity, Inc.//// Distributed under the Apache License, Version 2.0.// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// // include this before QOpenGLFramebufferObject, which includes an earlier version of OpenGL#include "InterfaceConfig.h" #include <QMutexLocker>#include <QOpenGLFramebufferObject>#include <QReadLocker>#include <QWriteLocker>#include <QThreadPool>#include <QtDebug> #include <glm/gtx/transform.hpp> #include <SharedUtil.h> #include <MetavoxelUtil.h>#include <ScriptCache.h> #include "Application.h"#include "MetavoxelSystem.h"#include "renderer/Model.h"#include "renderer/RenderUtil.h" REGISTER_META_OBJECT(DefaultMetavoxelRendererImplementation)REGISTER_META_OBJECT(SphereRenderer)REGISTER_META_OBJECT(StaticModelRenderer) static int bufferPointVectorMetaTypeId = qRegisterMetaType<BufferPointVector>(); void MetavoxelSystem::init() { MetavoxelClientManager::init(); DefaultMetavoxelRendererImplementation::init(); _pointBufferAttribute = AttributeRegistry::getInstance()->registerAttribute(new BufferDataAttribute("pointBuffer")); _heightfieldBufferAttribute = AttributeRegistry::getInstance()->registerAttribute( new BufferDataAttribute("heightfieldBuffer")); _heightfieldBufferAttribute->setLODThresholdMultiplier( AttributeRegistry::getInstance()->getHeightfieldAttribute()->getLODThresholdMultiplier()); _voxelBufferAttribute = AttributeRegistry::getInstance()->registerAttribute( new BufferDataAttribute("voxelBuffer")); _voxelBufferAttribute->setLODThresholdMultiplier( AttributeRegistry::getInstance()->getVoxelColorAttribute()->getLODThresholdMultiplier());} MetavoxelLOD MetavoxelSystem::getLOD() { QReadLocker locker(&_lodLock); return _lod;} class SimulateVisitor : public MetavoxelVisitor {public: SimulateVisitor(float deltaTime, const MetavoxelLOD& lod); virtual int visit(MetavoxelInfo& info); private: float _deltaTime;}; SimulateVisitor::SimulateVisitor(float deltaTime, const MetavoxelLOD& lod) : MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getRendererAttribute(), QVector<AttributePointer>(), lod), _deltaTime(deltaTime) {} int SimulateVisitor::visit(MetavoxelInfo& info) { if (!info.isLeaf) { return DEFAULT_ORDER; } static_cast<MetavoxelRenderer*>(info.inputValues.at(0).getInlineValue< SharedObjectPointer>().data())->getImplementation()->simulate(*_data, _deltaTime, info, _lod); return STOP_RECURSION;} void MetavoxelSystem::simulate(float deltaTime) { // update the lod { // the LOD threshold is temporarily tied to the avatar LOD parameter QWriteLocker locker(&_lodLock); const float BASE_LOD_THRESHOLD = 0.01f; _lod = MetavoxelLOD(Application::getInstance()->getCamera()->getPosition(), BASE_LOD_THRESHOLD * Menu::getInstance()->getAvatarLODDistanceMultiplier()); } SimulateVisitor simulateVisitor(deltaTime, getLOD()); guideToAugmented(simulateVisitor);} class RenderVisitor : public MetavoxelVisitor {public: RenderVisitor(const MetavoxelLOD& lod); virtual int visit(MetavoxelInfo& info);}; RenderVisitor::RenderVisitor(const MetavoxelLOD& lod) : MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getRendererAttribute(), QVector<AttributePointer>(), lod) {} int RenderVisitor::visit(MetavoxelInfo& info) { if (!info.isLeaf) { return DEFAULT_ORDER; } static_cast<MetavoxelRenderer*>(info.inputValues.at(0).getInlineValue< SharedObjectPointer>().data())->getImplementation()->render(*_data, info, _lod); return STOP_RECURSION;} void MetavoxelSystem::render() { // update the frustum ViewFrustum* viewFrustum = Application::getInstance()->getDisplayViewFrustum(); _frustum.set(viewFrustum->getFarTopLeft(), viewFrustum->getFarTopRight(), viewFrustum->getFarBottomLeft(), viewFrustum->getFarBottomRight(), viewFrustum->getNearTopLeft(), viewFrustum->getNearTopRight(), viewFrustum->getNearBottomLeft(), viewFrustum->getNearBottomRight()); RenderVisitor renderVisitor(getLOD()); guideToAugmented(renderVisitor, true); // give external parties a chance to join in emit rendering();} class RayHeightfieldIntersectionVisitor : public RayIntersectionVisitor {public: float intersectionDistance; RayHeightfieldIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, const MetavoxelLOD& lod); virtual int visit(MetavoxelInfo& info, float distance);}; RayHeightfieldIntersectionVisitor::RayHeightfieldIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, const MetavoxelLOD& lod) : RayIntersectionVisitor(origin, direction, QVector<AttributePointer>() << Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector<AttributePointer>(), lod), intersectionDistance(FLT_MAX) {} static const int EIGHT_BIT_MAXIMUM = 255;static const float EIGHT_BIT_MAXIMUM_RECIPROCAL = 1.0f / EIGHT_BIT_MAXIMUM; int RayHeightfieldIntersectionVisitor::visit(MetavoxelInfo& info, float distance) { if (!info.isLeaf) { return _order; } const HeightfieldBuffer* buffer = static_cast<HeightfieldBuffer*>( info.inputValues.at(0).getInlineValue<BufferDataPointer>().data()); if (!buffer) { return STOP_RECURSION; } const QByteArray& contents = buffer->getHeight(); const uchar* src = (const uchar*)contents.constData(); int size = glm::sqrt((float)contents.size()); int unextendedSize = size - HeightfieldBuffer::HEIGHT_EXTENSION; int highest = HeightfieldBuffer::HEIGHT_BORDER + unextendedSize; float heightScale = unextendedSize * EIGHT_BIT_MAXIMUM_RECIPROCAL; // find the initial location in heightfield coordinates glm::vec3 entry = (_origin + distance * _direction - info.minimum) * (float)unextendedSize / info.size; entry.x += HeightfieldBuffer::HEIGHT_BORDER; entry.z += HeightfieldBuffer::HEIGHT_BORDER; 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; while (withinBounds) { // find the heights at the corners of the current cell int floorX = qMin(qMax((int)floors.x, HeightfieldBuffer::HEIGHT_BORDER), highest); int floorZ = qMin(qMax((int)floors.z, HeightfieldBuffer::HEIGHT_BORDER), highest); int ceilX = qMin(qMax((int)ceils.x, HeightfieldBuffer::HEIGHT_BORDER), highest); int ceilZ = qMin(qMax((int)ceils.z, HeightfieldBuffer::HEIGHT_BORDER), highest); float upperLeft = src[floorZ * size + floorX] * heightScale; float upperRight = src[floorZ * size + ceilX] * heightScale; float lowerLeft = src[ceilZ * size + floorX] * heightScale; float lowerRight = src[ceilZ * size + 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) { if (_direction.y > 0.0f) { return SHORT_CIRCUIT; // 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 * _direction; withinBounds = (exit.y >= HeightfieldBuffer::HEIGHT_BORDER && exit.y <= highest); if (exitDistance == xDistance) { if (_direction.x > 0.0f) { nextFloors.x += 1.0f; withinBounds &= (nextCeils.x += 1.0f) <= highest; } else { withinBounds &= (nextFloors.x -= 1.0f) >= HeightfieldBuffer::HEIGHT_BORDER; nextCeils.x -= 1.0f; } } if (exitDistance == zDistance) { if (_direction.z > 0.0f) { nextFloors.z += 1.0f; withinBounds &= (nextCeils.z += 1.0f) <= highest; } else { withinBounds &= (nextFloors.z -= 1.0f) >= HeightfieldBuffer::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, _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) { intersectionDistance = qMin(intersectionDistance, distance + (accumulatedDistance + planeDistance) * (info.size / unextendedSize)); return SHORT_CIRCUIT; } } // 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) { intersectionDistance = qMin(intersectionDistance, distance + (accumulatedDistance + planeDistance) * (info.size / unextendedSize)); return SHORT_CIRCUIT; } } // no joy; continue on our way entry = exit; floors = nextFloors; ceils = nextCeils; accumulatedDistance += exitDistance; } return STOP_RECURSION;} bool MetavoxelSystem::findFirstRayHeightfieldIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) { RayHeightfieldIntersectionVisitor visitor(origin, direction, getLOD()); guideToAugmented(visitor); if (visitor.intersectionDistance == FLT_MAX) { return false; } distance = visitor.intersectionDistance; return true;} class HeightfieldHeightVisitor : public MetavoxelVisitor {public: float height; HeightfieldHeightVisitor(const MetavoxelLOD& lod, const glm::vec3& location); virtual int visit(MetavoxelInfo& info); private: glm::vec3 _location;}; HeightfieldHeightVisitor::HeightfieldHeightVisitor(const MetavoxelLOD& lod, const glm::vec3& location) : MetavoxelVisitor(QVector<AttributePointer>() << Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector<AttributePointer>(), lod), height(-FLT_MAX), _location(location) {} static const int REVERSE_ORDER = MetavoxelVisitor::encodeOrder(7, 6, 5, 4, 3, 2, 1, 0); int HeightfieldHeightVisitor::visit(MetavoxelInfo& info) { glm::vec3 relative = _location - info.minimum; if (relative.x < 0.0f || relative.z < 0.0f || relative.x > info.size || relative.z > info.size || height >= info.minimum.y + info.size) { return STOP_RECURSION; } if (!info.isLeaf) { return REVERSE_ORDER; } const HeightfieldBuffer* buffer = static_cast<HeightfieldBuffer*>( info.inputValues.at(0).getInlineValue<BufferDataPointer>().data()); if (!buffer) { return STOP_RECURSION; } const QByteArray& contents = buffer->getHeight(); const uchar* src = (const uchar*)contents.constData(); int size = glm::sqrt((float)contents.size()); int unextendedSize = size - HeightfieldBuffer::HEIGHT_EXTENSION; int highest = HeightfieldBuffer::HEIGHT_BORDER + unextendedSize; relative *= unextendedSize / info.size; relative.x += HeightfieldBuffer::HEIGHT_BORDER; relative.z += HeightfieldBuffer::HEIGHT_BORDER; // find the bounds of the cell containing the point and the shared vertex heights glm::vec3 floors = glm::floor(relative); glm::vec3 ceils = glm::ceil(relative); glm::vec3 fracts = glm::fract(relative); int floorX = qMin(qMax((int)floors.x, HeightfieldBuffer::HEIGHT_BORDER), highest); int floorZ = qMin(qMax((int)floors.z, HeightfieldBuffer::HEIGHT_BORDER), highest); int ceilX = qMin(qMax((int)ceils.x, HeightfieldBuffer::HEIGHT_BORDER), highest); int ceilZ = qMin(qMax((int)ceils.z, HeightfieldBuffer::HEIGHT_BORDER), highest); float upperLeft = src[floorZ * size + floorX]; float lowerRight = src[ceilZ * size + 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 * size + ceilX]; interpolatedHeight = glm::mix(interpolatedHeight, glm::mix(upperRight, lowerRight, fracts.z), (fracts.x - fracts.z) / (1.0f - fracts.z)); } else { float lowerLeft = src[ceilZ * size + floorX]; interpolatedHeight = glm::mix(glm::mix(upperLeft, lowerLeft, fracts.z), interpolatedHeight, fracts.x / fracts.z); } if (interpolatedHeight == 0.0f) { return STOP_RECURSION; // ignore zero values } // convert the interpolated height into world space height = qMax(height, info.minimum.y + interpolatedHeight * info.size * EIGHT_BIT_MAXIMUM_RECIPROCAL); return SHORT_CIRCUIT;} float MetavoxelSystem::getHeightfieldHeight(const glm::vec3& location) { HeightfieldHeightVisitor visitor(getLOD(), location); guideToAugmented(visitor); return visitor.height;} class HeightfieldCursorRenderVisitor : public MetavoxelVisitor {public: HeightfieldCursorRenderVisitor(const Box& bounds); virtual int visit(MetavoxelInfo& info); private: Box _bounds;}; HeightfieldCursorRenderVisitor::HeightfieldCursorRenderVisitor(const Box& bounds) : MetavoxelVisitor(QVector<AttributePointer>() << Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute()), _bounds(bounds) {} int HeightfieldCursorRenderVisitor::visit(MetavoxelInfo& info) { if (!info.getBounds().intersects(_bounds)) { return STOP_RECURSION; } if (!info.isLeaf) { return DEFAULT_ORDER; } BufferDataPointer buffer = info.inputValues.at(0).getInlineValue<BufferDataPointer>(); if (buffer) { buffer->render(true); } return STOP_RECURSION;} void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float radius) { glDepthFunc(GL_LEQUAL); glEnable(GL_CULL_FACE); glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(-1.0f, -1.0f); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().bind(); glActiveTexture(GL_TEXTURE4); float scale = 1.0f / radius; glm::vec4 sCoefficients(scale, 0.0f, 0.0f, -scale * position.x); glm::vec4 tCoefficients(0.0f, 0.0f, scale, -scale * position.z); glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&sCoefficients); glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&tCoefficients); glActiveTexture(GL_TEXTURE0); glm::vec3 extents(radius, radius, radius); HeightfieldCursorRenderVisitor visitor(Box(position - extents, position + extents)); guideToAugmented(visitor); DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().release(); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); glDisable(GL_POLYGON_OFFSET_FILL); glDisable(GL_CULL_FACE); glDepthFunc(GL_LESS);} void MetavoxelSystem::deleteTextures(int heightID, int colorID, int textureID) { glDeleteTextures(1, (GLuint*)&heightID); glDeleteTextures(1, (GLuint*)&colorID); glDeleteTextures(1, (GLuint*)&textureID);} MetavoxelClient* MetavoxelSystem::createClient(const SharedNodePointer& node) { return new MetavoxelSystemClient(node, _updater);} void MetavoxelSystem::guideToAugmented(MetavoxelVisitor& visitor, bool render) { foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { if (node->getType() == NodeType::MetavoxelServer) { QMutexLocker locker(&node->getMutex()); MetavoxelSystemClient* client = static_cast<MetavoxelSystemClient*>(node->getLinkedData()); if (client) { MetavoxelData data = client->getAugmentedData(); data.guide(visitor); if (render) { // save the rendered augmented data so that its cached texture references, etc., don't // get collected when we replace it with more recent versions client->setRenderedAugmentedData(data); } } } }} MetavoxelSystemClient::MetavoxelSystemClient(const SharedNodePointer& node, MetavoxelUpdater* updater) : MetavoxelClient(node, updater) {} void MetavoxelSystemClient::setAugmentedData(const MetavoxelData& data) {Lines 1–500 of 2496. Load more
// // MetavoxelSystem.cpp // interface/src // // Created by Andrzej Kapolka on 12/10/13. // Copyright 2013 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // // include this before QOpenGLFramebufferObject, which includes an earlier version of OpenGL #include "InterfaceConfig.h" #include <QMutexLocker> #include <QOpenGLFramebufferObject> #include <QReadLocker> #include <QWriteLocker> #include <QThreadPool> #include <QtDebug> #include <glm/gtx/transform.hpp> #include <SharedUtil.h> #include <MetavoxelUtil.h> #include <ScriptCache.h> #include "Application.h" #include "MetavoxelSystem.h" #include "renderer/Model.h" #include "renderer/RenderUtil.h" REGISTER_META_OBJECT(DefaultMetavoxelRendererImplementation) REGISTER_META_OBJECT(SphereRenderer) REGISTER_META_OBJECT(StaticModelRenderer) static int bufferPointVectorMetaTypeId = qRegisterMetaType<BufferPointVector>(); void MetavoxelSystem::init() { MetavoxelClientManager::init(); DefaultMetavoxelRendererImplementation::init(); _pointBufferAttribute = AttributeRegistry::getInstance()->registerAttribute(new BufferDataAttribute("pointBuffer")); _heightfieldBufferAttribute = AttributeRegistry::getInstance()->registerAttribute( new BufferDataAttribute("heightfieldBuffer")); _heightfieldBufferAttribute->setLODThresholdMultiplier( AttributeRegistry::getInstance()->getHeightfieldAttribute()->getLODThresholdMultiplier()); _voxelBufferAttribute = AttributeRegistry::getInstance()->registerAttribute( new BufferDataAttribute("voxelBuffer")); _voxelBufferAttribute->setLODThresholdMultiplier( AttributeRegistry::getInstance()->getVoxelColorAttribute()->getLODThresholdMultiplier()); } MetavoxelLOD MetavoxelSystem::getLOD() { QReadLocker locker(&_lodLock); return _lod; } class SimulateVisitor : public MetavoxelVisitor { public: SimulateVisitor(float deltaTime, const MetavoxelLOD& lod); virtual int visit(MetavoxelInfo& info); private: float _deltaTime; }; SimulateVisitor::SimulateVisitor(float deltaTime, const MetavoxelLOD& lod) : MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getRendererAttribute(), QVector<AttributePointer>(), lod), _deltaTime(deltaTime) { } int SimulateVisitor::visit(MetavoxelInfo& info) { if (!info.isLeaf) { return DEFAULT_ORDER; } static_cast<MetavoxelRenderer*>(info.inputValues.at(0).getInlineValue< SharedObjectPointer>().data())->getImplementation()->simulate(*_data, _deltaTime, info, _lod); return STOP_RECURSION; } void MetavoxelSystem::simulate(float deltaTime) { // update the lod { // the LOD threshold is temporarily tied to the avatar LOD parameter QWriteLocker locker(&_lodLock); const float BASE_LOD_THRESHOLD = 0.01f; _lod = MetavoxelLOD(Application::getInstance()->getCamera()->getPosition(), BASE_LOD_THRESHOLD * Menu::getInstance()->getAvatarLODDistanceMultiplier()); } SimulateVisitor simulateVisitor(deltaTime, getLOD()); guideToAugmented(simulateVisitor); } class RenderVisitor : public MetavoxelVisitor { public: RenderVisitor(const MetavoxelLOD& lod); virtual int visit(MetavoxelInfo& info); }; RenderVisitor::RenderVisitor(const MetavoxelLOD& lod) : MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getRendererAttribute(), QVector<AttributePointer>(), lod) { } int RenderVisitor::visit(MetavoxelInfo& info) { if (!info.isLeaf) { return DEFAULT_ORDER; } static_cast<MetavoxelRenderer*>(info.inputValues.at(0).getInlineValue< SharedObjectPointer>().data())->getImplementation()->render(*_data, info, _lod); return STOP_RECURSION; } void MetavoxelSystem::render() { // update the frustum ViewFrustum* viewFrustum = Application::getInstance()->getDisplayViewFrustum(); _frustum.set(viewFrustum->getFarTopLeft(), viewFrustum->getFarTopRight(), viewFrustum->getFarBottomLeft(), viewFrustum->getFarBottomRight(), viewFrustum->getNearTopLeft(), viewFrustum->getNearTopRight(), viewFrustum->getNearBottomLeft(), viewFrustum->getNearBottomRight()); RenderVisitor renderVisitor(getLOD()); guideToAugmented(renderVisitor, true); // give external parties a chance to join in emit rendering(); } class RayHeightfieldIntersectionVisitor : public RayIntersectionVisitor { public: float intersectionDistance; RayHeightfieldIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, const MetavoxelLOD& lod); virtual int visit(MetavoxelInfo& info, float distance); }; RayHeightfieldIntersectionVisitor::RayHeightfieldIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, const MetavoxelLOD& lod) : RayIntersectionVisitor(origin, direction, QVector<AttributePointer>() << Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector<AttributePointer>(), lod), intersectionDistance(FLT_MAX) { } static const int EIGHT_BIT_MAXIMUM = 255; static const float EIGHT_BIT_MAXIMUM_RECIPROCAL = 1.0f / EIGHT_BIT_MAXIMUM; int RayHeightfieldIntersectionVisitor::visit(MetavoxelInfo& info, float distance) { if (!info.isLeaf) { return _order; } const HeightfieldBuffer* buffer = static_cast<HeightfieldBuffer*>( info.inputValues.at(0).getInlineValue<BufferDataPointer>().data()); if (!buffer) { return STOP_RECURSION; } const QByteArray& contents = buffer->getHeight(); const uchar* src = (const uchar*)contents.constData(); int size = glm::sqrt((float)contents.size()); int unextendedSize = size - HeightfieldBuffer::HEIGHT_EXTENSION; int highest = HeightfieldBuffer::HEIGHT_BORDER + unextendedSize; float heightScale = unextendedSize * EIGHT_BIT_MAXIMUM_RECIPROCAL; // find the initial location in heightfield coordinates glm::vec3 entry = (_origin + distance * _direction - info.minimum) * (float)unextendedSize / info.size; entry.x += HeightfieldBuffer::HEIGHT_BORDER; entry.z += HeightfieldBuffer::HEIGHT_BORDER; 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; while (withinBounds) { // find the heights at the corners of the current cell int floorX = qMin(qMax((int)floors.x, HeightfieldBuffer::HEIGHT_BORDER), highest); int floorZ = qMin(qMax((int)floors.z, HeightfieldBuffer::HEIGHT_BORDER), highest); int ceilX = qMin(qMax((int)ceils.x, HeightfieldBuffer::HEIGHT_BORDER), highest); int ceilZ = qMin(qMax((int)ceils.z, HeightfieldBuffer::HEIGHT_BORDER), highest); float upperLeft = src[floorZ * size + floorX] * heightScale; float upperRight = src[floorZ * size + ceilX] * heightScale; float lowerLeft = src[ceilZ * size + floorX] * heightScale; float lowerRight = src[ceilZ * size + 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) { if (_direction.y > 0.0f) { return SHORT_CIRCUIT; // 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 * _direction; withinBounds = (exit.y >= HeightfieldBuffer::HEIGHT_BORDER && exit.y <= highest); if (exitDistance == xDistance) { if (_direction.x > 0.0f) { nextFloors.x += 1.0f; withinBounds &= (nextCeils.x += 1.0f) <= highest; } else { withinBounds &= (nextFloors.x -= 1.0f) >= HeightfieldBuffer::HEIGHT_BORDER; nextCeils.x -= 1.0f; } } if (exitDistance == zDistance) { if (_direction.z > 0.0f) { nextFloors.z += 1.0f; withinBounds &= (nextCeils.z += 1.0f) <= highest; } else { withinBounds &= (nextFloors.z -= 1.0f) >= HeightfieldBuffer::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, _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) { intersectionDistance = qMin(intersectionDistance, distance + (accumulatedDistance + planeDistance) * (info.size / unextendedSize)); return SHORT_CIRCUIT; } } // 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) { intersectionDistance = qMin(intersectionDistance, distance + (accumulatedDistance + planeDistance) * (info.size / unextendedSize)); return SHORT_CIRCUIT; } } // no joy; continue on our way entry = exit; floors = nextFloors; ceils = nextCeils; accumulatedDistance += exitDistance; } return STOP_RECURSION; } bool MetavoxelSystem::findFirstRayHeightfieldIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) { RayHeightfieldIntersectionVisitor visitor(origin, direction, getLOD()); guideToAugmented(visitor); if (visitor.intersectionDistance == FLT_MAX) { return false; } distance = visitor.intersectionDistance; return true; } class HeightfieldHeightVisitor : public MetavoxelVisitor { public: float height; HeightfieldHeightVisitor(const MetavoxelLOD& lod, const glm::vec3& location); virtual int visit(MetavoxelInfo& info); private: glm::vec3 _location; }; HeightfieldHeightVisitor::HeightfieldHeightVisitor(const MetavoxelLOD& lod, const glm::vec3& location) : MetavoxelVisitor(QVector<AttributePointer>() << Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector<AttributePointer>(), lod), height(-FLT_MAX), _location(location) { } static const int REVERSE_ORDER = MetavoxelVisitor::encodeOrder(7, 6, 5, 4, 3, 2, 1, 0); int HeightfieldHeightVisitor::visit(MetavoxelInfo& info) { glm::vec3 relative = _location - info.minimum; if (relative.x < 0.0f || relative.z < 0.0f || relative.x > info.size || relative.z > info.size || height >= info.minimum.y + info.size) { return STOP_RECURSION; } if (!info.isLeaf) { return REVERSE_ORDER; } const HeightfieldBuffer* buffer = static_cast<HeightfieldBuffer*>( info.inputValues.at(0).getInlineValue<BufferDataPointer>().data()); if (!buffer) { return STOP_RECURSION; } const QByteArray& contents = buffer->getHeight(); const uchar* src = (const uchar*)contents.constData(); int size = glm::sqrt((float)contents.size()); int unextendedSize = size - HeightfieldBuffer::HEIGHT_EXTENSION; int highest = HeightfieldBuffer::HEIGHT_BORDER + unextendedSize; relative *= unextendedSize / info.size; relative.x += HeightfieldBuffer::HEIGHT_BORDER; relative.z += HeightfieldBuffer::HEIGHT_BORDER; // find the bounds of the cell containing the point and the shared vertex heights glm::vec3 floors = glm::floor(relative); glm::vec3 ceils = glm::ceil(relative); glm::vec3 fracts = glm::fract(relative); int floorX = qMin(qMax((int)floors.x, HeightfieldBuffer::HEIGHT_BORDER), highest); int floorZ = qMin(qMax((int)floors.z, HeightfieldBuffer::HEIGHT_BORDER), highest); int ceilX = qMin(qMax((int)ceils.x, HeightfieldBuffer::HEIGHT_BORDER), highest); int ceilZ = qMin(qMax((int)ceils.z, HeightfieldBuffer::HEIGHT_BORDER), highest); float upperLeft = src[floorZ * size + floorX]; float lowerRight = src[ceilZ * size + 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 * size + ceilX]; interpolatedHeight = glm::mix(interpolatedHeight, glm::mix(upperRight, lowerRight, fracts.z), (fracts.x - fracts.z) / (1.0f - fracts.z)); } else { float lowerLeft = src[ceilZ * size + floorX]; interpolatedHeight = glm::mix(glm::mix(upperLeft, lowerLeft, fracts.z), interpolatedHeight, fracts.x / fracts.z); } if (interpolatedHeight == 0.0f) { return STOP_RECURSION; // ignore zero values } // convert the interpolated height into world space height = qMax(height, info.minimum.y + interpolatedHeight * info.size * EIGHT_BIT_MAXIMUM_RECIPROCAL); return SHORT_CIRCUIT; } float MetavoxelSystem::getHeightfieldHeight(const glm::vec3& location) { HeightfieldHeightVisitor visitor(getLOD(), location); guideToAugmented(visitor); return visitor.height; } class HeightfieldCursorRenderVisitor : public MetavoxelVisitor { public: HeightfieldCursorRenderVisitor(const Box& bounds); virtual int visit(MetavoxelInfo& info); private: Box _bounds; }; HeightfieldCursorRenderVisitor::HeightfieldCursorRenderVisitor(const Box& bounds) : MetavoxelVisitor(QVector<AttributePointer>() << Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute()), _bounds(bounds) { } int HeightfieldCursorRenderVisitor::visit(MetavoxelInfo& info) { if (!info.getBounds().intersects(_bounds)) { return STOP_RECURSION; } if (!info.isLeaf) { return DEFAULT_ORDER; } BufferDataPointer buffer = info.inputValues.at(0).getInlineValue<BufferDataPointer>(); if (buffer) { buffer->render(true); } return STOP_RECURSION; } void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float radius) { glDepthFunc(GL_LEQUAL); glEnable(GL_CULL_FACE); glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(-1.0f, -1.0f); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().bind(); glActiveTexture(GL_TEXTURE4); float scale = 1.0f / radius; glm::vec4 sCoefficients(scale, 0.0f, 0.0f, -scale * position.x); glm::vec4 tCoefficients(0.0f, 0.0f, scale, -scale * position.z); glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&sCoefficients); glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&tCoefficients); glActiveTexture(GL_TEXTURE0); glm::vec3 extents(radius, radius, radius); HeightfieldCursorRenderVisitor visitor(Box(position - extents, position + extents)); guideToAugmented(visitor); DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().release(); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); glDisable(GL_POLYGON_OFFSET_FILL); glDisable(GL_CULL_FACE); glDepthFunc(GL_LESS); } void MetavoxelSystem::deleteTextures(int heightID, int colorID, int textureID) { glDeleteTextures(1, (GLuint*)&heightID); glDeleteTextures(1, (GLuint*)&colorID); glDeleteTextures(1, (GLuint*)&textureID); } MetavoxelClient* MetavoxelSystem::createClient(const SharedNodePointer& node) { return new MetavoxelSystemClient(node, _updater); } void MetavoxelSystem::guideToAugmented(MetavoxelVisitor& visitor, bool render) { foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { if (node->getType() == NodeType::MetavoxelServer) { QMutexLocker locker(&node->getMutex()); MetavoxelSystemClient* client = static_cast<MetavoxelSystemClient*>(node->getLinkedData()); if (client) { MetavoxelData data = client->getAugmentedData(); data.guide(visitor); if (render) { // save the rendered augmented data so that its cached texture references, etc., don't // get collected when we replace it with more recent versions client->setRenderedAugmentedData(data); } } } } } MetavoxelSystemClient::MetavoxelSystemClient(const SharedNodePointer& node, MetavoxelUpdater* updater) : MetavoxelClient(node, updater) { } void MetavoxelSystemClient::setAugmentedData(const MetavoxelData& data) {