//// GeometryCache.cpp// interface/src/renderer//// Created by Andrzej Kapolka on 6/21/13.// Copyright 2013 High Fidelity, Inc.// Copyright 2024 Overte e.V.//// 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 <glm/gtx/transform.hpp>#include <shared/Shapes.h>#include <shared/PlatformHacks.h> #include <FSTReader.h>#include <NumericalConstants.h>#include <graphics/TextureMap.h>#include <graphics/BufferViewHelpers.h>#include <render/Args.h>#include <shaders/Shaders.h>#include <graphics/ShaderConstants.h> #include "render-utils/ShaderConstants.h"#include "TextureCache.h"#include "RenderUtilsLogging.h"#include "StencilMaskPass.h"#include "FadeEffect.h" #include "DeferredLightingEffect.h" namespace gr { using graphics::slot::texture::Texture; using graphics::slot::buffer::Buffer;} namespace ru { using render_utils::slot::texture::Texture; using render_utils::slot::buffer::Buffer;} //#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 TEXCOORD0_ELEMENT { gpu::VEC2, gpu::FLOAT, gpu::UV };static const gpu::Element TANGENT_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ };static const gpu::Element COLOR_ELEMENT { gpu::VEC4, gpu::NUINT8, gpu::RGBA };static const gpu::Element FADE4_ELEMENT { gpu::VEC4, gpu::FLOAT, gpu::XYZW };static const gpu::Element FADE1_ELEMENT { gpu::SCALAR, gpu::FLOAT, gpu::RAW }; static gpu::Stream::FormatPointer SOLID_STREAM_FORMAT;static gpu::Stream::FormatPointer WIRE_STREAM_FORMAT;static gpu::Stream::FormatPointer SOLID_STREAM_FADE_FORMAT;static gpu::Stream::FormatPointer WIRE_STREAM_FADE_FORMAT; static const uint SHAPE_VERTEX_STRIDE = sizeof(GeometryCache::ShapeVertex); // position, normal, texcoords, tangentstatic const uint SHAPE_NORMALS_OFFSET = offsetof(GeometryCache::ShapeVertex, normal);static const uint SHAPE_TEXCOORD0_OFFSET = offsetof(GeometryCache::ShapeVertex, uv);static const uint SHAPE_TANGENT_OFFSET = offsetof(GeometryCache::ShapeVertex, tangent); std::map<std::pair<bool, bool>, gpu::PipelinePointer> GeometryCache::_gridPipelines; 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 std::vector<ShapeVertex>& vertices) { gpu::Buffer::Size offset = vertexBuffer->getSize(); vertexBuffer->append(vertices); gpu::Buffer::Size viewSize = vertices.size() * sizeof(ShapeVertex); _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); _texCoordView = gpu::BufferView(vertexBuffer, offset + SHAPE_TEXCOORD0_OFFSET, viewSize, SHAPE_VERTEX_STRIDE, TEXCOORD0_ELEMENT); _tangentView = gpu::BufferView(vertexBuffer, offset + SHAPE_TANGENT_OFFSET, viewSize, SHAPE_VERTEX_STRIDE, TANGENT_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.setInputBuffer(gpu::Stream::TEXCOORD, _texCoordView); batch.setInputBuffer(gpu::Stream::TANGENT, _tangentView); 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;} 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; std::vector<GeometryCache::ShapeVertex> vertices; IndexVector solidIndices, wireIndices; IndexPairs wireSeenIndices; size_t faceCount = shape.faces.size(); size_t faceIndexCount = triangulatedFaceIndexCount<N>(); vertices.reserve(N * faceCount); 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); // Find two points on this face with the same Y tex coords, and find the vector going from the one with the smaller X tex coord to the one with the larger X tex coord vec3 faceTangent = vec3(0.0f); Index i1 = 0; Index i2 = i1 + 1; while (i1 < N) { if (shape.texCoords[f * N + i1].y == shape.texCoords[f * N + i2].y) { break; } if (i2 == N - 1) { i1++; i2 = i1 + 1; } else { i2++; } } if (i1 < N && i2 < N) { vec3 p1 = shape.vertices[face[i1]]; vec3 p2 = shape.vertices[face[i2]]; faceTangent = glm::normalize(p1 - p2); if (shape.texCoords[f * N + i1].x < shape.texCoords[f * N + i2].x) { faceTangent *= -1.0f; } } // Create the vertices for the face for (Index i = 0; i < N; i++) { Index originalIndex = face[i]; vertices.emplace_back(shape.vertices[originalIndex], faceNormal, shape.texCoords[f * N + i], faceTangent); } // 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);} vec2 calculateSphereTexCoord(const vec3& vertex) { float u = 1.0f - (std::atan2(-vertex.z, -vertex.x) / ((float)M_PI) + 1.0f) * 0.5f; if (vertex.y == 1.0f || vertex.y == -1.0f) { // Remember points at the top so we don't treat them as being along the seam u = NAN; } float v = 0.5f - std::asin(vertex.y) / (float)M_PI; return vec2(u, v);} const float M_PI_TIMES_2 = 2.0f * (float)M_PI; vec3 calculateSphereTangent(float u) { float phi = u * M_PI_TIMES_2; return -glm::normalize(glm::vec3(glm::sin(phi), 0.0f, glm::cos(phi)));} template <size_t N>void setupSmoothShape(GeometryCache::ShapeData& shapeData, const geometry::Solid<N>& shape, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer) { using namespace geometry; std::vector<GeometryCache::ShapeVertex> vertices; vertices.reserve(shape.vertices.size()); for (const auto& vertex : shape.vertices) { // We'll fill in the correct tangents later, once we correct the UVs vertices.emplace_back(vertex, vertex, calculateSphereTexCoord(vertex), vec3(0.0f)); } // We need to fix up the sphere's UVs because it's actually a tesselated icosahedron. See http://mft-dev.dk/uv-mapping-sphere/ size_t faceCount = shape.faces.size(); for (size_t f = 0; f < faceCount; f++) { // Fix zipper { float& u1 = vertices[shape.faces[f][0]].uv.x; float& u2 = vertices[shape.faces[f][1]].uv.x; float& u3 = vertices[shape.faces[f][2]].uv.x; if (glm::isnan(u1)) { u1 = (u2 + u3) / 2.0f; } if (glm::isnan(u2)) { u2 = (u1 + u3) / 2.0f; } if (glm::isnan(u3)) { u3 = (u1 + u2) / 2.0f; } const float U_THRESHOLD = 0.25f; float max = glm::max(u1, glm::max(u2, u3)); float min = glm::min(u1, glm::min(u2, u3)); if (max - min > U_THRESHOLD) { if (u1 < U_THRESHOLD) { u1 += 1.0f; } if (u2 < U_THRESHOLD) { u2 += 1.0f; } if (u3 < U_THRESHOLD) { u3 += 1.0f; } } } // Fix swirling at poles for (Index i = 0; i < N; i++) { Index originalIndex = shape.faces[f][i]; if (shape.vertices[originalIndex].y == 1.0f || shape.vertices[originalIndex].y == -1.0f) { float uSum = 0.0f; for (Index i2 = 1; i2 <= N - 1; i2++) { float u = vertices[shape.faces[f][(i + i2) % N]].uv.x; uSum += u; } uSum /= (float)(N - 1); vertices[originalIndex].uv.x = uSum; break; } } // Fill in tangents for (Index i = 0; i < N; i++) { vec3 tangent = calculateSphereTangent(vertices[shape.faces[f][i]].uv.x); vertices[shape.faces[f][i]].tangent = tangent; } } IndexVector solidIndices, wireIndices; IndexPairs wireSeenIndices; 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; std::vector<GeometryCache::ShapeVertex> 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.emplace_back(vec3(0.0f, 0.5f, 0.0f), vec3(0.0f, 1.0f, 0.0f), vec2((float)i / (float)N, 1.0f), vec3(0.0f)); } } else { for (const vec3& v : shape) { vertices.emplace_back(vec3(v.x, 0.5f, v.z), vec3(0.0f, 1.0f, 0.0f), vec2(v.x, v.z) + vec2(0.5f), vec3(1.0f, 0.0f, 0.0f)); } } for (const vec3& v : shape) { vertices.emplace_back(vec3(v.x, -0.5f, v.z), vec3(0.0f, -1.0f, 0.0f), vec2(-v.x, v.z) + vec2(0.5f), vec3(-1.0f, 0.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);Lines 1–500 of 2560. Load more
// // GeometryCache.cpp // interface/src/renderer // // Created by Andrzej Kapolka on 6/21/13. // Copyright 2013 High Fidelity, Inc. // Copyright 2024 Overte e.V. // // 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 <glm/gtx/transform.hpp> #include <shared/Shapes.h> #include <shared/PlatformHacks.h> #include <FSTReader.h> #include <NumericalConstants.h> #include <graphics/TextureMap.h> #include <graphics/BufferViewHelpers.h> #include <render/Args.h> #include <shaders/Shaders.h> #include <graphics/ShaderConstants.h> #include "render-utils/ShaderConstants.h" #include "TextureCache.h" #include "RenderUtilsLogging.h" #include "StencilMaskPass.h" #include "FadeEffect.h" #include "DeferredLightingEffect.h" namespace gr { using graphics::slot::texture::Texture; using graphics::slot::buffer::Buffer; } namespace ru { using render_utils::slot::texture::Texture; using render_utils::slot::buffer::Buffer; } //#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 TEXCOORD0_ELEMENT { gpu::VEC2, gpu::FLOAT, gpu::UV }; static const gpu::Element TANGENT_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ }; static const gpu::Element COLOR_ELEMENT { gpu::VEC4, gpu::NUINT8, gpu::RGBA }; static const gpu::Element FADE4_ELEMENT { gpu::VEC4, gpu::FLOAT, gpu::XYZW }; static const gpu::Element FADE1_ELEMENT { gpu::SCALAR, gpu::FLOAT, gpu::RAW }; static gpu::Stream::FormatPointer SOLID_STREAM_FORMAT; static gpu::Stream::FormatPointer WIRE_STREAM_FORMAT; static gpu::Stream::FormatPointer SOLID_STREAM_FADE_FORMAT; static gpu::Stream::FormatPointer WIRE_STREAM_FADE_FORMAT; static const uint SHAPE_VERTEX_STRIDE = sizeof(GeometryCache::ShapeVertex); // position, normal, texcoords, tangent static const uint SHAPE_NORMALS_OFFSET = offsetof(GeometryCache::ShapeVertex, normal); static const uint SHAPE_TEXCOORD0_OFFSET = offsetof(GeometryCache::ShapeVertex, uv); static const uint SHAPE_TANGENT_OFFSET = offsetof(GeometryCache::ShapeVertex, tangent); std::map<std::pair<bool, bool>, gpu::PipelinePointer> GeometryCache::_gridPipelines; 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 std::vector<ShapeVertex>& vertices) { gpu::Buffer::Size offset = vertexBuffer->getSize(); vertexBuffer->append(vertices); gpu::Buffer::Size viewSize = vertices.size() * sizeof(ShapeVertex); _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); _texCoordView = gpu::BufferView(vertexBuffer, offset + SHAPE_TEXCOORD0_OFFSET, viewSize, SHAPE_VERTEX_STRIDE, TEXCOORD0_ELEMENT); _tangentView = gpu::BufferView(vertexBuffer, offset + SHAPE_TANGENT_OFFSET, viewSize, SHAPE_VERTEX_STRIDE, TANGENT_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.setInputBuffer(gpu::Stream::TEXCOORD, _texCoordView); batch.setInputBuffer(gpu::Stream::TANGENT, _tangentView); 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; } 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; std::vector<GeometryCache::ShapeVertex> vertices; IndexVector solidIndices, wireIndices; IndexPairs wireSeenIndices; size_t faceCount = shape.faces.size(); size_t faceIndexCount = triangulatedFaceIndexCount<N>(); vertices.reserve(N * faceCount); 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); // Find two points on this face with the same Y tex coords, and find the vector going from the one with the smaller X tex coord to the one with the larger X tex coord vec3 faceTangent = vec3(0.0f); Index i1 = 0; Index i2 = i1 + 1; while (i1 < N) { if (shape.texCoords[f * N + i1].y == shape.texCoords[f * N + i2].y) { break; } if (i2 == N - 1) { i1++; i2 = i1 + 1; } else { i2++; } } if (i1 < N && i2 < N) { vec3 p1 = shape.vertices[face[i1]]; vec3 p2 = shape.vertices[face[i2]]; faceTangent = glm::normalize(p1 - p2); if (shape.texCoords[f * N + i1].x < shape.texCoords[f * N + i2].x) { faceTangent *= -1.0f; } } // Create the vertices for the face for (Index i = 0; i < N; i++) { Index originalIndex = face[i]; vertices.emplace_back(shape.vertices[originalIndex], faceNormal, shape.texCoords[f * N + i], faceTangent); } // 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); } vec2 calculateSphereTexCoord(const vec3& vertex) { float u = 1.0f - (std::atan2(-vertex.z, -vertex.x) / ((float)M_PI) + 1.0f) * 0.5f; if (vertex.y == 1.0f || vertex.y == -1.0f) { // Remember points at the top so we don't treat them as being along the seam u = NAN; } float v = 0.5f - std::asin(vertex.y) / (float)M_PI; return vec2(u, v); } const float M_PI_TIMES_2 = 2.0f * (float)M_PI; vec3 calculateSphereTangent(float u) { float phi = u * M_PI_TIMES_2; return -glm::normalize(glm::vec3(glm::sin(phi), 0.0f, glm::cos(phi))); } template <size_t N> void setupSmoothShape(GeometryCache::ShapeData& shapeData, const geometry::Solid<N>& shape, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer) { using namespace geometry; std::vector<GeometryCache::ShapeVertex> vertices; vertices.reserve(shape.vertices.size()); for (const auto& vertex : shape.vertices) { // We'll fill in the correct tangents later, once we correct the UVs vertices.emplace_back(vertex, vertex, calculateSphereTexCoord(vertex), vec3(0.0f)); } // We need to fix up the sphere's UVs because it's actually a tesselated icosahedron. See http://mft-dev.dk/uv-mapping-sphere/ size_t faceCount = shape.faces.size(); for (size_t f = 0; f < faceCount; f++) { // Fix zipper { float& u1 = vertices[shape.faces[f][0]].uv.x; float& u2 = vertices[shape.faces[f][1]].uv.x; float& u3 = vertices[shape.faces[f][2]].uv.x; if (glm::isnan(u1)) { u1 = (u2 + u3) / 2.0f; } if (glm::isnan(u2)) { u2 = (u1 + u3) / 2.0f; } if (glm::isnan(u3)) { u3 = (u1 + u2) / 2.0f; } const float U_THRESHOLD = 0.25f; float max = glm::max(u1, glm::max(u2, u3)); float min = glm::min(u1, glm::min(u2, u3)); if (max - min > U_THRESHOLD) { if (u1 < U_THRESHOLD) { u1 += 1.0f; } if (u2 < U_THRESHOLD) { u2 += 1.0f; } if (u3 < U_THRESHOLD) { u3 += 1.0f; } } } // Fix swirling at poles for (Index i = 0; i < N; i++) { Index originalIndex = shape.faces[f][i]; if (shape.vertices[originalIndex].y == 1.0f || shape.vertices[originalIndex].y == -1.0f) { float uSum = 0.0f; for (Index i2 = 1; i2 <= N - 1; i2++) { float u = vertices[shape.faces[f][(i + i2) % N]].uv.x; uSum += u; } uSum /= (float)(N - 1); vertices[originalIndex].uv.x = uSum; break; } } // Fill in tangents for (Index i = 0; i < N; i++) { vec3 tangent = calculateSphereTangent(vertices[shape.faces[f][i]].uv.x); vertices[shape.faces[f][i]].tangent = tangent; } } IndexVector solidIndices, wireIndices; IndexPairs wireSeenIndices; 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; std::vector<GeometryCache::ShapeVertex> 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.emplace_back(vec3(0.0f, 0.5f, 0.0f), vec3(0.0f, 1.0f, 0.0f), vec2((float)i / (float)N, 1.0f), vec3(0.0f)); } } else { for (const vec3& v : shape) { vertices.emplace_back(vec3(v.x, 0.5f, v.z), vec3(0.0f, 1.0f, 0.0f), vec2(v.x, v.z) + vec2(0.5f), vec3(1.0f, 0.0f, 0.0f)); } } for (const vec3& v : shape) { vertices.emplace_back(vec3(v.x, -0.5f, v.z), vec3(0.0f, -1.0f, 0.0f), vec2(-v.x, v.z) + vec2(0.5f), vec3(-1.0f, 0.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);