1698 lines · 70.2 KB · C++
RawDownload
////  GeometryCache.cpp//  interface/src/renderer////  Created by Andrzej Kapolka on 6/21/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 "GeometryCache.h" #include <cmath> #include <QNetworkReply>#include <QThreadPool> #include <FSTReader.h>#include <NumericalConstants.h>#include <shared/Shapes.h> #include "TextureCache.h"#include "RenderUtilsLogging.h" #include "gpu/StandardShaderLib.h" #include "model/TextureMap.h" #include "standardTransformPNTC_vert.h"#include "standardDrawTexture_frag.h" #include "simple_vert.h"#include "simple_textured_frag.h"#include "simple_textured_unlit_frag.h" #include "grid_frag.h" //#define WANT_DEBUG const int GeometryCache::UNKNOWN_ID = -1;  static const int VERTICES_PER_TRIANGLE = 3; static const gpu::Element POSITION_ELEMENT{ gpu::VEC3, gpu::FLOAT, gpu::XYZ };static const gpu::Element NORMAL_ELEMENT{ gpu::VEC3, gpu::FLOAT, gpu::XYZ };static const gpu::Element COLOR_ELEMENT{ gpu::VEC4, gpu::NUINT8, gpu::RGBA }; static gpu::Stream::FormatPointer SOLID_STREAM_FORMAT;static gpu::Stream::FormatPointer INSTANCED_SOLID_STREAM_FORMAT; static const uint SHAPE_VERTEX_STRIDE = sizeof(glm::vec3) * 2; // vertices and normalsstatic const uint SHAPE_NORMALS_OFFSET = sizeof(glm::vec3);static const gpu::Type SHAPE_INDEX_TYPE = gpu::UINT32;static const uint SHAPE_INDEX_SIZE = sizeof(gpu::uint32); void GeometryCache::ShapeData::setupVertices(gpu::BufferPointer& vertexBuffer, const geometry::VertexVector& vertices) {    vertexBuffer->append(vertices);     _positionView = gpu::BufferView(vertexBuffer, 0,        vertexBuffer->getSize(), SHAPE_VERTEX_STRIDE, POSITION_ELEMENT);    _normalView = gpu::BufferView(vertexBuffer, SHAPE_NORMALS_OFFSET,        vertexBuffer->getSize(), SHAPE_VERTEX_STRIDE, NORMAL_ELEMENT);} void GeometryCache::ShapeData::setupIndices(gpu::BufferPointer& indexBuffer, const geometry::IndexVector& indices, const geometry::IndexVector& wireIndices) {    _indices = indexBuffer;    if (!indices.empty()) {        _indexOffset = indexBuffer->getSize() / SHAPE_INDEX_SIZE;        _indexCount = indices.size();        indexBuffer->append(indices);    }     if (!wireIndices.empty()) {        _wireIndexOffset = indexBuffer->getSize() / SHAPE_INDEX_SIZE;        _wireIndexCount = wireIndices.size();        indexBuffer->append(wireIndices);    }} void GeometryCache::ShapeData::setupBatch(gpu::Batch& batch) const {    batch.setInputBuffer(gpu::Stream::POSITION, _positionView);    batch.setInputBuffer(gpu::Stream::NORMAL, _normalView);    batch.setIndexBuffer(SHAPE_INDEX_TYPE, _indices, 0);} void GeometryCache::ShapeData::draw(gpu::Batch& batch) const {    if (_indexCount) {        setupBatch(batch);        batch.drawIndexed(gpu::TRIANGLES, (gpu::uint32)_indexCount, (gpu::uint32)_indexOffset);    }} void GeometryCache::ShapeData::drawWire(gpu::Batch& batch) const {    if (_wireIndexCount) {        setupBatch(batch);        batch.drawIndexed(gpu::LINES, (gpu::uint32)_wireIndexCount, (gpu::uint32)_wireIndexOffset);    }} void GeometryCache::ShapeData::drawInstances(gpu::Batch& batch, size_t count) const {    if (_indexCount) {        setupBatch(batch);        batch.drawIndexedInstanced((gpu::uint32)count, gpu::TRIANGLES, (gpu::uint32)_indexCount, (gpu::uint32)_indexOffset);    }} void GeometryCache::ShapeData::drawWireInstances(gpu::Batch& batch, size_t count) const {    if (_wireIndexCount) {        setupBatch(batch);        batch.drawIndexedInstanced((gpu::uint32)count, gpu::LINES, (gpu::uint32)_wireIndexCount, (gpu::uint32)_wireIndexOffset);    }} static const size_t ICOSAHEDRON_TO_SPHERE_TESSELATION_COUNT = 3; size_t GeometryCache::getShapeTriangleCount(Shape shape) {    return _shapes[shape]._indexCount / VERTICES_PER_TRIANGLE;} size_t GeometryCache::getSphereTriangleCount() {    return getShapeTriangleCount(Sphere);} size_t GeometryCache::getCubeTriangleCount() {    return getShapeTriangleCount(Cube);} using IndexPair = uint64_t;using IndexPairs = std::unordered_set<IndexPair>; static IndexPair indexToken(geometry::Index a, geometry::Index b) {    if (a > b) {        std::swap(a, b);    }    return (((IndexPair)a) << 32) | ((IndexPair)b);} template <size_t N>void setupFlatShape(GeometryCache::ShapeData& shapeData, const geometry::Solid<N>& shape, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer) {    using namespace geometry;    Index baseVertex = (Index)(vertexBuffer->getSize() / SHAPE_VERTEX_STRIDE);    VertexVector vertices;    IndexVector solidIndices, wireIndices;    IndexPairs wireSeenIndices;     size_t faceCount = shape.faces.size();    size_t faceIndexCount = triangulatedFaceIndexCount<N>();     vertices.reserve(N * faceCount * 2);    solidIndices.reserve(faceIndexCount * faceCount);     for (size_t f = 0; f < faceCount; ++f) {        const Face<N>& face = shape.faces[f];        // Compute the face normal        vec3 faceNormal = shape.getFaceNormal(f);         // Create the vertices for the face        for (Index i = 0; i < N; ++i) {            Index originalIndex = face[i];            vertices.push_back(shape.vertices[originalIndex]);            vertices.push_back(faceNormal);        }         // Create the wire indices for unseen edges        for (Index i = 0; i < N; ++i) {            Index a = i;            Index b = (i + 1) % N;            auto token = indexToken(face[a], face[b]);            if (0 == wireSeenIndices.count(token)) {                wireSeenIndices.insert(token);                wireIndices.push_back(a + baseVertex);                wireIndices.push_back(b + baseVertex);            }        }         // Create the solid face indices        for (Index i = 0; i < N - 2; ++i) {            solidIndices.push_back(0 + baseVertex);            solidIndices.push_back(i + 1 + baseVertex);            solidIndices.push_back(i + 2 + baseVertex);        }        baseVertex += (Index)N;    }     shapeData.setupVertices(vertexBuffer, vertices);    shapeData.setupIndices(indexBuffer, solidIndices, wireIndices);} template <size_t N>void setupSmoothShape(GeometryCache::ShapeData& shapeData, const geometry::Solid<N>& shape, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer) {    using namespace geometry;     Index baseVertex = (Index)(vertexBuffer->getSize() / SHAPE_VERTEX_STRIDE);     VertexVector vertices;    vertices.reserve(shape.vertices.size() * 2);    for (const auto& vertex : shape.vertices) {        vertices.push_back(vertex);        vertices.push_back(vertex);    }     IndexVector solidIndices, wireIndices;    IndexPairs wireSeenIndices;     size_t faceCount = shape.faces.size();    size_t faceIndexCount = triangulatedFaceIndexCount<N>();     solidIndices.reserve(faceIndexCount * faceCount);     for (size_t f = 0; f < faceCount; ++f) {        const Face<N>& face = shape.faces[f];        // Create the wire indices for unseen edges        for (Index i = 0; i < N; ++i) {            Index a = face[i];            Index b = face[(i + 1) % N];            auto token = indexToken(a, b);            if (0 == wireSeenIndices.count(token)) {                wireSeenIndices.insert(token);                wireIndices.push_back(a + baseVertex);                wireIndices.push_back(b + baseVertex);            }        }         // Create the solid face indices        for (Index i = 0; i < N - 2; ++i) {            solidIndices.push_back(face[i] + baseVertex);            solidIndices.push_back(face[i + 1] + baseVertex);            solidIndices.push_back(face[i + 2] + baseVertex);        }    }     shapeData.setupVertices(vertexBuffer, vertices);    shapeData.setupIndices(indexBuffer, solidIndices, wireIndices);}  // FIXME solids need per-face vertices, but smooth shaded// components do not.  Find a way to support using draw elements// or draw arrays as appropriate// Maybe special case cone and cylinder since they combine flat// and smooth shadingvoid GeometryCache::buildShapes() {    using namespace geometry;    auto vertexBuffer = std::make_shared<gpu::Buffer>();    auto indexBuffer = std::make_shared<gpu::Buffer>();    // Cube     setupFlatShape(_shapes[Cube], geometry::cube(), _shapeVertices, _shapeIndices);    // Tetrahedron    setupFlatShape(_shapes[Tetrahedron], geometry::tetrahedron(), _shapeVertices, _shapeIndices);    // Icosahedron    setupFlatShape(_shapes[Icosahedron], geometry::icosahedron(), _shapeVertices, _shapeIndices);    // Octahedron    setupFlatShape(_shapes[Octahedron], geometry::octahedron(), _shapeVertices, _shapeIndices);    // Dodecahedron    setupFlatShape(_shapes[Dodecahedron], geometry::dodecahedron(), _shapeVertices, _shapeIndices);        // Sphere    // FIXME this uses way more vertices than required.  Should find a way to calculate the indices    // using shared vertices for better vertex caching    auto sphere = geometry::tesselate(geometry::icosahedron(), ICOSAHEDRON_TO_SPHERE_TESSELATION_COUNT);    sphere.fitDimension(1.0f);    setupSmoothShape(_shapes[Sphere], sphere, _shapeVertices, _shapeIndices);     // Line    {        Index baseVertex = (Index)(_shapeVertices->getSize() / SHAPE_VERTEX_STRIDE);        ShapeData& shapeData = _shapes[Line];        shapeData.setupVertices(_shapeVertices, VertexVector{            vec3(-0.5, 0, 0), vec3(-0.5f, 0, 0),            vec3(0.5f, 0, 0), vec3(0.5f, 0, 0)        });        IndexVector wireIndices;        // Only two indices        wireIndices.push_back(0 + baseVertex);        wireIndices.push_back(1 + baseVertex);        shapeData.setupIndices(_shapeIndices, IndexVector(), wireIndices);    }     // Not implememented yet:     //Triangle,    //Quad,    //Circle,    //Octahetron,    //Dodecahedron,    //Torus,    //Cone,    //Cylinder,} gpu::Stream::FormatPointer& getSolidStreamFormat() {    if (!SOLID_STREAM_FORMAT) {        SOLID_STREAM_FORMAT = std::make_shared<gpu::Stream::Format>(); // 1 for everyone        SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT);        SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT);    }    return SOLID_STREAM_FORMAT;} gpu::Stream::FormatPointer& getInstancedSolidStreamFormat() {    if (!INSTANCED_SOLID_STREAM_FORMAT) {        INSTANCED_SOLID_STREAM_FORMAT = std::make_shared<gpu::Stream::Format>(); // 1 for everyone        INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT);        INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT);        INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE);    }    return INSTANCED_SOLID_STREAM_FORMAT;} render::ShapePipelinePointer GeometryCache::_simplePipeline; GeometryCache::GeometryCache() :    _nextID(0){    buildShapes();    GeometryCache::_simplePipeline =        std::make_shared<render::ShapePipeline>(getSimplePipeline(), nullptr,            [](const render::ShapePipeline&, gpu::Batch& batch) {                // Set the defaults needed for a simple program                batch.setResourceTexture(render::ShapePipeline::Slot::MAP::ALBEDO,                    DependencyManager::get<TextureCache>()->getWhiteTexture());                batch.setResourceTexture(render::ShapePipeline::Slot::MAP::NORMAL_FITTING,                    DependencyManager::get<TextureCache>()->getNormalFittingTexture());            }        );} GeometryCache::~GeometryCache() {    #ifdef WANT_DEBUG        qCDebug(renderutils) << "GeometryCache::~GeometryCache()... ";        qCDebug(renderutils) << "    _registeredLine3DVBOs.size():" << _registeredLine3DVBOs.size();        qCDebug(renderutils) << "    _line3DVBOs.size():" << _line3DVBOs.size();        qCDebug(renderutils) << "    BatchItemDetails... population:" << GeometryCache::BatchItemDetails::population;    #endif //def WANT_DEBUG} void setupBatchInstance(gpu::Batch& batch, gpu::BufferPointer colorBuffer) {    gpu::BufferView colorView(colorBuffer, COLOR_ELEMENT);    batch.setInputBuffer(gpu::Stream::COLOR, colorView);} void GeometryCache::renderShape(gpu::Batch& batch, Shape shape) {    batch.setInputFormat(getSolidStreamFormat());    _shapes[shape].draw(batch);} void GeometryCache::renderWireShape(gpu::Batch& batch, Shape shape) {    batch.setInputFormat(getSolidStreamFormat());    _shapes[shape].drawWire(batch);} void GeometryCache::renderShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer) {    batch.setInputFormat(getInstancedSolidStreamFormat());    setupBatchInstance(batch, colorBuffer);    _shapes[shape].drawInstances(batch, count);} void GeometryCache::renderWireShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer) {    batch.setInputFormat(getInstancedSolidStreamFormat());    setupBatchInstance(batch, colorBuffer);    _shapes[shape].drawWireInstances(batch, count);} void GeometryCache::renderCube(gpu::Batch& batch) {    renderShape(batch, Cube);} void GeometryCache::renderWireCube(gpu::Batch& batch) {    renderWireShape(batch, Cube);} void GeometryCache::renderSphere(gpu::Batch& batch) {    renderShape(batch, Sphere);} void GeometryCache::renderWireSphere(gpu::Batch& batch) {    renderWireShape(batch, Sphere);} void GeometryCache::renderGrid(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner,                            int majorRows, int majorCols, float majorEdge,                            int minorRows, int minorCols, float minorEdge,                            const glm::vec4& color, bool isLayered, int id) {    static const glm::vec2 MIN_TEX_COORD(0.0f, 0.0f);    static const glm::vec2 MAX_TEX_COORD(1.0f, 1.0f);     bool registered = (id != UNKNOWN_ID);    Vec2FloatPair majorKey(glm::vec2(majorRows, majorCols), majorEdge);    Vec2FloatPair minorKey(glm::vec2(minorRows, minorCols), minorEdge);    Vec2FloatPairPair key(majorKey, minorKey);     // Make the gridbuffer    if ((registered && (!_registeredGridBuffers.contains(id) || _lastRegisteredGridBuffer[id] != key)) ||        (!registered && !_gridBuffers.contains(key))) {        GridSchema gridSchema;        GridBuffer gridBuffer = std::make_shared<gpu::Buffer>(sizeof(GridSchema), (const gpu::Byte*) &gridSchema);         if (registered && _registeredGridBuffers.contains(id)) {            gridBuffer = _registeredGridBuffers[id];        }         if (registered) {            _registeredGridBuffers[id] = gridBuffer;            _lastRegisteredGridBuffer[id] = key;        } else {            _gridBuffers[key] = gridBuffer;        }         gridBuffer.edit<GridSchema>().period = glm::vec4(majorRows, majorCols, minorRows, minorCols);        gridBuffer.edit<GridSchema>().offset.x = -(majorEdge / majorRows) / 2;        gridBuffer.edit<GridSchema>().offset.y = -(majorEdge / majorCols) / 2;        gridBuffer.edit<GridSchema>().offset.z = -(minorEdge / minorRows) / 2;        gridBuffer.edit<GridSchema>().offset.w = -(minorEdge / minorCols) / 2;        gridBuffer.edit<GridSchema>().edge = glm::vec4(glm::vec2(majorEdge),            // If rows or columns are not set, do not draw minor gridlines            glm::vec2((minorRows != 0 && minorCols != 0) ? minorEdge : 0.0f));    }     // Set the grid pipeline    useGridPipeline(batch, registered ? _registeredGridBuffers[id] : _gridBuffers[key], isLayered);     renderQuad(batch, minCorner, maxCorner, MIN_TEX_COORD, MAX_TEX_COORD, color, id);} void GeometryCache::updateVertices(int id, const QVector<glm::vec2>& points, const glm::vec4& color) {    BatchItemDetails& details = _registeredVertices[id];     if (details.isCreated) {        details.clear();        #ifdef WANT_DEBUG            qCDebug(renderutils) << "updateVertices()... RELEASING REGISTERED";        #endif // def WANT_DEBUG    }     const int FLOATS_PER_VERTEX = 2 + 3; // vertices + normals    const int NUM_POS_COORDS = 2;    const int VERTEX_NORMAL_OFFSET = NUM_POS_COORDS * sizeof(float);    details.isCreated = true;    details.vertices = points.size();    details.vertexSize = FLOATS_PER_VERTEX;        auto verticesBuffer = std::make_shared<gpu::Buffer>();    auto colorBuffer = std::make_shared<gpu::Buffer>();    auto streamFormat = std::make_shared<gpu::Stream::Format>();    auto stream = std::make_shared<gpu::BufferStream>();     details.verticesBuffer = verticesBuffer;    details.colorBuffer = colorBuffer;    details.streamFormat = streamFormat;    details.stream = stream;     details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ), 0);    details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET);    details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA));     details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride);    details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride);     details.vertices = points.size();    details.vertexSize = FLOATS_PER_VERTEX;     int compactColor = ((int(color.x * 255.0f) & 0xFF)) |                        ((int(color.y * 255.0f) & 0xFF) << 8) |                        ((int(color.z * 255.0f) & 0xFF) << 16) |                        ((int(color.w * 255.0f) & 0xFF) << 24);     float* vertexData = new float[details.vertices * FLOATS_PER_VERTEX];    float* vertex = vertexData;     int* colorData = new int[details.vertices];    int* colorDataAt = colorData;     const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f);    foreach (const glm::vec2& point, points) {        *(vertex++) = point.x;        *(vertex++) = point.y;        *(vertex++) = NORMAL.x;        *(vertex++) = NORMAL.y;        *(vertex++) = NORMAL.z;                *(colorDataAt++) = compactColor;    }     details.verticesBuffer->append(sizeof(float) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*) vertexData);    details.colorBuffer->append(sizeof(int) * details.vertices, (gpu::Byte*) colorData);    delete[] vertexData;    delete[] colorData;     #ifdef WANT_DEBUG        qCDebug(renderutils) << "new registered linestrip buffer made -- _registeredVertices.size():" << _registeredVertices.size();    #endif} void GeometryCache::updateVertices(int id, const QVector<glm::vec3>& points, const glm::vec4& color) {    BatchItemDetails& details = _registeredVertices[id];    if (details.isCreated) {        details.clear();        #ifdef WANT_DEBUG            qCDebug(renderutils) << "updateVertices()... RELEASING REGISTERED";

Lines 1–500 of 1698.