mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-28 00:55:12 +02:00
* Adjusts GeometryCache::computeSimpleHullPointListForShape to utilize ShapeData::_positionView as opposed to _normalView. ** Also filters out duplicate points or points close enough to be considered duplicate vertices. This provides ShapeFactory with better point data and should reduce hits to the dedupe pass within createConvexHull. Tested: * Octagon & Tetrahedron ** They looked better than the prior commit, behaved closer to their shape as opposed to cone or narrow cylinder like. Note: * These changes are based on debug info observations using the Octagon and discussion with Andrew Meadows via Slack & https://github.com/highfidelity/hifi/pull/11336#pullrequestreview-69939120 Changes Committed: modified: libraries/render-utils/src/GeometryCache.cpp modified: libraries/render-utils/src/GeometryCache.h
2346 lines
96 KiB
C++
2346 lines
96 KiB
C++
//
|
|
// GeometryCache.cpp
|
|
// interface/src/renderer
|
|
//
|
|
// Created by Andrzej Kapolka on 6/21/13.
|
|
// Copyright 2013 High Fidelity, Inc.
|
|
//
|
|
// Distributed under the Apache License, Version 2.0.
|
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
//
|
|
|
|
#include "GeometryCache.h"
|
|
|
|
#include <qmath.h>
|
|
#include <cmath>
|
|
|
|
#include <QtCore/QThreadPool>
|
|
#include <QtCore/QFileInfo>
|
|
#include <QtNetwork/QNetworkReply>
|
|
|
|
#include <shared/Shapes.h>
|
|
#include <shared/PlatformHacks.h>
|
|
|
|
#include <FSTReader.h>
|
|
#include <NumericalConstants.h>
|
|
|
|
#include "TextureCache.h"
|
|
#include "RenderUtilsLogging.h"
|
|
#include "StencilMaskPass.h"
|
|
#include "FadeEffect.h"
|
|
|
|
#include "gpu/StandardShaderLib.h"
|
|
|
|
#include "model/TextureMap.h"
|
|
#include "render/Args.h"
|
|
|
|
#include "standardTransformPNTC_vert.h"
|
|
#include "standardDrawTexture_frag.h"
|
|
|
|
#include "simple_vert.h"
|
|
#include "simple_textured_frag.h"
|
|
#include "simple_textured_unlit_frag.h"
|
|
#include "simple_fade_vert.h"
|
|
#include "simple_textured_fade_frag.h"
|
|
#include "simple_textured_unlit_fade_frag.h"
|
|
#include "simple_opaque_web_browser_frag.h"
|
|
#include "simple_transparent_web_browser_frag.h"
|
|
#include "glowLine_vert.h"
|
|
#include "glowLine_frag.h"
|
|
|
|
#include "grid_frag.h"
|
|
|
|
//#define WANT_DEBUG
|
|
|
|
// @note: Originally size entity::NUM_SHAPES
|
|
// As of Commit b93e91b9, render-utils no longer retains knowledge of
|
|
// entity lib, and thus doesn't know about entity::NUM_SHAPES. Should
|
|
// the enumerations be altered, this will need to be updated.
|
|
// @see ShapeEntityItem.h
|
|
static std::array<GeometryCache::Shape, (GeometryCache::NUM_SHAPES - 1)> MAPPING{ {
|
|
GeometryCache::Triangle,
|
|
GeometryCache::Quad,
|
|
GeometryCache::Hexagon,
|
|
GeometryCache::Octagon,
|
|
GeometryCache::Circle,
|
|
GeometryCache::Cube,
|
|
GeometryCache::Sphere,
|
|
GeometryCache::Tetrahedron,
|
|
GeometryCache::Octahedron,
|
|
GeometryCache::Dodecahedron,
|
|
GeometryCache::Icosahedron,
|
|
GeometryCache::Torus,
|
|
GeometryCache::Cone,
|
|
GeometryCache::Cylinder,
|
|
} };
|
|
|
|
static const std::array<const char * const, GeometryCache::NUM_SHAPES> GEOCACHE_SHAPE_STRINGS{ {
|
|
"Line",
|
|
"Triangle",
|
|
"Quad",
|
|
"Hexagon",
|
|
"Octagon",
|
|
"Circle",
|
|
"Cube",
|
|
"Sphere",
|
|
"Tetrahedron",
|
|
"Octahedron",
|
|
"Dodecahedron",
|
|
"Icosahedron",
|
|
"Torus",
|
|
"Cone",
|
|
"Cylinder"
|
|
} };
|
|
|
|
const int GeometryCache::UNKNOWN_ID = -1;
|
|
|
|
|
|
static const int VERTICES_PER_TRIANGLE = 3;
|
|
|
|
static const gpu::Element POSITION_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ };
|
|
static const gpu::Element NORMAL_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ };
|
|
static const gpu::Element COLOR_ELEMENT { gpu::VEC4, gpu::NUINT8, gpu::RGBA };
|
|
static const gpu::Element TEXCOORD4_ELEMENT { gpu::VEC4, gpu::FLOAT, gpu::XYZW };
|
|
|
|
static gpu::Stream::FormatPointer SOLID_STREAM_FORMAT;
|
|
static gpu::Stream::FormatPointer INSTANCED_SOLID_STREAM_FORMAT;
|
|
static gpu::Stream::FormatPointer INSTANCED_SOLID_FADE_STREAM_FORMAT;
|
|
|
|
static const uint SHAPE_VERTEX_STRIDE = sizeof(glm::vec3) * 2; // vertices and normals
|
|
static const uint SHAPE_NORMALS_OFFSET = sizeof(glm::vec3);
|
|
|
|
void GeometryCache::computeSimpleHullPointListForShape(const int entityShape, const glm::vec3 &entityHalfExtents, QVector<glm::vec3> &outPointList) {
|
|
|
|
auto geometryCache = DependencyManager::get<GeometryCache>();
|
|
const GeometryCache::Shape geometryShape = GeometryCache::getShapeForEntityShape( entityShape );
|
|
const GeometryCache::ShapeData * shapeData = geometryCache->getShapeData( geometryShape );
|
|
if (!shapeData){
|
|
//--EARLY EXIT--( data isn't ready for some reason... )
|
|
return;
|
|
}
|
|
|
|
const gpu::BufferView & shapeVerts = shapeData->_positionView;
|
|
const gpu::BufferView::Size numItems = shapeVerts.getNumElements();
|
|
|
|
outPointList.reserve((int)numItems);
|
|
QVector<glm::vec3> uniqueVerts;
|
|
uniqueVerts.reserve((int)numItems);
|
|
|
|
const float SQUARED_MAX_INCLUSIVE_FILTER_DISTANCE = 0.0025f;
|
|
for (gpu::BufferView::Index i = 0; i < (gpu::BufferView::Index)numItems; ++i) {
|
|
const int numUniquePoints = (int)uniqueVerts.size();
|
|
const geometry::Vec &curVert = shapeVerts.get<geometry::Vec>(i);
|
|
bool isUniquePoint = true;
|
|
|
|
for (int uniqueIndex = 0; uniqueIndex < numUniquePoints; ++uniqueIndex) {
|
|
const geometry::Vec knownVert = uniqueVerts[uniqueIndex];
|
|
const float distToKnownPoint = glm::length2(knownVert - curVert);
|
|
|
|
if (fabsf(distToKnownPoint) <= SQUARED_MAX_INCLUSIVE_FILTER_DISTANCE) {
|
|
isUniquePoint = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!isUniquePoint) {
|
|
|
|
//--EARLY ITERATION EXIT--
|
|
continue;
|
|
}
|
|
|
|
|
|
uniqueVerts.push_back(curVert);
|
|
outPointList.push_back(curVert * entityHalfExtents);
|
|
}
|
|
}
|
|
|
|
template <size_t SIDES>
|
|
std::vector<vec3> polygon() {
|
|
std::vector<vec3> result;
|
|
result.reserve(SIDES);
|
|
double angleIncrement = 2.0 * M_PI / SIDES;
|
|
for (size_t i = 0; i < SIDES; i++) {
|
|
double angle = (double)i * angleIncrement;
|
|
result.push_back(vec3{ cos(angle) * 0.5, 0.0, sin(angle) * 0.5 });
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void GeometryCache::ShapeData::setupVertices(gpu::BufferPointer& vertexBuffer, const geometry::VertexVector& vertices) {
|
|
gpu::Buffer::Size offset = vertexBuffer->getSize();
|
|
vertexBuffer->append(vertices);
|
|
|
|
gpu::Buffer::Size viewSize = vertices.size() * 2 * sizeof(glm::vec3);
|
|
|
|
_positionView = gpu::BufferView(vertexBuffer, offset,
|
|
viewSize, SHAPE_VERTEX_STRIDE, POSITION_ELEMENT);
|
|
_normalView = gpu::BufferView(vertexBuffer, offset + SHAPE_NORMALS_OFFSET,
|
|
viewSize, SHAPE_VERTEX_STRIDE, NORMAL_ELEMENT);
|
|
}
|
|
|
|
void GeometryCache::ShapeData::setupIndices(gpu::BufferPointer& indexBuffer, const geometry::IndexVector& indices, const geometry::IndexVector& wireIndices) {
|
|
gpu::Buffer::Size offset = indexBuffer->getSize();
|
|
if (!indices.empty()) {
|
|
for (uint32_t i = 0; i < indices.size(); ++i) {
|
|
indexBuffer->append((uint16_t)indices[i]);
|
|
}
|
|
}
|
|
gpu::Size viewSize = indices.size() * sizeof(uint16_t);
|
|
_indicesView = gpu::BufferView(indexBuffer, offset, viewSize, gpu::Element::INDEX_UINT16);
|
|
|
|
offset = indexBuffer->getSize();
|
|
if (!wireIndices.empty()) {
|
|
for (uint32_t i = 0; i < wireIndices.size(); ++i) {
|
|
indexBuffer->append((uint16_t)wireIndices[i]);
|
|
}
|
|
}
|
|
viewSize = wireIndices.size() * sizeof(uint16_t);
|
|
_wireIndicesView = gpu::BufferView(indexBuffer, offset, viewSize, gpu::Element::INDEX_UINT16);
|
|
}
|
|
|
|
void GeometryCache::ShapeData::setupBatch(gpu::Batch& batch) const {
|
|
batch.setInputBuffer(gpu::Stream::POSITION, _positionView);
|
|
batch.setInputBuffer(gpu::Stream::NORMAL, _normalView);
|
|
batch.setIndexBuffer(_indicesView);
|
|
}
|
|
|
|
void GeometryCache::ShapeData::draw(gpu::Batch& batch) const {
|
|
uint32_t numIndices = (uint32_t)_indicesView.getNumElements();
|
|
if (numIndices > 0) {
|
|
setupBatch(batch);
|
|
batch.drawIndexed(gpu::TRIANGLES, numIndices, 0);
|
|
}
|
|
}
|
|
|
|
void GeometryCache::ShapeData::drawWire(gpu::Batch& batch) const {
|
|
uint32_t numIndices = (uint32_t)_wireIndicesView.getNumElements();
|
|
if (numIndices > 0) {
|
|
batch.setInputBuffer(gpu::Stream::POSITION, _positionView);
|
|
batch.setInputBuffer(gpu::Stream::NORMAL, _normalView);
|
|
batch.setIndexBuffer(_wireIndicesView);
|
|
batch.drawIndexed(gpu::LINES, numIndices, 0);
|
|
}
|
|
}
|
|
|
|
void GeometryCache::ShapeData::drawInstances(gpu::Batch& batch, size_t count) const {
|
|
uint32_t numIndices = (uint32_t)_indicesView.getNumElements();
|
|
if (numIndices > 0) {
|
|
setupBatch(batch);
|
|
batch.drawIndexedInstanced((gpu::uint32)count, gpu::TRIANGLES, numIndices, 0);
|
|
}
|
|
}
|
|
|
|
void GeometryCache::ShapeData::drawWireInstances(gpu::Batch& batch, size_t count) const {
|
|
uint32_t numIndices = (uint32_t)_wireIndicesView.getNumElements();
|
|
if (numIndices > 0) {
|
|
batch.setInputBuffer(gpu::Stream::POSITION, _positionView);
|
|
batch.setInputBuffer(gpu::Stream::NORMAL, _normalView);
|
|
batch.setIndexBuffer(_wireIndicesView);
|
|
batch.drawIndexedInstanced((gpu::uint32)count, gpu::LINES, numIndices, 0);
|
|
}
|
|
}
|
|
|
|
static const size_t ICOSAHEDRON_TO_SPHERE_TESSELATION_COUNT = 3;
|
|
|
|
size_t GeometryCache::getShapeTriangleCount(Shape shape) {
|
|
return _shapes[shape]._indicesView.getNumElements() / VERTICES_PER_TRIANGLE;
|
|
}
|
|
|
|
size_t GeometryCache::getSphereTriangleCount() {
|
|
return getShapeTriangleCount(Sphere);
|
|
}
|
|
|
|
size_t GeometryCache::getCubeTriangleCount() {
|
|
return getShapeTriangleCount(Cube);
|
|
}
|
|
|
|
using IndexPair = uint64_t;
|
|
using IndexPairs = std::unordered_set<IndexPair>;
|
|
|
|
static IndexPair indexToken(geometry::Index a, geometry::Index b) {
|
|
if (a > b) {
|
|
std::swap(a, b);
|
|
}
|
|
return (((IndexPair)a) << 32) | ((IndexPair)b);
|
|
}
|
|
|
|
template <size_t N>
|
|
void setupFlatShape(GeometryCache::ShapeData& shapeData, const geometry::Solid<N>& shape, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer) {
|
|
using namespace geometry;
|
|
VertexVector vertices;
|
|
IndexVector solidIndices, wireIndices;
|
|
IndexPairs wireSeenIndices;
|
|
|
|
size_t faceCount = shape.faces.size();
|
|
size_t faceIndexCount = triangulatedFaceIndexCount<N>();
|
|
|
|
vertices.reserve(N * faceCount * 2);
|
|
solidIndices.reserve(faceIndexCount * faceCount);
|
|
|
|
Index baseVertex = 0;
|
|
for (size_t f = 0; f < faceCount; f++) {
|
|
const Face<N>& face = shape.faces[f];
|
|
// Compute the face normal
|
|
vec3 faceNormal = shape.getFaceNormal(f);
|
|
|
|
// Create the vertices for the face
|
|
for (Index i = 0; i < N; i++) {
|
|
Index originalIndex = face[i];
|
|
vertices.push_back(shape.vertices[originalIndex]);
|
|
vertices.push_back(faceNormal);
|
|
}
|
|
|
|
// Create the wire indices for unseen edges
|
|
for (Index i = 0; i < N; i++) {
|
|
Index a = i;
|
|
Index b = (i + 1) % N;
|
|
auto token = indexToken(face[a], face[b]);
|
|
if (0 == wireSeenIndices.count(token)) {
|
|
wireSeenIndices.insert(token);
|
|
wireIndices.push_back(a + baseVertex);
|
|
wireIndices.push_back(b + baseVertex);
|
|
}
|
|
}
|
|
|
|
// Create the solid face indices
|
|
for (Index i = 0; i < N - 2; i++) {
|
|
solidIndices.push_back(0 + baseVertex);
|
|
solidIndices.push_back(i + 1 + baseVertex);
|
|
solidIndices.push_back(i + 2 + baseVertex);
|
|
}
|
|
baseVertex += (Index)N;
|
|
}
|
|
|
|
shapeData.setupVertices(vertexBuffer, vertices);
|
|
shapeData.setupIndices(indexBuffer, solidIndices, wireIndices);
|
|
}
|
|
|
|
template <size_t N>
|
|
void setupSmoothShape(GeometryCache::ShapeData& shapeData, const geometry::Solid<N>& shape, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer) {
|
|
using namespace geometry;
|
|
|
|
VertexVector vertices;
|
|
vertices.reserve(shape.vertices.size() * 2);
|
|
for (const auto& vertex : shape.vertices) {
|
|
vertices.push_back(vertex);
|
|
vertices.push_back(vertex);
|
|
}
|
|
|
|
IndexVector solidIndices, wireIndices;
|
|
IndexPairs wireSeenIndices;
|
|
|
|
size_t faceCount = shape.faces.size();
|
|
size_t faceIndexCount = triangulatedFaceIndexCount<N>();
|
|
|
|
solidIndices.reserve(faceIndexCount * faceCount);
|
|
|
|
Index baseVertex = 0;
|
|
for (size_t f = 0; f < faceCount; f++) {
|
|
const Face<N>& face = shape.faces[f];
|
|
// Create the wire indices for unseen edges
|
|
for (Index i = 0; i < N; i++) {
|
|
Index a = face[i];
|
|
Index b = face[(i + 1) % N];
|
|
auto token = indexToken(a, b);
|
|
if (0 == wireSeenIndices.count(token)) {
|
|
wireSeenIndices.insert(token);
|
|
wireIndices.push_back(a + baseVertex);
|
|
wireIndices.push_back(b + baseVertex);
|
|
}
|
|
}
|
|
|
|
// Create the solid face indices
|
|
for (Index i = 0; i < N - 2; i++) {
|
|
solidIndices.push_back(face[i] + baseVertex);
|
|
solidIndices.push_back(face[i + 1] + baseVertex);
|
|
solidIndices.push_back(face[i + 2] + baseVertex);
|
|
}
|
|
}
|
|
|
|
shapeData.setupVertices(vertexBuffer, vertices);
|
|
shapeData.setupIndices(indexBuffer, solidIndices, wireIndices);
|
|
}
|
|
|
|
template <uint32_t N>
|
|
void extrudePolygon(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer, bool isConical = false) {
|
|
using namespace geometry;
|
|
VertexVector vertices;
|
|
IndexVector solidIndices, wireIndices;
|
|
|
|
// Top (if not conical) and bottom faces
|
|
std::vector<vec3> shape = polygon<N>();
|
|
if (isConical) {
|
|
for (uint32_t i = 0; i < N; i++) {
|
|
vertices.push_back(vec3(0.0f, 0.5f, 0.0f));
|
|
vertices.push_back(vec3(0.0f, 1.0f, 0.0f));
|
|
}
|
|
} else {
|
|
for (const vec3& v : shape) {
|
|
vertices.push_back(vec3(v.x, 0.5f, v.z));
|
|
vertices.push_back(vec3(0.0f, 1.0f, 0.0f));
|
|
}
|
|
}
|
|
for (const vec3& v : shape) {
|
|
vertices.push_back(vec3(v.x, -0.5f, v.z));
|
|
vertices.push_back(vec3(0.0f, -1.0f, 0.0f));
|
|
}
|
|
Index baseVertex = 0;
|
|
for (uint32_t i = 2; i < N; i++) {
|
|
solidIndices.push_back(baseVertex + 0);
|
|
solidIndices.push_back(baseVertex + i);
|
|
solidIndices.push_back(baseVertex + i - 1);
|
|
solidIndices.push_back(baseVertex + N);
|
|
solidIndices.push_back(baseVertex + i + N - 1);
|
|
solidIndices.push_back(baseVertex + i + N);
|
|
}
|
|
for (uint32_t i = 1; i <= N; i++) {
|
|
wireIndices.push_back(baseVertex + (i % N));
|
|
wireIndices.push_back(baseVertex + i - 1);
|
|
wireIndices.push_back(baseVertex + (i % N) + N);
|
|
wireIndices.push_back(baseVertex + (i - 1) + N);
|
|
}
|
|
|
|
// Now do the sides
|
|
baseVertex += 2 * N;
|
|
|
|
for (uint32_t i = 0; i < N; i++) {
|
|
vec3 left = shape[i];
|
|
vec3 right = shape[(i + 1) % N];
|
|
vec3 normal = glm::normalize(left + right);
|
|
vec3 topLeft = (isConical ? vec3(0.0f, 0.5f, 0.0f) : vec3(left.x, 0.5f, left.z));
|
|
vec3 topRight = (isConical ? vec3(0.0f, 0.5f, 0.0f) : vec3(right.x, 0.5f, right.z));
|
|
vec3 bottomLeft = vec3(left.x, -0.5f, left.z);
|
|
vec3 bottomRight = vec3(right.x, -0.5f, right.z);
|
|
|
|
vertices.push_back(topLeft);
|
|
vertices.push_back(normal);
|
|
vertices.push_back(bottomLeft);
|
|
vertices.push_back(normal);
|
|
vertices.push_back(topRight);
|
|
vertices.push_back(normal);
|
|
vertices.push_back(bottomRight);
|
|
vertices.push_back(normal);
|
|
|
|
solidIndices.push_back(baseVertex + 0);
|
|
solidIndices.push_back(baseVertex + 2);
|
|
solidIndices.push_back(baseVertex + 1);
|
|
solidIndices.push_back(baseVertex + 1);
|
|
solidIndices.push_back(baseVertex + 2);
|
|
solidIndices.push_back(baseVertex + 3);
|
|
wireIndices.push_back(baseVertex + 0);
|
|
wireIndices.push_back(baseVertex + 1);
|
|
wireIndices.push_back(baseVertex + 3);
|
|
wireIndices.push_back(baseVertex + 2);
|
|
baseVertex += 4;
|
|
}
|
|
|
|
shapeData.setupVertices(vertexBuffer, vertices);
|
|
shapeData.setupIndices(indexBuffer, solidIndices, wireIndices);
|
|
}
|
|
|
|
void drawCircle(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer) {
|
|
// Draw a circle with radius 1/4th the size of the bounding box
|
|
using namespace geometry;
|
|
|
|
VertexVector vertices;
|
|
IndexVector solidIndices, wireIndices;
|
|
const int NUM_CIRCLE_VERTICES = 64;
|
|
|
|
std::vector<vec3> shape = polygon<NUM_CIRCLE_VERTICES>();
|
|
for (const vec3& v : shape) {
|
|
vertices.push_back(vec3(v.x, 0.0f, v.z));
|
|
vertices.push_back(vec3(0.0f, 0.0f, 0.0f));
|
|
}
|
|
|
|
Index baseVertex = 0;
|
|
for (uint32_t i = 2; i < NUM_CIRCLE_VERTICES; i++) {
|
|
solidIndices.push_back(baseVertex + 0);
|
|
solidIndices.push_back(baseVertex + i);
|
|
solidIndices.push_back(baseVertex + i - 1);
|
|
solidIndices.push_back(baseVertex + NUM_CIRCLE_VERTICES);
|
|
solidIndices.push_back(baseVertex + i + NUM_CIRCLE_VERTICES - 1);
|
|
solidIndices.push_back(baseVertex + i + NUM_CIRCLE_VERTICES);
|
|
}
|
|
|
|
for (uint32_t i = 1; i <= NUM_CIRCLE_VERTICES; i++) {
|
|
wireIndices.push_back(baseVertex + (i % NUM_CIRCLE_VERTICES));
|
|
wireIndices.push_back(baseVertex + i - 1);
|
|
wireIndices.push_back(baseVertex + (i % NUM_CIRCLE_VERTICES) + NUM_CIRCLE_VERTICES);
|
|
wireIndices.push_back(baseVertex + (i - 1) + NUM_CIRCLE_VERTICES);
|
|
}
|
|
|
|
shapeData.setupVertices(vertexBuffer, vertices);
|
|
shapeData.setupIndices(indexBuffer, solidIndices, wireIndices);
|
|
}
|
|
|
|
// FIXME solids need per-face vertices, but smooth shaded
|
|
// components do not. Find a way to support using draw elements
|
|
// or draw arrays as appropriate
|
|
// Maybe special case cone and cylinder since they combine flat
|
|
// and smooth shading
|
|
void GeometryCache::buildShapes() {
|
|
using namespace geometry;
|
|
auto vertexBuffer = std::make_shared<gpu::Buffer>();
|
|
auto indexBuffer = std::make_shared<gpu::Buffer>();
|
|
// Cube
|
|
setupFlatShape(_shapes[Cube], geometry::cube(), _shapeVertices, _shapeIndices);
|
|
// 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, VertexVector {
|
|
vec3(-0.5f, 0.0f, 0.0f), vec3(-0.5f, 0.0f, 0.0f),
|
|
vec3(0.5f, 0.0f, 0.0f), vec3(0.5f, 0.0f, 0.0f)
|
|
});
|
|
IndexVector wireIndices;
|
|
// Only two indices
|
|
wireIndices.push_back(0);
|
|
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
|
|
drawCircle(_shapes[Circle], _shapeVertices, _shapeIndices);
|
|
// Not implemented yet:
|
|
//Quad,
|
|
//Torus,
|
|
|
|
}
|
|
|
|
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<gpu::Stream::Format>(); // 1 for everyone
|
|
SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT);
|
|
SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT);
|
|
}
|
|
return SOLID_STREAM_FORMAT;
|
|
}
|
|
|
|
gpu::Stream::FormatPointer& getInstancedSolidStreamFormat() {
|
|
if (!INSTANCED_SOLID_STREAM_FORMAT) {
|
|
INSTANCED_SOLID_STREAM_FORMAT = std::make_shared<gpu::Stream::Format>(); // 1 for everyone
|
|
INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT);
|
|
INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT);
|
|
INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE);
|
|
}
|
|
return INSTANCED_SOLID_STREAM_FORMAT;
|
|
}
|
|
|
|
gpu::Stream::FormatPointer& getInstancedSolidFadeStreamFormat() {
|
|
if (!INSTANCED_SOLID_FADE_STREAM_FORMAT) {
|
|
INSTANCED_SOLID_FADE_STREAM_FORMAT = std::make_shared<gpu::Stream::Format>(); // 1 for everyone
|
|
INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT);
|
|
INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT);
|
|
INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE);
|
|
INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD2, gpu::Stream::TEXCOORD2, TEXCOORD4_ELEMENT, 0, gpu::Stream::PER_INSTANCE);
|
|
INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD3, gpu::Stream::TEXCOORD3, TEXCOORD4_ELEMENT, 0, gpu::Stream::PER_INSTANCE);
|
|
INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD4, gpu::Stream::TEXCOORD4, TEXCOORD4_ELEMENT, 0, gpu::Stream::PER_INSTANCE);
|
|
}
|
|
return INSTANCED_SOLID_FADE_STREAM_FORMAT;
|
|
}
|
|
|
|
QHash<SimpleProgramKey, gpu::PipelinePointer> GeometryCache::_simplePrograms;
|
|
|
|
gpu::ShaderPointer GeometryCache::_simpleShader;
|
|
gpu::ShaderPointer GeometryCache::_unlitShader;
|
|
gpu::ShaderPointer GeometryCache::_simpleFadeShader;
|
|
gpu::ShaderPointer GeometryCache::_unlitFadeShader;
|
|
|
|
render::ShapePipelinePointer GeometryCache::_simpleOpaquePipeline;
|
|
render::ShapePipelinePointer GeometryCache::_simpleTransparentPipeline;
|
|
render::ShapePipelinePointer GeometryCache::_simpleOpaqueFadePipeline;
|
|
render::ShapePipelinePointer GeometryCache::_simpleTransparentFadePipeline;
|
|
render::ShapePipelinePointer GeometryCache::_simpleWirePipeline;
|
|
|
|
uint8_t GeometryCache::CUSTOM_PIPELINE_NUMBER = 0;
|
|
|
|
render::ShapePipelinePointer GeometryCache::shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key) {
|
|
initializeShapePipelines();
|
|
|
|
if (key.isWireframe()) {
|
|
return _simpleWirePipeline;
|
|
}
|
|
|
|
if (key.isFaded()) {
|
|
if (key.isTranslucent()) {
|
|
return _simpleTransparentFadePipeline;
|
|
}
|
|
else {
|
|
return _simpleOpaqueFadePipeline;
|
|
}
|
|
}
|
|
else {
|
|
if (key.isTranslucent()) {
|
|
return _simpleTransparentPipeline;
|
|
}
|
|
else {
|
|
return _simpleOpaquePipeline;
|
|
}
|
|
}
|
|
}
|
|
|
|
GeometryCache::GeometryCache() :
|
|
_nextID(0) {
|
|
// Let's register its special shapePipeline factory:
|
|
registerShapePipeline();
|
|
buildShapes();
|
|
}
|
|
|
|
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) {
|
|
_registeredQuad3DTextures.remove(id);
|
|
_lastRegisteredQuad2DTexture.remove(id);
|
|
_registeredQuad2DTextures.remove(id);
|
|
_lastRegisteredQuad3D.remove(id);
|
|
_registeredQuad3D.remove(id);
|
|
|
|
_lastRegisteredQuad2D.remove(id);
|
|
_registeredQuad2D.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);
|
|
}
|
|
|
|
void GeometryCache::initializeShapePipelines() {
|
|
if (!_simpleOpaquePipeline) {
|
|
_simpleOpaquePipeline = getShapePipeline(false, false, true, false);
|
|
_simpleTransparentPipeline = getShapePipeline(false, true, true, false);
|
|
_simpleOpaqueFadePipeline = getFadingShapePipeline(false, false, false, false, false);
|
|
_simpleTransparentFadePipeline = getFadingShapePipeline(false, true, false, false, false);
|
|
_simpleWirePipeline = getShapePipeline(false, false, true, true);
|
|
}
|
|
}
|
|
|
|
render::ShapePipelinePointer GeometryCache::getShapePipeline(bool textured, bool transparent, bool culled,
|
|
bool unlit, bool depthBias) {
|
|
|
|
return std::make_shared<render::ShapePipeline>(getSimplePipeline(textured, transparent, culled, unlit, depthBias, false, true), nullptr,
|
|
[](const render::ShapePipeline& , gpu::Batch& batch, render::Args*) {
|
|
batch.setResourceTexture(render::ShapePipeline::Slot::MAP::ALBEDO, DependencyManager::get<TextureCache>()->getWhiteTexture());
|
|
}
|
|
);
|
|
}
|
|
|
|
render::ShapePipelinePointer GeometryCache::getFadingShapePipeline(bool textured, bool transparent, bool culled,
|
|
bool unlit, bool depthBias) {
|
|
auto fadeEffect = DependencyManager::get<FadeEffect>();
|
|
auto fadeBatchSetter = fadeEffect->getBatchSetter();
|
|
auto fadeItemSetter = fadeEffect->getItemStoredSetter();
|
|
return std::make_shared<render::ShapePipeline>(getSimplePipeline(textured, transparent, culled, unlit, depthBias, true), nullptr,
|
|
[fadeBatchSetter, fadeItemSetter](const render::ShapePipeline& shapePipeline, gpu::Batch& batch, render::Args* args) {
|
|
batch.setResourceTexture(render::ShapePipeline::Slot::MAP::ALBEDO, DependencyManager::get<TextureCache>()->getWhiteTexture());
|
|
fadeBatchSetter(shapePipeline, batch, args);
|
|
},
|
|
fadeItemSetter
|
|
);
|
|
}
|
|
|
|
render::ShapePipelinePointer GeometryCache::getOpaqueShapePipeline(bool isFading) {
|
|
return isFading ? _simpleOpaqueFadePipeline : _simpleOpaquePipeline;
|
|
}
|
|
|
|
render::ShapePipelinePointer GeometryCache::getTransparentShapePipeline(bool isFading) {
|
|
return isFading ? _simpleTransparentFadePipeline : _simpleTransparentPipeline;
|
|
}
|
|
|
|
void GeometryCache::renderShape(gpu::Batch& batch, Shape shape) {
|
|
batch.setInputFormat(getSolidStreamFormat());
|
|
_shapes[shape].draw(batch);
|
|
}
|
|
|
|
void GeometryCache::renderWireShape(gpu::Batch& batch, Shape shape) {
|
|
batch.setInputFormat(getSolidStreamFormat());
|
|
_shapes[shape].drawWire(batch);
|
|
}
|
|
|
|
void setupBatchInstance(gpu::Batch& batch, gpu::BufferPointer colorBuffer) {
|
|
gpu::BufferView colorView(colorBuffer, COLOR_ELEMENT);
|
|
batch.setInputBuffer(gpu::Stream::COLOR, colorView);
|
|
}
|
|
|
|
void GeometryCache::renderShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer) {
|
|
batch.setInputFormat(getInstancedSolidStreamFormat());
|
|
setupBatchInstance(batch, colorBuffer);
|
|
_shapes[shape].drawInstances(batch, count);
|
|
}
|
|
|
|
void GeometryCache::renderWireShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer) {
|
|
batch.setInputFormat(getInstancedSolidStreamFormat());
|
|
setupBatchInstance(batch, colorBuffer);
|
|
_shapes[shape].drawWireInstances(batch, count);
|
|
}
|
|
|
|
void setupBatchFadeInstance(gpu::Batch& batch, gpu::BufferPointer colorBuffer,
|
|
gpu::BufferPointer fadeBuffer1, gpu::BufferPointer fadeBuffer2, gpu::BufferPointer fadeBuffer3) {
|
|
gpu::BufferView colorView(colorBuffer, COLOR_ELEMENT);
|
|
gpu::BufferView texCoord2View(fadeBuffer1, TEXCOORD4_ELEMENT);
|
|
gpu::BufferView texCoord3View(fadeBuffer2, TEXCOORD4_ELEMENT);
|
|
gpu::BufferView texCoord4View(fadeBuffer3, TEXCOORD4_ELEMENT);
|
|
batch.setInputBuffer(gpu::Stream::COLOR, colorView);
|
|
batch.setInputBuffer(gpu::Stream::TEXCOORD2, texCoord2View);
|
|
batch.setInputBuffer(gpu::Stream::TEXCOORD3, texCoord3View);
|
|
batch.setInputBuffer(gpu::Stream::TEXCOORD4, texCoord4View);
|
|
}
|
|
|
|
void GeometryCache::renderFadeShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer,
|
|
gpu::BufferPointer& fadeBuffer1, gpu::BufferPointer& fadeBuffer2, gpu::BufferPointer& fadeBuffer3) {
|
|
batch.setInputFormat(getInstancedSolidFadeStreamFormat());
|
|
setupBatchFadeInstance(batch, colorBuffer, fadeBuffer1, fadeBuffer2, fadeBuffer3);
|
|
_shapes[shape].drawInstances(batch, count);
|
|
}
|
|
|
|
void GeometryCache::renderWireFadeShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer,
|
|
gpu::BufferPointer& fadeBuffer1, gpu::BufferPointer& fadeBuffer2, gpu::BufferPointer& fadeBuffer3) {
|
|
batch.setInputFormat(getInstancedSolidFadeStreamFormat());
|
|
setupBatchFadeInstance(batch, colorBuffer, fadeBuffer1, fadeBuffer2, fadeBuffer3);
|
|
_shapes[shape].drawWireInstances(batch, count);
|
|
}
|
|
|
|
void GeometryCache::renderCube(gpu::Batch& batch) {
|
|
renderShape(batch, Cube);
|
|
}
|
|
|
|
void GeometryCache::renderWireCube(gpu::Batch& batch) {
|
|
renderWireShape(batch, Cube);
|
|
}
|
|
|
|
void GeometryCache::renderSphere(gpu::Batch& batch) {
|
|
renderShape(batch, Sphere);
|
|
}
|
|
|
|
void GeometryCache::renderWireSphere(gpu::Batch& batch) {
|
|
renderWireShape(batch, Sphere);
|
|
}
|
|
|
|
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 isLayered, int id) {
|
|
static const glm::vec2 MIN_TEX_COORD(0.0f, 0.0f);
|
|
static const glm::vec2 MAX_TEX_COORD(1.0f, 1.0f);
|
|
|
|
bool registered = (id != UNKNOWN_ID);
|
|
Vec2FloatPair majorKey(glm::vec2(majorRows, majorCols), majorEdge);
|
|
Vec2FloatPair minorKey(glm::vec2(minorRows, minorCols), minorEdge);
|
|
Vec2FloatPairPair key(majorKey, minorKey);
|
|
|
|
// Make the gridbuffer
|
|
if (registered && (!_registeredGridBuffers.contains(id) || _lastRegisteredGridBuffer[id] != key)) {
|
|
GridSchema gridSchema;
|
|
GridBuffer gridBuffer = std::make_shared<gpu::Buffer>(sizeof(GridSchema), (const gpu::Byte*) &gridSchema);
|
|
|
|
if (registered && _registeredGridBuffers.contains(id)) {
|
|
gridBuffer = _registeredGridBuffers[id];
|
|
}
|
|
|
|
_registeredGridBuffers[id] = gridBuffer;
|
|
_lastRegisteredGridBuffer[id] = key;
|
|
|
|
gridBuffer.edit<GridSchema>().period = glm::vec4(majorRows, majorCols, minorRows, minorCols);
|
|
gridBuffer.edit<GridSchema>().offset.x = -(majorEdge / majorRows) / 2;
|
|
gridBuffer.edit<GridSchema>().offset.y = -(majorEdge / majorCols) / 2;
|
|
gridBuffer.edit<GridSchema>().offset.z = -(minorEdge / minorRows) / 2;
|
|
gridBuffer.edit<GridSchema>().offset.w = -(minorEdge / minorCols) / 2;
|
|
gridBuffer.edit<GridSchema>().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, _registeredGridBuffers[id], isLayered);
|
|
|
|
renderQuad(batch, minCorner, maxCorner, MIN_TEX_COORD, MAX_TEX_COORD, color, id);
|
|
}
|
|
|
|
void GeometryCache::updateVertices(int id, const QVector<glm::vec2>& points, const QVector<glm::vec4>& 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 + 3; // vertices + normals
|
|
const int NUM_POS_COORDS = 2;
|
|
const int VERTEX_NORMAL_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>();
|
|
auto colorBuffer = std::make_shared<gpu::Buffer>();
|
|
auto streamFormat = std::make_shared<gpu::Stream::Format>();
|
|
auto stream = std::make_shared<gpu::BufferStream>();
|
|
|
|
details.verticesBuffer = verticesBuffer;
|
|
details.colorBuffer = colorBuffer;
|
|
details.streamFormat = streamFormat;
|
|
details.stream = stream;
|
|
|
|
details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ), 0);
|
|
details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET);
|
|
details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA));
|
|
|
|
details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride);
|
|
details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride);
|
|
|
|
details.vertices = points.size();
|
|
details.vertexSize = FLOATS_PER_VERTEX;
|
|
|
|
float* vertexData = new float[details.vertices * FLOATS_PER_VERTEX];
|
|
float* vertex = vertexData;
|
|
|
|
int* colorData = new int[details.vertices];
|
|
int* colorDataAt = colorData;
|
|
|
|
const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f);
|
|
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;
|
|
*(vertex++) = NORMAL.x;
|
|
*(vertex++) = NORMAL.y;
|
|
*(vertex++) = NORMAL.z;
|
|
if (i < colorCount) {
|
|
const auto& color = colors[i];
|
|
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);
|
|
}
|
|
*(colorDataAt++) = compactColor;
|
|
}
|
|
details.verticesBuffer->append(sizeof(float) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*) vertexData);
|
|
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<glm::vec2>& points, const glm::vec4& color) {
|
|
updateVertices(id, points, QVector<glm::vec4>({ color }));
|
|
}
|
|
|
|
void GeometryCache::updateVertices(int id, const QVector<glm::vec3>& points, const QVector<glm::vec4>& 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 + 3; // vertices + normals
|
|
const int NUM_POS_COORDS = 3;
|
|
const int VERTEX_NORMAL_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>();
|
|
auto colorBuffer = std::make_shared<gpu::Buffer>();
|
|
auto streamFormat = std::make_shared<gpu::Stream::Format>();
|
|
auto stream = std::make_shared<gpu::BufferStream>();
|
|
|
|
details.verticesBuffer = verticesBuffer;
|
|
details.colorBuffer = colorBuffer;
|
|
details.streamFormat = streamFormat;
|
|
details.stream = stream;
|
|
|
|
details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
|
|
details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET);
|
|
details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA));
|
|
|
|
details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride);
|
|
details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride);
|
|
|
|
details.vertices = points.size();
|
|
details.vertexSize = FLOATS_PER_VERTEX;
|
|
|
|
// 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;
|
|
|
|
const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f);
|
|
auto pointCount = points.size();
|
|
auto colorCount = colors.size();
|
|
for (auto i = 0; i < pointCount; i++) {
|
|
const glm::vec3& point = points[i];
|
|
if (i < colorCount) {
|
|
const glm::vec4& color = colors[i];
|
|
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);
|
|
}
|
|
*(vertex++) = point.x;
|
|
*(vertex++) = point.y;
|
|
*(vertex++) = point.z;
|
|
*(vertex++) = NORMAL.x;
|
|
*(vertex++) = NORMAL.y;
|
|
*(vertex++) = NORMAL.z;
|
|
*(colorDataAt++) = compactColor;
|
|
}
|
|
|
|
details.verticesBuffer->append(sizeof(float) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*) vertexData);
|
|
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<glm::vec3>& points, const glm::vec4& color) {
|
|
updateVertices(id, points, QVector<glm::vec4>({ color }));
|
|
}
|
|
|
|
void GeometryCache::updateVertices(int id, const QVector<glm::vec3>& points, const QVector<glm::vec2>& 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 + 3 + 2; // vertices + normals + tex coords
|
|
const int NUM_POS_COORDS = 3;
|
|
const int NUM_NORMAL_COORDS = 3;
|
|
const int VERTEX_NORMAL_OFFSET = NUM_POS_COORDS * sizeof(float);
|
|
const int VERTEX_TEX_OFFSET = VERTEX_NORMAL_OFFSET + NUM_NORMAL_COORDS * sizeof(float);
|
|
details.isCreated = true;
|
|
details.vertices = points.size();
|
|
details.vertexSize = FLOATS_PER_VERTEX;
|
|
|
|
auto verticesBuffer = std::make_shared<gpu::Buffer>();
|
|
auto colorBuffer = std::make_shared<gpu::Buffer>();
|
|
auto streamFormat = std::make_shared<gpu::Stream::Format>();
|
|
auto stream = std::make_shared<gpu::BufferStream>();
|
|
|
|
details.verticesBuffer = verticesBuffer;
|
|
details.colorBuffer = colorBuffer;
|
|
details.streamFormat = streamFormat;
|
|
details.stream = stream;
|
|
|
|
details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
|
|
details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET);
|
|
details.streamFormat->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), VERTEX_TEX_OFFSET);
|
|
details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA));
|
|
|
|
details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride);
|
|
details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride);
|
|
|
|
assert(points.size() == texCoords.size());
|
|
|
|
details.vertices = points.size();
|
|
details.vertexSize = FLOATS_PER_VERTEX;
|
|
|
|
int 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);
|
|
|
|
float* vertexData = new float[details.vertices * FLOATS_PER_VERTEX];
|
|
float* vertex = vertexData;
|
|
|
|
int* colorData = new int[details.vertices];
|
|
int* colorDataAt = colorData;
|
|
|
|
const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f);
|
|
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++) = NORMAL.x;
|
|
*(vertex++) = NORMAL.y;
|
|
*(vertex++) = NORMAL.z;
|
|
*(vertex++) = texCoord.x;
|
|
*(vertex++) = texCoord.y;
|
|
|
|
*(colorDataAt++) = compactColor;
|
|
}
|
|
|
|
details.verticesBuffer->append(sizeof(float) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*) vertexData);
|
|
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::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>();
|
|
auto colorBuffer = std::make_shared<gpu::Buffer>();
|
|
auto streamFormat = std::make_shared<gpu::Stream::Format>();
|
|
auto stream = std::make_shared<gpu::BufferStream>();
|
|
|
|
details.verticesBuffer = verticesBuffer;
|
|
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::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA));
|
|
|
|
details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride);
|
|
details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._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;
|
|
|
|
int 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);
|
|
int colors[NUM_VERTICES] = { compactColor, compactColor, compactColor, compactColor,
|
|
compactColor, compactColor, compactColor, compactColor };
|
|
|
|
|
|
details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer);
|
|
details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors);
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
const int FLOATS_PER_VERTEX = 2 + 3; // vertices + normals
|
|
const int VERTICES = 4; // 1 quad = 4 vertices
|
|
const int NUM_POS_COORDS = 2;
|
|
const int VERTEX_NORMAL_OFFSET = NUM_POS_COORDS * sizeof(float);
|
|
|
|
if (!details.isCreated) {
|
|
|
|
details.isCreated = true;
|
|
details.vertices = VERTICES;
|
|
details.vertexSize = FLOATS_PER_VERTEX;
|
|
|
|
auto verticesBuffer = std::make_shared<gpu::Buffer>();
|
|
auto colorBuffer = std::make_shared<gpu::Buffer>();
|
|
auto streamFormat = std::make_shared<gpu::Stream::Format>();
|
|
auto stream = std::make_shared<gpu::BufferStream>();
|
|
|
|
details.verticesBuffer = verticesBuffer;
|
|
details.colorBuffer = colorBuffer;
|
|
details.streamFormat = streamFormat;
|
|
details.stream = stream;
|
|
|
|
details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ), 0);
|
|
details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET);
|
|
details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA));
|
|
|
|
details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride);
|
|
details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride);
|
|
|
|
|
|
const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f);
|
|
float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = {
|
|
minCorner.x, minCorner.y, NORMAL.x, NORMAL.y, NORMAL.z,
|
|
maxCorner.x, minCorner.y, NORMAL.x, NORMAL.y, NORMAL.z,
|
|
minCorner.x, maxCorner.y, NORMAL.x, NORMAL.y, NORMAL.z,
|
|
maxCorner.x, maxCorner.y, NORMAL.x, NORMAL.y, NORMAL.z,
|
|
};
|
|
|
|
const int NUM_COLOR_SCALARS_PER_QUAD = 4;
|
|
int 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);
|
|
int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor };
|
|
|
|
details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer);
|
|
details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors);
|
|
}
|
|
|
|
batch.setInputFormat(details.streamFormat);
|
|
batch.setInputStream(0, *details.stream);
|
|
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
|
|
}
|
|
|
|
const int FLOATS_PER_VERTEX = 2 + 3 + 2; // vertices + normals + tex coords
|
|
const int VERTICES = 4; // 1 quad = 4 vertices
|
|
const int NUM_POS_COORDS = 2;
|
|
const int NUM_NORMAL_COORDS = 3;
|
|
const int VERTEX_NORMAL_OFFSET = NUM_POS_COORDS * sizeof(float);
|
|
const int VERTEX_TEXCOORD_OFFSET = VERTEX_NORMAL_OFFSET + NUM_NORMAL_COORDS * sizeof(float);
|
|
|
|
if (!details.isCreated) {
|
|
|
|
details.isCreated = true;
|
|
details.vertices = VERTICES;
|
|
details.vertexSize = FLOATS_PER_VERTEX;
|
|
|
|
auto verticesBuffer = std::make_shared<gpu::Buffer>();
|
|
auto colorBuffer = std::make_shared<gpu::Buffer>();
|
|
|
|
auto streamFormat = std::make_shared<gpu::Stream::Format>();
|
|
auto stream = std::make_shared<gpu::BufferStream>();
|
|
|
|
details.verticesBuffer = verticesBuffer;
|
|
details.colorBuffer = colorBuffer;
|
|
|
|
details.streamFormat = streamFormat;
|
|
details.stream = stream;
|
|
|
|
// zzmp: fix the normal across all renderQuad
|
|
details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ), 0);
|
|
details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET);
|
|
details.streamFormat->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), VERTEX_TEXCOORD_OFFSET);
|
|
details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA));
|
|
|
|
details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride);
|
|
details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride);
|
|
|
|
|
|
const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f);
|
|
float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = {
|
|
minCorner.x, minCorner.y, NORMAL.x, NORMAL.y, NORMAL.z, texCoordMinCorner.x, texCoordMinCorner.y,
|
|
maxCorner.x, minCorner.y, NORMAL.x, NORMAL.y, NORMAL.z, texCoordMaxCorner.x, texCoordMinCorner.y,
|
|
minCorner.x, maxCorner.y, NORMAL.x, NORMAL.y, NORMAL.z, texCoordMinCorner.x, texCoordMaxCorner.y,
|
|
maxCorner.x, maxCorner.y, NORMAL.x, NORMAL.y, NORMAL.z, texCoordMaxCorner.x, texCoordMaxCorner.y,
|
|
};
|
|
|
|
|
|
const int NUM_COLOR_SCALARS_PER_QUAD = 4;
|
|
int 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);
|
|
int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor };
|
|
|
|
details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer);
|
|
details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors);
|
|
}
|
|
|
|
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& 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
|
|
}
|
|
|
|
const int FLOATS_PER_VERTEX = 3 + 3; // vertices + normals
|
|
const int VERTICES = 4; // 1 quad = 4 vertices
|
|
const int NUM_POS_COORDS = 3;
|
|
const int VERTEX_NORMAL_OFFSET = NUM_POS_COORDS * sizeof(float);
|
|
|
|
if (!details.isCreated) {
|
|
|
|
details.isCreated = true;
|
|
details.vertices = VERTICES;
|
|
details.vertexSize = FLOATS_PER_VERTEX;
|
|
|
|
auto verticesBuffer = std::make_shared<gpu::Buffer>();
|
|
auto colorBuffer = std::make_shared<gpu::Buffer>();
|
|
|
|
auto streamFormat = std::make_shared<gpu::Stream::Format>();
|
|
auto stream = std::make_shared<gpu::BufferStream>();
|
|
|
|
details.verticesBuffer = verticesBuffer;
|
|
details.colorBuffer = colorBuffer;
|
|
|
|
details.streamFormat = streamFormat;
|
|
details.stream = stream;
|
|
|
|
details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
|
|
details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET);
|
|
details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA));
|
|
|
|
details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride);
|
|
details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride);
|
|
|
|
|
|
const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f);
|
|
float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = {
|
|
minCorner.x, minCorner.y, minCorner.z, NORMAL.x, NORMAL.y, NORMAL.z,
|
|
maxCorner.x, minCorner.y, minCorner.z, NORMAL.x, NORMAL.y, NORMAL.z,
|
|
minCorner.x, maxCorner.y, maxCorner.z, NORMAL.x, NORMAL.y, NORMAL.z,
|
|
maxCorner.x, maxCorner.y, maxCorner.z, NORMAL.x, NORMAL.y, NORMAL.z,
|
|
};
|
|
|
|
const int NUM_COLOR_SCALARS_PER_QUAD = 4;
|
|
int 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);
|
|
int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor };
|
|
|
|
details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer);
|
|
details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors);
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
const int FLOATS_PER_VERTEX = 3 + 3 + 2; // vertices + normals + tex coords
|
|
const int VERTICES = 4; // 1 quad = 4 vertices
|
|
const int NUM_POS_COORDS = 3;
|
|
const int NUM_NORMAL_COORDS = 3;
|
|
const int VERTEX_NORMAL_OFFSET = NUM_POS_COORDS * sizeof(float);
|
|
const int VERTEX_TEXCOORD_OFFSET = VERTEX_NORMAL_OFFSET + NUM_NORMAL_COORDS * sizeof(float);
|
|
|
|
|
|
if (!details.isCreated) {
|
|
|
|
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>();
|
|
auto colorBuffer = std::make_shared<gpu::Buffer>();
|
|
auto streamFormat = std::make_shared<gpu::Stream::Format>();
|
|
auto stream = std::make_shared<gpu::BufferStream>();
|
|
|
|
details.verticesBuffer = verticesBuffer;
|
|
details.colorBuffer = colorBuffer;
|
|
details.streamFormat = streamFormat;
|
|
details.stream = stream;
|
|
|
|
details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
|
|
details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET);
|
|
details.streamFormat->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), VERTEX_TEXCOORD_OFFSET);
|
|
details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA));
|
|
|
|
details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride);
|
|
details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride);
|
|
|
|
|
|
const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f);
|
|
float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = {
|
|
bottomLeft.x, bottomLeft.y, bottomLeft.z, NORMAL.x, NORMAL.y, NORMAL.z, texCoordBottomLeft.x, texCoordBottomLeft.y,
|
|
bottomRight.x, bottomRight.y, bottomRight.z, NORMAL.x, NORMAL.y, NORMAL.z, texCoordBottomRight.x, texCoordBottomRight.y,
|
|
topLeft.x, topLeft.y, topLeft.z, NORMAL.x, NORMAL.y, NORMAL.z, texCoordTopLeft.x, texCoordTopLeft.y,
|
|
topRight.x, topRight.y, topRight.z, NORMAL.x, NORMAL.y, NORMAL.z, texCoordTopRight.x, texCoordTopRight.y,
|
|
};
|
|
|
|
const int NUM_COLOR_SCALARS_PER_QUAD = 4;
|
|
int 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);
|
|
int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor };
|
|
|
|
details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer);
|
|
details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors);
|
|
}
|
|
|
|
batch.setInputFormat(details.streamFormat);
|
|
batch.setInputStream(0, *details.stream);
|
|
batch.draw(gpu::TRIANGLE_STRIP, 4, 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) {
|
|
|
|
int 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);
|
|
|
|
// 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 + 3; // vertices + normals
|
|
const int NUM_POS_COORDS = 3;
|
|
const int VERTEX_NORMAL_OFFSET = NUM_POS_COORDS * sizeof(float);
|
|
details.vertices = (segmentCountFloor + 1) * 2;
|
|
details.vertexSize = FLOATS_PER_VERTEX;
|
|
details.isCreated = true;
|
|
|
|
auto verticesBuffer = std::make_shared<gpu::Buffer>();
|
|
auto colorBuffer = std::make_shared<gpu::Buffer>();
|
|
auto streamFormat = std::make_shared<gpu::Stream::Format>();
|
|
auto stream = std::make_shared<gpu::BufferStream>();
|
|
|
|
details.verticesBuffer = verticesBuffer;
|
|
details.colorBuffer = colorBuffer;
|
|
details.streamFormat = streamFormat;
|
|
details.stream = stream;
|
|
|
|
details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
|
|
details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET);
|
|
details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA));
|
|
|
|
details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride);
|
|
details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride);
|
|
|
|
int* colorData = new int[details.vertices];
|
|
int* colorDataAt = colorData;
|
|
|
|
float* vertexData = new float[details.vertices * FLOATS_PER_VERTEX];
|
|
float* vertex = vertexData;
|
|
|
|
const glm::vec3 NORMAL(1.0f, 0.0f, 0.0f);
|
|
glm::vec3 point = start;
|
|
*(vertex++) = point.x;
|
|
*(vertex++) = point.y;
|
|
*(vertex++) = point.z;
|
|
*(vertex++) = NORMAL.x;
|
|
*(vertex++) = NORMAL.y;
|
|
*(vertex++) = NORMAL.z;
|
|
*(colorDataAt++) = compactColor;
|
|
|
|
for (int i = 0; i < segmentCountFloor; i++) {
|
|
point += dashVector;
|
|
*(vertex++) = point.x;
|
|
*(vertex++) = point.y;
|
|
*(vertex++) = point.z;
|
|
*(vertex++) = NORMAL.x;
|
|
*(vertex++) = NORMAL.y;
|
|
*(vertex++) = NORMAL.z;
|
|
*(colorDataAt++) = compactColor;
|
|
|
|
point += gapVector;
|
|
*(vertex++) = point.x;
|
|
*(vertex++) = point.y;
|
|
*(vertex++) = point.z;
|
|
*(vertex++) = NORMAL.x;
|
|
*(vertex++) = NORMAL.y;
|
|
*(vertex++) = NORMAL.z;
|
|
*(colorDataAt++) = compactColor;
|
|
}
|
|
*(vertex++) = end.x;
|
|
*(vertex++) = end.y;
|
|
*(vertex++) = end.z;
|
|
*(vertex++) = NORMAL.x;
|
|
*(vertex++) = NORMAL.y;
|
|
*(vertex++) = NORMAL.z;
|
|
*(colorDataAt++) = compactColor;
|
|
|
|
details.verticesBuffer->append(sizeof(float) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*) vertexData);
|
|
details.colorBuffer->append(sizeof(int) * details.vertices, (gpu::Byte*) colorData);
|
|
delete[] vertexData;
|
|
delete[] colorData;
|
|
|
|
#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);
|
|
}
|
|
|
|
|
|
int GeometryCache::BatchItemDetails::population = 0;
|
|
|
|
GeometryCache::BatchItemDetails::BatchItemDetails() :
|
|
verticesBuffer(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),
|
|
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();
|
|
colorBuffer.reset();
|
|
streamFormat.reset();
|
|
stream.reset();
|
|
}
|
|
|
|
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];
|
|
|
|
int compactColor1 = ((int(color1.x * 255.0f) & 0xFF)) |
|
|
((int(color1.y * 255.0f) & 0xFF) << 8) |
|
|
((int(color1.z * 255.0f) & 0xFF) << 16) |
|
|
((int(color1.w * 255.0f) & 0xFF) << 24);
|
|
|
|
int compactColor2 = ((int(color2.x * 255.0f) & 0xFF)) |
|
|
((int(color2.y * 255.0f) & 0xFF) << 8) |
|
|
((int(color2.z * 255.0f) & 0xFF) << 16) |
|
|
((int(color2.w * 255.0f) & 0xFF) << 24);
|
|
|
|
|
|
// 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
|
|
}
|
|
|
|
const int FLOATS_PER_VERTEX = 3 + 3; // vertices + normals
|
|
const int NUM_POS_COORDS = 3;
|
|
const int VERTEX_NORMAL_OFFSET = NUM_POS_COORDS * sizeof(float);
|
|
const int vertices = 2;
|
|
if (!details.isCreated) {
|
|
|
|
details.isCreated = true;
|
|
details.vertices = vertices;
|
|
details.vertexSize = FLOATS_PER_VERTEX;
|
|
|
|
auto verticesBuffer = std::make_shared<gpu::Buffer>();
|
|
auto colorBuffer = std::make_shared<gpu::Buffer>();
|
|
auto streamFormat = std::make_shared<gpu::Stream::Format>();
|
|
auto stream = std::make_shared<gpu::BufferStream>();
|
|
|
|
details.verticesBuffer = verticesBuffer;
|
|
details.colorBuffer = colorBuffer;
|
|
details.streamFormat = streamFormat;
|
|
details.stream = stream;
|
|
|
|
details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
|
|
details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET);
|
|
details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA));
|
|
|
|
details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride);
|
|
details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride);
|
|
|
|
const glm::vec3 NORMAL(1.0f, 0.0f, 0.0f);
|
|
float vertexBuffer[vertices * FLOATS_PER_VERTEX] = {
|
|
p1.x, p1.y, p1.z, NORMAL.x, NORMAL.y, NORMAL.z,
|
|
p2.x, p2.y, p2.z, NORMAL.x, NORMAL.y, NORMAL.z };
|
|
|
|
const int NUM_COLOR_SCALARS = 2;
|
|
int colors[NUM_COLOR_SCALARS] = { compactColor1, compactColor2 };
|
|
|
|
details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer);
|
|
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
|
|
}
|
|
|
|
// this is what it takes to render a quad
|
|
batch.setInputFormat(details.streamFormat);
|
|
batch.setInputStream(0, *details.stream);
|
|
batch.draw(gpu::LINES, 2, 0);
|
|
}
|
|
|
|
void GeometryCache::renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2,
|
|
const glm::vec4& color1, const glm::vec4& color2, int id) {
|
|
|
|
bool registered = (id != UNKNOWN_ID);
|
|
Vec2Pair key(p1, p2);
|
|
|
|
BatchItemDetails& details = _registeredLine2DVBOs[id];
|
|
|
|
int compactColor1 = ((int(color1.x * 255.0f) & 0xFF)) |
|
|
((int(color1.y * 255.0f) & 0xFF) << 8) |
|
|
((int(color1.z * 255.0f) & 0xFF) << 16) |
|
|
((int(color1.w * 255.0f) & 0xFF) << 24);
|
|
|
|
int compactColor2 = ((int(color2.x * 255.0f) & 0xFF)) |
|
|
((int(color2.y * 255.0f) & 0xFF) << 8) |
|
|
((int(color2.z * 255.0f) & 0xFF) << 16) |
|
|
((int(color2.w * 255.0f) & 0xFF) << 24);
|
|
|
|
|
|
// 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) {
|
|
Vec2Pair& lastKey = _lastRegisteredLine2D[id];
|
|
if (lastKey != key) {
|
|
details.clear();
|
|
_lastRegisteredLine2D[id] = key;
|
|
#ifdef WANT_DEBUG
|
|
qCDebug(renderutils) << "renderLine() 2D ... RELEASING REGISTERED line";
|
|
#endif // def WANT_DEBUG
|
|
}
|
|
#ifdef WANT_DEBUG
|
|
else {
|
|
qCDebug(renderutils) << "renderLine() 2D ... REUSING PREVIOUSLY REGISTERED line";
|
|
}
|
|
#endif // def WANT_DEBUG
|
|
}
|
|
|
|
const int FLOATS_PER_VERTEX = 2;
|
|
const int vertices = 2;
|
|
if (!details.isCreated) {
|
|
|
|
details.isCreated = true;
|
|
details.vertices = vertices;
|
|
details.vertexSize = FLOATS_PER_VERTEX;
|
|
|
|
auto verticesBuffer = std::make_shared<gpu::Buffer>();
|
|
auto colorBuffer = std::make_shared<gpu::Buffer>();
|
|
auto streamFormat = std::make_shared<gpu::Stream::Format>();
|
|
auto stream = std::make_shared<gpu::BufferStream>();
|
|
|
|
details.verticesBuffer = verticesBuffer;
|
|
details.colorBuffer = colorBuffer;
|
|
details.streamFormat = streamFormat;
|
|
details.stream = stream;
|
|
|
|
details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
|
|
details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA));
|
|
|
|
details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride);
|
|
details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride);
|
|
|
|
|
|
float vertexBuffer[vertices * FLOATS_PER_VERTEX] = { p1.x, p1.y, p2.x, p2.y };
|
|
|
|
const int NUM_COLOR_SCALARS = 2;
|
|
int colors[NUM_COLOR_SCALARS] = { compactColor1, compactColor2 };
|
|
|
|
details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer);
|
|
details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors);
|
|
|
|
#ifdef WANT_DEBUG
|
|
if (id == UNKNOWN_ID) {
|
|
qCDebug(renderutils) << "new renderLine() 2D VBO made -- _line3DVBOs.size():" << _line2DVBOs.size();
|
|
} else {
|
|
qCDebug(renderutils) << "new registered renderLine() 2D VBO made -- _registeredLine2DVBOs.size():" << _registeredLine2DVBOs.size();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// this is what it takes to render a quad
|
|
batch.setInputFormat(details.streamFormat);
|
|
batch.setInputStream(0, *details.stream);
|
|
batch.draw(gpu::LINES, 2, 0);
|
|
}
|
|
|
|
|
|
void GeometryCache::renderGlowLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2,
|
|
const glm::vec4& color, float glowIntensity, float glowWidth, int id) {
|
|
|
|
// Disable glow lines on OSX
|
|
#ifndef Q_OS_WIN
|
|
glowIntensity = 0.0f;
|
|
#endif
|
|
|
|
if (glowIntensity <= 0.0f) {
|
|
if (color.a >= 1.0f) {
|
|
bindSimpleProgram(batch, false, false, false, true, true);
|
|
} else {
|
|
bindSimpleProgram(batch, false, true, false, true, true);
|
|
}
|
|
renderLine(batch, p1, p2, color, id);
|
|
return;
|
|
}
|
|
|
|
// Compile the shaders
|
|
static const uint32_t LINE_DATA_SLOT = 1;
|
|
static std::once_flag once;
|
|
std::call_once(once, [&] {
|
|
auto state = std::make_shared<gpu::State>();
|
|
auto VS = gpu::Shader::createVertex(std::string(glowLine_vert));
|
|
auto PS = gpu::Shader::createPixel(std::string(glowLine_frag));
|
|
auto program = gpu::Shader::createProgram(VS, PS);
|
|
state->setCullMode(gpu::State::CULL_NONE);
|
|
state->setDepthTest(true, false, gpu::LESS_EQUAL);
|
|
state->setBlendFunction(true,
|
|
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);
|
|
|
|
PrepareStencil::testMask(*state);
|
|
|
|
gpu::Shader::BindingSet slotBindings;
|
|
slotBindings.insert(gpu::Shader::Binding(std::string("lineData"), LINE_DATA_SLOT));
|
|
gpu::Shader::makeProgram(*program, slotBindings);
|
|
_glowLinePipeline = gpu::Pipeline::create(program, state);
|
|
});
|
|
|
|
batch.setPipeline(_glowLinePipeline);
|
|
|
|
Vec3Pair key(p1, p2);
|
|
bool registered = (id != UNKNOWN_ID);
|
|
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;
|
|
}
|
|
}
|
|
|
|
const int NUM_VERTICES = 4;
|
|
if (!details.isCreated) {
|
|
details.isCreated = true;
|
|
details.uniformBuffer = std::make_shared<gpu::Buffer>();
|
|
|
|
struct LineData {
|
|
vec4 p1;
|
|
vec4 p2;
|
|
vec4 color;
|
|
};
|
|
|
|
LineData lineData { vec4(p1, 1.0f), vec4(p2, 1.0f), color };
|
|
details.uniformBuffer->resize(sizeof(LineData));
|
|
details.uniformBuffer->setSubData(0, lineData);
|
|
}
|
|
|
|
// The shader requires no vertices, only uniforms.
|
|
batch.setUniformBuffer(LINE_DATA_SLOT, details.uniformBuffer);
|
|
batch.draw(gpu::TRIANGLE_STRIP, NUM_VERTICES, 0);
|
|
}
|
|
|
|
void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) {
|
|
if (!_standardDrawPipeline) {
|
|
auto vs = gpu::Shader::createVertex(std::string(standardTransformPNTC_vert));
|
|
auto ps = gpu::Shader::createPixel(std::string(standardDrawTexture_frag));
|
|
auto program = gpu::Shader::createProgram(vs, ps);
|
|
gpu::Shader::makeProgram((*program));
|
|
|
|
auto state = std::make_shared<gpu::State>();
|
|
|
|
// enable decal blend
|
|
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
|
|
PrepareStencil::testMask(*state);
|
|
|
|
_standardDrawPipeline = gpu::Pipeline::create(program, state);
|
|
|
|
|
|
auto stateNoBlend = std::make_shared<gpu::State>();
|
|
PrepareStencil::testMaskDrawShape(*stateNoBlend);
|
|
|
|
auto noBlendPS = gpu::StandardShaderLib::getDrawTextureOpaquePS();
|
|
auto programNoBlend = gpu::Shader::createProgram(vs, noBlendPS);
|
|
gpu::Shader::makeProgram((*programNoBlend));
|
|
_standardDrawPipelineNoBlend = gpu::Pipeline::create(programNoBlend, stateNoBlend);
|
|
}
|
|
if (noBlend) {
|
|
batch.setPipeline(_standardDrawPipelineNoBlend);
|
|
} else {
|
|
batch.setPipeline(_standardDrawPipeline);
|
|
}
|
|
}
|
|
|
|
void GeometryCache::useGridPipeline(gpu::Batch& batch, GridBuffer gridBuffer, bool isLayered) {
|
|
if (!_gridPipeline) {
|
|
auto vs = gpu::Shader::createVertex(std::string(standardTransformPNTC_vert));
|
|
auto ps = gpu::Shader::createPixel(std::string(grid_frag));
|
|
auto program = gpu::Shader::createProgram(vs, ps);
|
|
gpu::Shader::makeProgram((*program));
|
|
_gridSlot = program->getUniformBuffers().findLocation("gridBuffer");
|
|
|
|
auto stateLayered = std::make_shared<gpu::State>();
|
|
stateLayered->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
|
|
PrepareStencil::testMask(*stateLayered);
|
|
_gridPipelineLayered = gpu::Pipeline::create(program, stateLayered);
|
|
|
|
auto state = std::make_shared<gpu::State>(stateLayered->getValues());
|
|
const float DEPTH_BIAS = 0.001f;
|
|
state->setDepthBias(DEPTH_BIAS);
|
|
state->setDepthTest(true, false, gpu::LESS_EQUAL);
|
|
PrepareStencil::testMaskDrawShape(*state);
|
|
_gridPipeline = gpu::Pipeline::create(program, state);
|
|
}
|
|
|
|
gpu::PipelinePointer pipeline = isLayered ? _gridPipelineLayered : _gridPipeline;
|
|
batch.setPipeline(pipeline);
|
|
batch.setUniformBuffer(_gridSlot, gridBuffer);
|
|
}
|
|
|
|
|
|
|
|
class SimpleProgramKey {
|
|
public:
|
|
enum FlagBit {
|
|
IS_TEXTURED_FLAG = 0,
|
|
IS_TRANSPARENT_FLAG,
|
|
IS_CULLED_FLAG,
|
|
IS_UNLIT_FLAG,
|
|
HAS_DEPTH_BIAS_FLAG,
|
|
IS_FADING_FLAG,
|
|
IS_ANTIALIASED_FLAG,
|
|
|
|
NUM_FLAGS,
|
|
};
|
|
|
|
enum Flag {
|
|
IS_TEXTURED = (1 << IS_TEXTURED_FLAG),
|
|
IS_TRANSPARENT = (1 << IS_TRANSPARENT_FLAG),
|
|
IS_CULLED = (1 << IS_CULLED_FLAG),
|
|
IS_UNLIT = (1 << IS_UNLIT_FLAG),
|
|
HAS_DEPTH_BIAS = (1 << HAS_DEPTH_BIAS_FLAG),
|
|
IS_FADING = (1 << IS_FADING_FLAG),
|
|
IS_ANTIALIASED = (1 << IS_ANTIALIASED_FLAG),
|
|
};
|
|
typedef unsigned short Flags;
|
|
|
|
bool isFlag(short flagNum) const { return bool((_flags & flagNum) != 0); }
|
|
|
|
bool isTextured() const { return isFlag(IS_TEXTURED); }
|
|
bool isTransparent() const { return isFlag(IS_TRANSPARENT); }
|
|
bool isCulled() const { return isFlag(IS_CULLED); }
|
|
bool isUnlit() const { return isFlag(IS_UNLIT); }
|
|
bool hasDepthBias() const { return isFlag(HAS_DEPTH_BIAS); }
|
|
bool isFading() const { return isFlag(IS_FADING); }
|
|
bool isAntiAliased() const { return isFlag(IS_ANTIALIASED); }
|
|
|
|
Flags _flags = 0;
|
|
short _spare = 0;
|
|
|
|
int getRaw() const { return *reinterpret_cast<const int*>(this); }
|
|
|
|
|
|
SimpleProgramKey(bool textured = false, bool transparent = false, bool culled = true,
|
|
bool unlit = false, bool depthBias = false, bool fading = false, bool isAntiAliased = true) {
|
|
_flags = (textured ? IS_TEXTURED : 0) | (transparent ? IS_TRANSPARENT : 0) | (culled ? IS_CULLED : 0) |
|
|
(unlit ? IS_UNLIT : 0) | (depthBias ? HAS_DEPTH_BIAS : 0) | (fading ? IS_FADING : 0) | (isAntiAliased ? IS_ANTIALIASED : 0);
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
static void buildWebShader(const std::string& vertShaderText, const std::string& fragShaderText, bool blendEnable,
|
|
gpu::ShaderPointer& shaderPointerOut, gpu::PipelinePointer& pipelinePointerOut) {
|
|
auto VS = gpu::Shader::createVertex(vertShaderText);
|
|
auto PS = gpu::Shader::createPixel(fragShaderText);
|
|
|
|
shaderPointerOut = gpu::Shader::createProgram(VS, PS);
|
|
|
|
gpu::Shader::BindingSet slotBindings;
|
|
gpu::Shader::makeProgram(*shaderPointerOut, slotBindings);
|
|
auto state = std::make_shared<gpu::State>();
|
|
state->setCullMode(gpu::State::CULL_NONE);
|
|
state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
|
state->setBlendFunction(blendEnable,
|
|
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);
|
|
|
|
PrepareStencil::testMaskDrawShapeNoAA(*state);
|
|
|
|
pipelinePointerOut = gpu::Pipeline::create(shaderPointerOut, state);
|
|
}
|
|
|
|
void GeometryCache::bindWebBrowserProgram(gpu::Batch& batch, bool transparent) {
|
|
batch.setPipeline(getWebBrowserProgram(transparent));
|
|
}
|
|
|
|
gpu::PipelinePointer GeometryCache::getWebBrowserProgram(bool transparent) {
|
|
static std::once_flag once;
|
|
std::call_once(once, [&]() {
|
|
buildWebShader(simple_vert, simple_opaque_web_browser_frag, false, _simpleOpaqueWebBrowserShader, _simpleOpaqueWebBrowserPipelineNoAA);
|
|
buildWebShader(simple_vert, simple_transparent_web_browser_frag, true, _simpleTransparentWebBrowserShader, _simpleTransparentWebBrowserPipelineNoAA);
|
|
});
|
|
|
|
return transparent ? _simpleTransparentWebBrowserPipelineNoAA : _simpleOpaqueWebBrowserPipelineNoAA;
|
|
}
|
|
|
|
void GeometryCache::bindSimpleProgram(gpu::Batch& batch, bool textured, bool transparent, bool culled, bool unlit, bool depthBiased, bool isAntiAliased) {
|
|
batch.setPipeline(getSimplePipeline(textured, transparent, culled, unlit, depthBiased, false, isAntiAliased));
|
|
|
|
// If not textured, set a default albedo map
|
|
if (!textured) {
|
|
batch.setResourceTexture(render::ShapePipeline::Slot::MAP::ALBEDO,
|
|
DependencyManager::get<TextureCache>()->getWhiteTexture());
|
|
}
|
|
}
|
|
|
|
gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool transparent, bool culled, bool unlit, bool depthBiased, bool fading, bool isAntiAliased) {
|
|
SimpleProgramKey config { textured, transparent, culled, unlit, depthBiased, fading, isAntiAliased };
|
|
|
|
// 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, [&]() {
|
|
auto VS = gpu::Shader::createVertex(std::string(simple_vert));
|
|
auto PS = gpu::Shader::createPixel(std::string(simple_textured_frag));
|
|
auto PSUnlit = gpu::Shader::createPixel(std::string(simple_textured_unlit_frag));
|
|
|
|
_simpleShader = gpu::Shader::createProgram(VS, PS);
|
|
_unlitShader = gpu::Shader::createProgram(VS, PSUnlit);
|
|
|
|
gpu::Shader::BindingSet slotBindings;
|
|
slotBindings.insert(gpu::Shader::Binding(std::string("originalTexture"), render::ShapePipeline::Slot::MAP::ALBEDO));
|
|
gpu::Shader::makeProgram(*_simpleShader, slotBindings);
|
|
gpu::Shader::makeProgram(*_unlitShader, slotBindings);
|
|
});
|
|
} else {
|
|
static std::once_flag once;
|
|
std::call_once(once, [&]() {
|
|
auto VS = gpu::Shader::createVertex(std::string(simple_fade_vert));
|
|
auto PS = gpu::Shader::createPixel(std::string(simple_textured_fade_frag));
|
|
auto PSUnlit = gpu::Shader::createPixel(std::string(simple_textured_unlit_fade_frag));
|
|
|
|
_simpleFadeShader = gpu::Shader::createProgram(VS, PS);
|
|
_unlitFadeShader = gpu::Shader::createProgram(VS, PSUnlit);
|
|
|
|
gpu::Shader::BindingSet slotBindings;
|
|
slotBindings.insert(gpu::Shader::Binding(std::string("originalTexture"), render::ShapePipeline::Slot::MAP::ALBEDO));
|
|
slotBindings.insert(gpu::Shader::Binding(std::string("fadeMaskMap"), render::ShapePipeline::Slot::MAP::FADE_MASK));
|
|
gpu::Shader::makeProgram(*_simpleFadeShader, slotBindings);
|
|
gpu::Shader::makeProgram(*_unlitFadeShader, slotBindings);
|
|
});
|
|
}
|
|
|
|
// If the pipeline did not exist, make it
|
|
auto state = std::make_shared<gpu::State>();
|
|
if (config.isCulled()) {
|
|
state->setCullMode(gpu::State::CULL_BACK);
|
|
} else {
|
|
state->setCullMode(gpu::State::CULL_NONE);
|
|
}
|
|
state->setDepthTest(true, true, gpu::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::testMask(*state) : PrepareStencil::testMaskDrawShape(*state);
|
|
} else {
|
|
PrepareStencil::testMaskDrawShapeNoAA(*state);
|
|
}
|
|
|
|
gpu::ShaderPointer program = (config.isUnlit()) ? (config.isFading() ? _unlitFadeShader : _unlitShader) : (config.isFading() ? _simpleFadeShader : _simpleShader);
|
|
gpu::PipelinePointer pipeline = gpu::Pipeline::create(program, state);
|
|
_simplePrograms.insert(config, pipeline);
|
|
return pipeline;
|
|
}
|
|
|
|
uint32_t 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;
|
|
|
|
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<render::ShapePipelinePointer>()(pipeline));
|
|
|
|
// Add color to named buffer
|
|
{
|
|
gpu::BufferPointer instanceColorBuffer = batch.getNamedBuffer(instanceName, INSTANCE_COLOR_BUFFER);
|
|
auto compactColor = 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<GeometryCache>()->renderWireShapeInstances(batch, shape, data.count(), data.buffers[INSTANCE_COLOR_BUFFER]);
|
|
}
|
|
else {
|
|
DependencyManager::get<GeometryCache>()->renderShapeInstances(batch, shape, data.count(), data.buffers[INSTANCE_COLOR_BUFFER]);
|
|
}
|
|
});
|
|
}
|
|
|
|
static const size_t INSTANCE_FADE_BUFFER1 = 1;
|
|
static const size_t INSTANCE_FADE_BUFFER2 = 2;
|
|
static const size_t INSTANCE_FADE_BUFFER3 = 3;
|
|
|
|
void renderFadeInstances(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, int fadeCategory, float fadeThreshold,
|
|
const glm::vec3& fadeNoiseOffset, const glm::vec3& fadeBaseOffset, const glm::vec3& fadeBaseInvSize, 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<render::ShapePipelinePointer>()(pipeline));
|
|
|
|
// Add color to named buffer
|
|
{
|
|
gpu::BufferPointer instanceColorBuffer = batch.getNamedBuffer(instanceName, INSTANCE_COLOR_BUFFER);
|
|
auto compactColor = toCompactColor(color);
|
|
instanceColorBuffer->append(compactColor);
|
|
}
|
|
// Add fade parameters to named buffers
|
|
{
|
|
gpu::BufferPointer fadeBuffer1 = batch.getNamedBuffer(instanceName, INSTANCE_FADE_BUFFER1);
|
|
gpu::BufferPointer fadeBuffer2 = batch.getNamedBuffer(instanceName, INSTANCE_FADE_BUFFER2);
|
|
gpu::BufferPointer fadeBuffer3 = batch.getNamedBuffer(instanceName, INSTANCE_FADE_BUFFER3);
|
|
// Pack parameters in 3 vec4s
|
|
glm::vec4 fadeData1;
|
|
glm::vec4 fadeData2;
|
|
glm::vec4 fadeData3;
|
|
FadeEffect::packToAttributes(fadeCategory, fadeThreshold, fadeNoiseOffset, fadeBaseOffset, fadeBaseInvSize,
|
|
fadeData1, fadeData2, fadeData3);
|
|
fadeBuffer1->append(fadeData1);
|
|
fadeBuffer2->append(fadeData2);
|
|
fadeBuffer3->append(fadeData3);
|
|
}
|
|
|
|
// Add call to named buffer
|
|
batch.setupNamedCalls(instanceName, [args, isWire, pipeline, shape](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) {
|
|
auto& buffers = data.buffers;
|
|
batch.setPipeline(pipeline->pipeline);
|
|
pipeline->prepare(batch, args);
|
|
|
|
if (isWire) {
|
|
DependencyManager::get<GeometryCache>()->renderWireFadeShapeInstances(batch, shape, data.count(),
|
|
buffers[INSTANCE_COLOR_BUFFER], buffers[INSTANCE_FADE_BUFFER1], buffers[INSTANCE_FADE_BUFFER2], buffers[INSTANCE_FADE_BUFFER3]);
|
|
}
|
|
else {
|
|
DependencyManager::get<GeometryCache>()->renderFadeShapeInstances(batch, shape, data.count(),
|
|
buffers[INSTANCE_COLOR_BUFFER], buffers[INSTANCE_FADE_BUFFER1], buffers[INSTANCE_FADE_BUFFER2], buffers[INSTANCE_FADE_BUFFER3]);
|
|
}
|
|
});
|
|
}
|
|
|
|
void GeometryCache::renderSolidShapeInstance(RenderArgs* args, gpu::Batch& batch, GeometryCache::Shape shape, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) {
|
|
assert(pipeline != nullptr);
|
|
renderInstances(args, batch, color, false, pipeline, shape);
|
|
}
|
|
|
|
void GeometryCache::renderWireShapeInstance(RenderArgs* args, gpu::Batch& batch, GeometryCache::Shape shape, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) {
|
|
assert(pipeline != nullptr);
|
|
renderInstances(args, batch, color, true, pipeline, shape);
|
|
}
|
|
|
|
void GeometryCache::renderSolidFadeShapeInstance(RenderArgs* args, gpu::Batch& batch, GeometryCache::Shape shape, const glm::vec4& color,
|
|
int fadeCategory, float fadeThreshold, const glm::vec3& fadeNoiseOffset, const glm::vec3& fadeBaseOffset, const glm::vec3& fadeBaseInvSize,
|
|
const render::ShapePipelinePointer& pipeline) {
|
|
assert(pipeline != nullptr);
|
|
renderFadeInstances(args, batch, color, fadeCategory, fadeThreshold, fadeNoiseOffset, fadeBaseOffset, fadeBaseInvSize, false, pipeline, shape);
|
|
}
|
|
|
|
void GeometryCache::renderWireFadeShapeInstance(RenderArgs* args, gpu::Batch& batch, GeometryCache::Shape shape, const glm::vec4& color,
|
|
int fadeCategory, float fadeThreshold, const glm::vec3& fadeNoiseOffset, const glm::vec3& fadeBaseOffset, const glm::vec3& fadeBaseInvSize,
|
|
const render::ShapePipelinePointer& pipeline) {
|
|
assert(pipeline != nullptr);
|
|
renderFadeInstances(args, batch, color, fadeCategory, fadeThreshold, fadeNoiseOffset, fadeBaseOffset, fadeBaseInvSize, true, 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::renderWireSphereInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) {
|
|
assert(pipeline != nullptr);
|
|
renderInstances(args, batch, color, true, pipeline, GeometryCache::Sphere);
|
|
}
|
|
|
|
// Enable this in a debug build to cause 'box' entities to iterate through all the
|
|
// available shape types, both solid and wireframes
|
|
//#define DEBUG_SHAPES
|
|
|
|
|
|
void GeometryCache::renderSolidCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) {
|
|
assert(pipeline != nullptr);
|
|
#ifdef DEBUG_SHAPES
|
|
static auto startTime = usecTimestampNow();
|
|
renderInstances(INSTANCE_NAME, batch, color, pipeline, [](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) {
|
|
|
|
auto usecs = usecTimestampNow();
|
|
usecs -= startTime;
|
|
auto msecs = usecs / USECS_PER_MSEC;
|
|
float seconds = msecs;
|
|
seconds /= MSECS_PER_SECOND;
|
|
float fractionalSeconds = seconds - floor(seconds);
|
|
int shapeIndex = (int)seconds;
|
|
|
|
// Every second we flip to the next shape.
|
|
static const int SHAPE_COUNT = 5;
|
|
GeometryCache::Shape shapes[SHAPE_COUNT] = {
|
|
GeometryCache::Cube,
|
|
GeometryCache::Tetrahedron,
|
|
GeometryCache::Sphere,
|
|
GeometryCache::Icosahedron,
|
|
GeometryCache::Line,
|
|
};
|
|
|
|
shapeIndex %= SHAPE_COUNT;
|
|
GeometryCache::Shape shape = shapes[shapeIndex];
|
|
|
|
// For the first half second for a given shape, show the wireframe, for the second half, show the solid.
|
|
if (fractionalSeconds > 0.5f) {
|
|
renderInstances(INSTANCE_NAME, batch, color, true, pipeline, shape);
|
|
} else {
|
|
renderInstances(INSTANCE_NAME, batch, color, false, pipeline, shape);
|
|
}
|
|
});
|
|
#else
|
|
renderInstances(args, batch, color, false, pipeline, GeometryCache::Cube);
|
|
#endif
|
|
}
|
|
|
|
void GeometryCache::renderWireCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) {
|
|
static const std::string INSTANCE_NAME = __FUNCTION__;
|
|
assert(pipeline != nullptr);
|
|
renderInstances(args, batch, color, true, pipeline, GeometryCache::Cube);
|
|
}
|