//// GeometryCache.cpp// interface/src/renderer//// Created by Andrzej Kapolka on 6/21/13.// Copyright 2013 High Fidelity, Inc.//// Distributed under the Apache License, Version 2.0.// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// #include "GeometryCache.h" #include <cmath> #include <QNetworkReply>#include <QThreadPool> #include <FSTReader.h>#include <NumericalConstants.h> #include "TextureCache.h"#include "RenderUtilsLogging.h" #include "standardTransformPNTC_vert.h"#include "standardDrawTexture_frag.h" #include "gpu/StandardShaderLib.h" #include "model/TextureMap.h" //#define WANT_DEBUG const int GeometryCache::UNKNOWN_ID = -1; static const int VERTICES_PER_TRIANGLE = 3; //static const uint FLOATS_PER_VERTEX = 3;//static const uint TRIANGLES_PER_QUAD = 2;//static const uint CUBE_FACES = 6;//static const uint CUBE_VERTICES_PER_FACE = 4;//static const uint CUBE_VERTICES = CUBE_FACES * CUBE_VERTICES_PER_FACE;//static const uint CUBE_VERTEX_POINTS = CUBE_VERTICES * FLOATS_PER_VERTEX;//static const uint CUBE_INDICES = CUBE_FACES * TRIANGLES_PER_QUAD * VERTICES_PER_TRIANGLE;//static const uint SPHERE_LATITUDES = 24;//static const uint SPHERE_MERIDIANS = SPHERE_LATITUDES * 2;//static const uint SPHERE_INDICES = SPHERE_MERIDIANS * (SPHERE_LATITUDES - 1) * TRIANGLES_PER_QUAD * VERTICES_PER_TRIANGLE; static const gpu::Element POSITION_ELEMENT{ gpu::VEC3, gpu::FLOAT, gpu::XYZ };static const gpu::Element NORMAL_ELEMENT{ gpu::VEC3, gpu::FLOAT, gpu::XYZ };static const gpu::Element COLOR_ELEMENT{ gpu::VEC4, gpu::NUINT8, gpu::RGBA };static const gpu::Element TRANSFORM_ELEMENT{ gpu::MAT4, gpu::FLOAT, gpu::XYZW }; static gpu::Stream::FormatPointer SOLID_STREAM_FORMAT;static gpu::Stream::FormatPointer INSTANCED_SOLID_STREAM_FORMAT; static const uint SHAPE_VERTEX_STRIDE = sizeof(glm::vec3) * 2; // vertices and normalsstatic const uint SHAPE_NORMALS_OFFSET = sizeof(glm::vec3);static const gpu::Type SHAPE_INDEX_TYPE = gpu::UINT16;static const uint SHAPE_INDEX_SIZE = sizeof(gpu::uint16); void GeometryCache::ShapeData::setupVertices(gpu::BufferPointer& vertexBuffer, const 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 IndexVector& indices, const 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); }} const VertexVector& icosahedronVertices() { static const float phi = (1.0f + sqrtf(5.0f)) / 2.0f; static const float a = 0.5f; static const float b = 1.0f / (2.0f * phi); static const VertexVector vertices{ // vec3(0, b, -a), vec3(-b, a, 0), vec3(b, a, 0), // vec3(0, b, a), vec3(b, a, 0), vec3(-b, a, 0), // vec3(0, b, a), vec3(-a, 0, b), vec3(0, -b, a), // vec3(0, b, a), vec3(0, -b, a), vec3(a, 0, b), // vec3(0, b, -a), vec3(a, 0, -b), vec3(0, -b, -a),// vec3(0, b, -a), vec3(0, -b, -a), vec3(-a, 0, -b), // vec3(0, -b, a), vec3(-b, -a, 0), vec3(b, -a, 0), // vec3(0, -b, -a), vec3(b, -a, 0), vec3(-b, -a, 0), // vec3(-b, a, 0), vec3(-a, 0, -b), vec3(-a, 0, b), // vec3(-b, -a, 0), vec3(-a, 0, b), vec3(-a, 0, -b), // vec3(b, a, 0), vec3(a, 0, b), vec3(a, 0, -b), // vec3(b, -a, 0), vec3(a, 0, -b), vec3(a, 0, b), // vec3(0, b, a), vec3(-b, a, 0), vec3(-a, 0, b), // vec3(0, b, a), vec3(a, 0, b), vec3(b, a, 0), // vec3(0, b, -a), vec3(-a, 0, -b), vec3(-b, a, 0), // vec3(0, b, -a), vec3(b, a, 0), vec3(a, 0, -b), // vec3(0, -b, -a), vec3(-b, -a, 0), vec3(-a, 0, -b), // vec3(0, -b, -a), vec3(a, 0, -b), vec3(b, -a, 0), // vec3(0, -b, a), vec3(-a, 0, b), vec3(-b, -a, 0), // vec3(0, -b, a), vec3(b, -a, 0), vec3(a, 0, b) }; // return vertices;} const VertexVector& tetrahedronVertices() { static const float a = 1.0f / sqrtf(2.0f); static const auto A = vec3(0, 1, a); static const auto B = vec3(0, -1, a); static const auto C = vec3(1, 0, -a); static const auto D = vec3(-1, 0, -a); static const VertexVector vertices{ A, B, C, D, B, A, C, D, A, C, B, D, }; return vertices;} static const size_t TESSELTATION_MULTIPLIER = 4;static const size_t ICOSAHEDRON_TO_SPHERE_TESSELATION_COUNT = 3;static const size_t VECTOR_TO_VECTOR_WITH_NORMAL_MULTIPLER = 2; VertexVector tesselate(const VertexVector& startingTriangles, int count) { VertexVector triangles = startingTriangles; if (0 != (triangles.size() % 3)) { throw std::runtime_error("Bad number of vertices for tesselation"); } for (size_t i = 0; i < triangles.size(); ++i) { triangles[i] = glm::normalize(triangles[i]); } VertexVector newTriangles; while (count) { newTriangles.clear(); // Tesselation takes one triangle and makes it into 4 triangles // See https://en.wikipedia.org/wiki/Space-filling_tree#/media/File:Space_Filling_Tree_Tri_iter_1_2_3.png newTriangles.reserve(triangles.size() * TESSELTATION_MULTIPLIER); for (size_t i = 0; i < triangles.size(); i += VERTICES_PER_TRIANGLE) { const vec3& a = triangles[i]; const vec3& b = triangles[i + 1]; const vec3& c = triangles[i + 2]; vec3 ab = glm::normalize(a + b); vec3 bc = glm::normalize(b + c); vec3 ca = glm::normalize(c + a); newTriangles.push_back(a); newTriangles.push_back(ab); newTriangles.push_back(ca); newTriangles.push_back(b); newTriangles.push_back(bc); newTriangles.push_back(ab); newTriangles.push_back(c); newTriangles.push_back(ca); newTriangles.push_back(bc); newTriangles.push_back(ab); newTriangles.push_back(bc); newTriangles.push_back(ca); } triangles.swap(newTriangles); --count; } return triangles;} 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);} // FIXME solids need per-face vertices, but smooth shaded// components do not. Find a way to support using draw elements// or draw arrays as appropriate// Maybe special case cone and cylinder since they combine flat// and smooth shadingvoid GeometryCache::buildShapes() { auto vertexBuffer = std::make_shared<gpu::Buffer>(); auto indexBuffer = std::make_shared<gpu::Buffer>(); size_t startingIndex = 0; // Cube startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE; { ShapeData& shapeData = _shapes[Cube]; VertexVector vertices; // front vertices.push_back(vec3(1, 1, 1)); vertices.push_back(vec3(0, 0, 1)); vertices.push_back(vec3(-1, 1, 1)); vertices.push_back(vec3(0, 0, 1)); vertices.push_back(vec3(-1, -1, 1)); vertices.push_back(vec3(0, 0, 1)); vertices.push_back(vec3(1, -1, 1)); vertices.push_back(vec3(0, 0, 1)); // right vertices.push_back(vec3(1, 1, 1)); vertices.push_back(vec3(1, 0, 0)); vertices.push_back(vec3(1, -1, 1)); vertices.push_back(vec3(1, 0, 0)); vertices.push_back(vec3(1, -1, -1)); vertices.push_back(vec3(1, 0, 0)); vertices.push_back(vec3(1, 1, -1)); vertices.push_back(vec3(1, 0, 0)); // top vertices.push_back(vec3(1, 1, 1)); vertices.push_back(vec3(0, 1, 0)); vertices.push_back(vec3(1, 1, -1)); vertices.push_back(vec3(0, 1, 0)); vertices.push_back(vec3(-1, 1, -1)); vertices.push_back(vec3(0, 1, 0)); vertices.push_back(vec3(-1, 1, 1)); vertices.push_back(vec3(0, 1, 0)); // left vertices.push_back(vec3(-1, 1, 1)); vertices.push_back(vec3(-1, 0, 0)); vertices.push_back(vec3(-1, 1, -1)); vertices.push_back(vec3(-1, 0, 0)); vertices.push_back(vec3(-1, -1, -1)); vertices.push_back(vec3(-1, 0, 0)); vertices.push_back(vec3(-1, -1, 1)); vertices.push_back(vec3(-1, 0, 0)); // bottom vertices.push_back(vec3(-1, -1, -1)); vertices.push_back(vec3(0, -1, 0)); vertices.push_back(vec3(1, -1, -1)); vertices.push_back(vec3(0, -1, 0)); vertices.push_back(vec3(1, -1, 1)); vertices.push_back(vec3(0, -1, 0)); vertices.push_back(vec3(-1, -1, 1)); vertices.push_back(vec3(0, -1, 0)); // back vertices.push_back(vec3(1, -1, -1)); vertices.push_back(vec3(0, 0, -1)); vertices.push_back(vec3(-1, -1, -1)); vertices.push_back(vec3(0, 0, -1)); vertices.push_back(vec3(-1, 1, -1)); vertices.push_back(vec3(0, 0, -1)); vertices.push_back(vec3(1, 1, -1)); vertices.push_back(vec3(0, 0, -1)); static const size_t VERTEX_FORMAT_SIZE = 2; static const size_t VERTEX_OFFSET = 0; for (size_t i = 0; i < vertices.size(); ++i) { auto vertexIndex = i; // Make a unit cube by having the vertices (at index N) // while leaving the normals (at index N + 1) alone if (VERTEX_OFFSET == vertexIndex % VERTEX_FORMAT_SIZE) { vertices[vertexIndex] *= 0.5f; } } shapeData.setupVertices(_shapeVertices, vertices); IndexVector indices{ 0, 1, 2, 2, 3, 0, // front 4, 5, 6, 6, 7, 4, // right 8, 9, 10, 10, 11, 8, // top 12, 13, 14, 14, 15, 12, // left 16, 17, 18, 18, 19, 16, // bottom 20, 21, 22, 22, 23, 20 // back }; for (auto& index : indices) { index += (uint16_t)startingIndex; } IndexVector wireIndices{ 0, 1, 1, 2, 2, 3, 3, 0, // front 20, 21, 21, 22, 22, 23, 23, 20, // back 0, 23, 1, 22, 2, 21, 3, 20 // sides }; for (size_t i = 0; i < wireIndices.size(); ++i) { indices[i] += (uint16_t)startingIndex; } shapeData.setupIndices(_shapeIndices, indices, wireIndices); } // Tetrahedron startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE; { ShapeData& shapeData = _shapes[Tetrahedron]; size_t vertexCount = 4; VertexVector vertices; { VertexVector originalVertices = tetrahedronVertices(); vertexCount = originalVertices.size(); vertices.reserve(originalVertices.size() * VECTOR_TO_VECTOR_WITH_NORMAL_MULTIPLER); for (size_t i = 0; i < originalVertices.size(); i += VERTICES_PER_TRIANGLE) { auto triangleStartIndex = i; vec3 faceNormal; for (size_t j = 0; j < VERTICES_PER_TRIANGLE; ++j) { auto triangleVertexIndex = j; auto vertexIndex = triangleStartIndex + triangleVertexIndex; faceNormal += originalVertices[vertexIndex]; } faceNormal = glm::normalize(faceNormal); for (size_t j = 0; j < VERTICES_PER_TRIANGLE; ++j) { auto triangleVertexIndex = j; auto vertexIndex = triangleStartIndex + triangleVertexIndex; vertices.push_back(glm::normalize(originalVertices[vertexIndex])); vertices.push_back(faceNormal); } } } shapeData.setupVertices(_shapeVertices, vertices); IndexVector indices; for (size_t i = 0; i < vertexCount; i += VERTICES_PER_TRIANGLE) { auto triangleStartIndex = i; for (size_t j = 0; j < VERTICES_PER_TRIANGLE; ++j) { auto triangleVertexIndex = j; auto vertexIndex = triangleStartIndex + triangleVertexIndex; indices.push_back((uint16_t)(vertexIndex + startingIndex)); } } IndexVector wireIndices{ 0, 1, 1, 2, 2, 0, 0, 3, 1, 3, 2, 3, }; for (size_t i = 0; i < wireIndices.size(); ++i) { wireIndices[i] += (uint16_t)startingIndex; } shapeData.setupIndices(_shapeIndices, indices, wireIndices); } // Sphere // FIXME this uses way more vertices than required. Should find a way to calculate the indices // using shared vertices for better vertex caching startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE; { ShapeData& shapeData = _shapes[Sphere]; VertexVector vertices; IndexVector indices; { VertexVector originalVertices = tesselate(icosahedronVertices(), ICOSAHEDRON_TO_SPHERE_TESSELATION_COUNT); vertices.reserve(originalVertices.size() * VECTOR_TO_VECTOR_WITH_NORMAL_MULTIPLER); for (size_t i = 0; i < originalVertices.size(); i += VERTICES_PER_TRIANGLE) { auto triangleStartIndex = i; for (int j = 0; j < VERTICES_PER_TRIANGLE; ++j) { auto triangleVertexIndex = j; auto vertexIndex = triangleStartIndex + triangleVertexIndex; const auto& vertex = originalVertices[i + j]; // Spheres use the same values for vertices and normals vertices.push_back(vertex); vertices.push_back(vertex); indices.push_back((uint16_t)(vertexIndex + startingIndex)); } } } shapeData.setupVertices(_shapeVertices, vertices); // FIXME don't use solid indices for wire drawing. shapeData.setupIndices(_shapeIndices, indices, indices); } // Icosahedron startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE; { ShapeData& shapeData = _shapes[Icosahedron]; VertexVector vertices; IndexVector indices; { const VertexVector& originalVertices = icosahedronVertices(); vertices.reserve(originalVertices.size() * VECTOR_TO_VECTOR_WITH_NORMAL_MULTIPLER); for (size_t i = 0; i < originalVertices.size(); i += 3) { auto triangleStartIndex = i; vec3 faceNormal; for (int j = 0; j < VERTICES_PER_TRIANGLE; ++j) { auto triangleVertexIndex = j; auto vertexIndex = triangleStartIndex + triangleVertexIndex; faceNormal += originalVertices[vertexIndex]; } faceNormal = glm::normalize(faceNormal); for (int j = 0; j < VERTICES_PER_TRIANGLE; ++j) { auto triangleVertexIndex = j; auto vertexIndex = triangleStartIndex + triangleVertexIndex; vertices.push_back(glm::normalize(originalVertices[vertexIndex])); vertices.push_back(faceNormal); indices.push_back((uint16_t)(vertexIndex + startingIndex)); } } } shapeData.setupVertices(_shapeVertices, vertices); // FIXME don't use solid indices for wire drawing. shapeData.setupIndices(_shapeIndices, indices, indices); } // Line startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE; { ShapeData& shapeData = _shapes[Line]; shapeData.setupVertices(_shapeVertices, VertexVector{ vec3(-0.5, 0, 0), vec3(-0.5f, 0, 0), vec3(0.5f, 0, 0), vec3(0.5f, 0, 0) }); IndexVector wireIndices; // Only two indices wireIndices.push_back(0 + (uint16_t)startingIndex); wireIndices.push_back(1 + (uint16_t)startingIndex); shapeData.setupIndices(_shapeIndices, IndexVector(), wireIndices); } // Not implememented yet: //Triangle, //Quad, //Circle, //Octahetron, //Dodecahedron, //Torus, //Cone, //Cylinder,} gpu::Stream::FormatPointer& getSolidStreamFormat() { if (!SOLID_STREAM_FORMAT) { SOLID_STREAM_FORMAT = std::make_shared<gpu::Stream::Format>(); // 1 for everyone SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT); SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT); } return SOLID_STREAM_FORMAT;} gpu::Stream::FormatPointer& getInstancedSolidStreamFormat() { if (!INSTANCED_SOLID_STREAM_FORMAT) { INSTANCED_SOLID_STREAM_FORMAT = std::make_shared<gpu::Stream::Format>(); // 1 for everyone INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT); INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT); INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE);Lines 1–500 of 1743. Load more
// // GeometryCache.cpp // interface/src/renderer // // Created by Andrzej Kapolka on 6/21/13. // Copyright 2013 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "GeometryCache.h" #include <cmath> #include <QNetworkReply> #include <QThreadPool> #include <FSTReader.h> #include <NumericalConstants.h> #include "TextureCache.h" #include "RenderUtilsLogging.h" #include "standardTransformPNTC_vert.h" #include "standardDrawTexture_frag.h" #include "gpu/StandardShaderLib.h" #include "model/TextureMap.h" //#define WANT_DEBUG const int GeometryCache::UNKNOWN_ID = -1; static const int VERTICES_PER_TRIANGLE = 3; //static const uint FLOATS_PER_VERTEX = 3; //static const uint TRIANGLES_PER_QUAD = 2; //static const uint CUBE_FACES = 6; //static const uint CUBE_VERTICES_PER_FACE = 4; //static const uint CUBE_VERTICES = CUBE_FACES * CUBE_VERTICES_PER_FACE; //static const uint CUBE_VERTEX_POINTS = CUBE_VERTICES * FLOATS_PER_VERTEX; //static const uint CUBE_INDICES = CUBE_FACES * TRIANGLES_PER_QUAD * VERTICES_PER_TRIANGLE; //static const uint SPHERE_LATITUDES = 24; //static const uint SPHERE_MERIDIANS = SPHERE_LATITUDES * 2; //static const uint SPHERE_INDICES = SPHERE_MERIDIANS * (SPHERE_LATITUDES - 1) * TRIANGLES_PER_QUAD * VERTICES_PER_TRIANGLE; static const gpu::Element POSITION_ELEMENT{ gpu::VEC3, gpu::FLOAT, gpu::XYZ }; static const gpu::Element NORMAL_ELEMENT{ gpu::VEC3, gpu::FLOAT, gpu::XYZ }; static const gpu::Element COLOR_ELEMENT{ gpu::VEC4, gpu::NUINT8, gpu::RGBA }; static const gpu::Element TRANSFORM_ELEMENT{ gpu::MAT4, gpu::FLOAT, gpu::XYZW }; static gpu::Stream::FormatPointer SOLID_STREAM_FORMAT; static gpu::Stream::FormatPointer INSTANCED_SOLID_STREAM_FORMAT; static const uint SHAPE_VERTEX_STRIDE = sizeof(glm::vec3) * 2; // vertices and normals static const uint SHAPE_NORMALS_OFFSET = sizeof(glm::vec3); static const gpu::Type SHAPE_INDEX_TYPE = gpu::UINT16; static const uint SHAPE_INDEX_SIZE = sizeof(gpu::uint16); void GeometryCache::ShapeData::setupVertices(gpu::BufferPointer& vertexBuffer, const 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 IndexVector& indices, const 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); } } const VertexVector& icosahedronVertices() { static const float phi = (1.0f + sqrtf(5.0f)) / 2.0f; static const float a = 0.5f; static const float b = 1.0f / (2.0f * phi); static const VertexVector vertices{ // vec3(0, b, -a), vec3(-b, a, 0), vec3(b, a, 0), // vec3(0, b, a), vec3(b, a, 0), vec3(-b, a, 0), // vec3(0, b, a), vec3(-a, 0, b), vec3(0, -b, a), // vec3(0, b, a), vec3(0, -b, a), vec3(a, 0, b), // vec3(0, b, -a), vec3(a, 0, -b), vec3(0, -b, -a),// vec3(0, b, -a), vec3(0, -b, -a), vec3(-a, 0, -b), // vec3(0, -b, a), vec3(-b, -a, 0), vec3(b, -a, 0), // vec3(0, -b, -a), vec3(b, -a, 0), vec3(-b, -a, 0), // vec3(-b, a, 0), vec3(-a, 0, -b), vec3(-a, 0, b), // vec3(-b, -a, 0), vec3(-a, 0, b), vec3(-a, 0, -b), // vec3(b, a, 0), vec3(a, 0, b), vec3(a, 0, -b), // vec3(b, -a, 0), vec3(a, 0, -b), vec3(a, 0, b), // vec3(0, b, a), vec3(-b, a, 0), vec3(-a, 0, b), // vec3(0, b, a), vec3(a, 0, b), vec3(b, a, 0), // vec3(0, b, -a), vec3(-a, 0, -b), vec3(-b, a, 0), // vec3(0, b, -a), vec3(b, a, 0), vec3(a, 0, -b), // vec3(0, -b, -a), vec3(-b, -a, 0), vec3(-a, 0, -b), // vec3(0, -b, -a), vec3(a, 0, -b), vec3(b, -a, 0), // vec3(0, -b, a), vec3(-a, 0, b), vec3(-b, -a, 0), // vec3(0, -b, a), vec3(b, -a, 0), vec3(a, 0, b) }; // return vertices; } const VertexVector& tetrahedronVertices() { static const float a = 1.0f / sqrtf(2.0f); static const auto A = vec3(0, 1, a); static const auto B = vec3(0, -1, a); static const auto C = vec3(1, 0, -a); static const auto D = vec3(-1, 0, -a); static const VertexVector vertices{ A, B, C, D, B, A, C, D, A, C, B, D, }; return vertices; } static const size_t TESSELTATION_MULTIPLIER = 4; static const size_t ICOSAHEDRON_TO_SPHERE_TESSELATION_COUNT = 3; static const size_t VECTOR_TO_VECTOR_WITH_NORMAL_MULTIPLER = 2; VertexVector tesselate(const VertexVector& startingTriangles, int count) { VertexVector triangles = startingTriangles; if (0 != (triangles.size() % 3)) { throw std::runtime_error("Bad number of vertices for tesselation"); } for (size_t i = 0; i < triangles.size(); ++i) { triangles[i] = glm::normalize(triangles[i]); } VertexVector newTriangles; while (count) { newTriangles.clear(); // Tesselation takes one triangle and makes it into 4 triangles // See https://en.wikipedia.org/wiki/Space-filling_tree#/media/File:Space_Filling_Tree_Tri_iter_1_2_3.png newTriangles.reserve(triangles.size() * TESSELTATION_MULTIPLIER); for (size_t i = 0; i < triangles.size(); i += VERTICES_PER_TRIANGLE) { const vec3& a = triangles[i]; const vec3& b = triangles[i + 1]; const vec3& c = triangles[i + 2]; vec3 ab = glm::normalize(a + b); vec3 bc = glm::normalize(b + c); vec3 ca = glm::normalize(c + a); newTriangles.push_back(a); newTriangles.push_back(ab); newTriangles.push_back(ca); newTriangles.push_back(b); newTriangles.push_back(bc); newTriangles.push_back(ab); newTriangles.push_back(c); newTriangles.push_back(ca); newTriangles.push_back(bc); newTriangles.push_back(ab); newTriangles.push_back(bc); newTriangles.push_back(ca); } triangles.swap(newTriangles); --count; } return triangles; } 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); } // FIXME solids need per-face vertices, but smooth shaded // components do not. Find a way to support using draw elements // or draw arrays as appropriate // Maybe special case cone and cylinder since they combine flat // and smooth shading void GeometryCache::buildShapes() { auto vertexBuffer = std::make_shared<gpu::Buffer>(); auto indexBuffer = std::make_shared<gpu::Buffer>(); size_t startingIndex = 0; // Cube startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE; { ShapeData& shapeData = _shapes[Cube]; VertexVector vertices; // front vertices.push_back(vec3(1, 1, 1)); vertices.push_back(vec3(0, 0, 1)); vertices.push_back(vec3(-1, 1, 1)); vertices.push_back(vec3(0, 0, 1)); vertices.push_back(vec3(-1, -1, 1)); vertices.push_back(vec3(0, 0, 1)); vertices.push_back(vec3(1, -1, 1)); vertices.push_back(vec3(0, 0, 1)); // right vertices.push_back(vec3(1, 1, 1)); vertices.push_back(vec3(1, 0, 0)); vertices.push_back(vec3(1, -1, 1)); vertices.push_back(vec3(1, 0, 0)); vertices.push_back(vec3(1, -1, -1)); vertices.push_back(vec3(1, 0, 0)); vertices.push_back(vec3(1, 1, -1)); vertices.push_back(vec3(1, 0, 0)); // top vertices.push_back(vec3(1, 1, 1)); vertices.push_back(vec3(0, 1, 0)); vertices.push_back(vec3(1, 1, -1)); vertices.push_back(vec3(0, 1, 0)); vertices.push_back(vec3(-1, 1, -1)); vertices.push_back(vec3(0, 1, 0)); vertices.push_back(vec3(-1, 1, 1)); vertices.push_back(vec3(0, 1, 0)); // left vertices.push_back(vec3(-1, 1, 1)); vertices.push_back(vec3(-1, 0, 0)); vertices.push_back(vec3(-1, 1, -1)); vertices.push_back(vec3(-1, 0, 0)); vertices.push_back(vec3(-1, -1, -1)); vertices.push_back(vec3(-1, 0, 0)); vertices.push_back(vec3(-1, -1, 1)); vertices.push_back(vec3(-1, 0, 0)); // bottom vertices.push_back(vec3(-1, -1, -1)); vertices.push_back(vec3(0, -1, 0)); vertices.push_back(vec3(1, -1, -1)); vertices.push_back(vec3(0, -1, 0)); vertices.push_back(vec3(1, -1, 1)); vertices.push_back(vec3(0, -1, 0)); vertices.push_back(vec3(-1, -1, 1)); vertices.push_back(vec3(0, -1, 0)); // back vertices.push_back(vec3(1, -1, -1)); vertices.push_back(vec3(0, 0, -1)); vertices.push_back(vec3(-1, -1, -1)); vertices.push_back(vec3(0, 0, -1)); vertices.push_back(vec3(-1, 1, -1)); vertices.push_back(vec3(0, 0, -1)); vertices.push_back(vec3(1, 1, -1)); vertices.push_back(vec3(0, 0, -1)); static const size_t VERTEX_FORMAT_SIZE = 2; static const size_t VERTEX_OFFSET = 0; for (size_t i = 0; i < vertices.size(); ++i) { auto vertexIndex = i; // Make a unit cube by having the vertices (at index N) // while leaving the normals (at index N + 1) alone if (VERTEX_OFFSET == vertexIndex % VERTEX_FORMAT_SIZE) { vertices[vertexIndex] *= 0.5f; } } shapeData.setupVertices(_shapeVertices, vertices); IndexVector indices{ 0, 1, 2, 2, 3, 0, // front 4, 5, 6, 6, 7, 4, // right 8, 9, 10, 10, 11, 8, // top 12, 13, 14, 14, 15, 12, // left 16, 17, 18, 18, 19, 16, // bottom 20, 21, 22, 22, 23, 20 // back }; for (auto& index : indices) { index += (uint16_t)startingIndex; } IndexVector wireIndices{ 0, 1, 1, 2, 2, 3, 3, 0, // front 20, 21, 21, 22, 22, 23, 23, 20, // back 0, 23, 1, 22, 2, 21, 3, 20 // sides }; for (size_t i = 0; i < wireIndices.size(); ++i) { indices[i] += (uint16_t)startingIndex; } shapeData.setupIndices(_shapeIndices, indices, wireIndices); } // Tetrahedron startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE; { ShapeData& shapeData = _shapes[Tetrahedron]; size_t vertexCount = 4; VertexVector vertices; { VertexVector originalVertices = tetrahedronVertices(); vertexCount = originalVertices.size(); vertices.reserve(originalVertices.size() * VECTOR_TO_VECTOR_WITH_NORMAL_MULTIPLER); for (size_t i = 0; i < originalVertices.size(); i += VERTICES_PER_TRIANGLE) { auto triangleStartIndex = i; vec3 faceNormal; for (size_t j = 0; j < VERTICES_PER_TRIANGLE; ++j) { auto triangleVertexIndex = j; auto vertexIndex = triangleStartIndex + triangleVertexIndex; faceNormal += originalVertices[vertexIndex]; } faceNormal = glm::normalize(faceNormal); for (size_t j = 0; j < VERTICES_PER_TRIANGLE; ++j) { auto triangleVertexIndex = j; auto vertexIndex = triangleStartIndex + triangleVertexIndex; vertices.push_back(glm::normalize(originalVertices[vertexIndex])); vertices.push_back(faceNormal); } } } shapeData.setupVertices(_shapeVertices, vertices); IndexVector indices; for (size_t i = 0; i < vertexCount; i += VERTICES_PER_TRIANGLE) { auto triangleStartIndex = i; for (size_t j = 0; j < VERTICES_PER_TRIANGLE; ++j) { auto triangleVertexIndex = j; auto vertexIndex = triangleStartIndex + triangleVertexIndex; indices.push_back((uint16_t)(vertexIndex + startingIndex)); } } IndexVector wireIndices{ 0, 1, 1, 2, 2, 0, 0, 3, 1, 3, 2, 3, }; for (size_t i = 0; i < wireIndices.size(); ++i) { wireIndices[i] += (uint16_t)startingIndex; } shapeData.setupIndices(_shapeIndices, indices, wireIndices); } // Sphere // FIXME this uses way more vertices than required. Should find a way to calculate the indices // using shared vertices for better vertex caching startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE; { ShapeData& shapeData = _shapes[Sphere]; VertexVector vertices; IndexVector indices; { VertexVector originalVertices = tesselate(icosahedronVertices(), ICOSAHEDRON_TO_SPHERE_TESSELATION_COUNT); vertices.reserve(originalVertices.size() * VECTOR_TO_VECTOR_WITH_NORMAL_MULTIPLER); for (size_t i = 0; i < originalVertices.size(); i += VERTICES_PER_TRIANGLE) { auto triangleStartIndex = i; for (int j = 0; j < VERTICES_PER_TRIANGLE; ++j) { auto triangleVertexIndex = j; auto vertexIndex = triangleStartIndex + triangleVertexIndex; const auto& vertex = originalVertices[i + j]; // Spheres use the same values for vertices and normals vertices.push_back(vertex); vertices.push_back(vertex); indices.push_back((uint16_t)(vertexIndex + startingIndex)); } } } shapeData.setupVertices(_shapeVertices, vertices); // FIXME don't use solid indices for wire drawing. shapeData.setupIndices(_shapeIndices, indices, indices); } // Icosahedron startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE; { ShapeData& shapeData = _shapes[Icosahedron]; VertexVector vertices; IndexVector indices; { const VertexVector& originalVertices = icosahedronVertices(); vertices.reserve(originalVertices.size() * VECTOR_TO_VECTOR_WITH_NORMAL_MULTIPLER); for (size_t i = 0; i < originalVertices.size(); i += 3) { auto triangleStartIndex = i; vec3 faceNormal; for (int j = 0; j < VERTICES_PER_TRIANGLE; ++j) { auto triangleVertexIndex = j; auto vertexIndex = triangleStartIndex + triangleVertexIndex; faceNormal += originalVertices[vertexIndex]; } faceNormal = glm::normalize(faceNormal); for (int j = 0; j < VERTICES_PER_TRIANGLE; ++j) { auto triangleVertexIndex = j; auto vertexIndex = triangleStartIndex + triangleVertexIndex; vertices.push_back(glm::normalize(originalVertices[vertexIndex])); vertices.push_back(faceNormal); indices.push_back((uint16_t)(vertexIndex + startingIndex)); } } } shapeData.setupVertices(_shapeVertices, vertices); // FIXME don't use solid indices for wire drawing. shapeData.setupIndices(_shapeIndices, indices, indices); } // Line startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE; { ShapeData& shapeData = _shapes[Line]; shapeData.setupVertices(_shapeVertices, VertexVector{ vec3(-0.5, 0, 0), vec3(-0.5f, 0, 0), vec3(0.5f, 0, 0), vec3(0.5f, 0, 0) }); IndexVector wireIndices; // Only two indices wireIndices.push_back(0 + (uint16_t)startingIndex); wireIndices.push_back(1 + (uint16_t)startingIndex); shapeData.setupIndices(_shapeIndices, IndexVector(), wireIndices); } // Not implememented yet: //Triangle, //Quad, //Circle, //Octahetron, //Dodecahedron, //Torus, //Cone, //Cylinder, } gpu::Stream::FormatPointer& getSolidStreamFormat() { if (!SOLID_STREAM_FORMAT) { SOLID_STREAM_FORMAT = std::make_shared<gpu::Stream::Format>(); // 1 for everyone SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT); SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT); } return SOLID_STREAM_FORMAT; } gpu::Stream::FormatPointer& getInstancedSolidStreamFormat() { if (!INSTANCED_SOLID_STREAM_FORMAT) { INSTANCED_SOLID_STREAM_FORMAT = std::make_shared<gpu::Stream::Format>(); // 1 for everyone INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT); INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT); INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE);