2419 lines · 105.2 KB · C++
RawDownload
////  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 2419.