// // 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 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 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, gpu::PipelinePointer> GeometryCache::_gridPipelines; void GeometryCache::computeSimpleHullPointListForShape(const int entityShape, const glm::vec3 &entityExtents, QVector &outPointList) { auto geometryCache = DependencyManager::get(); 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 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(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 std::vector polygon() { std::vector 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& 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; static IndexPair indexToken(geometry::Index a, geometry::Index b) { if (a > b) { std::swap(a, b); } return (((IndexPair)a) << 32) | ((IndexPair)b); } template void setupFlatShape(GeometryCache::ShapeData& shapeData, const geometry::Solid& shape, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer) { using namespace geometry; std::vector vertices; IndexVector solidIndices, wireIndices; IndexPairs wireSeenIndices; size_t faceCount = shape.faces.size(); size_t faceIndexCount = triangulatedFaceIndexCount(); vertices.reserve(N * faceCount); solidIndices.reserve(faceIndexCount * faceCount); Index baseVertex = 0; for (size_t f = 0; f < faceCount; f++) { const Face& 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 void setupSmoothShape(GeometryCache::ShapeData& shapeData, const geometry::Solid& shape, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer) { using namespace geometry; std::vector 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(); solidIndices.reserve(faceIndexCount * faceCount); Index baseVertex = 0; for (size_t f = 0; f < faceCount; f++) { const Face& 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 void extrudePolygon(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer, bool isConical = false) { using namespace geometry; std::vector vertices; IndexVector solidIndices, wireIndices; // Top (if not conical) and bottom faces std::vector shape = polygon(); 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); 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); vec3 tangent = glm::normalize(bottomLeft - bottomRight); // Our tex coords go in the opposite direction as our vertices float u = 1.0f - (float)i / (float)N; float u2 = 1.0f - (float)(i + 1) / (float)N; vertices.emplace_back(topLeft, normal, vec2(u, 0.0f), tangent); vertices.emplace_back(bottomLeft, normal, vec2(u, 1.0f), tangent); vertices.emplace_back(topRight, normal, vec2(u2, 0.0f), tangent); vertices.emplace_back(bottomRight, normal, vec2(u2, 1.0f), tangent); 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); } // 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::VertexBuffer); auto indexBuffer = std::make_shared(gpu::Buffer::IndexBuffer); // 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 auto sphere = geometry::tesselate(geometry::icosahedron(), ICOSAHEDRON_TO_SPHERE_TESSELATION_COUNT); sphere.fitDimension(1.0f); setupSmoothShape(_shapes[Sphere], sphere, _shapeVertices, _shapeIndices); // Line { ShapeData& shapeData = _shapes[Line]; shapeData.setupVertices(_shapeVertices, std::vector { ShapeVertex(vec3(-0.5f, 0.0f, 0.0f), vec3(-0.5f, 0.0f, 0.0f), vec2(0.0f, 0.0f), vec3(0.0f, 0.0f, 0.0f)), ShapeVertex(vec3(0.5f, 0.0f, 0.0f), vec3(0.5f, 0.0f, 0.0f), vec2(0.0f, 0.0f), vec3(0.0f, 0.0f, 0.0f)) }); IndexVector wireIndices; // Only two indices wireIndices.push_back(0); wireIndices.push_back(1); shapeData.setupIndices(_shapeIndices, IndexVector(), wireIndices); } //Triangle, extrudePolygon<3>(_shapes[Triangle], _shapeVertices, _shapeIndices); //Hexagon, extrudePolygon<6>(_shapes[Hexagon], _shapeVertices, _shapeIndices); //Octagon, extrudePolygon<8>(_shapes[Octagon], _shapeVertices, _shapeIndices); //Cylinder, extrudePolygon<64>(_shapes[Cylinder], _shapeVertices, _shapeIndices); //Cone, extrudePolygon<64>(_shapes[Cone], _shapeVertices, _shapeIndices, true); // Circle renders as flat Cylinder extrudePolygon<64>(_shapes[Circle], _shapeVertices, _shapeIndices); //Torus is implemented in renderTorus, not with ShapeData, because the inner radius can change } const GeometryCache::ShapeData * GeometryCache::getShapeData(const Shape shape) const { if (((int)shape < 0) || ((int)shape >= (int)_shapes.size())) { qCWarning(renderutils) << "GeometryCache::getShapeData - Invalid shape " << shape << " specified. Returning default fallback."; //--EARLY EXIT--( No valid shape data for shape ) return nullptr; } return &_shapes[shape]; } GeometryCache::Shape GeometryCache::getShapeForEntityShape(int entityShape) { if ((entityShape < 0) || (entityShape >= (int)MAPPING.size())) { qCWarning(renderutils) << "GeometryCache::getShapeForEntityShape - Invalid shape " << entityShape << " specified. Returning default fallback."; //--EARLY EXIT--( fall back to default assumption ) return GeometryCache::Sphere; } return MAPPING[entityShape]; } QString GeometryCache::stringFromShape(GeometryCache::Shape geoShape) { if (((int)geoShape < 0) || ((int)geoShape >= (int)GeometryCache::NUM_SHAPES)) { qCWarning(renderutils) << "GeometryCache::stringFromShape - Invalid shape " << geoShape << " specified."; //--EARLY EXIT-- return "INVALID_GEOCACHE_SHAPE"; } return GEOCACHE_SHAPE_STRINGS[geoShape]; } gpu::Stream::FormatPointer& getSolidStreamFormat() { if (!SOLID_STREAM_FORMAT) { SOLID_STREAM_FORMAT = std::make_shared(); // 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); SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD0, gpu::Stream::TEXCOORD0, TEXCOORD0_ELEMENT); SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::TANGENT, gpu::Stream::TANGENT, TANGENT_ELEMENT); SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE); } return SOLID_STREAM_FORMAT; } gpu::Stream::FormatPointer& getWireStreamFormat() { if (!WIRE_STREAM_FORMAT) { WIRE_STREAM_FORMAT = std::make_shared(); // 1 for everyone WIRE_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT); WIRE_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT); WIRE_STREAM_FORMAT->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE); } return WIRE_STREAM_FORMAT; } gpu::Stream::FormatPointer& getSolidStreamFadeFormat() { if (!SOLID_STREAM_FADE_FORMAT) { SOLID_STREAM_FADE_FORMAT = std::make_shared(); // 1 for everyone SOLID_STREAM_FADE_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT); SOLID_STREAM_FADE_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT); SOLID_STREAM_FADE_FORMAT->setAttribute(gpu::Stream::TEXCOORD0, gpu::Stream::TEXCOORD0, TEXCOORD0_ELEMENT); SOLID_STREAM_FADE_FORMAT->setAttribute(gpu::Stream::TANGENT, gpu::Stream::TANGENT, TANGENT_ELEMENT); SOLID_STREAM_FADE_FORMAT->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE); SOLID_STREAM_FADE_FORMAT->setAttribute(gpu::Stream::FADE1, gpu::Stream::FADE1, FADE4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); SOLID_STREAM_FADE_FORMAT->setAttribute(gpu::Stream::FADE2, gpu::Stream::FADE2, FADE4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); SOLID_STREAM_FADE_FORMAT->setAttribute(gpu::Stream::FADE3, gpu::Stream::FADE3, FADE4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); SOLID_STREAM_FADE_FORMAT->setAttribute(gpu::Stream::FADE4, gpu::Stream::FADE4, FADE4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); SOLID_STREAM_FADE_FORMAT->setAttribute(gpu::Stream::FADE5, gpu::Stream::FADE5, FADE4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); SOLID_STREAM_FADE_FORMAT->setAttribute(gpu::Stream::FADE6, gpu::Stream::FADE6, FADE4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); SOLID_STREAM_FADE_FORMAT->setAttribute(gpu::Stream::FADE7, gpu::Stream::FADE7, FADE1_ELEMENT, 0, gpu::Stream::PER_INSTANCE); } return SOLID_STREAM_FADE_FORMAT; } gpu::Stream::FormatPointer& getWireStreamFadeFormat() { if (!WIRE_STREAM_FADE_FORMAT) { WIRE_STREAM_FADE_FORMAT = std::make_shared(); // 1 for everyone WIRE_STREAM_FADE_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT); WIRE_STREAM_FADE_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT); WIRE_STREAM_FADE_FORMAT->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE); WIRE_STREAM_FADE_FORMAT->setAttribute(gpu::Stream::FADE1, gpu::Stream::FADE1, FADE4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); WIRE_STREAM_FADE_FORMAT->setAttribute(gpu::Stream::FADE2, gpu::Stream::FADE2, FADE4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); WIRE_STREAM_FADE_FORMAT->setAttribute(gpu::Stream::FADE3, gpu::Stream::FADE3, FADE4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); WIRE_STREAM_FADE_FORMAT->setAttribute(gpu::Stream::FADE4, gpu::Stream::FADE4, FADE4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); WIRE_STREAM_FADE_FORMAT->setAttribute(gpu::Stream::FADE5, gpu::Stream::FADE5, FADE4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); WIRE_STREAM_FADE_FORMAT->setAttribute(gpu::Stream::FADE6, gpu::Stream::FADE6, FADE4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); WIRE_STREAM_FADE_FORMAT->setAttribute(gpu::Stream::FADE7, gpu::Stream::FADE7, FADE1_ELEMENT, 0, gpu::Stream::PER_INSTANCE); } return WIRE_STREAM_FADE_FORMAT; } std::map, gpu::ShaderPointer> GeometryCache::_shapeShaders; std::map, render::ShapePipelinePointer> GeometryCache::_shapePipelines; QHash GeometryCache::_simplePrograms; GeometryCache::GeometryCache() : _nextID(0) { // Let's register its special shapePipeline factory: initializeShapePipelines(); buildShapes(); } GeometryCache::~GeometryCache() { #ifdef WANT_DEBUG qCDebug(renderutils) << "GeometryCache::~GeometryCache()... "; qCDebug(renderutils) << " _registeredLine3DVBOs.size():" << _registeredLine3DVBOs.size(); qCDebug(renderutils) << " _line3DVBOs.size():" << _line3DVBOs.size(); qCDebug(renderutils) << " BatchItemDetails... population:" << GeometryCache::BatchItemDetails::population; #endif //def WANT_DEBUG } void GeometryCache::releaseID(int id) { _lastRegisteredQuad3DTexture.remove(id); _registeredQuad3DTextures.remove(id); _lastRegisteredQuad2DTexture.remove(id); _registeredQuad2DTextures.remove(id); _lastRegisteredQuad2DTextureFade.remove(id); _registeredQuad2DTexturesFade.remove(id); _lastRegisteredQuad3D.remove(id); _registeredQuad3D.remove(id); _lastRegisteredQuad2D.remove(id); _registeredQuad2D.remove(id); _lastRegisteredQuad2DFade.remove(id); _registeredQuad2DFade.remove(id); _lastRegisteredBevelRects.remove(id); _registeredBevelRects.remove(id); _lastRegisteredLine3D.remove(id); _registeredLine3DVBOs.remove(id); _lastRegisteredLine2D.remove(id); _registeredLine2DVBOs.remove(id); _registeredVertices.remove(id); _lastRegisteredDashedLines.remove(id); _registeredDashedLines.remove(id); _lastRegisteredGridBuffer.remove(id); _registeredGridBuffers.remove(id); _lastRegisteredTorusBuffer.remove(id); _registeredTorusBuffers.remove(id); _lastRegisteredTorusBufferFade.remove(id); _registeredTorusBuffersFade.remove(id); } void GeometryCache::initializeShapePipelines() { if (_shapePipelines.empty()) { const int NUM_PIPELINES = 16; for (int i = 0; i < NUM_PIPELINES; ++i) { bool transparent = i & 1; bool unlit = i & 2; bool forward = i & 4; bool fading = i & 8; for (int cullFaceMode = graphics::MaterialKey::CullFaceMode::CULL_NONE; cullFaceMode < graphics::MaterialKey::CullFaceMode::NUM_CULL_FACE_MODES; cullFaceMode++) { auto cullMode = (graphics::MaterialKey::CullFaceMode)cullFaceMode; _shapePipelines[std::make_tuple(transparent, unlit, forward, fading, cullMode)] = fading ? getFadingShapePipeline(false, transparent, unlit, false, forward, cullMode) : getShapePipeline(false, transparent, unlit, false, forward, cullMode); } } } } render::ShapePipelinePointer GeometryCache::getShapePipeline(bool textured, bool transparent, bool unlit, bool depthBias, bool forward, graphics::MaterialKey::CullFaceMode cullFaceMode) { return std::make_shared(getSimplePipeline(textured, transparent, unlit, depthBias, false, true, forward, cullFaceMode), nullptr, [](const render::ShapePipeline*, gpu::Batch& batch, render::Args* args) { batch.setResourceTexture(gr::Texture::MaterialAlbedo, DependencyManager::get()->getWhiteTexture()); auto deferredLightingEffect = DependencyManager::get(); deferredLightingEffect->setupKeyLightBatch(args, batch); deferredLightingEffect->setupLocalLightsBatch(batch); } ); } render::ShapePipelinePointer GeometryCache::getFadingShapePipeline(bool textured, bool transparent, bool unlit, bool depthBias, bool forward, graphics::MaterialKey::CullFaceMode cullFaceMode) { auto fadeBatchSetter = FadeEffect::getBatchSetter(); auto fadeItemSetter = FadeEffect::getItemUniformSetter(); return std::make_shared(getSimplePipeline(textured, transparent, unlit, depthBias, true, true, forward, cullFaceMode), nullptr, [fadeBatchSetter](const render::ShapePipeline*, gpu::Batch& batch, render::Args* args) { batch.setResourceTexture(gr::Texture::MaterialAlbedo, DependencyManager::get()->getWhiteTexture()); auto deferredLightingEffect = DependencyManager::get(); deferredLightingEffect->setupKeyLightBatch(args, batch); deferredLightingEffect->setupLocalLightsBatch(batch); fadeBatchSetter(nullptr, batch, args); }, fadeItemSetter ); } void setupColorInputBuffer(gpu::Batch& batch, gpu::BufferPointer colorBuffer) { gpu::BufferView colorView(colorBuffer, COLOR_ELEMENT); batch.setInputBuffer(gpu::Stream::COLOR, colorView); } void GeometryCache::renderShape(gpu::Batch& batch, Shape shape, gpu::BufferPointer& colorBuffer) { batch.setInputFormat(getSolidStreamFormat()); setupColorInputBuffer(batch, colorBuffer); _shapes[shape].draw(batch); } void GeometryCache::renderWireShape(gpu::Batch& batch, Shape shape, gpu::BufferPointer& colorBuffer) { batch.setInputFormat(getWireStreamFormat()); setupColorInputBuffer(batch, colorBuffer); _shapes[shape].drawWire(batch); } void GeometryCache::renderShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer) { batch.setInputFormat(getSolidStreamFormat()); setupColorInputBuffer(batch, colorBuffer); _shapes[shape].drawInstances(batch, count); } void GeometryCache::renderWireShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer) { batch.setInputFormat(getWireStreamFormat()); setupColorInputBuffer(batch, colorBuffer); _shapes[shape].drawWireInstances(batch, count); } void setupFadeInputBuffers(gpu::Batch& batch, const FadeBuffers& fadeBuffers) { fadeBuffers.bind(batch); } void GeometryCache::renderShapeFade(gpu::Batch& batch, Shape shape, gpu::BufferPointer& colorBuffer, const FadeBuffers& fadeBuffers) { batch.setInputFormat(getSolidStreamFadeFormat()); setupColorInputBuffer(batch, colorBuffer); setupFadeInputBuffers(batch, fadeBuffers); _shapes[shape].draw(batch); } void GeometryCache::renderWireShapeFade(gpu::Batch& batch, Shape shape, gpu::BufferPointer& colorBuffer, const FadeBuffers& fadeBuffers) { batch.setInputFormat(getWireStreamFadeFormat()); setupColorInputBuffer(batch, colorBuffer); setupFadeInputBuffers(batch, fadeBuffers); _shapes[shape].drawWire(batch); } void GeometryCache::renderShapeFadeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer, const FadeBuffers& fadeBuffers) { batch.setInputFormat(getSolidStreamFadeFormat()); setupColorInputBuffer(batch, colorBuffer); setupFadeInputBuffers(batch, fadeBuffers); _shapes[shape].drawInstances(batch, count); } void GeometryCache::renderWireShapeFadeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer, const FadeBuffers& fadeBuffers) { batch.setInputFormat(getWireStreamFadeFormat()); setupColorInputBuffer(batch, colorBuffer); setupFadeInputBuffers(batch, fadeBuffers); _shapes[shape].drawWireInstances(batch, count); } void GeometryCache::renderGrid(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner, int majorRows, int majorCols, float majorEdge, int minorRows, int minorCols, float minorEdge, const glm::vec4& color, bool forward, int id) { if (majorRows == 0 || majorCols == 0) { return; } Vec2FloatPair majorKey(glm::vec2(majorRows, majorCols), majorEdge); Vec2FloatPair minorKey(glm::vec2(minorRows, minorCols), minorEdge); Vec2FloatPairPair key(majorKey, minorKey); // Make the gridbuffer GridBuffer gridBuffer; if (id != UNKNOWN_ID) { auto gridBufferIter = _registeredGridBuffers.find(id); bool hadGridBuffer = gridBufferIter != _registeredGridBuffers.end(); if (hadGridBuffer) { gridBuffer = gridBufferIter.value(); } else { GridSchema gridSchema; gridBuffer = std::make_shared(gpu::Buffer::UniformBuffer, sizeof(GridSchema), (const gpu::Byte*)&gridSchema); } if (!hadGridBuffer || _lastRegisteredGridBuffer[id] != key) { _registeredGridBuffers[id] = gridBuffer; _lastRegisteredGridBuffer[id] = key; gridBuffer.edit().period = glm::vec4(majorRows, majorCols, minorRows, minorCols); gridBuffer.edit().offset.x = -(majorEdge / majorRows) / 2; gridBuffer.edit().offset.y = -(majorEdge / majorCols) / 2; gridBuffer.edit().offset.z = minorRows == 0 ? 0 : -(minorEdge / minorRows) / 2; gridBuffer.edit().offset.w = minorCols == 0 ? 0 : -(minorEdge / minorCols) / 2; gridBuffer.edit().edge = glm::vec4(glm::vec2(majorEdge), // If rows or columns are not set, do not draw minor gridlines glm::vec2((minorRows != 0 && minorCols != 0) ? minorEdge : 0.0f)); } } // Set the grid pipeline useGridPipeline(batch, gridBuffer, color.a < 1.0f, forward); static const glm::vec2 MIN_TEX_COORD(0.0f, 0.0f); static const glm::vec2 MAX_TEX_COORD(1.0f, 1.0f); renderQuad(batch, minCorner, maxCorner, MIN_TEX_COORD, MAX_TEX_COORD, color, id); } void GeometryCache::updateVertices(int id, const QVector& points, const QVector& colors) { BatchItemDetails& details = _registeredVertices[id]; if (details.isCreated) { details.clear(); #ifdef WANT_DEBUG qCDebug(renderutils) << "updateVertices()... RELEASING REGISTERED"; #endif // def WANT_DEBUG } const int FLOATS_PER_VERTEX = 2; // vertices details.isCreated = true; details.vertices = points.size(); details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto normalBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto colorBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.normalBuffer = normalBuffer; details.colorBuffer = colorBuffer; details.streamFormat = streamFormat; details.stream = stream; details.streamFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ)); details.streamFormat->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::POSITION)._stride); details.stream->addBuffer(details.normalBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::NORMAL)._stride); details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::COLOR)._stride); float* vertexData = new float[details.vertices * FLOATS_PER_VERTEX]; float* vertex = vertexData; int* colorData = new int[details.vertices]; int* colorDataAt = colorData; auto pointCount = points.size(); auto colorCount = colors.size(); int compactColor = 0; for (auto i = 0; i < pointCount; i++) { const auto& point = points[i]; *(vertex++) = point.x; *(vertex++) = point.y; if (i < colorCount) { const auto& color = colors[i]; compactColor = GeometryCache::toCompactColor(color); } *(colorDataAt++) = compactColor; } details.verticesBuffer->append(sizeof(float) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*) vertexData); const glm::vec3 NORMAL(0.0f, -1.0f, 0.0f); details.normalBuffer->append(3 * sizeof(float), (gpu::Byte*) glm::value_ptr(NORMAL)); details.colorBuffer->append(sizeof(int) * details.vertices, (gpu::Byte*) colorData); delete[] vertexData; delete[] colorData; #ifdef WANT_DEBUG qCDebug(renderutils) << "new registered linestrip buffer made -- _registeredVertices.size():" << _registeredVertices.size(); #endif } void GeometryCache::updateVertices(int id, const QVector& points, const glm::vec4& color) { updateVertices(id, points, QVector({ color })); } void GeometryCache::updateVertices(int id, const QVector& points, const QVector& colors) { BatchItemDetails& details = _registeredVertices[id]; if (details.isCreated) { details.clear(); #ifdef WANT_DEBUG qCDebug(renderutils) << "updateVertices()... RELEASING REGISTERED"; #endif // def WANT_DEBUG } const int FLOATS_PER_VERTEX = 3; // vertices details.isCreated = true; details.vertices = points.size(); details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto normalBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto colorBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.normalBuffer = normalBuffer; details.colorBuffer = colorBuffer; details.streamFormat = streamFormat; details.stream = stream; details.streamFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); details.streamFormat->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::POSITION)._stride); details.stream->addBuffer(details.normalBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::NORMAL)._stride); details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::COLOR)._stride); // Default to white int compactColor = 0xFFFFFFFF; float* vertexData = new float[details.vertices * FLOATS_PER_VERTEX]; float* vertex = vertexData; int* colorData = new int[details.vertices]; int* colorDataAt = colorData; auto pointCount = points.size(); auto colorCount = colors.size(); for (auto i = 0; i < pointCount; i++) { const glm::vec3& point = points[i]; *(vertex++) = point.x; *(vertex++) = point.y; *(vertex++) = point.z; if (i < colorCount) { const glm::vec4& color = colors[i]; compactColor = GeometryCache::toCompactColor(color); } *(colorDataAt++) = compactColor; } details.verticesBuffer->append(sizeof(float) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*) vertexData); const glm::vec3 NORMAL(0.0f, -1.0f, 0.0f); details.normalBuffer->append(3 * sizeof(float), (gpu::Byte*) glm::value_ptr(NORMAL)); details.colorBuffer->append(sizeof(int) * details.vertices, (gpu::Byte*) colorData); delete[] vertexData; delete[] colorData; #ifdef WANT_DEBUG qCDebug(renderutils) << "new registered linestrip buffer made -- _registeredVertices.size():" << _registeredVertices.size(); #endif } void GeometryCache::updateVertices(int id, const QVector& points, const glm::vec4& color) { updateVertices(id, points, QVector({ color })); } void GeometryCache::updateVertices(int id, const QVector& points, const QVector& texCoords, const glm::vec4& color) { BatchItemDetails& details = _registeredVertices[id]; if (details.isCreated) { details.clear(); #ifdef WANT_DEBUG qCDebug(renderutils) << "updateVertices()... RELEASING REGISTERED"; #endif // def WANT_DEBUG } const int FLOATS_PER_VERTEX = 3 + 2; // vertices + tex coords const int NUM_POS_COORDS = 3; const int VERTEX_TEX_OFFSET = NUM_POS_COORDS * sizeof(float); details.isCreated = true; details.vertices = points.size(); details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto normalBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto colorBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.normalBuffer = normalBuffer; details.colorBuffer = colorBuffer; details.streamFormat = streamFormat; details.stream = stream; details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); details.streamFormat->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), VERTEX_TEX_OFFSET); details.streamFormat->setAttribute(gpu::Stream::NORMAL, 1, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::COLOR, 2, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE); details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); details.stream->addBuffer(details.normalBuffer, 0, details.streamFormat->getChannels().at(1)._stride); details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(2)._stride); assert(points.size() == texCoords.size()); float* vertexData = new float[details.vertices * FLOATS_PER_VERTEX]; float* vertex = vertexData; for (int i = 0; i < points.size(); i++) { glm::vec3 point = points[i]; glm::vec2 texCoord = texCoords[i]; *(vertex++) = point.x; *(vertex++) = point.y; *(vertex++) = point.z; *(vertex++) = texCoord.x; *(vertex++) = texCoord.y; } details.verticesBuffer->append(sizeof(float) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*)vertexData); const glm::vec3 NORMAL(0.0f, -1.0f, 0.0f); details.normalBuffer->append(3 * sizeof(float), (gpu::Byte*) glm::value_ptr(NORMAL)); int compactColor = GeometryCache::toCompactColor(color); details.colorBuffer->append(sizeof(compactColor), (gpu::Byte*) &compactColor); delete[] vertexData; #ifdef WANT_DEBUG qCDebug(renderutils) << "new registered linestrip buffer made -- _registeredVertices.size():" << _registeredVertices.size(); #endif } void GeometryCache::renderVertices(gpu::Batch& batch, gpu::Primitive primitiveType, int id) { BatchItemDetails& details = _registeredVertices[id]; if (details.isCreated) { batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); batch.draw(primitiveType, details.vertices, 0); } } void GeometryCache::renderBevelCornersRect(gpu::Batch& batch, int x, int y, int width, int height, int bevelDistance, const glm::vec4& color, int id) { bool registered = (id != UNKNOWN_ID); Vec3Pair key(glm::vec3(x, y, 0.0f), glm::vec3(width, height, bevelDistance)); BatchItemDetails& details = _registeredBevelRects[id]; // if this is a registered quad, and we have buffers, then check to see if the geometry changed and rebuild if needed if (registered && details.isCreated) { Vec3Pair& lastKey = _lastRegisteredBevelRects[id]; if (lastKey != key) { details.clear(); _lastRegisteredBevelRects[id] = key; #ifdef WANT_DEBUG qCDebug(renderutils) << "renderBevelCornersRect()... RELEASING REGISTERED"; #endif // def WANT_DEBUG } #ifdef WANT_DEBUG else { qCDebug(renderutils) << "renderBevelCornersRect()... REUSING PREVIOUSLY REGISTERED"; } #endif // def WANT_DEBUG } if (!details.isCreated) { static const int FLOATS_PER_VERTEX = 2; // vertices static const int NUM_VERTICES = 8; static const int NUM_FLOATS = NUM_VERTICES * FLOATS_PER_VERTEX; details.isCreated = true; details.vertices = NUM_VERTICES; details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto colorBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.colorBuffer = colorBuffer; details.streamFormat = streamFormat; details.stream = stream; details.streamFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ)); details.streamFormat->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE); details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::POSITION)._stride); details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::COLOR)._stride); float vertexBuffer[NUM_FLOATS]; // only vertices, no normals because we're a 2D quad int vertexPoint = 0; // Triangle strip points // 3 ------ 5 // // / \ // // 1 7 // // | | // // 2 8 // // \ / // // 4 ------ 6 // // 1 vertexBuffer[vertexPoint++] = x; vertexBuffer[vertexPoint++] = y + height - bevelDistance; // 2 vertexBuffer[vertexPoint++] = x; vertexBuffer[vertexPoint++] = y + bevelDistance; // 3 vertexBuffer[vertexPoint++] = x + bevelDistance; vertexBuffer[vertexPoint++] = y + height; // 4 vertexBuffer[vertexPoint++] = x + bevelDistance; vertexBuffer[vertexPoint++] = y; // 5 vertexBuffer[vertexPoint++] = x + width - bevelDistance; vertexBuffer[vertexPoint++] = y + height; // 6 vertexBuffer[vertexPoint++] = x + width - bevelDistance; vertexBuffer[vertexPoint++] = y; // 7 vertexBuffer[vertexPoint++] = x + width; vertexBuffer[vertexPoint++] = y + height - bevelDistance; // 8 vertexBuffer[vertexPoint++] = x + width; vertexBuffer[vertexPoint++] = y + bevelDistance; details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); int compactColor = GeometryCache::toCompactColor(color); details.colorBuffer->append(sizeof(compactColor), (gpu::Byte*) &compactColor); } batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); batch.draw(gpu::TRIANGLE_STRIP, details.vertices, 0); } void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner, const glm::vec4& color, int id) { bool registered = (id != UNKNOWN_ID); Vec4Pair key(glm::vec4(minCorner.x, minCorner.y, maxCorner.x, maxCorner.y), color); BatchItemDetails& details = _registeredQuad2D[id]; // if this is a registered quad, and we have buffers, then check to see if the geometry changed and rebuild if needed if (registered && details.isCreated) { Vec4Pair & lastKey = _lastRegisteredQuad2D[id]; if (lastKey != key) { details.clear(); _lastRegisteredQuad2D[id] = key; #ifdef WANT_DEBUG qCDebug(renderutils) << "renderQuad() 2D ... RELEASING REGISTERED"; #endif // def WANT_DEBUG } #ifdef WANT_DEBUG else { qCDebug(renderutils) << "renderQuad() 2D ... REUSING PREVIOUSLY REGISTERED"; } #endif // def WANT_DEBUG } if (!details.isCreated) { static const int FLOATS_PER_VERTEX = 2; // vertices static const int VERTICES = 4; // 1 quad = 4 vertices details.isCreated = true; details.vertices = VERTICES; details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto normalBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto colorBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.normalBuffer = normalBuffer; details.colorBuffer = colorBuffer; details.streamFormat = streamFormat; details.stream = stream; details.streamFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ)); details.streamFormat->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE); details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::POSITION)._stride); details.stream->addBuffer(details.normalBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::NORMAL)._stride); details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::COLOR)._stride); float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { minCorner.x, minCorner.y, maxCorner.x, minCorner.y, minCorner.x, maxCorner.y, maxCorner.x, maxCorner.y, }; details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); details.normalBuffer->append(3 * sizeof(float), (gpu::Byte*) glm::value_ptr(NORMAL)); int compactColor = GeometryCache::toCompactColor(color); details.colorBuffer->append(sizeof(compactColor), (gpu::Byte*) &compactColor); } batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); batch.draw(gpu::TRIANGLE_STRIP, 4, 0); } void GeometryCache::renderQuadFade(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner, const glm::vec4& color, const FadeBuffers& fadeBuffers, int id) { bool registered = (id != UNKNOWN_ID); Vec4Pair key(glm::vec4(minCorner.x, minCorner.y, maxCorner.x, maxCorner.y), color); BatchItemDetails& details = _registeredQuad2DFade[id]; // if this is a registered quad, and we have buffers, then check to see if the geometry changed and rebuild if needed if (registered && details.isCreated) { Vec4Pair& lastKey = _lastRegisteredQuad2DFade[id]; if (lastKey != key) { details.clear(); _lastRegisteredQuad2DFade[id] = key; #ifdef WANT_DEBUG qCDebug(renderutils) << "renderQuadFade() 2D ... RELEASING REGISTERED"; #endif // def WANT_DEBUG } #ifdef WANT_DEBUG else { qCDebug(renderutils) << "renderQuadFade() 2D ... REUSING PREVIOUSLY REGISTERED"; } #endif // def WANT_DEBUG } if (!details.isCreated) { static const int FLOATS_PER_VERTEX = 2; // vertices static const int VERTICES = 4; // 1 quad = 4 vertices details.isCreated = true; details.vertices = VERTICES; details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto normalBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto colorBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.normalBuffer = normalBuffer; details.colorBuffer = colorBuffer; details.streamFormat = streamFormat; details.stream = stream; details.streamFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ)); details.streamFormat->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::FADE1, gpu::Stream::FADE1, FADE4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::FADE2, gpu::Stream::FADE2, FADE4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::FADE3, gpu::Stream::FADE3, FADE4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::FADE4, gpu::Stream::FADE4, FADE4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::FADE5, gpu::Stream::FADE5, FADE4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::FADE6, gpu::Stream::FADE6, FADE4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::FADE7, gpu::Stream::FADE7, FADE1_ELEMENT, 0, gpu::Stream::PER_INSTANCE); details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::POSITION)._stride); details.stream->addBuffer(details.normalBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::NORMAL)._stride); details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::COLOR)._stride); float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { minCorner.x, minCorner.y, maxCorner.x, minCorner.y, minCorner.x, maxCorner.y, maxCorner.x, maxCorner.y, }; details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); details.normalBuffer->append(3 * sizeof(float), (gpu::Byte*) glm::value_ptr(NORMAL)); int compactColor = GeometryCache::toCompactColor(color); details.colorBuffer->append(sizeof(compactColor), (gpu::Byte*) &compactColor); } batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); fadeBuffers.bind(batch); batch.draw(gpu::TRIANGLE_STRIP, 4, 0); } void GeometryCache::renderUnitQuad(gpu::Batch& batch, const glm::vec4& color, int id) { static const glm::vec2 topLeft(-1, 1); static const glm::vec2 bottomRight(1, -1); static const glm::vec2 texCoordTopLeft(0.0f, 1.0f); static const glm::vec2 texCoordBottomRight(1.0f, 0.0f); renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, color, id); } void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner, const glm::vec2& texCoordMinCorner, const glm::vec2& texCoordMaxCorner, const glm::vec4& color, int id) { Vec4PairVec4 key(Vec4Pair(glm::vec4(minCorner.x, minCorner.y, maxCorner.x, maxCorner.y), glm::vec4(texCoordMinCorner.x, texCoordMinCorner.y, texCoordMaxCorner.x, texCoordMaxCorner.y)), color); BatchItemDetails& details = _registeredQuad2DTextures[id]; // if this is a registered quad, and we have buffers, then check to see if the geometry changed and rebuild if needed if (details.isCreated) { Vec4PairVec4& lastKey = _lastRegisteredQuad2DTexture[id]; if (lastKey != key) { details.clear(); _lastRegisteredQuad2DTexture[id] = key; #ifdef WANT_DEBUG qCDebug(renderutils) << "renderQuad() 2D+texture ... RELEASING REGISTERED"; #endif // def WANT_DEBUG } #ifdef WANT_DEBUG else { qCDebug(renderutils) << "renderQuad() 2D+texture ... REUSING PREVIOUSLY REGISTERED"; } #endif // def WANT_DEBUG } if (!details.isCreated) { static const int FLOATS_PER_VERTEX = 2 + 2; // vertices + tex coords static const int VERTICES = 4; // 1 quad = 4 vertices static const int NUM_POS_COORDS = 2; static const int VERTEX_TEXCOORD_OFFSET = NUM_POS_COORDS * sizeof(float); details.isCreated = true; details.vertices = VERTICES; details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto normalBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto colorBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.normalBuffer = normalBuffer; details.colorBuffer = colorBuffer; details.streamFormat = streamFormat; details.stream = stream; details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ)); details.streamFormat->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), VERTEX_TEXCOORD_OFFSET); details.streamFormat->setAttribute(gpu::Stream::NORMAL, 1, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::COLOR, 2, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE); details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); details.stream->addBuffer(details.normalBuffer, 0, details.streamFormat->getChannels().at(1)._stride); details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(2)._stride); float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { minCorner.x, minCorner.y, texCoordMinCorner.x, texCoordMinCorner.y, maxCorner.x, minCorner.y, texCoordMaxCorner.x, texCoordMinCorner.y, minCorner.x, maxCorner.y, texCoordMinCorner.x, texCoordMaxCorner.y, maxCorner.x, maxCorner.y, texCoordMaxCorner.x, texCoordMaxCorner.y, }; details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); details.normalBuffer->append(3 * sizeof(float), (gpu::Byte*) glm::value_ptr(NORMAL)); int compactColor = GeometryCache::toCompactColor(color); details.colorBuffer->append(sizeof(compactColor), (gpu::Byte*) &compactColor); } batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); batch.draw(gpu::TRIANGLE_STRIP, 4, 0); } void GeometryCache::renderQuadFade(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner, const glm::vec2& texCoordMinCorner, const glm::vec2& texCoordMaxCorner, const glm::vec4& color, const FadeBuffers& fadeBuffers, int id) { Vec4PairVec4 key(Vec4Pair(glm::vec4(minCorner.x, minCorner.y, maxCorner.x, maxCorner.y), glm::vec4(texCoordMinCorner.x, texCoordMinCorner.y, texCoordMaxCorner.x, texCoordMaxCorner.y)), color); BatchItemDetails& details = _registeredQuad2DTexturesFade[id]; // if this is a registered quad, and we have buffers, then check to see if the geometry changed and rebuild if needed if (details.isCreated) { Vec4PairVec4& lastKey = _lastRegisteredQuad2DTextureFade[id]; if (lastKey != key) { details.clear(); _lastRegisteredQuad2DTextureFade[id] = key; #ifdef WANT_DEBUG qCDebug(renderutils) << "renderQuad() 2D+texture ... RELEASING REGISTERED"; #endif // def WANT_DEBUG } #ifdef WANT_DEBUG else { qCDebug(renderutils) << "renderQuad() 2D+texture ... REUSING PREVIOUSLY REGISTERED"; } #endif // def WANT_DEBUG } if (!details.isCreated) { static const int FLOATS_PER_VERTEX = 2 + 2; // vertices + tex coords static const int VERTICES = 4; // 1 quad = 4 vertices static const int NUM_POS_COORDS = 2; static const int VERTEX_TEXCOORD_OFFSET = NUM_POS_COORDS * sizeof(float); details.isCreated = true; details.vertices = VERTICES; details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto normalBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto colorBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.normalBuffer = normalBuffer; details.colorBuffer = colorBuffer; details.streamFormat = streamFormat; details.stream = stream; details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ)); details.streamFormat->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), VERTEX_TEXCOORD_OFFSET); details.streamFormat->setAttribute(gpu::Stream::NORMAL, 1, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::COLOR, 2, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::FADE1, gpu::Stream::FADE1, FADE4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::FADE2, gpu::Stream::FADE2, FADE4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::FADE3, gpu::Stream::FADE3, FADE4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::FADE4, gpu::Stream::FADE4, FADE4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::FADE5, gpu::Stream::FADE5, FADE4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::FADE6, gpu::Stream::FADE6, FADE4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::FADE7, gpu::Stream::FADE7, FADE1_ELEMENT, 0, gpu::Stream::PER_INSTANCE); details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); details.stream->addBuffer(details.normalBuffer, 0, details.streamFormat->getChannels().at(1)._stride); details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(2)._stride); float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { minCorner.x, minCorner.y, texCoordMinCorner.x, texCoordMinCorner.y, maxCorner.x, minCorner.y, texCoordMaxCorner.x, texCoordMinCorner.y, minCorner.x, maxCorner.y, texCoordMinCorner.x, texCoordMaxCorner.y, maxCorner.x, maxCorner.y, texCoordMaxCorner.x, texCoordMaxCorner.y, }; details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); details.normalBuffer->append(3 * sizeof(float), (gpu::Byte*) glm::value_ptr(NORMAL)); int compactColor = GeometryCache::toCompactColor(color); details.colorBuffer->append(sizeof(compactColor), (gpu::Byte*) &compactColor); } batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); fadeBuffers.bind(batch); batch.draw(gpu::TRIANGLE_STRIP, 4, 0); } void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, const glm::vec3& maxCorner, const glm::vec4& color, int id) { bool registered = (id != UNKNOWN_ID); Vec3PairVec4 key(Vec3Pair(minCorner, maxCorner), color); BatchItemDetails& details = _registeredQuad3D[id]; // if this is a registered quad, and we have buffers, then check to see if the geometry changed and rebuild if needed if (registered && details.isCreated) { Vec3PairVec4& lastKey = _lastRegisteredQuad3D[id]; if (lastKey != key) { details.clear(); _lastRegisteredQuad3D[id] = key; #ifdef WANT_DEBUG qCDebug(renderutils) << "renderQuad() 3D ... RELEASING REGISTERED"; #endif // def WANT_DEBUG } #ifdef WANT_DEBUG else { qCDebug(renderutils) << "renderQuad() 3D ... REUSING PREVIOUSLY REGISTERED"; } #endif // def WANT_DEBUG } if (!details.isCreated) { static const int FLOATS_PER_VERTEX = 3; // vertices static const int VERTICES = 4; // 1 quad = 4 vertices details.isCreated = true; details.vertices = VERTICES; details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto normalBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto colorBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.normalBuffer = normalBuffer; details.colorBuffer = colorBuffer; details.streamFormat = streamFormat; details.stream = stream; details.streamFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); details.streamFormat->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE); details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::POSITION)._stride); details.stream->addBuffer(details.normalBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::NORMAL)._stride); details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::COLOR)._stride); float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { minCorner.x, minCorner.y, minCorner.z, maxCorner.x, minCorner.y, minCorner.z, minCorner.x, maxCorner.y, maxCorner.z, maxCorner.x, maxCorner.y, maxCorner.z, }; details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); details.normalBuffer->append(3 * sizeof(float), (gpu::Byte*) glm::value_ptr(NORMAL)); int compactColor = GeometryCache::toCompactColor(color); details.colorBuffer->append(sizeof(compactColor), (gpu::Byte*) &compactColor); } batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); batch.draw(gpu::TRIANGLE_STRIP, 4, 0); } void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, const glm::vec3& bottomLeft, const glm::vec3& bottomRight, const glm::vec3& topRight, const glm::vec2& texCoordTopLeft, const glm::vec2& texCoordBottomLeft, const glm::vec2& texCoordBottomRight, const glm::vec2& texCoordTopRight, const glm::vec4& color, int id) { #ifdef WANT_DEBUG qCDebug(renderutils) << "renderQuad() vec3 + texture VBO..."; qCDebug(renderutils) << " topLeft:" << topLeft; qCDebug(renderutils) << " bottomLeft:" << bottomLeft; qCDebug(renderutils) << " bottomRight:" << bottomRight; qCDebug(renderutils) << " topRight:" << topRight; qCDebug(renderutils) << " texCoordTopLeft:" << texCoordTopLeft; qCDebug(renderutils) << " texCoordBottomRight:" << texCoordBottomRight; qCDebug(renderutils) << " color:" << color; #endif //def WANT_DEBUG bool registered = (id != UNKNOWN_ID); Vec3PairVec4Pair key(Vec3Pair(topLeft, bottomRight), Vec4Pair(glm::vec4(texCoordTopLeft.x, texCoordTopLeft.y, texCoordBottomRight.x, texCoordBottomRight.y), color)); BatchItemDetails& details = _registeredQuad3DTextures[id]; // if this is a registered quad, and we have buffers, then check to see if the geometry changed and rebuild if needed if (registered && details.isCreated) { Vec3PairVec4Pair& lastKey = _lastRegisteredQuad3DTexture[id]; if (lastKey != key) { details.clear(); _lastRegisteredQuad3DTexture[id] = key; #ifdef WANT_DEBUG qCDebug(renderutils) << "renderQuad() 3D+texture ... RELEASING REGISTERED"; #endif // def WANT_DEBUG } #ifdef WANT_DEBUG else { qCDebug(renderutils) << "renderQuad() 3D+texture ... REUSING PREVIOUSLY REGISTERED"; } #endif // def WANT_DEBUG } if (!details.isCreated) { static const int FLOATS_PER_VERTEX = 3 + 2; // vertices + tex coords static const int VERTICES = 4; // 1 quad = 4 vertices static const int NUM_POS_COORDS = 3; static const int VERTEX_TEXCOORD_OFFSET = NUM_POS_COORDS * sizeof(float); details.isCreated = true; details.vertices = VERTICES; details.vertexSize = FLOATS_PER_VERTEX; // NOTE: this isn't used for BatchItemDetails maybe we can get rid of it auto verticesBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto normalBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto colorBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.normalBuffer = normalBuffer; details.colorBuffer = colorBuffer; details.colorBuffer = colorBuffer; details.streamFormat = streamFormat; details.stream = stream; details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); details.streamFormat->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), VERTEX_TEXCOORD_OFFSET); details.streamFormat->setAttribute(gpu::Stream::NORMAL, 1, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::COLOR, 2, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE); details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); details.stream->addBuffer(details.normalBuffer, 0, details.streamFormat->getChannels().at(1)._stride); details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(2)._stride); float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { bottomLeft.x, bottomLeft.y, bottomLeft.z, texCoordBottomLeft.x, texCoordBottomLeft.y, bottomRight.x, bottomRight.y, bottomRight.z, texCoordBottomRight.x, texCoordBottomRight.y, topLeft.x, topLeft.y, topLeft.z, texCoordTopLeft.x, texCoordTopLeft.y, topRight.x, topRight.y, topRight.z, texCoordTopRight.x, texCoordTopRight.y, }; details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); details.normalBuffer->append(3 * sizeof(float), (gpu::Byte*) glm::value_ptr(NORMAL)); int compactColor = GeometryCache::toCompactColor(color); details.colorBuffer->append(sizeof(compactColor), (gpu::Byte*) &compactColor); } batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); batch.draw(gpu::TRIANGLE_STRIP, 4, 0); } void GeometryCache::renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2, const glm::vec4& color1, const glm::vec4& color2, int id) { bool registered = (id != UNKNOWN_ID); Vec3Pair key(p1, p2); BatchItemDetails& details = _registeredLine3DVBOs[id]; // if this is a registered quad, and we have buffers, then check to see if the geometry changed and rebuild if needed if (registered && details.isCreated) { Vec3Pair& lastKey = _lastRegisteredLine3D[id]; if (lastKey != key) { details.clear(); _lastRegisteredLine3D[id] = key; #ifdef WANT_DEBUG qCDebug(renderutils) << "renderLine() 3D ... RELEASING REGISTERED line"; #endif // def WANT_DEBUG } #ifdef WANT_DEBUG else { qCDebug(renderutils) << "renderLine() 3D ... REUSING PREVIOUSLY REGISTERED line"; } #endif // def WANT_DEBUG } if (!details.isCreated) { static const int FLOATS_PER_VERTEX = 3; // vertices static const int vertices = 2; details.isCreated = true; details.vertices = vertices; details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto normalBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto colorBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.normalBuffer = normalBuffer; details.colorBuffer = colorBuffer; details.streamFormat = streamFormat; details.stream = stream; details.streamFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); details.streamFormat->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::POSITION)._stride); details.stream->addBuffer(details.normalBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::NORMAL)._stride); details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::COLOR)._stride); float vertexBuffer[vertices * FLOATS_PER_VERTEX] = { p1.x, p1.y, p1.z, p2.x, p2.y, p2.z }; const int NUM_COLOR_SCALARS = 2; int compactColor1 = GeometryCache::toCompactColor(color1); int compactColor2 = GeometryCache::toCompactColor(color2); int colors[NUM_COLOR_SCALARS] = { compactColor1, compactColor2 }; details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); details.normalBuffer->append(3 * sizeof(float), (gpu::Byte*) glm::value_ptr(NORMAL)); details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); #ifdef WANT_DEBUG if (id == UNKNOWN_ID) { qCDebug(renderutils) << "new renderLine() 3D VBO made -- _line3DVBOs.size():" << _line3DVBOs.size(); } else { qCDebug(renderutils) << "new registered renderLine() 3D VBO made -- _registeredLine3DVBOs.size():" << _registeredLine3DVBOs.size(); } #endif } batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); batch.draw(gpu::LINES, 2, 0); } void GeometryCache::renderDashedLine(gpu::Batch& batch, const glm::vec3& start, const glm::vec3& end, const glm::vec4& color, const float dash_length, const float gap_length, int id) { bool registered = (id != UNKNOWN_ID); Vec3PairVec2Pair key(Vec3Pair(start, end), Vec2Pair(glm::vec2(color.x, color.y), glm::vec2(color.z, color.w))); BatchItemDetails& details = _registeredDashedLines[id]; // if this is a registered , and we have buffers, then check to see if the geometry changed and rebuild if needed if (registered && details.isCreated) { if (_lastRegisteredDashedLines[id] != key) { details.clear(); _lastRegisteredDashedLines[id] = key; #ifdef WANT_DEBUG qCDebug(renderutils) << "renderDashedLine()... RELEASING REGISTERED"; #endif // def WANT_DEBUG } } if (!details.isCreated) { // draw each line segment with appropriate gaps const float SEGMENT_LENGTH = dash_length + gap_length; float length = glm::distance(start, end); float segmentCount = length / SEGMENT_LENGTH; int segmentCountFloor = (int)glm::floor(segmentCount); glm::vec3 segmentVector = (end - start) / segmentCount; glm::vec3 dashVector = segmentVector / SEGMENT_LENGTH * dash_length; glm::vec3 gapVector = segmentVector / SEGMENT_LENGTH * gap_length; const int FLOATS_PER_VERTEX = 3; // vertices details.isCreated = true; details.vertices = (segmentCountFloor + 1) * 2; details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto normalBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto colorBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.normalBuffer = normalBuffer; details.colorBuffer = colorBuffer; details.streamFormat = streamFormat; details.stream = stream; details.streamFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); details.streamFormat->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE); details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::POSITION)._stride); details.stream->addBuffer(details.normalBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::NORMAL)._stride); details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::COLOR)._stride); float* vertexData = new float[details.vertices * FLOATS_PER_VERTEX]; float* vertex = vertexData; glm::vec3 point = start; *(vertex++) = point.x; *(vertex++) = point.y; *(vertex++) = point.z; for (int i = 0; i < segmentCountFloor; i++) { point += dashVector; *(vertex++) = point.x; *(vertex++) = point.y; *(vertex++) = point.z; point += gapVector; *(vertex++) = point.x; *(vertex++) = point.y; *(vertex++) = point.z; } *(vertex++) = end.x; *(vertex++) = end.y; *(vertex++) = end.z; details.verticesBuffer->append(sizeof(float) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*) vertexData); const glm::vec3 NORMAL(1.0f, 0.0f, 0.0f); details.normalBuffer->append(3 * sizeof(float), (gpu::Byte*) glm::value_ptr(NORMAL)); int compactColor = GeometryCache::toCompactColor(color); details.colorBuffer->append(sizeof(compactColor), (gpu::Byte*) &compactColor); delete[] vertexData; #ifdef WANT_DEBUG if (registered) { qCDebug(renderutils) << "new registered dashed line buffer made -- _registeredVertices:" << _registeredDashedLines.size(); } else { qCDebug(renderutils) << "new dashed lines buffer made -- _dashedLines:" << _dashedLines.size(); } #endif } batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); batch.draw(gpu::LINES, details.vertices, 0); } void GeometryCache::renderTorus(gpu::Batch& batch, float innerRadius, gpu::BufferPointer& colorBuffer, int id) { bool registered = (id != UNKNOWN_ID); BatchItemDetails& details = _registeredTorusBuffers[id]; // if this is a registered, and we have buffers, then check to see if the geometry changed and rebuild if needed if (registered && details.isCreated) { if (_lastRegisteredTorusBuffer[id] != innerRadius) { details.clear(); _lastRegisteredTorusBuffer[id] = innerRadius; #ifdef WANT_DEBUG qCDebug(renderutils) << "renderTorus()... RELEASING REGISTERED"; #endif // def WANT_DEBUG } } if (!details.isCreated) { const int NUM_DIVISIONS = 64; const float ANGLE_STEP = 2.0f * ((float)M_PI) / NUM_DIVISIONS; const int FLOATS_PER_VERTEX = 3 + 3 + 2 + 3; // vertices + normals + tex coords + tangents details.isCreated = true; details.vertices = 6 * (NUM_DIVISIONS + 1) * (NUM_DIVISIONS + 1); details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto indicesBuffer = std::make_shared(gpu::Buffer::IndexBuffer); auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.uniformBuffer = indicesBuffer; // we use the uniformBuffer to store the incides details.streamFormat = streamFormat; details.stream = stream; details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, POSITION_ELEMENT); details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, NORMAL_ELEMENT, 3 * sizeof(float)); details.streamFormat->setAttribute(gpu::Stream::TEXCOORD0, 0, TEXCOORD0_ELEMENT, 6 * sizeof(float)); details.streamFormat->setAttribute(gpu::Stream::TANGENT, 0, TANGENT_ELEMENT, 8 * sizeof(float)); details.streamFormat->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE); details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); // Build our initial circle. Keep track of the center point for normal calculations. glm::vec3 center = glm::vec3(0.5f - innerRadius, 0.0f, 0.0f); std::array points; for (int i = 0; i <= NUM_DIVISIONS; i++) { // Inner radius only affects X. Y still needs to be -0.5 -> 0.5. points[i] = center + glm::vec3(innerRadius * glm::cos(i * ANGLE_STEP), 0.5f * glm::sin(i * ANGLE_STEP), 0.0f); } glm::mat3 normalMatrix = glm::transpose(glm::inverse(glm::scale(glm::vec3(1.0f, 0.5f / innerRadius, 1.0f)))); // Build all of our vertex data float* vertexData = new float[details.vertices * FLOATS_PER_VERTEX]; float* vertex = vertexData; for (int y = 0; y <= NUM_DIVISIONS; y++) { const float ANGLE = y * ANGLE_STEP; for (int i = 0; i <= NUM_DIVISIONS; i++) { glm::vec3 currentCenter = fabs(center.x) * glm::vec3(glm::cos(ANGLE), 0.0f, glm::sin(ANGLE)); // Position glm::vec3 currentPoint = glm::vec3(0.0f, points[i].y, 0.0f) + fabs(points[i].x) * glm::vec3(glm::cos(ANGLE), 0.0f, glm::sin(ANGLE)); *(vertex++) = currentPoint.x; *(vertex++) = currentPoint.y; *(vertex++) = currentPoint.z; // Normal glm::vec3 currentNormal = glm::normalize(normalMatrix * glm::normalize(currentPoint - currentCenter)); *(vertex++) = currentNormal.x; *(vertex++) = currentNormal.y; *(vertex++) = currentNormal.z; // UV glm::vec2 currentUV = glm::vec2((float)i / NUM_DIVISIONS, (float)y / NUM_DIVISIONS); *(vertex++) = currentUV.x; *(vertex++) = currentUV.y; // Tangent int nextIndex = (i + 1) % (NUM_DIVISIONS + 1) + (i / (NUM_DIVISIONS + 1)); glm::vec3 currentTangent = glm::normalize(glm::cross(currentNormal, points[nextIndex] - points[i])); *(vertex++) = currentTangent.x; *(vertex++) = currentTangent.y; *(vertex++) = currentTangent.z; } } details.verticesBuffer->append(sizeof(float) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*)vertexData); delete[] vertexData; // Build our indices for (int y = 0; y < NUM_DIVISIONS; y++) { for (int i = 0; i < NUM_DIVISIONS; i++) { const int baseIndex = i + (NUM_DIVISIONS + 1) * y; details.uniformBuffer->append((uint16_t)baseIndex); details.uniformBuffer->append((uint16_t)(baseIndex + 1)); details.uniformBuffer->append((uint16_t)(baseIndex + NUM_DIVISIONS + 1)); details.uniformBuffer->append((uint16_t)(baseIndex + 1)); details.uniformBuffer->append((uint16_t)(baseIndex + NUM_DIVISIONS + 2)); details.uniformBuffer->append((uint16_t)(baseIndex + NUM_DIVISIONS + 1)); } } #ifdef WANT_DEBUG if (registered) { qCDebug(renderutils) << "new registered torus buffer made -- _registeredTorusBuffers:" << _registeredTorusBuffers.size(); } else { qCDebug(renderutils) << "new torus buffer made -- _registeredTorusBuffers:" << _registeredTorusBuffers.size(); } #endif } // TODO: instanced rendering gpu::BufferView colorView(colorBuffer, COLOR_ELEMENT); batch.setInputBuffer(gpu::Stream::COLOR, colorView); batch.setInputStream(0, *details.stream); batch.setInputFormat(details.streamFormat); batch.setIndexBuffer(gpu::Type::UINT16, details.uniformBuffer, 0); // indices are stored in uniformBuffer batch.drawIndexed(gpu::TRIANGLES, details.vertices, 0); } void GeometryCache::renderTorusFade(gpu::Batch& batch, float innerRadius, gpu::BufferPointer& colorBuffer, const FadeBuffers& fadeBuffers, int id) { bool registered = (id != UNKNOWN_ID); BatchItemDetails& details = _registeredTorusBuffersFade[id]; // if this is a registered, and we have buffers, then check to see if the geometry changed and rebuild if needed if (registered && details.isCreated) { if (_lastRegisteredTorusBufferFade[id] != innerRadius) { details.clear(); _lastRegisteredTorusBufferFade[id] = innerRadius; #ifdef WANT_DEBUG qCDebug(renderutils) << "renderTorus()... RELEASING REGISTERED"; #endif // def WANT_DEBUG } } if (!details.isCreated) { const int NUM_DIVISIONS = 64; const float ANGLE_STEP = 2.0f * ((float)M_PI) / NUM_DIVISIONS; const int FLOATS_PER_VERTEX = 3 + 3 + 2 + 3; // vertices + normals + tex coords + tangents details.isCreated = true; details.vertices = 6 * (NUM_DIVISIONS + 1) * (NUM_DIVISIONS + 1); details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(gpu::Buffer::VertexBuffer); auto indicesBuffer = std::make_shared(gpu::Buffer::IndexBuffer); auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.uniformBuffer = indicesBuffer; // we use the uniformBuffer to store the incides details.streamFormat = streamFormat; details.stream = stream; details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, POSITION_ELEMENT); details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, NORMAL_ELEMENT, 3 * sizeof(float)); details.streamFormat->setAttribute(gpu::Stream::TEXCOORD0, 0, TEXCOORD0_ELEMENT, 6 * sizeof(float)); details.streamFormat->setAttribute(gpu::Stream::TANGENT, 0, TANGENT_ELEMENT, 8 * sizeof(float)); details.streamFormat->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::FADE1, gpu::Stream::FADE1, FADE4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::FADE2, gpu::Stream::FADE2, FADE4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::FADE3, gpu::Stream::FADE3, FADE4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::FADE4, gpu::Stream::FADE4, FADE4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::FADE5, gpu::Stream::FADE5, FADE4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::FADE6, gpu::Stream::FADE6, FADE4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); details.streamFormat->setAttribute(gpu::Stream::FADE7, gpu::Stream::FADE7, FADE1_ELEMENT, 0, gpu::Stream::PER_INSTANCE); details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); // Build our initial circle. Keep track of the center point for normal calculations. glm::vec3 center = glm::vec3(0.5f - innerRadius, 0.0f, 0.0f); std::array points; for (int i = 0; i <= NUM_DIVISIONS; i++) { // Inner radius only affects X. Y still needs to be -0.5 -> 0.5. points[i] = center + glm::vec3(innerRadius * glm::cos(i * ANGLE_STEP), 0.5f * glm::sin(i * ANGLE_STEP), 0.0f); } glm::mat3 normalMatrix = glm::transpose(glm::inverse(glm::scale(glm::vec3(1.0f, 0.5f / innerRadius, 1.0f)))); // Build all of our vertex data float* vertexData = new float[details.vertices * FLOATS_PER_VERTEX]; float* vertex = vertexData; for (int y = 0; y <= NUM_DIVISIONS; y++) { const float ANGLE = y * ANGLE_STEP; for (int i = 0; i <= NUM_DIVISIONS; i++) { glm::vec3 currentCenter = fabs(center.x) * glm::vec3(glm::cos(ANGLE), 0.0f, glm::sin(ANGLE)); // Position glm::vec3 currentPoint = glm::vec3(0.0f, points[i].y, 0.0f) + fabs(points[i].x) * glm::vec3(glm::cos(ANGLE), 0.0f, glm::sin(ANGLE)); *(vertex++) = currentPoint.x; *(vertex++) = currentPoint.y; *(vertex++) = currentPoint.z; // Normal glm::vec3 currentNormal = glm::normalize(normalMatrix * glm::normalize(currentPoint - currentCenter)); *(vertex++) = currentNormal.x; *(vertex++) = currentNormal.y; *(vertex++) = currentNormal.z; // UV glm::vec2 currentUV = glm::vec2((float)i / NUM_DIVISIONS, (float)y / NUM_DIVISIONS); *(vertex++) = currentUV.x; *(vertex++) = currentUV.y; // Tangent int nextIndex = (i + 1) % (NUM_DIVISIONS + 1) + (i / (NUM_DIVISIONS + 1)); glm::vec3 currentTangent = glm::normalize(glm::cross(currentNormal, points[nextIndex] - points[i])); *(vertex++) = currentTangent.x; *(vertex++) = currentTangent.y; *(vertex++) = currentTangent.z; } } details.verticesBuffer->append(sizeof(float) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*)vertexData); delete[] vertexData; // Build our indices for (int y = 0; y < NUM_DIVISIONS; y++) { for (int i = 0; i < NUM_DIVISIONS; i++) { const int baseIndex = i + (NUM_DIVISIONS + 1) * y; details.uniformBuffer->append((uint16_t)baseIndex); details.uniformBuffer->append((uint16_t)(baseIndex + 1)); details.uniformBuffer->append((uint16_t)(baseIndex + NUM_DIVISIONS + 1)); details.uniformBuffer->append((uint16_t)(baseIndex + 1)); details.uniformBuffer->append((uint16_t)(baseIndex + NUM_DIVISIONS + 2)); details.uniformBuffer->append((uint16_t)(baseIndex + NUM_DIVISIONS + 1)); } } #ifdef WANT_DEBUG if (registered) { qCDebug(renderutils) << "new registered torus buffer made -- _registeredTorusBuffersFade:" << _registeredTorusBuffersFade.size(); } else { qCDebug(renderutils) << "new torus buffer made -- _registeredTorusBuffersFade:" << _registeredTorusBuffersFade.size(); } #endif } // TODO: instanced rendering gpu::BufferView colorView(colorBuffer, COLOR_ELEMENT); batch.setInputBuffer(gpu::Stream::COLOR, colorView); batch.setInputStream(0, *details.stream); batch.setInputFormat(details.streamFormat); batch.setIndexBuffer(gpu::Type::UINT16, details.uniformBuffer, 0); // indices are stored in uniformBuffer fadeBuffers.bind(batch); batch.drawIndexed(gpu::TRIANGLES, details.vertices, 0); } int GeometryCache::BatchItemDetails::population = 0; GeometryCache::BatchItemDetails::BatchItemDetails() : verticesBuffer(NULL), normalBuffer(NULL), colorBuffer(NULL), streamFormat(NULL), stream(NULL), vertices(0), vertexSize(0), isCreated(false) { population++; #ifdef WANT_DEBUG qCDebug(renderutils) << "BatchItemDetails()... population:" << population << "**********************************"; #endif } GeometryCache::BatchItemDetails::BatchItemDetails(const GeometryCache::BatchItemDetails& other) : verticesBuffer(other.verticesBuffer), normalBuffer(other.normalBuffer), colorBuffer(other.colorBuffer), streamFormat(other.streamFormat), stream(other.stream), vertices(other.vertices), vertexSize(other.vertexSize), isCreated(other.isCreated) { population++; #ifdef WANT_DEBUG qCDebug(renderutils) << "BatchItemDetails()... population:" << population << "**********************************"; #endif } GeometryCache::BatchItemDetails::~BatchItemDetails() { population--; clear(); #ifdef WANT_DEBUG qCDebug(renderutils) << "~BatchItemDetails()... population:" << population << "**********************************"; #endif } void GeometryCache::BatchItemDetails::clear() { isCreated = false; uniformBuffer.reset(); verticesBuffer.reset(); normalBuffer.reset(); colorBuffer.reset(); streamFormat.reset(); stream.reset(); } void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) { static std::once_flag once; std::call_once(once, [&]() { auto program = gpu::Shader::createProgram(shader::render_utils::program::standardDrawTexture); auto state = std::make_shared(); // enable decal blend state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); PrepareStencil::testMaskResetNoAA(*state); _standardDrawPipeline = gpu::Pipeline::create(program, state); auto stateNoBlend = std::make_shared(); PrepareStencil::testMaskDrawShape(*stateNoBlend); auto programNoBlend = gpu::Shader::createProgram(shader::render_utils::program::standardDrawTextureNoBlend); _standardDrawPipelineNoBlend = gpu::Pipeline::create(programNoBlend, stateNoBlend); }); if (noBlend) { batch.setPipeline(_standardDrawPipelineNoBlend); } else { batch.setPipeline(_standardDrawPipeline); } } void GeometryCache::useGridPipeline(gpu::Batch& batch, GridBuffer gridBuffer, bool transparent, bool forward) { if (_gridPipelines.empty()) { using namespace shader::render_utils::program; static const std::vector> keys = { std::make_tuple(false, false, grid), std::make_tuple(false, true, grid_forward), std::make_tuple(true, false, grid_translucent), std::make_tuple(true, true, grid_translucent_forward) }; for (auto& key : keys) { gpu::StatePointer state = std::make_shared(); state->setDepthTest(true, !std::get<0>(key), ComparisonFunction::LESS_EQUAL); if (std::get<0>(key)) { PrepareStencil::testMaskResetNoAA(*state); } else { PrepareStencil::testMaskDrawShape(*state); } state->setBlendFunction(std::get<0>(key), gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); state->setCullMode(gpu::State::CULL_NONE); state->setDepthBias(-1.0f); state->setDepthBiasSlopeScale(-1.0f); _gridPipelines[{std::get<0>(key), std::get<1>(key)}] = gpu::Pipeline::create(gpu::Shader::createProgram(std::get<2>(key)), state); } } batch.setPipeline(_gridPipelines[{ transparent, forward }]); batch.setUniformBuffer(0, gridBuffer); } class SimpleProgramKey { public: enum FlagBit { IS_TEXTURED_BIT = 0, IS_TRANSPARENT_BIT, IS_UNLIT_BIT, IS_DEPTH_BIASED_BIT, IS_FADING_BIT, IS_ANTIALIASED_BIT, IS_FORWARD_BIT, IS_CULL_FACE_NONE_BIT, // if neither of these are set, we're CULL_FACE_BACK IS_CULL_FACE_FRONT_BIT, NUM_FLAGS, }; typedef std::bitset Flags; bool isTextured() const { return _flags[IS_TEXTURED_BIT]; } bool isTransparent() const { return _flags[IS_TRANSPARENT_BIT]; } bool isUnlit() const { return _flags[IS_UNLIT_BIT]; } bool hasDepthBias() const { return _flags[IS_DEPTH_BIASED_BIT]; } bool isFading() const { return _flags[IS_FADING_BIT]; } bool isAntiAliased() const { return _flags[IS_ANTIALIASED_BIT]; } bool isForward() const { return _flags[IS_FORWARD_BIT]; } bool isCullFaceNone() const { return _flags[IS_CULL_FACE_NONE_BIT]; } bool isCullFaceFront() const { return _flags[IS_CULL_FACE_FRONT_BIT]; } Flags _flags = 0; unsigned long getRaw() const { return _flags.to_ulong(); } SimpleProgramKey(bool textured = false, bool transparent = false, bool unlit = false, bool depthBias = false, bool fading = false, bool isAntiAliased = true, bool forward = false, graphics::MaterialKey::CullFaceMode cullFaceMode = graphics::MaterialKey::CULL_BACK) { _flags.set(IS_TEXTURED_BIT, textured); _flags.set(IS_TRANSPARENT_BIT, transparent); _flags.set(IS_UNLIT_BIT, unlit); _flags.set(IS_DEPTH_BIASED_BIT, depthBias); _flags.set(IS_FADING_BIT, fading); _flags.set(IS_ANTIALIASED_BIT, isAntiAliased); _flags.set(IS_FORWARD_BIT, forward); switch (cullFaceMode) { case graphics::MaterialKey::CullFaceMode::CULL_NONE: _flags.set(IS_CULL_FACE_NONE_BIT); _flags.reset(IS_CULL_FACE_FRONT_BIT); break; case graphics::MaterialKey::CullFaceMode::CULL_FRONT: _flags.reset(IS_CULL_FACE_NONE_BIT); _flags.set(IS_CULL_FACE_FRONT_BIT); break; case graphics::MaterialKey::CullFaceMode::CULL_BACK: _flags.reset(IS_CULL_FACE_NONE_BIT); _flags.reset(IS_CULL_FACE_FRONT_BIT); break; default: break; } } SimpleProgramKey(int bitmask) : _flags(bitmask) {} }; inline uint qHash(const SimpleProgramKey& key, uint seed) { return qHash(key.getRaw(), seed); } inline bool operator==(const SimpleProgramKey& a, const SimpleProgramKey& b) { return a.getRaw() == b.getRaw(); } void GeometryCache::bindSimpleProgram(gpu::Batch& batch, bool textured, bool transparent, bool unlit, bool depthBiased, bool isAntiAliased, bool forward, graphics::MaterialKey::CullFaceMode cullFaceMode) { batch.setPipeline(getSimplePipeline(textured, transparent, unlit, depthBiased, false, isAntiAliased, forward, cullFaceMode)); // If not textured, set a default albedo map if (!textured) { batch.setResourceTexture(gr::Texture::MaterialAlbedo, DependencyManager::get()->getWhiteTexture()); } } gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool transparent, bool unlit, bool depthBiased, bool fading, bool isAntiAliased, bool forward, graphics::MaterialKey::CullFaceMode cullFaceMode) { SimpleProgramKey config { textured, transparent, unlit, depthBiased, fading, isAntiAliased, forward, cullFaceMode }; // If the pipeline already exists, return it auto it = _simplePrograms.find(config); if (it != _simplePrograms.end()) { return it.value(); } // Compile the shaders if (!fading) { static std::once_flag once; std::call_once(once, [&]() { using namespace shader::render_utils::program; _shapeShaders[std::make_tuple(false, false, true, false)] = gpu::Shader::createProgram(simple_forward); _shapeShaders[std::make_tuple(true, false, true, false)] = gpu::Shader::createProgram(simple_translucent_forward); _shapeShaders[std::make_tuple(false, true, true, false)] = gpu::Shader::createProgram(simple_unlit_forward); _shapeShaders[std::make_tuple(true, true, true, false)] = gpu::Shader::createProgram(simple_translucent_unlit_forward); _shapeShaders[std::make_tuple(false, false, false, false)] = gpu::Shader::createProgram(simple); _shapeShaders[std::make_tuple(true, false, false, false)] = gpu::Shader::createProgram(simple_translucent); _shapeShaders[std::make_tuple(false, true, false, false)] = gpu::Shader::createProgram(simple_unlit); _shapeShaders[std::make_tuple(true, true, false, false)] = gpu::Shader::createProgram(simple_translucent_unlit); }); } else { static std::once_flag once; std::call_once(once, [&]() { using namespace shader::render_utils::program; // Fading is currently disabled during forward rendering _shapeShaders[std::make_tuple(false, false, true, true)] = gpu::Shader::createProgram(simple_forward); _shapeShaders[std::make_tuple(true, false, true, true)] = gpu::Shader::createProgram(simple_translucent_forward); _shapeShaders[std::make_tuple(false, true, true, true)] = gpu::Shader::createProgram(simple_unlit_forward); _shapeShaders[std::make_tuple(true, true, true, true)] = gpu::Shader::createProgram(simple_translucent_unlit_forward); _shapeShaders[std::make_tuple(false, false, false, true)] = gpu::Shader::createProgram(simple_fade); _shapeShaders[std::make_tuple(true, false, false, true)] = gpu::Shader::createProgram(simple_translucent_fade); _shapeShaders[std::make_tuple(false, true, false, true)] = gpu::Shader::createProgram(simple_unlit_fade); _shapeShaders[std::make_tuple(true, true, false, true)] = gpu::Shader::createProgram(simple_translucent_unlit_fade); }); } // If the pipeline did not exist, make it auto state = std::make_shared(); if (config.isCullFaceNone()) { state->setCullMode(gpu::State::CULL_NONE); } else if (config.isCullFaceFront()) { state->setCullMode(gpu::State::CULL_FRONT); } else { state->setCullMode(gpu::State::CULL_BACK); } state->setDepthTest(true, !config.isTransparent(), ComparisonFunction::LESS_EQUAL); if (config.hasDepthBias()) { state->setDepthBias(-1.0f); state->setDepthBiasSlopeScale(-1.0f); } state->setBlendFunction(config.isTransparent(), gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); if (config.isAntiAliased()) { config.isTransparent() ? PrepareStencil::testMaskResetNoAA(*state) : PrepareStencil::testMaskDrawShape(*state); } else { PrepareStencil::testMaskDrawShapeNoAA(*state); } gpu::ShaderPointer program = _shapeShaders[std::make_tuple(config.isTransparent(), config.isUnlit(), config.isForward(), config.isFading())]; gpu::PipelinePointer pipeline = gpu::Pipeline::create(program, state); _simplePrograms.insert(config, pipeline); return pipeline; } uint32_t GeometryCache::toCompactColor(const glm::vec4& color) { uint32_t compactColor = ((int(color.x * 255.0f) & 0xFF)) | ((int(color.y * 255.0f) & 0xFF) << 8) | ((int(color.z * 255.0f) & 0xFF) << 16) | ((int(color.w * 255.0f) & 0xFF) << 24); return compactColor; } static const size_t INSTANCE_COLOR_BUFFER = 0; static const size_t INSTANCE_FADE1_BUFFER = 1; static const size_t INSTANCE_FADE2_BUFFER = 2; static const size_t INSTANCE_FADE3_BUFFER = 3; static const size_t INSTANCE_FADE4_BUFFER = 4; static const size_t INSTANCE_FADE5_BUFFER = 5; static const size_t INSTANCE_FADE6_BUFFER = 6; static const size_t INSTANCE_FADE7_BUFFER = 7; void renderInstances(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, bool isWire, const render::ShapePipelinePointer& pipeline, GeometryCache::Shape shape) { // Add pipeline to name std::string instanceName = (isWire ? "wire_shapes_" : "solid_shapes_") + std::to_string(shape) + "_" + std::to_string(std::hash()(pipeline)); // Add color to named buffer { gpu::BufferPointer instanceColorBuffer = batch.getNamedBuffer(instanceName, INSTANCE_COLOR_BUFFER); const uint32_t compactColor = GeometryCache::toCompactColor(color); instanceColorBuffer->append(compactColor); } // Add call to named buffer batch.setupNamedCalls(instanceName, [args, isWire, pipeline, shape](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { batch.setPipeline(pipeline->pipeline); pipeline->prepare(batch, args); if (isWire) { DependencyManager::get()->renderWireShapeInstances(batch, shape, data.count(), data.buffers[INSTANCE_COLOR_BUFFER]); } else { DependencyManager::get()->renderShapeInstances(batch, shape, data.count(), data.buffers[INSTANCE_COLOR_BUFFER]); } }); } void renderFadeInstances(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, const FadeObjectParams& fadeParams, bool isWire, const render::ShapePipelinePointer& pipeline, GeometryCache::Shape shape) { // Add pipeline to name std::string instanceName = (isWire ? "wire_fade_shapes_" : "solid_fade_shapes_") + std::to_string(shape) + "_" + std::to_string(std::hash()(pipeline)); // Add color to named buffer { gpu::BufferPointer instanceColorBuffer = batch.getNamedBuffer(instanceName, INSTANCE_COLOR_BUFFER); const uint32_t compactColor = GeometryCache::toCompactColor(color); instanceColorBuffer->append(compactColor); } // Add fade params to named buffers { FadeBuffers fadeBuffers = { batch.getNamedBuffer(instanceName, INSTANCE_FADE1_BUFFER), batch.getNamedBuffer(instanceName, INSTANCE_FADE2_BUFFER), batch.getNamedBuffer(instanceName, INSTANCE_FADE3_BUFFER), batch.getNamedBuffer(instanceName, INSTANCE_FADE4_BUFFER), batch.getNamedBuffer(instanceName, INSTANCE_FADE5_BUFFER), batch.getNamedBuffer(instanceName, INSTANCE_FADE6_BUFFER), batch.getNamedBuffer(instanceName, INSTANCE_FADE7_BUFFER), }; fadeBuffers.append(fadeParams); } // Add call to named buffer batch.setupNamedCalls(instanceName, [args, isWire, pipeline, shape](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { batch.setPipeline(pipeline->pipeline); pipeline->prepare(batch, args); FadeBuffers fadeBuffers = { data.buffers[INSTANCE_FADE1_BUFFER], data.buffers[INSTANCE_FADE2_BUFFER], data.buffers[INSTANCE_FADE3_BUFFER], data.buffers[INSTANCE_FADE4_BUFFER], data.buffers[INSTANCE_FADE5_BUFFER], data.buffers[INSTANCE_FADE6_BUFFER], data.buffers[INSTANCE_FADE7_BUFFER], }; if (isWire) { DependencyManager::get()->renderWireShapeFadeInstances(batch, shape, data.count(), data.buffers[INSTANCE_COLOR_BUFFER], fadeBuffers); } else { DependencyManager::get()->renderShapeFadeInstances(batch, shape, data.count(), data.buffers[INSTANCE_COLOR_BUFFER], fadeBuffers); } }); } void GeometryCache::renderShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, bool wire, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { assert(pipeline != nullptr); renderInstances(args, batch, color, wire, pipeline, shape); } void GeometryCache::renderSolidSphereInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { assert(pipeline != nullptr); renderInstances(args, batch, color, false, pipeline, GeometryCache::Sphere); } void GeometryCache::renderWireCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { assert(pipeline != nullptr); renderInstances(args, batch, color, true, pipeline, GeometryCache::Cube); } void GeometryCache::renderShapeFadeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, bool wire, const glm::vec4& color, const FadeObjectParams& fadeParams, const render::ShapePipelinePointer& pipeline) { assert(pipeline != nullptr); renderFadeInstances(args, batch, color, fadeParams, wire, pipeline, shape); } graphics::MeshPointer GeometryCache::meshFromShape(Shape geometryShape, glm::vec3 color) { auto shapeData = getShapeData(geometryShape); qDebug() << "GeometryCache::getMeshProxyListFromShape" << shapeData << stringFromShape(geometryShape); auto positionsBufferView = buffer_helpers::clone(shapeData->_positionView); auto normalsBufferView = buffer_helpers::clone(shapeData->_normalView); auto indexBufferView = buffer_helpers::clone(shapeData->_indicesView); gpu::BufferView::Size numVertices = positionsBufferView.getNumElements(); Q_ASSERT(numVertices == normalsBufferView.getNumElements()); // apply input color across all vertices auto colorsBufferView = buffer_helpers::clone(shapeData->_normalView); for (gpu::BufferView::Size i = 0; i < numVertices; i++) { colorsBufferView.edit((gpu::BufferView::Index)i) = color; } graphics::MeshPointer mesh(std::make_shared()); mesh->setVertexBuffer(positionsBufferView); mesh->setIndexBuffer(indexBufferView); mesh->addAttribute(gpu::Stream::NORMAL, normalsBufferView); mesh->addAttribute(gpu::Stream::COLOR, colorsBufferView); const auto startIndex = 0, baseVertex = 0; graphics::Mesh::Part part(startIndex, (graphics::Index)indexBufferView.getNumElements(), baseVertex, graphics::Mesh::TRIANGLES); auto partBuffer = new gpu::Buffer(gpu::Buffer::IndirectBuffer, sizeof(graphics::Mesh::Part), (gpu::Byte*)&part); mesh->setPartBuffer(gpu::BufferView(partBuffer, gpu::Element::PART_DRAWCALL)); mesh->modelName = GeometryCache::stringFromShape(geometryShape).toStdString(); mesh->displayName = QString("GeometryCache/shape::%1").arg(GeometryCache::stringFromShape(geometryShape)).toStdString(); return mesh; } void FadeBuffers::append(const FadeObjectParams& fadeParams) { _fade1Buffer->append(4 * sizeof(float), (gpu::Byte*)glm::value_ptr(fadeParams.noiseOffsetAndInverted)); _fade2Buffer->append(4 * sizeof(float), (gpu::Byte*)glm::value_ptr(fadeParams.baseOffsetAndThreshold)); _fade3Buffer->append(4 * sizeof(float), (gpu::Byte*)glm::value_ptr(fadeParams.baseInvSizeAndLevel)); _fade4Buffer->append(4 * sizeof(float), (gpu::Byte*)glm::value_ptr(fadeParams.noiseInvSizeAndLevel)); _fade5Buffer->append(4 * sizeof(float), (gpu::Byte*)glm::value_ptr(fadeParams.innerEdgeColor)); _fade6Buffer->append(4 * sizeof(float), (gpu::Byte*)glm::value_ptr(fadeParams.outerEdgeColor)); _fade7Buffer->append(1 * sizeof(float), (gpu::Byte*)(&fadeParams.edgeWidthInv)); } void FadeBuffers::clear() { _fade1Buffer->resize(0); _fade2Buffer->resize(0); _fade3Buffer->resize(0); _fade4Buffer->resize(0); _fade5Buffer->resize(0); _fade6Buffer->resize(0); _fade7Buffer->resize(0); } void FadeBuffers::bind(gpu::Batch& batch) const { gpu::BufferView fade1View(_fade1Buffer, FADE4_ELEMENT); gpu::BufferView fade2View(_fade2Buffer, FADE4_ELEMENT); gpu::BufferView fade3View(_fade3Buffer, FADE4_ELEMENT); gpu::BufferView fade4View(_fade4Buffer, FADE4_ELEMENT); gpu::BufferView fade5View(_fade5Buffer, FADE4_ELEMENT); gpu::BufferView fade6View(_fade6Buffer, FADE4_ELEMENT); gpu::BufferView fade7View(_fade7Buffer, FADE1_ELEMENT); batch.setInputBuffer(gpu::Stream::FADE1, fade1View); batch.setInputBuffer(gpu::Stream::FADE2, fade2View); batch.setInputBuffer(gpu::Stream::FADE3, fade3View); batch.setInputBuffer(gpu::Stream::FADE4, fade4View); batch.setInputBuffer(gpu::Stream::FADE5, fade5View); batch.setInputBuffer(gpu::Stream::FADE6, fade6View); batch.setInputBuffer(gpu::Stream::FADE7, fade7View); }