2491 lines · 106.5 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 <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());        loadLightProgram("shaders/directional_light.frag", _directionalLight, _directionalLightLocations);    loadLightProgram("shaders/directional_light_shadow_map.frag", _directionalLightShadowMap,        _directionalLightShadowMapLocations);    loadLightProgram("shaders/directional_light_cascaded_shadow_map.frag", _directionalLightCascadedShadowMap,        _directionalLightCascadedShadowMapLocations);} 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;} const GLenum COLOR_DRAW_BUFFERS[] = { GL_COLOR_ATTACHMENT0 };const GLenum NORMAL_DRAW_BUFFERS[] = { GL_COLOR_ATTACHMENT1 };const GLenum COLOR_NORMAL_DRAW_BUFFERS[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }; void MetavoxelSystem::render() {    // update the frustum    ViewFrustum* viewFrustum = Application::getInstance()->getViewFrustum();    _frustum.set(viewFrustum->getFarTopLeft(), viewFrustum->getFarTopRight(), viewFrustum->getFarBottomLeft(),        viewFrustum->getFarBottomRight(), viewFrustum->getNearTopLeft(), viewFrustum->getNearTopRight(),        viewFrustum->getNearBottomLeft(), viewFrustum->getNearBottomRight());        _needToLight = false;     // clear the normal buffer    glDrawBuffers(sizeof(NORMAL_DRAW_BUFFERS) / sizeof(NORMAL_DRAW_BUFFERS[0]), NORMAL_DRAW_BUFFERS);    glClear(GL_COLOR_BUFFER_BIT);     glDrawBuffers(sizeof(COLOR_DRAW_BUFFERS) / sizeof(COLOR_DRAW_BUFFERS[0]), COLOR_DRAW_BUFFERS);        RenderVisitor renderVisitor(getLOD());    guideToAugmented(renderVisitor, true);        // give external parties a chance to join in    emit rendering();        if (!_needToLight) {        return; // skip lighting if not needed    }        // perform deferred lighting, rendering to free fbo    glPushMatrix();    glLoadIdentity();        glMatrixMode(GL_PROJECTION);    glPushMatrix();    glLoadIdentity();        glDisable(GL_BLEND);    glDisable(GL_LIGHTING);    glDisable(GL_DEPTH_TEST);    glDepthMask(false);        QOpenGLFramebufferObject* primaryFBO = Application::getInstance()->getTextureCache()->getPrimaryFramebufferObject();    primaryFBO->release();        QOpenGLFramebufferObject* freeFBO = Application::getInstance()->getGlowEffect()->getFreeFramebufferObject();    freeFBO->bind();    glClear(GL_COLOR_BUFFER_BIT);        glBindTexture(GL_TEXTURE_2D, primaryFBO->texture());        glActiveTexture(GL_TEXTURE1);    glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getPrimaryNormalTextureID());        if (Menu::getInstance()->getShadowsEnabled()) {        glActiveTexture(GL_TEXTURE2);        glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getPrimaryDepthTextureID());                glActiveTexture(GL_TEXTURE3);        glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getShadowDepthTextureID());                ProgramObject* program = &_directionalLightShadowMap;        const LightLocations* locations = &_directionalLightShadowMapLocations;        if (Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows)) {            program = &_directionalLightCascadedShadowMap;            locations = &_directionalLightCascadedShadowMapLocations;            _directionalLightCascadedShadowMap.bind();            _directionalLightCascadedShadowMap.setUniform(locations->shadowDistances,                Application::getInstance()->getShadowDistances());                } else {            program->bind();        }        program->setUniformValue(locations->shadowScale,            1.0f / Application::getInstance()->getTextureCache()->getShadowFramebufferObject()->width());                float left, right, bottom, top, nearVal, farVal;        glm::vec4 nearClipPlane, farClipPlane;        Application::getInstance()->computeOffAxisFrustum(            left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane);        program->setUniformValue(locations->nearLocation, nearVal);        program->setUniformValue(locations->depthScale, (farVal - nearVal) / farVal);        float nearScale = -1.0f / nearVal;        program->setUniformValue(locations->depthTexCoordOffset, left * nearScale, bottom * nearScale);        program->setUniformValue(locations->depthTexCoordScale, (right - left) * nearScale, (top - bottom) * nearScale);                renderFullscreenQuad();                program->release();                glBindTexture(GL_TEXTURE_2D, 0);                glActiveTexture(GL_TEXTURE2);        glBindTexture(GL_TEXTURE_2D, 0);                glActiveTexture(GL_TEXTURE1);        } else {        _directionalLight.bind();        renderFullscreenQuad();        _directionalLight.release();            }        glBindTexture(GL_TEXTURE_2D, 0);    glActiveTexture(GL_TEXTURE0);        freeFBO->release();        // now transfer the lit region to the primary fbo    glEnable(GL_BLEND);        primaryFBO->bind();        glBindTexture(GL_TEXTURE_2D, freeFBO->texture());    glEnable(GL_TEXTURE_2D);        glColor4f(1.0f, 1.0f, 1.0f, 1.0f);        renderFullscreenQuad();        glBindTexture(GL_TEXTURE_2D, 0);    glDisable(GL_TEXTURE_2D);        glEnable(GL_LIGHTING);    glEnable(GL_DEPTH_TEST);    glDepthMask(true);        glDisable(GL_ALPHA_TEST);        glPopMatrix();        glMatrixMode(GL_MODELVIEW);    glPopMatrix();} 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);

Lines 1–500 of 2491.