//// 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 <gpu/GLBackend.h> #include "TextureCache.h"#include "RenderUtilsLogging.h" #include "standardTransformPNTC_vert.h"#include "standardDrawTexture_frag.h" #include "gpu/StandardShaderLib.h" //#define WANT_DEBUG const int GeometryCache::UNKNOWN_ID = -1; static const uint FLOATS_PER_VERTEX = 3;static const uint VERTICES_PER_TRIANGLE = 3;static const uint TRIANGLES_PER_QUAD = 2;static const uint CUBE_FACES = 6;static const uint CUBE_VERTICES_PER_FACE = 4;static const uint CUBE_VERTICES = CUBE_FACES * CUBE_VERTICES_PER_FACE;static const uint CUBE_VERTEX_POINTS = CUBE_VERTICES * FLOATS_PER_VERTEX;static const uint CUBE_INDICES = CUBE_FACES * TRIANGLES_PER_QUAD * VERTICES_PER_TRIANGLE;static const uint SPHERE_LATITUDES = 24;static const uint SPHERE_MERIDIANS = SPHERE_LATITUDES * 2;static const uint SPHERE_INDICES = SPHERE_MERIDIANS * (SPHERE_LATITUDES - 1) * TRIANGLES_PER_QUAD * VERTICES_PER_TRIANGLE; 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 const gpu::Element TRANSFORM_ELEMENT{ gpu::MAT4, gpu::FLOAT, gpu::XYZW }; 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); void GeometryCache::ShapeData::setupVertices(gpu::BufferPointer& vertexBuffer, const VVertex& 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 VIndex& indices, const VIndex& wireIndices) { _indices = indexBuffer; if (!indices.empty()) { _indexOffset = indexBuffer->getSize(); _indexCount = indices.size(); indexBuffer->append(indices); } if (!wireIndices.empty()) { _wireIndexOffset = indexBuffer->getSize(); _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);} void GeometryCache::ShapeData::draw(gpu::Batch& batch) const { if (_indexCount) { setupBatch(batch); batch.setIndexBuffer(gpu::UINT16, _indices, _indexOffset); batch.drawIndexed(gpu::TRIANGLES, _indexCount); }} void GeometryCache::ShapeData::drawWire(gpu::Batch& batch) const { if (_wireIndexCount) { setupBatch(batch); batch.setIndexBuffer(gpu::UINT16, _indices, _wireIndexOffset); batch.drawIndexed(gpu::LINES, _wireIndexCount); }} void GeometryCache::ShapeData::drawInstances(gpu::Batch& batch, size_t count) const { if (_indexCount) { setupBatch(batch); batch.setIndexBuffer(gpu::UINT16, _indices, _indexOffset); batch.drawIndexedInstanced(count, gpu::TRIANGLES, _indexCount); }} void GeometryCache::ShapeData::drawWireInstances(gpu::Batch& batch, size_t count) const { if (_wireIndexCount) { setupBatch(batch); batch.setIndexBuffer(gpu::UINT16, _indices, _wireIndexOffset); batch.drawIndexedInstanced(count, gpu::LINES, _wireIndexCount); }} const VVertex& icosahedronVertices() { static const float phi = (1.0 + sqrt(5.0)) / 2.0; static const float a = 0.5; static const float b = 1.0 / (2.0 * phi); static const VVertex vertices{ // vec3(0, b, -a), vec3(-b, a, 0), vec3(b, a, 0), // vec3(0, b, a), vec3(b, a, 0), vec3(-b, a, 0), // vec3(0, b, a), vec3(-a, 0, b), vec3(0, -b, a), // vec3(0, b, a), vec3(0, -b, a), vec3(a, 0, b), // vec3(0, b, -a), vec3(a, 0, -b), vec3(0, -b, -a),// vec3(0, b, -a), vec3(0, -b, -a), vec3(-a, 0, -b), // vec3(0, -b, a), vec3(-b, -a, 0), vec3(b, -a, 0), // vec3(0, -b, -a), vec3(b, -a, 0), vec3(-b, -a, 0), // vec3(-b, a, 0), vec3(-a, 0, -b), vec3(-a, 0, b), // vec3(-b, -a, 0), vec3(-a, 0, b), vec3(-a, 0, -b), // vec3(b, a, 0), vec3(a, 0, b), vec3(a, 0, -b), // vec3(b, -a, 0), vec3(a, 0, -b), vec3(a, 0, b), // vec3(0, b, a), vec3(-b, a, 0), vec3(-a, 0, b), // vec3(0, b, a), vec3(a, 0, b), vec3(b, a, 0), // vec3(0, b, -a), vec3(-a, 0, -b), vec3(-b, a, 0), // vec3(0, b, -a), vec3(b, a, 0), vec3(a, 0, -b), // vec3(0, -b, -a), vec3(-b, -a, 0), vec3(-a, 0, -b), // vec3(0, -b, -a), vec3(a, 0, -b), vec3(b, -a, 0), // vec3(0, -b, a), vec3(-a, 0, b), vec3(-b, -a, 0), // vec3(0, -b, a), vec3(b, -a, 0), vec3(a, 0, b) }; // return vertices;} const VVertex& tetrahedronVertices() { static const float a = 1.0f / sqrt(2.0f); static const auto A = vec3(0, 1, a); static const auto B = vec3(0, -1, a); static const auto C = vec3(1, 0, -a); static const auto D = vec3(-1, 0, -a); static const VVertex vertices{ A, B, C, D, B, A, C, D, A, C, B, D, }; return vertices;} VVertex tesselate(const VVertex& startingTriangles, int count) { VVertex triangles = startingTriangles; if (0 != (triangles.size() % 3)) { throw std::runtime_error("Bad number of vertices for tesselation"); } for (size_t i = 0; i < triangles.size(); ++i) { triangles[i] = glm::normalize(triangles[i]); } VVertex newTriangles; while (count) { newTriangles.clear(); newTriangles.reserve(triangles.size() * 4); for (size_t i = 0; i < triangles.size(); i += 3) { const vec3& a = triangles[i]; const vec3& b = triangles[i + 1]; const vec3& c = triangles[i + 2]; vec3 ab = glm::normalize(a + b); vec3 bc = glm::normalize(b + c); vec3 ca = glm::normalize(c + a); newTriangles.push_back(a); newTriangles.push_back(ab); newTriangles.push_back(ca); newTriangles.push_back(b); newTriangles.push_back(bc); newTriangles.push_back(ab); newTriangles.push_back(c); newTriangles.push_back(ca); newTriangles.push_back(bc); newTriangles.push_back(ab); newTriangles.push_back(bc); newTriangles.push_back(ca); } triangles.swap(newTriangles); --count; } return triangles;} // 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() { auto vertexBuffer = std::make_shared<gpu::Buffer>(); auto indexBuffer = std::make_shared<gpu::Buffer>(); uint16_t startingIndex = 0; // Cube startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE; { ShapeData& shapeData = _shapes[Cube]; VVertex vertices; // front vertices.push_back(vec3(1, 1, 1)); vertices.push_back(vec3(0, 0, 1)); vertices.push_back(vec3(-1, 1, 1)); vertices.push_back(vec3(0, 0, 1)); vertices.push_back(vec3(-1, -1, 1)); vertices.push_back(vec3(0, 0, 1)); vertices.push_back(vec3(1, -1, 1)); vertices.push_back(vec3(0, 0, 1)); // right vertices.push_back(vec3(1, 1, 1)); vertices.push_back(vec3(1, 0, 0)); vertices.push_back(vec3(1, -1, 1)); vertices.push_back(vec3(1, 0, 0)); vertices.push_back(vec3(1, -1, -1)); vertices.push_back(vec3(1, 0, 0)); vertices.push_back(vec3(1, 1, -1)); vertices.push_back(vec3(1, 0, 0)); // top vertices.push_back(vec3(1, 1, 1)); vertices.push_back(vec3(0, 1, 0)); vertices.push_back(vec3(1, 1, -1)); vertices.push_back(vec3(0, 1, 0)); vertices.push_back(vec3(-1, 1, -1)); vertices.push_back(vec3(0, 1, 0)); vertices.push_back(vec3(-1, 1, 1)); vertices.push_back(vec3(0, 1, 0)); // left vertices.push_back(vec3(-1, 1, 1)); vertices.push_back(vec3(-1, 0, 0)); vertices.push_back(vec3(-1, 1, -1)); vertices.push_back(vec3(-1, 0, 0)); vertices.push_back(vec3(-1, -1, -1)); vertices.push_back(vec3(-1, 0, 0)); vertices.push_back(vec3(-1, -1, 1)); vertices.push_back(vec3(-1, 0, 0)); // bottom vertices.push_back(vec3(-1, -1, -1)); vertices.push_back(vec3(0, -1, 0)); vertices.push_back(vec3(1, -1, -1)); vertices.push_back(vec3(0, -1, 0)); vertices.push_back(vec3(1, -1, 1)); vertices.push_back(vec3(0, -1, 0)); vertices.push_back(vec3(-1, -1, 1)); vertices.push_back(vec3(0, -1, 0)); // back vertices.push_back(vec3(1, -1, -1)); vertices.push_back(vec3(0, 0, -1)); vertices.push_back(vec3(-1, -1, -1)); vertices.push_back(vec3(0, 0, -1)); vertices.push_back(vec3(-1, 1, -1)); vertices.push_back(vec3(0, 0, -1)); vertices.push_back(vec3(1, 1, -1)); vertices.push_back(vec3(0, 0, -1)); for (size_t i = 0; i < vertices.size(); ++i) { if (0 == i % 2) { vertices[i] *= 0.5f; } } shapeData.setupVertices(_shapeVertices, vertices); VIndex indices{ 0, 1, 2, 2, 3, 0, // front 4, 5, 6, 6, 7, 4, // right 8, 9, 10, 10, 11, 8, // top 12, 13, 14, 14, 15, 12, // left 16, 17, 18, 18, 19, 16, // bottom 20, 21, 22, 22, 23, 20 // back }; for (int i = 0; i < indices.size(); ++i) { indices[i] += startingIndex; } VIndex wireIndices{ 0, 1, 1, 2, 2, 3, 3, 0, // front 20, 21, 21, 22, 22, 23, 23, 20, // back 0, 23, 1, 22, 2, 21, 3, 20 // sides }; for (int i = 0; i < wireIndices.size(); ++i) { indices[i] += startingIndex; } shapeData.setupIndices(_shapeIndices, indices, wireIndices); } // Tetrahedron startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE; { ShapeData& shapeData = _shapes[Tetrahedron]; size_t vertexCount = 4; VVertex vertices; { VVertex originalVertices = tetrahedronVertices(); vertexCount = originalVertices.size(); vertices.reserve(originalVertices.size() * 2); for (size_t i = 0; i < originalVertices.size(); i += 3) { vec3 faceNormal; for (size_t j = 0; j < 3; ++j) { faceNormal += originalVertices[i + j]; } faceNormal = glm::normalize(faceNormal); for (size_t j = 0; j < 3; ++j) { vertices.push_back(glm::normalize(originalVertices[i + j])); vertices.push_back(faceNormal); } } } shapeData.setupVertices(_shapeVertices, vertices); VIndex indices; for (size_t i = 0; i < vertexCount; i += 3) { for (size_t j = 0; j < 3; ++j) { indices.push_back(i + j + startingIndex); } } VIndex wireIndices{ 0, 1, 1, 2, 2, 0, 0, 3, 1, 3, 2, 3, }; for (int i = 0; i < wireIndices.size(); ++i) { wireIndices[i] += startingIndex; } shapeData.setupIndices(_shapeIndices, indices, wireIndices); } // Sphere // FIXME this uses way more vertices than required. Should find a way to calculate the indices // using shared vertices for better vertex caching startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE; { ShapeData& shapeData = _shapes[Sphere]; VVertex vertices; VIndex indices; { VVertex originalVertices = tesselate(icosahedronVertices(), 3); vertices.reserve(originalVertices.size() * 2); for (size_t i = 0; i < originalVertices.size(); i += 3) { for (int j = 0; j < 3; ++j) { vertices.push_back(originalVertices[i + j]); vertices.push_back(originalVertices[i + j]); indices.push_back(i + j + startingIndex); } } } shapeData.setupVertices(_shapeVertices, vertices); // FIXME don't use solid indices for wire drawing. shapeData.setupIndices(_shapeIndices, indices, indices); } // Icosahedron startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE; { ShapeData& shapeData = _shapes[Icosahedron]; VVertex vertices; VIndex indices; { const VVertex& originalVertices = icosahedronVertices(); vertices.reserve(originalVertices.size() * 2); for (size_t i = 0; i < originalVertices.size(); i += 3) { vec3 faceNormal; for (size_t j = 0; j < 3; ++j) { faceNormal += originalVertices[i + j]; } faceNormal = glm::normalize(faceNormal); for (int j = 0; j < 3; ++j) { vertices.push_back(glm::normalize(originalVertices[i + j])); vertices.push_back(faceNormal); indices.push_back(i + j + startingIndex); } } } shapeData.setupVertices(_shapeVertices, vertices); // FIXME don't use solid indices for wire drawing. shapeData.setupIndices(_shapeIndices, indices, indices); } //Triangle, //Quad, //Circle, //Octahetron, //Dodecahedron, //Torus, //Cone, //Cylinder, // Line startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE; { ShapeData& shapeData = _shapes[Line]; shapeData.setupVertices(_shapeVertices, VVertex{ vec3(-0.5, 0, 0), vec3(-0.5, 0, 0), vec3(0.5f, 0, 0), vec3(0.5f, 0, 0) }); VIndex wireIndices; wireIndices.push_back(0 + startingIndex); wireIndices.push_back(1 + startingIndex); shapeData.setupIndices(_shapeIndices, VIndex(), wireIndices); }} 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); INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::INSTANCE_XFM, gpu::Stream::INSTANCE_XFM, TRANSFORM_ELEMENT, 0, gpu::Stream::PER_INSTANCE); } return INSTANCED_SOLID_STREAM_FORMAT;} GeometryCache::GeometryCache() { const qint64 GEOMETRY_DEFAULT_UNUSED_MAX_SIZE = DEFAULT_UNUSED_MAX_SIZE; setUnusedResourceCacheSize(GEOMETRY_DEFAULT_UNUSED_MAX_SIZE); buildShapes();} 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} QSharedPointer<Resource> GeometryCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) { // NetworkGeometry is no longer a subclass of Resource, but requires this method because, it is pure virtual. assert(false); return QSharedPointer<Resource>();} void setupBatchInstance(gpu::Batch& batch, gpu::BufferPointer transformBuffer, gpu::BufferPointer colorBuffer) { gpu::BufferView colorView(colorBuffer, COLOR_ELEMENT); batch.setInputBuffer(gpu::Stream::COLOR, colorView); gpu::BufferView instanceXfmView(transformBuffer, 0, transformBuffer->getSize(), TRANSFORM_ELEMENT); batch.setInputBuffer(gpu::Stream::INSTANCE_XFM, instanceXfmView);} 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& transformBuffer, gpu::BufferPointer& colorBuffer) { batch.setInputFormat(getInstancedSolidStreamFormat()); setupBatchInstance(batch, transformBuffer, colorBuffer); _shapes[shape].drawInstances(batch, count);Lines 1–500 of 2122. Load more
// // 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 <gpu/GLBackend.h> #include "TextureCache.h" #include "RenderUtilsLogging.h" #include "standardTransformPNTC_vert.h" #include "standardDrawTexture_frag.h" #include "gpu/StandardShaderLib.h" //#define WANT_DEBUG const int GeometryCache::UNKNOWN_ID = -1; static const uint FLOATS_PER_VERTEX = 3; static const uint VERTICES_PER_TRIANGLE = 3; static const uint TRIANGLES_PER_QUAD = 2; static const uint CUBE_FACES = 6; static const uint CUBE_VERTICES_PER_FACE = 4; static const uint CUBE_VERTICES = CUBE_FACES * CUBE_VERTICES_PER_FACE; static const uint CUBE_VERTEX_POINTS = CUBE_VERTICES * FLOATS_PER_VERTEX; static const uint CUBE_INDICES = CUBE_FACES * TRIANGLES_PER_QUAD * VERTICES_PER_TRIANGLE; static const uint SPHERE_LATITUDES = 24; static const uint SPHERE_MERIDIANS = SPHERE_LATITUDES * 2; static const uint SPHERE_INDICES = SPHERE_MERIDIANS * (SPHERE_LATITUDES - 1) * TRIANGLES_PER_QUAD * VERTICES_PER_TRIANGLE; 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 const gpu::Element TRANSFORM_ELEMENT{ gpu::MAT4, gpu::FLOAT, gpu::XYZW }; 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 normals static const uint SHAPE_NORMALS_OFFSET = sizeof(glm::vec3); void GeometryCache::ShapeData::setupVertices(gpu::BufferPointer& vertexBuffer, const VVertex& 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 VIndex& indices, const VIndex& wireIndices) { _indices = indexBuffer; if (!indices.empty()) { _indexOffset = indexBuffer->getSize(); _indexCount = indices.size(); indexBuffer->append(indices); } if (!wireIndices.empty()) { _wireIndexOffset = indexBuffer->getSize(); _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); } void GeometryCache::ShapeData::draw(gpu::Batch& batch) const { if (_indexCount) { setupBatch(batch); batch.setIndexBuffer(gpu::UINT16, _indices, _indexOffset); batch.drawIndexed(gpu::TRIANGLES, _indexCount); } } void GeometryCache::ShapeData::drawWire(gpu::Batch& batch) const { if (_wireIndexCount) { setupBatch(batch); batch.setIndexBuffer(gpu::UINT16, _indices, _wireIndexOffset); batch.drawIndexed(gpu::LINES, _wireIndexCount); } } void GeometryCache::ShapeData::drawInstances(gpu::Batch& batch, size_t count) const { if (_indexCount) { setupBatch(batch); batch.setIndexBuffer(gpu::UINT16, _indices, _indexOffset); batch.drawIndexedInstanced(count, gpu::TRIANGLES, _indexCount); } } void GeometryCache::ShapeData::drawWireInstances(gpu::Batch& batch, size_t count) const { if (_wireIndexCount) { setupBatch(batch); batch.setIndexBuffer(gpu::UINT16, _indices, _wireIndexOffset); batch.drawIndexedInstanced(count, gpu::LINES, _wireIndexCount); } } const VVertex& icosahedronVertices() { static const float phi = (1.0 + sqrt(5.0)) / 2.0; static const float a = 0.5; static const float b = 1.0 / (2.0 * phi); static const VVertex vertices{ // vec3(0, b, -a), vec3(-b, a, 0), vec3(b, a, 0), // vec3(0, b, a), vec3(b, a, 0), vec3(-b, a, 0), // vec3(0, b, a), vec3(-a, 0, b), vec3(0, -b, a), // vec3(0, b, a), vec3(0, -b, a), vec3(a, 0, b), // vec3(0, b, -a), vec3(a, 0, -b), vec3(0, -b, -a),// vec3(0, b, -a), vec3(0, -b, -a), vec3(-a, 0, -b), // vec3(0, -b, a), vec3(-b, -a, 0), vec3(b, -a, 0), // vec3(0, -b, -a), vec3(b, -a, 0), vec3(-b, -a, 0), // vec3(-b, a, 0), vec3(-a, 0, -b), vec3(-a, 0, b), // vec3(-b, -a, 0), vec3(-a, 0, b), vec3(-a, 0, -b), // vec3(b, a, 0), vec3(a, 0, b), vec3(a, 0, -b), // vec3(b, -a, 0), vec3(a, 0, -b), vec3(a, 0, b), // vec3(0, b, a), vec3(-b, a, 0), vec3(-a, 0, b), // vec3(0, b, a), vec3(a, 0, b), vec3(b, a, 0), // vec3(0, b, -a), vec3(-a, 0, -b), vec3(-b, a, 0), // vec3(0, b, -a), vec3(b, a, 0), vec3(a, 0, -b), // vec3(0, -b, -a), vec3(-b, -a, 0), vec3(-a, 0, -b), // vec3(0, -b, -a), vec3(a, 0, -b), vec3(b, -a, 0), // vec3(0, -b, a), vec3(-a, 0, b), vec3(-b, -a, 0), // vec3(0, -b, a), vec3(b, -a, 0), vec3(a, 0, b) }; // return vertices; } const VVertex& tetrahedronVertices() { static const float a = 1.0f / sqrt(2.0f); static const auto A = vec3(0, 1, a); static const auto B = vec3(0, -1, a); static const auto C = vec3(1, 0, -a); static const auto D = vec3(-1, 0, -a); static const VVertex vertices{ A, B, C, D, B, A, C, D, A, C, B, D, }; return vertices; } VVertex tesselate(const VVertex& startingTriangles, int count) { VVertex triangles = startingTriangles; if (0 != (triangles.size() % 3)) { throw std::runtime_error("Bad number of vertices for tesselation"); } for (size_t i = 0; i < triangles.size(); ++i) { triangles[i] = glm::normalize(triangles[i]); } VVertex newTriangles; while (count) { newTriangles.clear(); newTriangles.reserve(triangles.size() * 4); for (size_t i = 0; i < triangles.size(); i += 3) { const vec3& a = triangles[i]; const vec3& b = triangles[i + 1]; const vec3& c = triangles[i + 2]; vec3 ab = glm::normalize(a + b); vec3 bc = glm::normalize(b + c); vec3 ca = glm::normalize(c + a); newTriangles.push_back(a); newTriangles.push_back(ab); newTriangles.push_back(ca); newTriangles.push_back(b); newTriangles.push_back(bc); newTriangles.push_back(ab); newTriangles.push_back(c); newTriangles.push_back(ca); newTriangles.push_back(bc); newTriangles.push_back(ab); newTriangles.push_back(bc); newTriangles.push_back(ca); } triangles.swap(newTriangles); --count; } return triangles; } // 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 shading void GeometryCache::buildShapes() { auto vertexBuffer = std::make_shared<gpu::Buffer>(); auto indexBuffer = std::make_shared<gpu::Buffer>(); uint16_t startingIndex = 0; // Cube startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE; { ShapeData& shapeData = _shapes[Cube]; VVertex vertices; // front vertices.push_back(vec3(1, 1, 1)); vertices.push_back(vec3(0, 0, 1)); vertices.push_back(vec3(-1, 1, 1)); vertices.push_back(vec3(0, 0, 1)); vertices.push_back(vec3(-1, -1, 1)); vertices.push_back(vec3(0, 0, 1)); vertices.push_back(vec3(1, -1, 1)); vertices.push_back(vec3(0, 0, 1)); // right vertices.push_back(vec3(1, 1, 1)); vertices.push_back(vec3(1, 0, 0)); vertices.push_back(vec3(1, -1, 1)); vertices.push_back(vec3(1, 0, 0)); vertices.push_back(vec3(1, -1, -1)); vertices.push_back(vec3(1, 0, 0)); vertices.push_back(vec3(1, 1, -1)); vertices.push_back(vec3(1, 0, 0)); // top vertices.push_back(vec3(1, 1, 1)); vertices.push_back(vec3(0, 1, 0)); vertices.push_back(vec3(1, 1, -1)); vertices.push_back(vec3(0, 1, 0)); vertices.push_back(vec3(-1, 1, -1)); vertices.push_back(vec3(0, 1, 0)); vertices.push_back(vec3(-1, 1, 1)); vertices.push_back(vec3(0, 1, 0)); // left vertices.push_back(vec3(-1, 1, 1)); vertices.push_back(vec3(-1, 0, 0)); vertices.push_back(vec3(-1, 1, -1)); vertices.push_back(vec3(-1, 0, 0)); vertices.push_back(vec3(-1, -1, -1)); vertices.push_back(vec3(-1, 0, 0)); vertices.push_back(vec3(-1, -1, 1)); vertices.push_back(vec3(-1, 0, 0)); // bottom vertices.push_back(vec3(-1, -1, -1)); vertices.push_back(vec3(0, -1, 0)); vertices.push_back(vec3(1, -1, -1)); vertices.push_back(vec3(0, -1, 0)); vertices.push_back(vec3(1, -1, 1)); vertices.push_back(vec3(0, -1, 0)); vertices.push_back(vec3(-1, -1, 1)); vertices.push_back(vec3(0, -1, 0)); // back vertices.push_back(vec3(1, -1, -1)); vertices.push_back(vec3(0, 0, -1)); vertices.push_back(vec3(-1, -1, -1)); vertices.push_back(vec3(0, 0, -1)); vertices.push_back(vec3(-1, 1, -1)); vertices.push_back(vec3(0, 0, -1)); vertices.push_back(vec3(1, 1, -1)); vertices.push_back(vec3(0, 0, -1)); for (size_t i = 0; i < vertices.size(); ++i) { if (0 == i % 2) { vertices[i] *= 0.5f; } } shapeData.setupVertices(_shapeVertices, vertices); VIndex indices{ 0, 1, 2, 2, 3, 0, // front 4, 5, 6, 6, 7, 4, // right 8, 9, 10, 10, 11, 8, // top 12, 13, 14, 14, 15, 12, // left 16, 17, 18, 18, 19, 16, // bottom 20, 21, 22, 22, 23, 20 // back }; for (int i = 0; i < indices.size(); ++i) { indices[i] += startingIndex; } VIndex wireIndices{ 0, 1, 1, 2, 2, 3, 3, 0, // front 20, 21, 21, 22, 22, 23, 23, 20, // back 0, 23, 1, 22, 2, 21, 3, 20 // sides }; for (int i = 0; i < wireIndices.size(); ++i) { indices[i] += startingIndex; } shapeData.setupIndices(_shapeIndices, indices, wireIndices); } // Tetrahedron startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE; { ShapeData& shapeData = _shapes[Tetrahedron]; size_t vertexCount = 4; VVertex vertices; { VVertex originalVertices = tetrahedronVertices(); vertexCount = originalVertices.size(); vertices.reserve(originalVertices.size() * 2); for (size_t i = 0; i < originalVertices.size(); i += 3) { vec3 faceNormal; for (size_t j = 0; j < 3; ++j) { faceNormal += originalVertices[i + j]; } faceNormal = glm::normalize(faceNormal); for (size_t j = 0; j < 3; ++j) { vertices.push_back(glm::normalize(originalVertices[i + j])); vertices.push_back(faceNormal); } } } shapeData.setupVertices(_shapeVertices, vertices); VIndex indices; for (size_t i = 0; i < vertexCount; i += 3) { for (size_t j = 0; j < 3; ++j) { indices.push_back(i + j + startingIndex); } } VIndex wireIndices{ 0, 1, 1, 2, 2, 0, 0, 3, 1, 3, 2, 3, }; for (int i = 0; i < wireIndices.size(); ++i) { wireIndices[i] += startingIndex; } shapeData.setupIndices(_shapeIndices, indices, wireIndices); } // Sphere // FIXME this uses way more vertices than required. Should find a way to calculate the indices // using shared vertices for better vertex caching startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE; { ShapeData& shapeData = _shapes[Sphere]; VVertex vertices; VIndex indices; { VVertex originalVertices = tesselate(icosahedronVertices(), 3); vertices.reserve(originalVertices.size() * 2); for (size_t i = 0; i < originalVertices.size(); i += 3) { for (int j = 0; j < 3; ++j) { vertices.push_back(originalVertices[i + j]); vertices.push_back(originalVertices[i + j]); indices.push_back(i + j + startingIndex); } } } shapeData.setupVertices(_shapeVertices, vertices); // FIXME don't use solid indices for wire drawing. shapeData.setupIndices(_shapeIndices, indices, indices); } // Icosahedron startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE; { ShapeData& shapeData = _shapes[Icosahedron]; VVertex vertices; VIndex indices; { const VVertex& originalVertices = icosahedronVertices(); vertices.reserve(originalVertices.size() * 2); for (size_t i = 0; i < originalVertices.size(); i += 3) { vec3 faceNormal; for (size_t j = 0; j < 3; ++j) { faceNormal += originalVertices[i + j]; } faceNormal = glm::normalize(faceNormal); for (int j = 0; j < 3; ++j) { vertices.push_back(glm::normalize(originalVertices[i + j])); vertices.push_back(faceNormal); indices.push_back(i + j + startingIndex); } } } shapeData.setupVertices(_shapeVertices, vertices); // FIXME don't use solid indices for wire drawing. shapeData.setupIndices(_shapeIndices, indices, indices); } //Triangle, //Quad, //Circle, //Octahetron, //Dodecahedron, //Torus, //Cone, //Cylinder, // Line startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE; { ShapeData& shapeData = _shapes[Line]; shapeData.setupVertices(_shapeVertices, VVertex{ vec3(-0.5, 0, 0), vec3(-0.5, 0, 0), vec3(0.5f, 0, 0), vec3(0.5f, 0, 0) }); VIndex wireIndices; wireIndices.push_back(0 + startingIndex); wireIndices.push_back(1 + startingIndex); shapeData.setupIndices(_shapeIndices, VIndex(), wireIndices); } } 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); INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::INSTANCE_XFM, gpu::Stream::INSTANCE_XFM, TRANSFORM_ELEMENT, 0, gpu::Stream::PER_INSTANCE); } return INSTANCED_SOLID_STREAM_FORMAT; } GeometryCache::GeometryCache() { const qint64 GEOMETRY_DEFAULT_UNUSED_MAX_SIZE = DEFAULT_UNUSED_MAX_SIZE; setUnusedResourceCacheSize(GEOMETRY_DEFAULT_UNUSED_MAX_SIZE); buildShapes(); } 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 } QSharedPointer<Resource> GeometryCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) { // NetworkGeometry is no longer a subclass of Resource, but requires this method because, it is pure virtual. assert(false); return QSharedPointer<Resource>(); } void setupBatchInstance(gpu::Batch& batch, gpu::BufferPointer transformBuffer, gpu::BufferPointer colorBuffer) { gpu::BufferView colorView(colorBuffer, COLOR_ELEMENT); batch.setInputBuffer(gpu::Stream::COLOR, colorView); gpu::BufferView instanceXfmView(transformBuffer, 0, transformBuffer->getSize(), TRANSFORM_ELEMENT); batch.setInputBuffer(gpu::Stream::INSTANCE_XFM, instanceXfmView); } 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& transformBuffer, gpu::BufferPointer& colorBuffer) { batch.setInputFormat(getInstancedSolidStreamFormat()); setupBatchInstance(batch, transformBuffer, colorBuffer); _shapes[shape].drawInstances(batch, count);