//// 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 <shared/Shapes.h>#include <shared/PlatformHacks.h> #include <FSTReader.h>#include <NumericalConstants.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_transparent_web_browser_frag.h"#include "glowLine_vert.h"#include "glowLine_frag.h" #include "grid_frag.h" //#define WANT_DEBUG // @note: Originally size entity::NUM_SHAPES// As of Commit b93e91b9, render-utils no longer retains knowledge of// entity lib, and thus doesn't know about entity::NUM_SHAPES. Should// the enumerations be altered, this will need to be updated.// @see ShapeEntityItem.hstatic std::array<GeometryCache::Shape, (GeometryCache::NUM_SHAPES - 1)> MAPPING{ { GeometryCache::Triangle, GeometryCache::Quad, GeometryCache::Hexagon, GeometryCache::Octagon, GeometryCache::Circle, GeometryCache::Cube, GeometryCache::Sphere, GeometryCache::Tetrahedron, GeometryCache::Octahedron, GeometryCache::Dodecahedron, GeometryCache::Icosahedron, GeometryCache::Torus, GeometryCache::Cone, GeometryCache::Cylinder,} }; static const std::array<const char * const, GeometryCache::NUM_SHAPES> GEOCACHE_SHAPE_STRINGS{ { "Line", "Triangle", "Quad", "Hexagon", "Octagon", "Circle", "Cube", "Sphere", "Tetrahedron", "Octahedron", "Dodecahedron", "Icosahedron", "Torus", "Cone", "Cylinder" } }; 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 const gpu::Element TEXCOORD4_ELEMENT { gpu::VEC4, gpu::FLOAT, gpu::XYZW }; static gpu::Stream::FormatPointer SOLID_STREAM_FORMAT;static gpu::Stream::FormatPointer INSTANCED_SOLID_STREAM_FORMAT;static gpu::Stream::FormatPointer INSTANCED_SOLID_FADE_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::computeSimpleHullPointListForShape(const int entityShape, const glm::vec3 &entityExtents, QVector<glm::vec3> &outPointList) { auto geometryCache = DependencyManager::get<GeometryCache>(); const GeometryCache::Shape geometryShape = GeometryCache::getShapeForEntityShape( entityShape ); const GeometryCache::ShapeData * shapeData = geometryCache->getShapeData( geometryShape ); if (!shapeData){ //--EARLY EXIT--( data isn't ready for some reason... ) return; } const gpu::BufferView & shapeVerts = shapeData->_positionView; const gpu::BufferView::Size numItems = shapeVerts.getNumElements(); outPointList.reserve((int)numItems); QVector<glm::vec3> uniqueVerts; uniqueVerts.reserve((int)numItems); const float MAX_INCLUSIVE_FILTER_DISTANCE_SQUARED = 1.0e-6f; //< 1mm^2 for (gpu::BufferView::Index i = 0; i < (gpu::BufferView::Index)numItems; ++i) { const int numUniquePoints = (int)uniqueVerts.size(); const geometry::Vec &curVert = shapeVerts.get<geometry::Vec>(i); bool isUniquePoint = true; for (int uniqueIndex = 0; uniqueIndex < numUniquePoints; ++uniqueIndex) { const geometry::Vec knownVert = uniqueVerts[uniqueIndex]; const float distToKnownPoint = glm::length2(knownVert - curVert); if (distToKnownPoint <= MAX_INCLUSIVE_FILTER_DISTANCE_SQUARED) { isUniquePoint = false; break; } } if (!isUniquePoint) { //--EARLY ITERATION EXIT-- continue; } uniqueVerts.push_back(curVert); outPointList.push_back(curVert * entityExtents); }} 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) { gpu::Buffer::Size offset = vertexBuffer->getSize(); vertexBuffer->append(vertices); gpu::Buffer::Size viewSize = vertices.size() * sizeof(glm::vec3); _positionView = gpu::BufferView(vertexBuffer, offset, viewSize, SHAPE_VERTEX_STRIDE, POSITION_ELEMENT); _normalView = gpu::BufferView(vertexBuffer, offset + SHAPE_NORMALS_OFFSET, viewSize, SHAPE_VERTEX_STRIDE, NORMAL_ELEMENT);} void GeometryCache::ShapeData::setupIndices(gpu::BufferPointer& indexBuffer, const geometry::IndexVector& indices, const geometry::IndexVector& wireIndices) { gpu::Buffer::Size offset = indexBuffer->getSize(); if (!indices.empty()) { for (uint32_t i = 0; i < indices.size(); ++i) { indexBuffer->append((uint16_t)indices[i]); } } gpu::Size viewSize = indices.size() * sizeof(uint16_t); _indicesView = gpu::BufferView(indexBuffer, offset, viewSize, gpu::Element::INDEX_UINT16); offset = indexBuffer->getSize(); if (!wireIndices.empty()) { for (uint32_t i = 0; i < wireIndices.size(); ++i) { indexBuffer->append((uint16_t)wireIndices[i]); } } viewSize = wireIndices.size() * sizeof(uint16_t); _wireIndicesView = gpu::BufferView(indexBuffer, offset, viewSize, gpu::Element::INDEX_UINT16);} void GeometryCache::ShapeData::setupBatch(gpu::Batch& batch) const { batch.setInputBuffer(gpu::Stream::POSITION, _positionView); batch.setInputBuffer(gpu::Stream::NORMAL, _normalView); batch.setIndexBuffer(_indicesView);} void GeometryCache::ShapeData::draw(gpu::Batch& batch) const { uint32_t numIndices = (uint32_t)_indicesView.getNumElements(); if (numIndices > 0) { setupBatch(batch); batch.drawIndexed(gpu::TRIANGLES, numIndices, 0); }} void GeometryCache::ShapeData::drawWire(gpu::Batch& batch) const { uint32_t numIndices = (uint32_t)_wireIndicesView.getNumElements(); if (numIndices > 0) { batch.setInputBuffer(gpu::Stream::POSITION, _positionView); batch.setInputBuffer(gpu::Stream::NORMAL, _normalView); batch.setIndexBuffer(_wireIndicesView); batch.drawIndexed(gpu::LINES, numIndices, 0); }} void GeometryCache::ShapeData::drawInstances(gpu::Batch& batch, size_t count) const { uint32_t numIndices = (uint32_t)_indicesView.getNumElements(); if (numIndices > 0) { setupBatch(batch); batch.drawIndexedInstanced((gpu::uint32)count, gpu::TRIANGLES, numIndices, 0); }} void GeometryCache::ShapeData::drawWireInstances(gpu::Batch& batch, size_t count) const { uint32_t numIndices = (uint32_t)_wireIndicesView.getNumElements(); if (numIndices > 0) { batch.setInputBuffer(gpu::Stream::POSITION, _positionView); batch.setInputBuffer(gpu::Stream::NORMAL, _normalView); batch.setIndexBuffer(_wireIndicesView); batch.drawIndexedInstanced((gpu::uint32)count, gpu::LINES, numIndices, 0); }} static const size_t ICOSAHEDRON_TO_SPHERE_TESSELATION_COUNT = 3; size_t GeometryCache::getShapeTriangleCount(Shape shape) { return _shapes[shape]._indicesView.getNumElements() / 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; 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); Index baseVertex = 0; 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; 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); Index baseVertex = 0; 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; 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)); } Index baseVertex = 0; 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; 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)); } Index baseVertex = 0; 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); //Quad renders as flat Cube setupFlatShape(_shapes[Quad], 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 cachingLines 1–500 of 2346. 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 <shared/Shapes.h> #include <shared/PlatformHacks.h> #include <FSTReader.h> #include <NumericalConstants.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_transparent_web_browser_frag.h" #include "glowLine_vert.h" #include "glowLine_frag.h" #include "grid_frag.h" //#define WANT_DEBUG // @note: Originally size entity::NUM_SHAPES // As of Commit b93e91b9, render-utils no longer retains knowledge of // entity lib, and thus doesn't know about entity::NUM_SHAPES. Should // the enumerations be altered, this will need to be updated. // @see ShapeEntityItem.h static std::array<GeometryCache::Shape, (GeometryCache::NUM_SHAPES - 1)> MAPPING{ { GeometryCache::Triangle, GeometryCache::Quad, GeometryCache::Hexagon, GeometryCache::Octagon, GeometryCache::Circle, GeometryCache::Cube, GeometryCache::Sphere, GeometryCache::Tetrahedron, GeometryCache::Octahedron, GeometryCache::Dodecahedron, GeometryCache::Icosahedron, GeometryCache::Torus, GeometryCache::Cone, GeometryCache::Cylinder, } }; static const std::array<const char * const, GeometryCache::NUM_SHAPES> GEOCACHE_SHAPE_STRINGS{ { "Line", "Triangle", "Quad", "Hexagon", "Octagon", "Circle", "Cube", "Sphere", "Tetrahedron", "Octahedron", "Dodecahedron", "Icosahedron", "Torus", "Cone", "Cylinder" } }; 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 const gpu::Element TEXCOORD4_ELEMENT { gpu::VEC4, gpu::FLOAT, gpu::XYZW }; static gpu::Stream::FormatPointer SOLID_STREAM_FORMAT; static gpu::Stream::FormatPointer INSTANCED_SOLID_STREAM_FORMAT; static gpu::Stream::FormatPointer INSTANCED_SOLID_FADE_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::computeSimpleHullPointListForShape(const int entityShape, const glm::vec3 &entityExtents, QVector<glm::vec3> &outPointList) { auto geometryCache = DependencyManager::get<GeometryCache>(); const GeometryCache::Shape geometryShape = GeometryCache::getShapeForEntityShape( entityShape ); const GeometryCache::ShapeData * shapeData = geometryCache->getShapeData( geometryShape ); if (!shapeData){ //--EARLY EXIT--( data isn't ready for some reason... ) return; } const gpu::BufferView & shapeVerts = shapeData->_positionView; const gpu::BufferView::Size numItems = shapeVerts.getNumElements(); outPointList.reserve((int)numItems); QVector<glm::vec3> uniqueVerts; uniqueVerts.reserve((int)numItems); const float MAX_INCLUSIVE_FILTER_DISTANCE_SQUARED = 1.0e-6f; //< 1mm^2 for (gpu::BufferView::Index i = 0; i < (gpu::BufferView::Index)numItems; ++i) { const int numUniquePoints = (int)uniqueVerts.size(); const geometry::Vec &curVert = shapeVerts.get<geometry::Vec>(i); bool isUniquePoint = true; for (int uniqueIndex = 0; uniqueIndex < numUniquePoints; ++uniqueIndex) { const geometry::Vec knownVert = uniqueVerts[uniqueIndex]; const float distToKnownPoint = glm::length2(knownVert - curVert); if (distToKnownPoint <= MAX_INCLUSIVE_FILTER_DISTANCE_SQUARED) { isUniquePoint = false; break; } } if (!isUniquePoint) { //--EARLY ITERATION EXIT-- continue; } uniqueVerts.push_back(curVert); outPointList.push_back(curVert * entityExtents); } } 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) { gpu::Buffer::Size offset = vertexBuffer->getSize(); vertexBuffer->append(vertices); gpu::Buffer::Size viewSize = vertices.size() * sizeof(glm::vec3); _positionView = gpu::BufferView(vertexBuffer, offset, viewSize, SHAPE_VERTEX_STRIDE, POSITION_ELEMENT); _normalView = gpu::BufferView(vertexBuffer, offset + SHAPE_NORMALS_OFFSET, viewSize, SHAPE_VERTEX_STRIDE, NORMAL_ELEMENT); } void GeometryCache::ShapeData::setupIndices(gpu::BufferPointer& indexBuffer, const geometry::IndexVector& indices, const geometry::IndexVector& wireIndices) { gpu::Buffer::Size offset = indexBuffer->getSize(); if (!indices.empty()) { for (uint32_t i = 0; i < indices.size(); ++i) { indexBuffer->append((uint16_t)indices[i]); } } gpu::Size viewSize = indices.size() * sizeof(uint16_t); _indicesView = gpu::BufferView(indexBuffer, offset, viewSize, gpu::Element::INDEX_UINT16); offset = indexBuffer->getSize(); if (!wireIndices.empty()) { for (uint32_t i = 0; i < wireIndices.size(); ++i) { indexBuffer->append((uint16_t)wireIndices[i]); } } viewSize = wireIndices.size() * sizeof(uint16_t); _wireIndicesView = gpu::BufferView(indexBuffer, offset, viewSize, gpu::Element::INDEX_UINT16); } void GeometryCache::ShapeData::setupBatch(gpu::Batch& batch) const { batch.setInputBuffer(gpu::Stream::POSITION, _positionView); batch.setInputBuffer(gpu::Stream::NORMAL, _normalView); batch.setIndexBuffer(_indicesView); } void GeometryCache::ShapeData::draw(gpu::Batch& batch) const { uint32_t numIndices = (uint32_t)_indicesView.getNumElements(); if (numIndices > 0) { setupBatch(batch); batch.drawIndexed(gpu::TRIANGLES, numIndices, 0); } } void GeometryCache::ShapeData::drawWire(gpu::Batch& batch) const { uint32_t numIndices = (uint32_t)_wireIndicesView.getNumElements(); if (numIndices > 0) { batch.setInputBuffer(gpu::Stream::POSITION, _positionView); batch.setInputBuffer(gpu::Stream::NORMAL, _normalView); batch.setIndexBuffer(_wireIndicesView); batch.drawIndexed(gpu::LINES, numIndices, 0); } } void GeometryCache::ShapeData::drawInstances(gpu::Batch& batch, size_t count) const { uint32_t numIndices = (uint32_t)_indicesView.getNumElements(); if (numIndices > 0) { setupBatch(batch); batch.drawIndexedInstanced((gpu::uint32)count, gpu::TRIANGLES, numIndices, 0); } } void GeometryCache::ShapeData::drawWireInstances(gpu::Batch& batch, size_t count) const { uint32_t numIndices = (uint32_t)_wireIndicesView.getNumElements(); if (numIndices > 0) { batch.setInputBuffer(gpu::Stream::POSITION, _positionView); batch.setInputBuffer(gpu::Stream::NORMAL, _normalView); batch.setIndexBuffer(_wireIndicesView); batch.drawIndexedInstanced((gpu::uint32)count, gpu::LINES, numIndices, 0); } } static const size_t ICOSAHEDRON_TO_SPHERE_TESSELATION_COUNT = 3; size_t GeometryCache::getShapeTriangleCount(Shape shape) { return _shapes[shape]._indicesView.getNumElements() / 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; 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); Index baseVertex = 0; 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; 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); Index baseVertex = 0; 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; 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)); } Index baseVertex = 0; 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; 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)); } Index baseVertex = 0; 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); //Quad renders as flat Cube setupFlatShape(_shapes[Quad], 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