//// 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 <qmath.h>#include <cmath> #include <QtCore/QThreadPool>#include <QtCore/QFileInfo>#include <QtNetwork/QNetworkReply> #include <FSTReader.h>#include <NumericalConstants.h>#include <shared/Shapes.h> #include "TextureCache.h"#include "RenderUtilsLogging.h"#include "StencilMaskPass.h"#include "FadeEffect.h" #include "gpu/StandardShaderLib.h" #include "model/TextureMap.h"#include "render/Args.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 "simple_fade_vert.h"#include "simple_textured_fade_frag.h"#include "simple_textured_unlit_fade_frag.h"#include "simple_opaque_web_browser_frag.h"#include "simple_opaque_web_browser_overlay_frag.h"#include "simple_transparent_web_browser_frag.h"#include "simple_transparent_web_browser_overlay_frag.h"#include "glowLine_vert.h"#include "glowLine_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); template <size_t SIDES>std::vector<vec3> polygon() { std::vector<vec3> result; result.reserve(SIDES); double angleIncrement = 2.0 * M_PI / SIDES; for (size_t i = 0; i < SIDES; i++) { double angle = (double)i * angleIncrement; result.push_back(vec3{ cos(angle) * 0.5, 0.0, sin(angle) * 0.5 }); } return result;} 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);} template <uint32_t N>void extrudePolygon(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer, bool isConical = false) { using namespace geometry; Index baseVertex = (Index)(vertexBuffer->getSize() / SHAPE_VERTEX_STRIDE); VertexVector vertices; IndexVector solidIndices, wireIndices; // Top (if not conical) and bottom faces std::vector<vec3> shape = polygon<N>(); if (isConical) { for (uint32_t i = 0; i < N; i++) { vertices.push_back(vec3(0.0f, 0.5f, 0.0f)); vertices.push_back(vec3(0.0f, 1.0f, 0.0f)); } } else { for (const vec3& v : shape) { vertices.push_back(vec3(v.x, 0.5f, v.z)); vertices.push_back(vec3(0.0f, 1.0f, 0.0f)); } } for (const vec3& v : shape) { vertices.push_back(vec3(v.x, -0.5f, v.z)); vertices.push_back(vec3(0.0f, -1.0f, 0.0f)); } for (uint32_t i = 2; i < N; i++) { solidIndices.push_back(baseVertex + 0); solidIndices.push_back(baseVertex + i); solidIndices.push_back(baseVertex + i - 1); solidIndices.push_back(baseVertex + N); solidIndices.push_back(baseVertex + i + N - 1); solidIndices.push_back(baseVertex + i + N); } for (uint32_t i = 1; i <= N; i++) { wireIndices.push_back(baseVertex + (i % N)); wireIndices.push_back(baseVertex + i - 1); wireIndices.push_back(baseVertex + (i % N) + N); wireIndices.push_back(baseVertex + (i - 1) + N); } // Now do the sides baseVertex += 2 * N; for (uint32_t i = 0; i < N; i++) { vec3 left = shape[i]; vec3 right = shape[(i + 1) % N]; vec3 normal = glm::normalize(left + right); vec3 topLeft = (isConical ? vec3(0.0f, 0.5f, 0.0f) : vec3(left.x, 0.5f, left.z)); vec3 topRight = (isConical ? vec3(0.0f, 0.5f, 0.0f) : vec3(right.x, 0.5f, right.z)); vec3 bottomLeft = vec3(left.x, -0.5f, left.z); vec3 bottomRight = vec3(right.x, -0.5f, right.z); vertices.push_back(topLeft); vertices.push_back(normal); vertices.push_back(bottomLeft); vertices.push_back(normal); vertices.push_back(topRight); vertices.push_back(normal); vertices.push_back(bottomRight); vertices.push_back(normal); solidIndices.push_back(baseVertex + 0); solidIndices.push_back(baseVertex + 2); solidIndices.push_back(baseVertex + 1); solidIndices.push_back(baseVertex + 1); solidIndices.push_back(baseVertex + 2); solidIndices.push_back(baseVertex + 3); wireIndices.push_back(baseVertex + 0); wireIndices.push_back(baseVertex + 1); wireIndices.push_back(baseVertex + 3); wireIndices.push_back(baseVertex + 2); baseVertex += 4; } shapeData.setupVertices(vertexBuffer, vertices); shapeData.setupIndices(indexBuffer, solidIndices, wireIndices);} void drawCircle(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer) { // Draw a circle with radius 1/4th the size of the bounding box using namespace geometry; Index baseVertex = (Index)(vertexBuffer->getSize() / SHAPE_VERTEX_STRIDE); VertexVector vertices; IndexVector solidIndices, wireIndices; const int NUM_CIRCLE_VERTICES = 64; std::vector<vec3> shape = polygon<NUM_CIRCLE_VERTICES>(); for (const vec3& v : shape) { vertices.push_back(vec3(v.x, 0.0f, v.z)); vertices.push_back(vec3(0.0f, 0.0f, 0.0f)); } for (uint32_t i = 2; i < NUM_CIRCLE_VERTICES; i++) { solidIndices.push_back(baseVertex + 0); solidIndices.push_back(baseVertex + i); solidIndices.push_back(baseVertex + i - 1); solidIndices.push_back(baseVertex + NUM_CIRCLE_VERTICES); solidIndices.push_back(baseVertex + i + NUM_CIRCLE_VERTICES - 1); solidIndices.push_back(baseVertex + i + NUM_CIRCLE_VERTICES); } for (uint32_t i = 1; i <= NUM_CIRCLE_VERTICES; i++) { wireIndices.push_back(baseVertex + (i % NUM_CIRCLE_VERTICES)); wireIndices.push_back(baseVertex + i - 1); wireIndices.push_back(baseVertex + (i % NUM_CIRCLE_VERTICES) + NUM_CIRCLE_VERTICES); wireIndices.push_back(baseVertex + (i - 1) + NUM_CIRCLE_VERTICES); } 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.5f, 0.0f, 0.0f), vec3(-0.5f, 0.0f, 0.0f), vec3(0.5f, 0.0f, 0.0f), vec3(0.5f, 0.0f, 0.0f) }); IndexVector wireIndices; // Only two indices wireIndices.push_back(0 + baseVertex); wireIndices.push_back(1 + baseVertex); shapeData.setupIndices(_shapeIndices, IndexVector(), wireIndices); } //Triangle, extrudePolygon<3>(_shapes[Triangle], _shapeVertices, _shapeIndices); //Hexagon, extrudePolygon<6>(_shapes[Hexagon], _shapeVertices, _shapeIndices); //Octagon, extrudePolygon<8>(_shapes[Octagon], _shapeVertices, _shapeIndices); //Cylinder, extrudePolygon<64>(_shapes[Cylinder], _shapeVertices, _shapeIndices); //Cone, extrudePolygon<64>(_shapes[Cone], _shapeVertices, _shapeIndices, true); //Circle drawCircle(_shapes[Circle], _shapeVertices, _shapeIndices); // Not implememented yet: //Quad, //Torus, } 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;} QHash<SimpleProgramKey, gpu::PipelinePointer> GeometryCache::_simplePrograms; gpu::ShaderPointer GeometryCache::_simpleShader;gpu::ShaderPointer GeometryCache::_unlitShader;gpu::ShaderPointer GeometryCache::_simpleFadeShader;gpu::ShaderPointer GeometryCache::_unlitFadeShader; render::ShapePipelinePointer GeometryCache::_simpleOpaquePipeline;render::ShapePipelinePointer GeometryCache::_simpleTransparentPipeline;render::ShapePipelinePointer GeometryCache::_simpleOpaqueFadePipeline;render::ShapePipelinePointer GeometryCache::_simpleTransparentFadePipeline;render::ShapePipelinePointer GeometryCache::_simpleWirePipeline; uint8_t GeometryCache::CUSTOM_PIPELINE_NUMBER = 0; render::ShapePipelinePointer GeometryCache::shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key) { initializeShapePipelines(); if (key.isWireframe()) { return _simpleWirePipeline; } if (key.isFaded()) { if (key.isTranslucent()) { return _simpleTransparentFadePipeline; } else { return _simpleOpaqueFadePipeline; } } else { if (key.isTranslucent()) { return _simpleTransparentPipeline; } else { return _simpleOpaquePipeline; } }} GeometryCache::GeometryCache() :_nextID(0) { // Let's register its special shapePipeline factory: registerShapePipeline(); buildShapes();} Lines 1–500 of 2124. 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 <qmath.h> #include <cmath> #include <QtCore/QThreadPool> #include <QtCore/QFileInfo> #include <QtNetwork/QNetworkReply> #include <FSTReader.h> #include <NumericalConstants.h> #include <shared/Shapes.h> #include "TextureCache.h" #include "RenderUtilsLogging.h" #include "StencilMaskPass.h" #include "FadeEffect.h" #include "gpu/StandardShaderLib.h" #include "model/TextureMap.h" #include "render/Args.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 "simple_fade_vert.h" #include "simple_textured_fade_frag.h" #include "simple_textured_unlit_fade_frag.h" #include "simple_opaque_web_browser_frag.h" #include "simple_opaque_web_browser_overlay_frag.h" #include "simple_transparent_web_browser_frag.h" #include "simple_transparent_web_browser_overlay_frag.h" #include "glowLine_vert.h" #include "glowLine_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 normals static 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); template <size_t SIDES> std::vector<vec3> polygon() { std::vector<vec3> result; result.reserve(SIDES); double angleIncrement = 2.0 * M_PI / SIDES; for (size_t i = 0; i < SIDES; i++) { double angle = (double)i * angleIncrement; result.push_back(vec3{ cos(angle) * 0.5, 0.0, sin(angle) * 0.5 }); } return result; } 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); } template <uint32_t N> void extrudePolygon(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer, bool isConical = false) { using namespace geometry; Index baseVertex = (Index)(vertexBuffer->getSize() / SHAPE_VERTEX_STRIDE); VertexVector vertices; IndexVector solidIndices, wireIndices; // Top (if not conical) and bottom faces std::vector<vec3> shape = polygon<N>(); if (isConical) { for (uint32_t i = 0; i < N; i++) { vertices.push_back(vec3(0.0f, 0.5f, 0.0f)); vertices.push_back(vec3(0.0f, 1.0f, 0.0f)); } } else { for (const vec3& v : shape) { vertices.push_back(vec3(v.x, 0.5f, v.z)); vertices.push_back(vec3(0.0f, 1.0f, 0.0f)); } } for (const vec3& v : shape) { vertices.push_back(vec3(v.x, -0.5f, v.z)); vertices.push_back(vec3(0.0f, -1.0f, 0.0f)); } for (uint32_t i = 2; i < N; i++) { solidIndices.push_back(baseVertex + 0); solidIndices.push_back(baseVertex + i); solidIndices.push_back(baseVertex + i - 1); solidIndices.push_back(baseVertex + N); solidIndices.push_back(baseVertex + i + N - 1); solidIndices.push_back(baseVertex + i + N); } for (uint32_t i = 1; i <= N; i++) { wireIndices.push_back(baseVertex + (i % N)); wireIndices.push_back(baseVertex + i - 1); wireIndices.push_back(baseVertex + (i % N) + N); wireIndices.push_back(baseVertex + (i - 1) + N); } // Now do the sides baseVertex += 2 * N; for (uint32_t i = 0; i < N; i++) { vec3 left = shape[i]; vec3 right = shape[(i + 1) % N]; vec3 normal = glm::normalize(left + right); vec3 topLeft = (isConical ? vec3(0.0f, 0.5f, 0.0f) : vec3(left.x, 0.5f, left.z)); vec3 topRight = (isConical ? vec3(0.0f, 0.5f, 0.0f) : vec3(right.x, 0.5f, right.z)); vec3 bottomLeft = vec3(left.x, -0.5f, left.z); vec3 bottomRight = vec3(right.x, -0.5f, right.z); vertices.push_back(topLeft); vertices.push_back(normal); vertices.push_back(bottomLeft); vertices.push_back(normal); vertices.push_back(topRight); vertices.push_back(normal); vertices.push_back(bottomRight); vertices.push_back(normal); solidIndices.push_back(baseVertex + 0); solidIndices.push_back(baseVertex + 2); solidIndices.push_back(baseVertex + 1); solidIndices.push_back(baseVertex + 1); solidIndices.push_back(baseVertex + 2); solidIndices.push_back(baseVertex + 3); wireIndices.push_back(baseVertex + 0); wireIndices.push_back(baseVertex + 1); wireIndices.push_back(baseVertex + 3); wireIndices.push_back(baseVertex + 2); baseVertex += 4; } shapeData.setupVertices(vertexBuffer, vertices); shapeData.setupIndices(indexBuffer, solidIndices, wireIndices); } void drawCircle(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer) { // Draw a circle with radius 1/4th the size of the bounding box using namespace geometry; Index baseVertex = (Index)(vertexBuffer->getSize() / SHAPE_VERTEX_STRIDE); VertexVector vertices; IndexVector solidIndices, wireIndices; const int NUM_CIRCLE_VERTICES = 64; std::vector<vec3> shape = polygon<NUM_CIRCLE_VERTICES>(); for (const vec3& v : shape) { vertices.push_back(vec3(v.x, 0.0f, v.z)); vertices.push_back(vec3(0.0f, 0.0f, 0.0f)); } for (uint32_t i = 2; i < NUM_CIRCLE_VERTICES; i++) { solidIndices.push_back(baseVertex + 0); solidIndices.push_back(baseVertex + i); solidIndices.push_back(baseVertex + i - 1); solidIndices.push_back(baseVertex + NUM_CIRCLE_VERTICES); solidIndices.push_back(baseVertex + i + NUM_CIRCLE_VERTICES - 1); solidIndices.push_back(baseVertex + i + NUM_CIRCLE_VERTICES); } for (uint32_t i = 1; i <= NUM_CIRCLE_VERTICES; i++) { wireIndices.push_back(baseVertex + (i % NUM_CIRCLE_VERTICES)); wireIndices.push_back(baseVertex + i - 1); wireIndices.push_back(baseVertex + (i % NUM_CIRCLE_VERTICES) + NUM_CIRCLE_VERTICES); wireIndices.push_back(baseVertex + (i - 1) + NUM_CIRCLE_VERTICES); } 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 shading void 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.5f, 0.0f, 0.0f), vec3(-0.5f, 0.0f, 0.0f), vec3(0.5f, 0.0f, 0.0f), vec3(0.5f, 0.0f, 0.0f) }); IndexVector wireIndices; // Only two indices wireIndices.push_back(0 + baseVertex); wireIndices.push_back(1 + baseVertex); shapeData.setupIndices(_shapeIndices, IndexVector(), wireIndices); } //Triangle, extrudePolygon<3>(_shapes[Triangle], _shapeVertices, _shapeIndices); //Hexagon, extrudePolygon<6>(_shapes[Hexagon], _shapeVertices, _shapeIndices); //Octagon, extrudePolygon<8>(_shapes[Octagon], _shapeVertices, _shapeIndices); //Cylinder, extrudePolygon<64>(_shapes[Cylinder], _shapeVertices, _shapeIndices); //Cone, extrudePolygon<64>(_shapes[Cone], _shapeVertices, _shapeIndices, true); //Circle drawCircle(_shapes[Circle], _shapeVertices, _shapeIndices); // Not implememented yet: //Quad, //Torus, } 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; } QHash<SimpleProgramKey, gpu::PipelinePointer> GeometryCache::_simplePrograms; gpu::ShaderPointer GeometryCache::_simpleShader; gpu::ShaderPointer GeometryCache::_unlitShader; gpu::ShaderPointer GeometryCache::_simpleFadeShader; gpu::ShaderPointer GeometryCache::_unlitFadeShader; render::ShapePipelinePointer GeometryCache::_simpleOpaquePipeline; render::ShapePipelinePointer GeometryCache::_simpleTransparentPipeline; render::ShapePipelinePointer GeometryCache::_simpleOpaqueFadePipeline; render::ShapePipelinePointer GeometryCache::_simpleTransparentFadePipeline; render::ShapePipelinePointer GeometryCache::_simpleWirePipeline; uint8_t GeometryCache::CUSTOM_PIPELINE_NUMBER = 0; render::ShapePipelinePointer GeometryCache::shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key) { initializeShapePipelines(); if (key.isWireframe()) { return _simpleWirePipeline; } if (key.isFaded()) { if (key.isTranslucent()) { return _simpleTransparentFadePipeline; } else { return _simpleOpaqueFadePipeline; } } else { if (key.isTranslucent()) { return _simpleTransparentPipeline; } else { return _simpleOpaquePipeline; } } } GeometryCache::GeometryCache() : _nextID(0) { // Let's register its special shapePipeline factory: registerShapePipeline(); buildShapes(); }