Andrzej KapolkaBasic metavoxels example.13efaba12 years agoHistory
2867 lines · 125.1 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 <MetavoxelMessages.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(CuboidRenderer)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();} void MetavoxelSystem::refreshVoxelData() {    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) {                QMetaObject::invokeMethod(client, "refreshVoxelData");            }        }    }} 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 RayVoxelIntersectionVisitor : public RayIntersectionVisitor {public:        float intersectionDistance;        RayVoxelIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, const MetavoxelLOD& lod);        virtual int visit(MetavoxelInfo& info, float distance);}; RayVoxelIntersectionVisitor::RayVoxelIntersectionVisitor(const glm::vec3& origin,        const glm::vec3& direction, const MetavoxelLOD& lod) :    RayIntersectionVisitor(origin, direction, QVector<AttributePointer>() <<        Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(), QVector<AttributePointer>(), lod),    intersectionDistance(FLT_MAX) {} int RayVoxelIntersectionVisitor::visit(MetavoxelInfo& info, float distance) {    if (!info.isLeaf) {        return _order;    }    const VoxelBuffer* buffer = static_cast<VoxelBuffer*>(        info.inputValues.at(0).getInlineValue<BufferDataPointer>().data());    if (!buffer) {        return STOP_RECURSION;    }    glm::vec3 entry = ((_origin + distance * _direction) - info.minimum) / info.size;    if (buffer->findFirstRayIntersection(entry, _origin, _direction, intersectionDistance)) {        return SHORT_CIRCUIT;    }    return STOP_RECURSION;} bool MetavoxelSystem::findFirstRayVoxelIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) {    RayVoxelIntersectionVisitor 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;} void MetavoxelSystem::paintHeightfieldColor(const glm::vec3& position, float radius, const QColor& color) {    MetavoxelEditMessage edit = { QVariant::fromValue(PaintHeightfieldMaterialEdit(position, radius, SharedObjectPointer(), color)) };    applyEdit(edit, true);} void MetavoxelSystem::paintHeightfieldMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material) {    MetavoxelEditMessage edit = { QVariant::fromValue(PaintHeightfieldMaterialEdit(position, radius, material)) };    applyMaterialEdit(edit, true);} void MetavoxelSystem::paintVoxelColor(const glm::vec3& position, float radius, const QColor& color) {    MetavoxelEditMessage edit = { QVariant::fromValue(PaintVoxelMaterialEdit(position, radius, SharedObjectPointer(), color)) };    applyEdit(edit, true);} void MetavoxelSystem::paintVoxelMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material) {    MetavoxelEditMessage edit = { QVariant::fromValue(PaintVoxelMaterialEdit(position, radius, material)) };    applyMaterialEdit(edit, true);} void MetavoxelSystem::setVoxelColor(const SharedObjectPointer& spanner, const QColor& color) {    MetavoxelEditMessage edit = { QVariant::fromValue(VoxelMaterialSpannerEdit(spanner, SharedObjectPointer(), color)) };    applyEdit(edit, true);} void MetavoxelSystem::setVoxelMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material) {    MetavoxelEditMessage edit = { QVariant::fromValue(VoxelMaterialSpannerEdit(spanner, material)) };    applyMaterialEdit(edit, true);} class CursorRenderVisitor : public MetavoxelVisitor {public:        CursorRenderVisitor(const AttributePointer& attribute, const Box& bounds);        virtual int visit(MetavoxelInfo& info); private:        Box _bounds;}; CursorRenderVisitor::CursorRenderVisitor(const AttributePointer& attribute, const Box& bounds) :

Lines 1–500 of 2867.