mirror of
https://github.com/overte-org/overte.git
synced 2025-08-07 07:19:21 +02:00
remove PrimitiveRenderer
This commit is contained in:
parent
208d3c8413
commit
3eb7314c98
4 changed files with 61 additions and 1733 deletions
|
@ -1,742 +0,0 @@
|
||||||
//
|
|
||||||
// PrimitiveRenderer.cpp
|
|
||||||
// interface/src/voxels
|
|
||||||
//
|
|
||||||
// Copyright 2014 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 <QMutexLocker>
|
|
||||||
|
|
||||||
#include "InterfaceConfig.h"
|
|
||||||
#include "OctreeElement.h"
|
|
||||||
#include "PrimitiveRenderer.h"
|
|
||||||
|
|
||||||
Primitive::Primitive() {
|
|
||||||
}
|
|
||||||
|
|
||||||
Primitive::~Primitive() {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simple dispatch between API and SPI
|
|
||||||
|
|
||||||
const VertexElementList& Primitive::vertexElements() const {
|
|
||||||
return vVertexElements();
|
|
||||||
}
|
|
||||||
|
|
||||||
VertexElementIndexList& Primitive::vertexElementIndices() {
|
|
||||||
return vVertexElementIndices();
|
|
||||||
}
|
|
||||||
|
|
||||||
TriElementList& Primitive::triElements() {
|
|
||||||
return vTriElements();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Primitive::releaseVertexElements() {
|
|
||||||
vReleaseVertexElements();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long Primitive::getMemoryUsage() {
|
|
||||||
return vGetMemoryUsage();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Cube::Cube(
|
|
||||||
float x,
|
|
||||||
float y,
|
|
||||||
float z,
|
|
||||||
float s,
|
|
||||||
unsigned char r,
|
|
||||||
unsigned char g,
|
|
||||||
unsigned char b,
|
|
||||||
unsigned char faceExclusions
|
|
||||||
) :
|
|
||||||
_cpuMemoryUsage(0) {
|
|
||||||
init(x, y, z, s, r, g, b, faceExclusions);
|
|
||||||
}
|
|
||||||
|
|
||||||
Cube::~Cube() {
|
|
||||||
terminate();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Cube::init(
|
|
||||||
float x,
|
|
||||||
float y,
|
|
||||||
float z,
|
|
||||||
float s,
|
|
||||||
unsigned char r,
|
|
||||||
unsigned char g,
|
|
||||||
unsigned char b,
|
|
||||||
unsigned char faceExclusions
|
|
||||||
) {
|
|
||||||
|
|
||||||
initializeVertices(x, y, z, s, r, g, b, faceExclusions);
|
|
||||||
initializeTris(faceExclusions);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Cube::terminate() {
|
|
||||||
|
|
||||||
terminateTris();
|
|
||||||
terminateVertices();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Cube::initializeVertices(
|
|
||||||
float x,
|
|
||||||
float y,
|
|
||||||
float z,
|
|
||||||
float s,
|
|
||||||
unsigned char r,
|
|
||||||
unsigned char g,
|
|
||||||
unsigned char b,
|
|
||||||
unsigned char faceExclusions
|
|
||||||
) {
|
|
||||||
|
|
||||||
for (int i = 0; i < _sNumVerticesPerCube; i++) {
|
|
||||||
// Check whether the vertex is necessary for the faces indicated by faceExclusions bit mask.
|
|
||||||
// uncomment this line to load all faces: if (~0x00 & _sFaceIndexToHalfSpaceMask[i >> 2]) {
|
|
||||||
// uncomment this line to include shared faces: if (faceExclusions & _sFaceIndexToHalfSpaceMask[i >> 2]) {
|
|
||||||
// uncomment this line to exclude shared faces:
|
|
||||||
if (~faceExclusions & _sFaceIndexToHalfSpaceMask[i >> 2]) {
|
|
||||||
|
|
||||||
VertexElement* v = new VertexElement();
|
|
||||||
if (v) {
|
|
||||||
// Construct vertex position
|
|
||||||
v->position.x = x + s * _sVertexIndexToConstructionVector[i][0];
|
|
||||||
v->position.y = y + s * _sVertexIndexToConstructionVector[i][1];
|
|
||||||
v->position.z = z + s * _sVertexIndexToConstructionVector[i][2];
|
|
||||||
|
|
||||||
// Construct vertex normal
|
|
||||||
v->normal.x = _sVertexIndexToNormalVector[i >> 2][0];
|
|
||||||
v->normal.y = _sVertexIndexToNormalVector[i >> 2][1];
|
|
||||||
v->normal.z = _sVertexIndexToNormalVector[i >> 2][2];
|
|
||||||
|
|
||||||
// Construct vertex color
|
|
||||||
//#define FALSE_COLOR
|
|
||||||
#ifndef FALSE_COLOR
|
|
||||||
v->color.r = r;
|
|
||||||
v->color.g = g;
|
|
||||||
v->color.b = b;
|
|
||||||
v->color.a = 255;
|
|
||||||
#else
|
|
||||||
static unsigned char falseColor[6][3] = {
|
|
||||||
192, 0, 0, // Bot
|
|
||||||
0, 192, 0, // Top
|
|
||||||
0, 0, 192, // Right
|
|
||||||
192, 0, 192, // Left
|
|
||||||
192, 192, 0, // Near
|
|
||||||
192, 192, 192 // Far
|
|
||||||
};
|
|
||||||
v->color.r = falseColor[i >> 2][0];
|
|
||||||
v->color.g = falseColor[i >> 2][1];
|
|
||||||
v->color.b = falseColor[i >> 2][2];
|
|
||||||
v->color.a = 255;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Add vertex element to list
|
|
||||||
_vertices.push_back(v);
|
|
||||||
_cpuMemoryUsage += sizeof(VertexElement);
|
|
||||||
_cpuMemoryUsage += sizeof(VertexElement*);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Cube::terminateVertices() {
|
|
||||||
|
|
||||||
for (VertexElementList::iterator it = _vertices.begin(); it != _vertices.end(); ++it) {
|
|
||||||
delete *it;
|
|
||||||
}
|
|
||||||
_cpuMemoryUsage -= _vertices.size() * (sizeof(VertexElement) + sizeof(VertexElement*));
|
|
||||||
_vertices.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Cube::initializeTris(
|
|
||||||
unsigned char faceExclusions
|
|
||||||
) {
|
|
||||||
|
|
||||||
int index = 0;
|
|
||||||
for (int i = 0; i < _sNumFacesPerCube; i++) {
|
|
||||||
// Check whether the vertex is necessary for the faces indicated by faceExclusions bit mask.
|
|
||||||
// uncomment this line to load all faces: if (~0x00 & _sFaceIndexToHalfSpaceMask[i]) {
|
|
||||||
// uncomment this line to include shared faces: if (faceExclusions & _sFaceIndexToHalfSpaceMask[i]) {
|
|
||||||
// uncomment this line to exclude shared faces:
|
|
||||||
if (~faceExclusions & _sFaceIndexToHalfSpaceMask[i]) {
|
|
||||||
|
|
||||||
int start = index;
|
|
||||||
// Create the triangulated face, two tris, six indices referencing four vertices, both
|
|
||||||
// with cw winding order, such that:
|
|
||||||
|
|
||||||
// A-B
|
|
||||||
// |\|
|
|
||||||
// D-C
|
|
||||||
|
|
||||||
// Store triangle ABC
|
|
||||||
|
|
||||||
TriElement* tri = new TriElement();
|
|
||||||
if (tri) {
|
|
||||||
tri->indices[0] = index++;
|
|
||||||
tri->indices[1] = index++;
|
|
||||||
tri->indices[2] = index;
|
|
||||||
|
|
||||||
// Add tri element to list
|
|
||||||
_tris.push_back(tri);
|
|
||||||
_cpuMemoryUsage += sizeof(TriElement);
|
|
||||||
_cpuMemoryUsage += sizeof(TriElement*);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now store triangle ACD
|
|
||||||
tri = new TriElement();
|
|
||||||
if (tri) {
|
|
||||||
tri->indices[0] = start;
|
|
||||||
tri->indices[1] = index++;
|
|
||||||
tri->indices[2] = index++;
|
|
||||||
|
|
||||||
// Add tri element to list
|
|
||||||
_tris.push_back(tri);
|
|
||||||
_cpuMemoryUsage += sizeof(TriElement);
|
|
||||||
_cpuMemoryUsage += sizeof(TriElement*);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Cube::terminateTris() {
|
|
||||||
|
|
||||||
for (TriElementList::iterator it = _tris.begin(); it != _tris.end(); ++it) {
|
|
||||||
delete *it;
|
|
||||||
}
|
|
||||||
_cpuMemoryUsage -= _tris.size() * (sizeof(TriElement) + sizeof(TriElement*));
|
|
||||||
_tris.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
const VertexElementList& Cube::vVertexElements() const {
|
|
||||||
return _vertices;
|
|
||||||
}
|
|
||||||
|
|
||||||
VertexElementIndexList& Cube::vVertexElementIndices() {
|
|
||||||
return _vertexIndices;
|
|
||||||
}
|
|
||||||
|
|
||||||
TriElementList& Cube::vTriElements() {
|
|
||||||
return _tris;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Cube::vReleaseVertexElements() {
|
|
||||||
terminateVertices();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long Cube::vGetMemoryUsage() {
|
|
||||||
return _cpuMemoryUsage;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char Cube::_sFaceIndexToHalfSpaceMask[6] = {
|
|
||||||
OctreeElement::HalfSpace::Bottom,
|
|
||||||
OctreeElement::HalfSpace::Top,
|
|
||||||
OctreeElement::HalfSpace::Right,
|
|
||||||
OctreeElement::HalfSpace::Left,
|
|
||||||
OctreeElement::HalfSpace::Near,
|
|
||||||
OctreeElement::HalfSpace::Far,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Construction vectors ordered such that the vertices of each face are
|
|
||||||
// clockwise in a right-handed coordinate system with B-L-N at 0,0,0.
|
|
||||||
float Cube::_sVertexIndexToConstructionVector[24][3] = {
|
|
||||||
// Bottom
|
|
||||||
{ 0,0,0 },
|
|
||||||
{ 1,0,0 },
|
|
||||||
{ 1,0,1 },
|
|
||||||
{ 0,0,1 },
|
|
||||||
// Top
|
|
||||||
{ 0,1,0 },
|
|
||||||
{ 0,1,1 },
|
|
||||||
{ 1,1,1 },
|
|
||||||
{ 1,1,0 },
|
|
||||||
// Right
|
|
||||||
{ 1,0,0 },
|
|
||||||
{ 1,1,0 },
|
|
||||||
{ 1,1,1 },
|
|
||||||
{ 1,0,1 },
|
|
||||||
// Left
|
|
||||||
{ 0,0,0 },
|
|
||||||
{ 0,0,1 },
|
|
||||||
{ 0,1,1 },
|
|
||||||
{ 0,1,0 },
|
|
||||||
// Near
|
|
||||||
{ 0,0,0 },
|
|
||||||
{ 0,1,0 },
|
|
||||||
{ 1,1,0 },
|
|
||||||
{ 1,0,0 },
|
|
||||||
// Far
|
|
||||||
{ 0,0,1 },
|
|
||||||
{ 1,0,1 },
|
|
||||||
{ 1,1,1 },
|
|
||||||
{ 0,1,1 },
|
|
||||||
};
|
|
||||||
|
|
||||||
// Normals for a right-handed coordinate system
|
|
||||||
float Cube::_sVertexIndexToNormalVector[6][3] = {
|
|
||||||
{ 0,-1, 0 }, // Bottom
|
|
||||||
{ 0, 1, 0 }, // Top
|
|
||||||
{ 1, 0, 0 }, // Right
|
|
||||||
{ -1, 0, 0 }, // Left
|
|
||||||
{ 0, 0,-1 }, // Near
|
|
||||||
{ 0, 0, 1 }, // Far
|
|
||||||
};
|
|
||||||
|
|
||||||
Renderer::Renderer() {
|
|
||||||
}
|
|
||||||
|
|
||||||
Renderer::~Renderer() {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simple dispatch between API and SPI
|
|
||||||
int Renderer::add(
|
|
||||||
Primitive* primitive
|
|
||||||
) {
|
|
||||||
return vAdd(primitive);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Renderer::remove(
|
|
||||||
int id
|
|
||||||
) {
|
|
||||||
vRemove(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Renderer::release() {
|
|
||||||
vRelease();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Renderer::render() {
|
|
||||||
vRender();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long Renderer::getMemoryUsage() {
|
|
||||||
return vGetMemoryUsage();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long Renderer::getMemoryUsageGPU() {
|
|
||||||
return vGetMemoryUsageGPU();
|
|
||||||
}
|
|
||||||
|
|
||||||
PrimitiveRenderer::PrimitiveRenderer(
|
|
||||||
int maxCount
|
|
||||||
) :
|
|
||||||
_maxCount(maxCount),
|
|
||||||
_triBufferId(0),
|
|
||||||
_vertexBufferId(0),
|
|
||||||
|
|
||||||
_vertexElementCount(0),
|
|
||||||
_maxVertexElementCount(0),
|
|
||||||
|
|
||||||
_triElementCount(0),
|
|
||||||
_maxTriElementCount(0),
|
|
||||||
|
|
||||||
_primitives(),
|
|
||||||
_primitiveCount(0),
|
|
||||||
|
|
||||||
_gpuMemoryUsage(0),
|
|
||||||
_cpuMemoryUsage(0)
|
|
||||||
|
|
||||||
{
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
PrimitiveRenderer::~PrimitiveRenderer() {
|
|
||||||
|
|
||||||
terminate();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrimitiveRenderer::init() {
|
|
||||||
|
|
||||||
initializeGL();
|
|
||||||
initializeBookkeeping();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrimitiveRenderer::initializeGL() {
|
|
||||||
|
|
||||||
glGenBuffers(1, &_triBufferId);
|
|
||||||
glGenBuffers(1, &_vertexBufferId);
|
|
||||||
|
|
||||||
// Set up the element array buffer containing the index ids
|
|
||||||
_maxTriElementCount = _maxCount * 2;
|
|
||||||
int size = _maxTriElementCount * _sIndicesPerTri * sizeof(GLint);
|
|
||||||
_gpuMemoryUsage += size;
|
|
||||||
|
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _triBufferId);
|
|
||||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, 0, GL_DYNAMIC_DRAW);
|
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
|
||||||
|
|
||||||
// Set up the array buffer in the form of array of structures
|
|
||||||
// I chose AOS because it maximizes the amount of data tranferred
|
|
||||||
// by a single glBufferSubData call.
|
|
||||||
_maxVertexElementCount = _maxCount * 8;
|
|
||||||
size = _maxVertexElementCount * sizeof(VertexElement);
|
|
||||||
_gpuMemoryUsage += size;
|
|
||||||
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferId);
|
|
||||||
glBufferData(GL_ARRAY_BUFFER, size, 0, GL_DYNAMIC_DRAW);
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
||||||
|
|
||||||
// Initialize the first tri element in the buffer to all zeros, the
|
|
||||||
// degenerate case
|
|
||||||
deconstructTriElement(0);
|
|
||||||
|
|
||||||
// Initialize the first vertex element in the buffer to all zeros, the
|
|
||||||
// degenerate case
|
|
||||||
deconstructVertexElement(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrimitiveRenderer::initializeBookkeeping() {
|
|
||||||
|
|
||||||
// Start primitive count at one, because zero is reserved for the degenerate triangle
|
|
||||||
_primitives.resize(_maxCount + 1);
|
|
||||||
|
|
||||||
// Set the counters
|
|
||||||
_primitiveCount = 1;
|
|
||||||
_vertexElementCount = 1;
|
|
||||||
_triElementCount = 1;
|
|
||||||
|
|
||||||
// Guesstimate the memory consumption
|
|
||||||
_cpuMemoryUsage = sizeof(PrimitiveRenderer);
|
|
||||||
_cpuMemoryUsage += _availablePrimitiveIndex.capacity() * sizeof(int);
|
|
||||||
_cpuMemoryUsage += _availableVertexElementIndex.capacity() * sizeof(int);
|
|
||||||
_cpuMemoryUsage += _availableTriElementIndex.capacity() * sizeof(int);
|
|
||||||
_cpuMemoryUsage += _deconstructTriElementIndex.capacity() * sizeof(int);
|
|
||||||
_cpuMemoryUsage += _constructPrimitiveIndex.capacity() * sizeof(int);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrimitiveRenderer::terminate() {
|
|
||||||
|
|
||||||
terminateBookkeeping();
|
|
||||||
terminateGL();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrimitiveRenderer::terminateGL() {
|
|
||||||
|
|
||||||
if (_vertexBufferId) {
|
|
||||||
glDeleteBuffers(1, &_vertexBufferId);
|
|
||||||
_vertexBufferId = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_triBufferId) {
|
|
||||||
glDeleteBuffers(1, &_triBufferId);
|
|
||||||
_triBufferId = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrimitiveRenderer::terminateBookkeeping() {
|
|
||||||
|
|
||||||
// Delete all of the primitives
|
|
||||||
for (int i = _primitiveCount + 1; --i > 0; ) {
|
|
||||||
Primitive* primitive = _primitives[i];
|
|
||||||
if (primitive) {
|
|
||||||
_cpuMemoryUsage -= primitive->getMemoryUsage();
|
|
||||||
_primitives[i] = 0;
|
|
||||||
delete primitive;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drain the queues
|
|
||||||
_availablePrimitiveIndex.clear();
|
|
||||||
_availableVertexElementIndex.clear();
|
|
||||||
_availableTriElementIndex.clear();
|
|
||||||
_deconstructTriElementIndex.clear();
|
|
||||||
_constructPrimitiveIndex.clear();
|
|
||||||
|
|
||||||
_cpuMemoryUsage = sizeof(PrimitiveRenderer) + _primitives.size() * sizeof(Primitive *);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrimitiveRenderer::constructElements(
|
|
||||||
Primitive* primitive
|
|
||||||
) {
|
|
||||||
|
|
||||||
// Load vertex elements
|
|
||||||
VertexElementIndexList& vertexElementIndexList = primitive->vertexElementIndices();
|
|
||||||
const VertexElementList& vertices = primitive->vertexElements();
|
|
||||||
{
|
|
||||||
for (VertexElementList::const_iterator it = vertices.begin(); it != vertices.end(); ++it ) {
|
|
||||||
int index = getAvailableVertexElementIndex();
|
|
||||||
if (index != 0) {
|
|
||||||
// Store the vertex element index in the primitive's
|
|
||||||
// vertex element index list
|
|
||||||
vertexElementIndexList.push_back(index);
|
|
||||||
|
|
||||||
VertexElement* vertex = *it;
|
|
||||||
transferVertexElement(index, vertex);
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load tri elements
|
|
||||||
if (vertexElementIndexList.size() == vertices.size()) {
|
|
||||||
TriElementList& tris = primitive->triElements();
|
|
||||||
|
|
||||||
for (TriElementList::iterator it = tris.begin(); it != tris.end(); ++it) {
|
|
||||||
TriElement* tri = *it;
|
|
||||||
int index = getAvailableTriElementIndex();
|
|
||||||
if (index != 0) {
|
|
||||||
int k;
|
|
||||||
k = tri->indices[0];
|
|
||||||
tri->indices[0] = vertexElementIndexList[k];
|
|
||||||
|
|
||||||
k = tri->indices[1];
|
|
||||||
tri->indices[1] = vertexElementIndexList[k];
|
|
||||||
|
|
||||||
k = tri->indices[2];
|
|
||||||
tri->indices[2] = vertexElementIndexList[k];
|
|
||||||
|
|
||||||
tri->id = index;
|
|
||||||
transferTriElement(index, tri->indices);
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// TODO: failure mode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrimitiveRenderer::deconstructElements(
|
|
||||||
Primitive* primitive
|
|
||||||
) {
|
|
||||||
|
|
||||||
// Schedule the tri elements of the face for deconstruction
|
|
||||||
{
|
|
||||||
TriElementList& tris = primitive->triElements();
|
|
||||||
|
|
||||||
for (TriElementList::const_iterator it = tris.begin(); it != tris.end(); ++it) {
|
|
||||||
const TriElement* tri = *it;
|
|
||||||
|
|
||||||
if (tri->id) {
|
|
||||||
// Put the tri element index into decon queue
|
|
||||||
_deconstructTriElementIndex.push(tri->id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the vertex element index to the available queue, it is not necessary
|
|
||||||
// to zero the data
|
|
||||||
{
|
|
||||||
VertexElementIndexList& vertexIndexList = primitive->vertexElementIndices();
|
|
||||||
|
|
||||||
for (VertexElementIndexList::const_iterator it = vertexIndexList.begin(); it != vertexIndexList.end(); ++it) {
|
|
||||||
int index = *it;
|
|
||||||
|
|
||||||
if (index) {
|
|
||||||
// Put the vertex element index into the available queue
|
|
||||||
_availableVertexElementIndex.push(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete primitive;
|
|
||||||
}
|
|
||||||
|
|
||||||
int PrimitiveRenderer::getAvailablePrimitiveIndex() {
|
|
||||||
|
|
||||||
int index;
|
|
||||||
|
|
||||||
// Check the available primitive index queue first for an available index.
|
|
||||||
if (!_availablePrimitiveIndex.isEmpty()) {
|
|
||||||
index = _availablePrimitiveIndex.pop();
|
|
||||||
} else if (_primitiveCount < _maxCount) {
|
|
||||||
// There are no primitive indices available from the queue,
|
|
||||||
// make one up
|
|
||||||
index = _primitiveCount++;
|
|
||||||
} else {
|
|
||||||
index = 0;
|
|
||||||
}
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
int PrimitiveRenderer::getAvailableVertexElementIndex() {
|
|
||||||
|
|
||||||
int index;
|
|
||||||
|
|
||||||
// Check the available vertex element queue first for an available index.
|
|
||||||
if (!_availableVertexElementIndex.isEmpty()) {
|
|
||||||
index = _availableVertexElementIndex.pop();
|
|
||||||
} else if (_vertexElementCount < _maxVertexElementCount) {
|
|
||||||
// There are no vertex elements available from the queue,
|
|
||||||
// grab one from the end of the list
|
|
||||||
index = _vertexElementCount++;
|
|
||||||
} else {
|
|
||||||
index = 0;
|
|
||||||
}
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
int PrimitiveRenderer::getAvailableTriElementIndex() {
|
|
||||||
|
|
||||||
int index;
|
|
||||||
|
|
||||||
// Check the tri elements scheduled for deconstruction queue first to
|
|
||||||
// intercept and reuse an index without it having to be destroyed
|
|
||||||
if (!_deconstructTriElementIndex.isEmpty()) {
|
|
||||||
index = _deconstructTriElementIndex.pop();
|
|
||||||
} else if (!_availableTriElementIndex.isEmpty()) {
|
|
||||||
// Nothing available in the deconstruction queue, now
|
|
||||||
// check the available tri element queue for an available index.
|
|
||||||
index = _availableTriElementIndex.pop();
|
|
||||||
} else if (_triElementCount < _maxTriElementCount) {
|
|
||||||
// There are no reusable tri elements available from the queue,
|
|
||||||
// grab one from the end of the list
|
|
||||||
index = _triElementCount++;
|
|
||||||
} else {
|
|
||||||
index = 0;
|
|
||||||
}
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrimitiveRenderer::deconstructTriElement(
|
|
||||||
int idx
|
|
||||||
) {
|
|
||||||
|
|
||||||
// Set the tri element to the degenerate case.
|
|
||||||
static int degenerate[3] = { 0, 0, 0 };
|
|
||||||
transferTriElement(idx, degenerate);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrimitiveRenderer::deconstructVertexElement(
|
|
||||||
int idx
|
|
||||||
) {
|
|
||||||
|
|
||||||
// Set the vertex element to the degenerate case.
|
|
||||||
VertexElement degenerate;
|
|
||||||
memset(°enerate, 0, sizeof(degenerate));
|
|
||||||
|
|
||||||
transferVertexElement(idx, °enerate);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrimitiveRenderer::transferVertexElement(
|
|
||||||
int idx,
|
|
||||||
VertexElement* vertex
|
|
||||||
) {
|
|
||||||
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferId);
|
|
||||||
glBufferSubData(GL_ARRAY_BUFFER, idx * sizeof(VertexElement), sizeof(VertexElement), vertex);
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrimitiveRenderer::transferTriElement(
|
|
||||||
int idx,
|
|
||||||
int tri[3]
|
|
||||||
) {
|
|
||||||
|
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _triBufferId);
|
|
||||||
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, idx * _sBytesPerTriElement, _sBytesPerTriElement, tri);
|
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int PrimitiveRenderer::vAdd(
|
|
||||||
Primitive* primitive
|
|
||||||
) {
|
|
||||||
|
|
||||||
QMutexLocker lock(&_guard);
|
|
||||||
int id = getAvailablePrimitiveIndex();
|
|
||||||
if (id != 0) {
|
|
||||||
// Take ownership of primitive, including responsibility
|
|
||||||
// for destruction
|
|
||||||
_primitives[id] = primitive;
|
|
||||||
_constructPrimitiveIndex.push(id);
|
|
||||||
_cpuMemoryUsage += primitive->getMemoryUsage();
|
|
||||||
}
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrimitiveRenderer::vRemove(
|
|
||||||
int id
|
|
||||||
) {
|
|
||||||
|
|
||||||
if (id != 0) {
|
|
||||||
QMutexLocker lock(&_guard);
|
|
||||||
|
|
||||||
// Locate and remove the primitive by id in the vector map
|
|
||||||
Primitive* primitive = _primitives[id];
|
|
||||||
if (primitive) {
|
|
||||||
_primitives[id] = 0;
|
|
||||||
_cpuMemoryUsage -= primitive->getMemoryUsage();
|
|
||||||
deconstructElements(primitive);
|
|
||||||
|
|
||||||
// Queue the index onto the available primitive stack.
|
|
||||||
_availablePrimitiveIndex.push(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrimitiveRenderer::vRelease() {
|
|
||||||
|
|
||||||
QMutexLocker lock(&_guard);
|
|
||||||
|
|
||||||
terminateBookkeeping();
|
|
||||||
initializeBookkeeping();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrimitiveRenderer::vRender() {
|
|
||||||
|
|
||||||
int id;
|
|
||||||
QMutexLocker lock(&_guard);
|
|
||||||
|
|
||||||
// Iterate over the set of triangle element array buffer ids scheduled for
|
|
||||||
// destruction. Set the triangle element to the degenerate case. Queue the id
|
|
||||||
// onto the available tri element stack.
|
|
||||||
while (!_deconstructTriElementIndex.isEmpty()) {
|
|
||||||
id = _deconstructTriElementIndex.pop();
|
|
||||||
deconstructTriElement(id);
|
|
||||||
_availableTriElementIndex.push(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate over the set of primitive ids scheduled for construction. Transfer
|
|
||||||
// primitive data to the GPU.
|
|
||||||
while (!_constructPrimitiveIndex.isEmpty()) {
|
|
||||||
id = _constructPrimitiveIndex.pop();
|
|
||||||
Primitive* primitive = _primitives[id];
|
|
||||||
if (primitive) {
|
|
||||||
constructElements(primitive);
|
|
||||||
|
|
||||||
// No need to keep an extra copy of the vertices
|
|
||||||
_cpuMemoryUsage -= primitive->getMemoryUsage();
|
|
||||||
primitive->releaseVertexElements();
|
|
||||||
_cpuMemoryUsage += primitive->getMemoryUsage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The application uses clockwise winding for the definition of front face, this renderer
|
|
||||||
// aalso uses clockwise (that is the gl default) to construct the triangulation
|
|
||||||
// so...
|
|
||||||
//glFrontFace(GL_CW);
|
|
||||||
glEnable(GL_CULL_FACE);
|
|
||||||
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferId);
|
|
||||||
|
|
||||||
glEnableClientState(GL_VERTEX_ARRAY);
|
|
||||||
glVertexPointer(3, GL_FLOAT, sizeof(VertexElement), 0);
|
|
||||||
|
|
||||||
glEnableClientState(GL_NORMAL_ARRAY);
|
|
||||||
glNormalPointer(GL_FLOAT, sizeof(VertexElement), (const GLvoid*)12);
|
|
||||||
|
|
||||||
glEnableClientState(GL_COLOR_ARRAY);
|
|
||||||
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(VertexElement), (const GLvoid*)24);
|
|
||||||
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
||||||
|
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _triBufferId);
|
|
||||||
glDrawElements(GL_TRIANGLES, 3 * _triElementCount, GL_UNSIGNED_INT, 0);
|
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
|
||||||
|
|
||||||
glDisable(GL_CULL_FACE);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long PrimitiveRenderer::vGetMemoryUsage() {
|
|
||||||
return _cpuMemoryUsage;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long PrimitiveRenderer::vGetMemoryUsageGPU() {
|
|
||||||
return _gpuMemoryUsage;
|
|
||||||
}
|
|
|
@ -1,503 +0,0 @@
|
||||||
//
|
|
||||||
// PrimitiveRenderer.h
|
|
||||||
// interface/src/voxels
|
|
||||||
//
|
|
||||||
// Created by Norman Craft.
|
|
||||||
// Copyright 2014 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
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef hifi_PrimitiveRenderer_h
|
|
||||||
#define hifi_PrimitiveRenderer_h
|
|
||||||
|
|
||||||
#include <QStack>
|
|
||||||
#include <QVector>
|
|
||||||
#include <QMutex>
|
|
||||||
|
|
||||||
/// Vertex element structure.
|
|
||||||
/// Using the array of structures approach to specifying
|
|
||||||
/// vertex data to GL cuts down on the calls to glBufferSubData
|
|
||||||
///
|
|
||||||
typedef
|
|
||||||
struct __VertexElement {
|
|
||||||
struct __position {
|
|
||||||
float x;
|
|
||||||
float y;
|
|
||||||
float z;
|
|
||||||
} position;
|
|
||||||
struct __normal {
|
|
||||||
float x;
|
|
||||||
float y;
|
|
||||||
float z;
|
|
||||||
} normal;
|
|
||||||
struct __color {
|
|
||||||
unsigned char r;
|
|
||||||
unsigned char g;
|
|
||||||
unsigned char b;
|
|
||||||
unsigned char a;
|
|
||||||
} color;
|
|
||||||
} VertexElement;
|
|
||||||
|
|
||||||
/// Triangle element index structure.
|
|
||||||
/// Specify the vertex indices of the triangle and its element index.
|
|
||||||
///
|
|
||||||
typedef
|
|
||||||
struct __TriElement {
|
|
||||||
int indices[3];
|
|
||||||
int id;
|
|
||||||
|
|
||||||
} TriElement;
|
|
||||||
|
|
||||||
/// Vertex element list container.
|
|
||||||
///
|
|
||||||
typedef QVector<VertexElement *> VertexElementList;
|
|
||||||
|
|
||||||
/// Vertex element index list container.
|
|
||||||
///
|
|
||||||
typedef QVector<int> VertexElementIndexList;
|
|
||||||
|
|
||||||
/// Triangle element list container
|
|
||||||
///
|
|
||||||
typedef QVector<TriElement *> TriElementList;
|
|
||||||
|
|
||||||
///
|
|
||||||
/// @class Primitive
|
|
||||||
/// Primitive Interface class.
|
|
||||||
/// Abstract class for accessing vertex and tri elements of geometric primitives
|
|
||||||
///
|
|
||||||
///
|
|
||||||
class Primitive {
|
|
||||||
public:
|
|
||||||
virtual ~Primitive();
|
|
||||||
|
|
||||||
// API methods go here
|
|
||||||
|
|
||||||
/// Vertex element accessor.
|
|
||||||
/// @return A list of vertex elements of the primitive
|
|
||||||
///
|
|
||||||
const VertexElementList& vertexElements() const;
|
|
||||||
|
|
||||||
/// Vertex element index accessor.
|
|
||||||
/// @return A list of vertex element indices of the primitive
|
|
||||||
///
|
|
||||||
VertexElementIndexList& vertexElementIndices();
|
|
||||||
|
|
||||||
/// Tri element accessor.
|
|
||||||
/// @return A list of tri elements of the primitive
|
|
||||||
///
|
|
||||||
TriElementList& triElements();
|
|
||||||
|
|
||||||
/// Release vertex elements.
|
|
||||||
///
|
|
||||||
void releaseVertexElements();
|
|
||||||
|
|
||||||
/// Get memory usage.
|
|
||||||
///
|
|
||||||
unsigned long getMemoryUsage();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/// Default constructor prohibited to API user, restricted to service implementer.
|
|
||||||
///
|
|
||||||
Primitive();
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// Copy constructor prohibited.
|
|
||||||
///
|
|
||||||
Primitive(
|
|
||||||
const Primitive& copy
|
|
||||||
);
|
|
||||||
|
|
||||||
// SPI methods are defined here
|
|
||||||
|
|
||||||
/// Vertex element accessor.
|
|
||||||
/// Service implementer to provide private override for this method
|
|
||||||
/// in derived class
|
|
||||||
///
|
|
||||||
virtual const VertexElementList& vVertexElements() const = 0;
|
|
||||||
|
|
||||||
/// Vertex element index accessor.
|
|
||||||
/// Service implementer to provide private override for this method
|
|
||||||
/// in derived class
|
|
||||||
///
|
|
||||||
virtual VertexElementIndexList& vVertexElementIndices() = 0;
|
|
||||||
|
|
||||||
/// Tri element accessor.
|
|
||||||
/// Service implementer to provide private override for this method
|
|
||||||
/// in derived class
|
|
||||||
///
|
|
||||||
virtual TriElementList& vTriElements() = 0;
|
|
||||||
|
|
||||||
/// Release vertex elements.
|
|
||||||
/// Service implementer to provide private override for this method
|
|
||||||
/// in derived class
|
|
||||||
///
|
|
||||||
virtual void vReleaseVertexElements() = 0;
|
|
||||||
|
|
||||||
/// Get memory usage.
|
|
||||||
/// Service implementer to provide private override for this method
|
|
||||||
/// in derived class
|
|
||||||
///
|
|
||||||
virtual unsigned long vGetMemoryUsage() = 0;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
///
|
|
||||||
/// @class Cube
|
|
||||||
/// Class for accessing the vertex and triangle elements of a cube
|
|
||||||
///
|
|
||||||
class Cube: public Primitive {
|
|
||||||
public:
|
|
||||||
/// Configuration dependency injection constructor.
|
|
||||||
///
|
|
||||||
Cube(
|
|
||||||
float x, ///< Cube location on X-axis
|
|
||||||
float y, ///< Cube location on Y-axis
|
|
||||||
float z, ///< Cube location on Z-axis
|
|
||||||
float s, ///< Cube size
|
|
||||||
unsigned char r, ///< Cube red color component
|
|
||||||
unsigned char g, ///< Cube green color component
|
|
||||||
unsigned char b, ///< Cube blue color component
|
|
||||||
unsigned char faces ///< Bitmask of faces of cube excluded from construction
|
|
||||||
);
|
|
||||||
|
|
||||||
~Cube();
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// Copy constructor prohibited.
|
|
||||||
///
|
|
||||||
Cube (
|
|
||||||
const Cube& cube
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Cube initialization
|
|
||||||
///
|
|
||||||
void init(
|
|
||||||
float x, ///< Cube location on X-axis
|
|
||||||
float y, ///< Cube location on Y-axis
|
|
||||||
float z, ///< Cube location on Z-axis
|
|
||||||
float s, ///< Cube size
|
|
||||||
unsigned char r, ///< Cube red color component
|
|
||||||
unsigned char g, ///< Cube green color component
|
|
||||||
unsigned char b, ///< Cube blue color component
|
|
||||||
unsigned char faceExclusions ///< Bitmask of faces of cube excluded from construction
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Cube termination
|
|
||||||
///
|
|
||||||
void terminate();
|
|
||||||
|
|
||||||
/// Initialize cube's vertex list
|
|
||||||
///
|
|
||||||
void initializeVertices(
|
|
||||||
float x, ///< Cube location on X-axis
|
|
||||||
float y, ///< Cube location on Y-axis
|
|
||||||
float z, ///< Cube location on Z-axis
|
|
||||||
float s, ///< Cube size
|
|
||||||
unsigned char r, ///< Cube red color component
|
|
||||||
unsigned char g, ///< Cube green color component
|
|
||||||
unsigned char b, ///< Cube blue color component
|
|
||||||
unsigned char faceExclusions ///< Bitmask of faces of cube excluded from construction
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Terminate cube's vertex list
|
|
||||||
///
|
|
||||||
void terminateVertices();
|
|
||||||
|
|
||||||
/// Initialize cube's triangle list
|
|
||||||
///
|
|
||||||
void initializeTris(
|
|
||||||
unsigned char faceExclusions
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Terminate cube's triangle list
|
|
||||||
///
|
|
||||||
void terminateTris();
|
|
||||||
|
|
||||||
// SPI virtual override methods go here
|
|
||||||
|
|
||||||
const VertexElementList& vVertexElements() const;
|
|
||||||
VertexElementIndexList& vVertexElementIndices();
|
|
||||||
TriElementList& vTriElements();
|
|
||||||
void vReleaseVertexElements();
|
|
||||||
unsigned long vGetMemoryUsage();
|
|
||||||
|
|
||||||
private:
|
|
||||||
VertexElementList _vertices; ///< Vertex element list
|
|
||||||
VertexElementIndexList _vertexIndices; ///< Vertex element index list
|
|
||||||
TriElementList _tris; ///< Tri element list
|
|
||||||
|
|
||||||
unsigned long _cpuMemoryUsage; ///< Memory allocation of object
|
|
||||||
|
|
||||||
static const int _sNumFacesPerCube = 6; ///< Number of faces per cube
|
|
||||||
static const int _sNumVerticesPerCube = 24; ///< Number of vertices per cube
|
|
||||||
static unsigned char _sFaceIndexToHalfSpaceMask[6]; ///< index to bitmask map
|
|
||||||
static float _sVertexIndexToConstructionVector[24][3]; ///< Vertex index to construction vector map
|
|
||||||
static float _sVertexIndexToNormalVector[6][3]; ///< Vertex index to normal vector map
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
///
|
|
||||||
/// @class Renderer
|
|
||||||
/// GL renderer interface class.
|
|
||||||
/// Abstract class for rendering geometric primitives in GL
|
|
||||||
///
|
|
||||||
class Renderer {
|
|
||||||
public:
|
|
||||||
virtual ~Renderer();
|
|
||||||
|
|
||||||
// API methods go here
|
|
||||||
|
|
||||||
/// Add primitive to renderer database.
|
|
||||||
///
|
|
||||||
int add(
|
|
||||||
Primitive* primitive ///< Primitive instance to be added
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Remove primitive from renderer database.
|
|
||||||
///
|
|
||||||
void remove(
|
|
||||||
int id ///< Primitive id to be removed
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Clear all primitives from renderer database
|
|
||||||
///
|
|
||||||
void release();
|
|
||||||
|
|
||||||
/// Render primitive database.
|
|
||||||
/// The render method assumes appropriate GL context and state has
|
|
||||||
/// already been provided for
|
|
||||||
///
|
|
||||||
void render();
|
|
||||||
|
|
||||||
/// Get memory usage.
|
|
||||||
///
|
|
||||||
unsigned long getMemoryUsage();
|
|
||||||
|
|
||||||
/// Get GPU memory usage.
|
|
||||||
///
|
|
||||||
unsigned long getMemoryUsageGPU();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/// Default constructor prohibited to API user, restricted to service implementer.
|
|
||||||
///
|
|
||||||
Renderer();
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// Copy constructor prohibited.
|
|
||||||
///
|
|
||||||
Renderer(
|
|
||||||
const Renderer& copy
|
|
||||||
);
|
|
||||||
|
|
||||||
// SPI methods are defined here
|
|
||||||
|
|
||||||
/// Add primitive to renderer database.
|
|
||||||
/// Service implementer to provide private override for this method
|
|
||||||
/// in derived class
|
|
||||||
/// @return Primitive id
|
|
||||||
///
|
|
||||||
virtual int vAdd(
|
|
||||||
Primitive* primitive ///< Primitive instance to be added
|
|
||||||
) = 0;
|
|
||||||
|
|
||||||
/// Remove primitive from renderer database.
|
|
||||||
/// Service implementer to provide private override for this method
|
|
||||||
/// in derived class
|
|
||||||
///
|
|
||||||
virtual void vRemove(
|
|
||||||
int id ///< Primitive id
|
|
||||||
) = 0;
|
|
||||||
|
|
||||||
/// Clear all primitives from renderer database
|
|
||||||
/// Service implementer to provide private override for this method
|
|
||||||
/// in derived class
|
|
||||||
///
|
|
||||||
virtual void vRelease() = 0;
|
|
||||||
|
|
||||||
/// Render primitive database.
|
|
||||||
/// Service implementer to provide private virtual override for this method
|
|
||||||
/// in derived class
|
|
||||||
///
|
|
||||||
virtual void vRender() = 0;
|
|
||||||
|
|
||||||
/// Get memory usage.
|
|
||||||
///
|
|
||||||
virtual unsigned long vGetMemoryUsage() = 0;
|
|
||||||
|
|
||||||
/// Get GPU memory usage.
|
|
||||||
///
|
|
||||||
virtual unsigned long vGetMemoryUsageGPU() = 0;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
///
|
|
||||||
/// @class PrimitiveRenderer
|
|
||||||
/// Renderer implementation class for the rendering of geometric primitives
|
|
||||||
/// using GL element array and GL array buffers
|
|
||||||
///
|
|
||||||
class PrimitiveRenderer : public Renderer {
|
|
||||||
public:
|
|
||||||
/// Configuration dependency injection constructor.
|
|
||||||
///
|
|
||||||
PrimitiveRenderer(
|
|
||||||
int maxCount ///< Max count
|
|
||||||
);
|
|
||||||
|
|
||||||
~PrimitiveRenderer();
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// Default constructor prohibited.
|
|
||||||
///
|
|
||||||
PrimitiveRenderer();
|
|
||||||
|
|
||||||
/// Copy constructor prohibited.
|
|
||||||
///
|
|
||||||
PrimitiveRenderer(
|
|
||||||
const PrimitiveRenderer& renderer
|
|
||||||
);
|
|
||||||
|
|
||||||
void init();
|
|
||||||
void terminate();
|
|
||||||
|
|
||||||
/// Allocate and initialize GL buffers.
|
|
||||||
///
|
|
||||||
void initializeGL();
|
|
||||||
|
|
||||||
/// Terminate and deallocate GL buffers.
|
|
||||||
///
|
|
||||||
void terminateGL();
|
|
||||||
|
|
||||||
void initializeBookkeeping();
|
|
||||||
void terminateBookkeeping();
|
|
||||||
|
|
||||||
/// Construct the elements of the faces of the primitive.
|
|
||||||
///
|
|
||||||
void constructElements(
|
|
||||||
Primitive* primitive ///< Primitive instance
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Deconstruct the elements of the faces of the primitive.
|
|
||||||
///
|
|
||||||
void deconstructElements(
|
|
||||||
Primitive* primitive ///< Primitive instance
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Deconstruct the triangle element from the GL buffer.
|
|
||||||
///
|
|
||||||
void deconstructTriElement(
|
|
||||||
int idx ///< Triangle element index
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Deconstruct the vertex element from the GL buffer.
|
|
||||||
///
|
|
||||||
void deconstructVertexElement(
|
|
||||||
int idx ///< Vertex element index
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Transfer the vertex element to the GL buffer.
|
|
||||||
///
|
|
||||||
void transferVertexElement(
|
|
||||||
int idx, ///< Vertex element index
|
|
||||||
VertexElement *vertex ///< Vertex element instance
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Transfer the triangle element to the GL buffer.
|
|
||||||
///
|
|
||||||
void transferTriElement(
|
|
||||||
int idx, ///< Triangle element index
|
|
||||||
int tri[3] ///< Triangle element data
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Get available primitive index.
|
|
||||||
/// Get an available primitive index from either the recycling
|
|
||||||
/// queue or incrementing the counter
|
|
||||||
///
|
|
||||||
int getAvailablePrimitiveIndex();
|
|
||||||
|
|
||||||
/// Get available vertex element index.
|
|
||||||
/// Get an available vertex element index from either the recycling
|
|
||||||
/// queue or incrementing the counter
|
|
||||||
///
|
|
||||||
int getAvailableVertexElementIndex();
|
|
||||||
|
|
||||||
/// Get available triangle element index.
|
|
||||||
/// Get an available triangle element index from either the elements
|
|
||||||
/// scheduled for deconstruction queue, the recycling
|
|
||||||
/// queue or incrementing the counter
|
|
||||||
///
|
|
||||||
int getAvailableTriElementIndex();
|
|
||||||
|
|
||||||
// SPI virtual override methods go here
|
|
||||||
|
|
||||||
/// Add primitive to renderer database.
|
|
||||||
///
|
|
||||||
int vAdd(
|
|
||||||
Primitive* primitive ///< Primitive instance to be added
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Remove primitive from renderer database.
|
|
||||||
///
|
|
||||||
void vRemove(
|
|
||||||
int id ///< Primitive id to be removed
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Clear all primitives from renderer database
|
|
||||||
///
|
|
||||||
void vRelease();
|
|
||||||
|
|
||||||
/// Render triangle database.
|
|
||||||
///
|
|
||||||
void vRender();
|
|
||||||
|
|
||||||
/// Get memory usage.
|
|
||||||
///
|
|
||||||
unsigned long vGetMemoryUsage();
|
|
||||||
|
|
||||||
/// Get gpu memory usage.
|
|
||||||
///
|
|
||||||
unsigned long vGetMemoryUsageGPU();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
int _maxCount; ///< Maximum count of tris
|
|
||||||
|
|
||||||
// GL related parameters
|
|
||||||
|
|
||||||
GLuint _triBufferId; ///< GL element array buffer id
|
|
||||||
GLuint _vertexBufferId; ///< GL vertex array buffer id
|
|
||||||
|
|
||||||
// Book keeping parameters
|
|
||||||
|
|
||||||
int _vertexElementCount; ///< Count of vertices
|
|
||||||
int _maxVertexElementCount; ///< Max count of vertices
|
|
||||||
|
|
||||||
int _triElementCount; ///< Count of triangles
|
|
||||||
int _maxTriElementCount; ///< Max count of triangles
|
|
||||||
|
|
||||||
QVector<Primitive *> _primitives; ///< Vector of primitive
|
|
||||||
int _primitiveCount; ///< Count of primitives
|
|
||||||
|
|
||||||
QStack<int> _availablePrimitiveIndex; ///< Queue of primitive indices available
|
|
||||||
QStack<int> _availableVertexElementIndex; ///< Queue of vertex element indices available
|
|
||||||
QStack<int> _availableTriElementIndex; ///< Queue of triangle element indices available
|
|
||||||
QStack<int> _deconstructTriElementIndex; ///< Queue of triangle element indices requiring deletion from GL
|
|
||||||
QStack<int> _constructPrimitiveIndex; ///< Queue of primitives requiring addition to GL
|
|
||||||
|
|
||||||
QMutex _guard;
|
|
||||||
|
|
||||||
// Statistics parameters, not necessary for proper operation
|
|
||||||
|
|
||||||
unsigned long _gpuMemoryUsage; ///< GPU memory used by this instance
|
|
||||||
unsigned long _cpuMemoryUsage; ///< CPU memory used by this instance
|
|
||||||
|
|
||||||
|
|
||||||
static const int _sIndicesPerTri = 3;
|
|
||||||
static const int _sBytesPerTriElement = sizeof(GLint) * _sIndicesPerTri;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif // hifi_PrimitiveRenderer_h
|
|
|
@ -68,10 +68,6 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels, VoxelTree* tree)
|
||||||
_initialized(false),
|
_initialized(false),
|
||||||
_writeArraysLock(QReadWriteLock::Recursive),
|
_writeArraysLock(QReadWriteLock::Recursive),
|
||||||
_readArraysLock(QReadWriteLock::Recursive),
|
_readArraysLock(QReadWriteLock::Recursive),
|
||||||
_inOcclusions(false),
|
|
||||||
_showCulledSharedFaces(false),
|
|
||||||
_usePrimitiveRenderer(false),
|
|
||||||
_renderer(0),
|
|
||||||
_drawHaze(false),
|
_drawHaze(false),
|
||||||
_farHazeDistance(300.0f),
|
_farHazeDistance(300.0f),
|
||||||
_hazeColor(grayColor)
|
_hazeColor(grayColor)
|
||||||
|
@ -112,7 +108,7 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels, VoxelTree* tree)
|
||||||
void VoxelSystem::elementDeleted(OctreeElement* element) {
|
void VoxelSystem::elementDeleted(OctreeElement* element) {
|
||||||
VoxelTreeElement* voxel = (VoxelTreeElement*)element;
|
VoxelTreeElement* voxel = (VoxelTreeElement*)element;
|
||||||
if (voxel->getVoxelSystem() == this) {
|
if (voxel->getVoxelSystem() == this) {
|
||||||
if ((_voxelsInWriteArrays != 0) || _usePrimitiveRenderer) {
|
if ((_voxelsInWriteArrays != 0)) {
|
||||||
forceRemoveNodeFromArrays(voxel);
|
forceRemoveNodeFromArrays(voxel);
|
||||||
} else {
|
} else {
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings)) {
|
if (Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings)) {
|
||||||
|
@ -288,9 +284,6 @@ void VoxelSystem::cleanupVoxelMemory() {
|
||||||
_readColorsArray = NULL;
|
_readColorsArray = NULL;
|
||||||
_writeColorsArray = NULL;
|
_writeColorsArray = NULL;
|
||||||
|
|
||||||
delete _renderer;
|
|
||||||
_renderer = 0;
|
|
||||||
|
|
||||||
delete[] _writeVoxelDirtyArray;
|
delete[] _writeVoxelDirtyArray;
|
||||||
delete[] _readVoxelDirtyArray;
|
delete[] _readVoxelDirtyArray;
|
||||||
_writeVoxelDirtyArray = _readVoxelDirtyArray = NULL;
|
_writeVoxelDirtyArray = _readVoxelDirtyArray = NULL;
|
||||||
|
@ -384,8 +377,6 @@ void VoxelSystem::initVoxelMemory() {
|
||||||
Application::resourcesPath() + "shaders/voxel.frag");
|
Application::resourcesPath() + "shaders/voxel.frag");
|
||||||
_program.link();
|
_program.link();
|
||||||
}
|
}
|
||||||
_renderer = new PrimitiveRenderer(_maxVoxels);
|
|
||||||
|
|
||||||
_initialized = true;
|
_initialized = true;
|
||||||
|
|
||||||
_writeArraysLock.unlock();
|
_writeArraysLock.unlock();
|
||||||
|
@ -511,10 +502,6 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
|
||||||
_callsToTreesToArrays++;
|
_callsToTreesToArrays++;
|
||||||
|
|
||||||
if (_writeRenderFullVBO) {
|
if (_writeRenderFullVBO) {
|
||||||
if (_usePrimitiveRenderer) {
|
|
||||||
_renderer->release();
|
|
||||||
clearAllNodesBufferIndex();
|
|
||||||
}
|
|
||||||
clearFreeBufferIndexes();
|
clearFreeBufferIndexes();
|
||||||
}
|
}
|
||||||
_voxelsUpdated = newTreeToArrays(_tree->getRoot());
|
_voxelsUpdated = newTreeToArrays(_tree->getRoot());
|
||||||
|
@ -525,24 +512,17 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
|
||||||
_voxelsUpdated = 0;
|
_voxelsUpdated = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_usePrimitiveRenderer) {
|
// lock on the buffer write lock so we can't modify the data when the GPU is reading it
|
||||||
if (_voxelsUpdated) {
|
_readArraysLock.lockForWrite();
|
||||||
_voxelsDirty=true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// lock on the buffer write lock so we can't modify the data when the GPU is reading it
|
|
||||||
_readArraysLock.lockForWrite();
|
|
||||||
|
|
||||||
if (_voxelsUpdated) {
|
|
||||||
_voxelsDirty=true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy the newly written data to the arrays designated for reading, only does something if _voxelsDirty && _voxelsUpdated
|
|
||||||
copyWrittenDataToReadArrays(didWriteFullVBO);
|
|
||||||
_readArraysLock.unlock();
|
|
||||||
|
|
||||||
|
if (_voxelsUpdated) {
|
||||||
|
_voxelsDirty=true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// copy the newly written data to the arrays designated for reading, only does something if _voxelsDirty && _voxelsUpdated
|
||||||
|
copyWrittenDataToReadArrays(didWriteFullVBO);
|
||||||
|
_readArraysLock.unlock();
|
||||||
|
|
||||||
quint64 end = usecTimestampNow();
|
quint64 end = usecTimestampNow();
|
||||||
int elapsedmsec = (end - start) / 1000;
|
int elapsedmsec = (end - start) / 1000;
|
||||||
_setupNewVoxelsForDrawingLastFinished = end;
|
_setupNewVoxelsForDrawingLastFinished = end;
|
||||||
|
@ -569,26 +549,22 @@ void VoxelSystem::setupNewVoxelsForDrawingSingleNode(bool allowBailEarly) {
|
||||||
return; // bail early, it hasn't been long enough since the last time we ran
|
return; // bail early, it hasn't been long enough since the last time we ran
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_usePrimitiveRenderer) {
|
// lock on the buffer write lock so we can't modify the data when the GPU is reading it
|
||||||
_voxelsDirty = true; // if we got this far, then we can assume some voxels are dirty
|
{
|
||||||
_voxelsUpdated = 0;
|
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||||
} else {
|
"setupNewVoxelsForDrawingSingleNode()... _bufferWriteLock.lock();" );
|
||||||
// lock on the buffer write lock so we can't modify the data when the GPU is reading it
|
_readArraysLock.lockForWrite();
|
||||||
{
|
|
||||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
|
||||||
"setupNewVoxelsForDrawingSingleNode()... _bufferWriteLock.lock();" );
|
|
||||||
_readArraysLock.lockForWrite();
|
|
||||||
}
|
|
||||||
|
|
||||||
_voxelsDirty = true; // if we got this far, then we can assume some voxels are dirty
|
|
||||||
|
|
||||||
// copy the newly written data to the arrays designated for reading, only does something if _voxelsDirty && _voxelsUpdated
|
|
||||||
copyWrittenDataToReadArrays(_writeRenderFullVBO);
|
|
||||||
|
|
||||||
// after...
|
|
||||||
_voxelsUpdated = 0;
|
|
||||||
_readArraysLock.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_voxelsDirty = true; // if we got this far, then we can assume some voxels are dirty
|
||||||
|
|
||||||
|
// copy the newly written data to the arrays designated for reading, only does something if _voxelsDirty && _voxelsUpdated
|
||||||
|
copyWrittenDataToReadArrays(_writeRenderFullVBO);
|
||||||
|
|
||||||
|
// after...
|
||||||
|
_voxelsUpdated = 0;
|
||||||
|
_readArraysLock.unlock();
|
||||||
|
|
||||||
quint64 end = usecTimestampNow();
|
quint64 end = usecTimestampNow();
|
||||||
int elapsedmsec = (end - start) / 1000;
|
int elapsedmsec = (end - start) / 1000;
|
||||||
_setupNewVoxelsForDrawingLastFinished = end;
|
_setupNewVoxelsForDrawingLastFinished = end;
|
||||||
|
@ -862,22 +838,13 @@ int VoxelSystem::forceRemoveNodeFromArrays(VoxelTreeElement* node) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_usePrimitiveRenderer) {
|
// if the node is not in the VBOs then we have nothing to do!
|
||||||
if (node->isKnownBufferIndex()) {
|
if (node->isKnownBufferIndex()) {
|
||||||
int primitiveIndex = node->getBufferIndex();
|
// If this node has not yet been written to the array, then add it to the end of the array.
|
||||||
_renderer->remove(primitiveIndex);
|
glBufferIndex nodeIndex = node->getBufferIndex();
|
||||||
node->setBufferIndex(GLBUFFER_INDEX_UNKNOWN);
|
node->setBufferIndex(GLBUFFER_INDEX_UNKNOWN);
|
||||||
return 1;
|
freeBufferIndex(nodeIndex); // NOTE: This will make the node invisible!
|
||||||
}
|
return 1; // updated!
|
||||||
} else {
|
|
||||||
// if the node is not in the VBOs then we have nothing to do!
|
|
||||||
if (node->isKnownBufferIndex()) {
|
|
||||||
// If this node has not yet been written to the array, then add it to the end of the array.
|
|
||||||
glBufferIndex nodeIndex = node->getBufferIndex();
|
|
||||||
node->setBufferIndex(GLBUFFER_INDEX_UNKNOWN);
|
|
||||||
freeBufferIndex(nodeIndex); // NOTE: This will make the node invisible!
|
|
||||||
return 1; // updated!
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0; // not-updated
|
return 0; // not-updated
|
||||||
}
|
}
|
||||||
|
@ -909,43 +876,17 @@ int VoxelSystem::updateNodeInArrays(VoxelTreeElement* node, bool reuseIndex, boo
|
||||||
float voxelScale = node->getScale();
|
float voxelScale = node->getScale();
|
||||||
nodeColor const & color = node->getColor();
|
nodeColor const & color = node->getColor();
|
||||||
|
|
||||||
if (_usePrimitiveRenderer) {
|
glBufferIndex nodeIndex = GLBUFFER_INDEX_UNKNOWN;
|
||||||
if (node->isKnownBufferIndex()) {
|
if (reuseIndex && node->isKnownBufferIndex()) {
|
||||||
int primitiveIndex = node->getBufferIndex();
|
nodeIndex = node->getBufferIndex();
|
||||||
_renderer->remove(primitiveIndex);
|
|
||||||
node->setBufferIndex(GLBUFFER_INDEX_UNKNOWN);
|
|
||||||
} else {
|
|
||||||
node->setVoxelSystem(this);
|
|
||||||
}
|
|
||||||
unsigned char occlusions;
|
|
||||||
if (_showCulledSharedFaces) {
|
|
||||||
occlusions = ~node->getInteriorOcclusions();
|
|
||||||
} else {
|
|
||||||
occlusions = node->getInteriorOcclusions();
|
|
||||||
}
|
|
||||||
if (occlusions != OctreeElement::HalfSpace::All) {
|
|
||||||
Cube* cube = new Cube(
|
|
||||||
startVertex.x, startVertex.y, startVertex.z, voxelScale,
|
|
||||||
color[RED_INDEX], color[GREEN_INDEX], color[BLUE_INDEX],
|
|
||||||
occlusions);
|
|
||||||
if (cube) {
|
|
||||||
int primitiveIndex = _renderer->add(cube);
|
|
||||||
node->setBufferIndex(primitiveIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
glBufferIndex nodeIndex = GLBUFFER_INDEX_UNKNOWN;
|
nodeIndex = getNextBufferIndex();
|
||||||
if (reuseIndex && node->isKnownBufferIndex()) {
|
node->setBufferIndex(nodeIndex);
|
||||||
nodeIndex = node->getBufferIndex();
|
node->setVoxelSystem(this);
|
||||||
} else {
|
|
||||||
nodeIndex = getNextBufferIndex();
|
|
||||||
node->setBufferIndex(nodeIndex);
|
|
||||||
node->setVoxelSystem(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// populate the array with points for the 8 vertices and RGB color for each added vertex
|
|
||||||
updateArraysDetails(nodeIndex, startVertex, voxelScale, node->getColor());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// populate the array with points for the 8 vertices and RGB color for each added vertex
|
||||||
|
updateArraysDetails(nodeIndex, startVertex, voxelScale, node->getColor());
|
||||||
return 1; // updated!
|
return 1; // updated!
|
||||||
} else {
|
} else {
|
||||||
// If we shouldn't render, and we're in reuseIndex mode, then free our index, this only operates
|
// If we shouldn't render, and we're in reuseIndex mode, then free our index, this only operates
|
||||||
|
@ -1072,24 +1013,22 @@ void VoxelSystem::updateVBOs() {
|
||||||
};
|
};
|
||||||
// would like to include _callsToTreesToArrays
|
// would like to include _callsToTreesToArrays
|
||||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), buffer);
|
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), buffer);
|
||||||
if (! _usePrimitiveRenderer) {
|
if (_voxelsDirty) {
|
||||||
if (_voxelsDirty) {
|
|
||||||
|
// attempt to lock the read arrays, to for copying from them to the actual GPU VBOs.
|
||||||
// attempt to lock the read arrays, to for copying from them to the actual GPU VBOs.
|
// if we fail to get the lock, that's ok, our VBOs will update on the next frame...
|
||||||
// if we fail to get the lock, that's ok, our VBOs will update on the next frame...
|
const int WAIT_FOR_LOCK_IN_MS = 5;
|
||||||
const int WAIT_FOR_LOCK_IN_MS = 5;
|
if (_readArraysLock.tryLockForRead(WAIT_FOR_LOCK_IN_MS)) {
|
||||||
if (_readArraysLock.tryLockForRead(WAIT_FOR_LOCK_IN_MS)) {
|
if (_readRenderFullVBO) {
|
||||||
if (_readRenderFullVBO) {
|
updateFullVBOs();
|
||||||
updateFullVBOs();
|
|
||||||
} else {
|
|
||||||
updatePartialVBOs();
|
|
||||||
}
|
|
||||||
_voxelsDirty = false;
|
|
||||||
_readRenderFullVBO = false;
|
|
||||||
_readArraysLock.unlock();
|
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "updateVBOs().... couldn't get _readArraysLock.tryLockForRead()";
|
updatePartialVBOs();
|
||||||
}
|
}
|
||||||
|
_voxelsDirty = false;
|
||||||
|
_readRenderFullVBO = false;
|
||||||
|
_readArraysLock.unlock();
|
||||||
|
} else {
|
||||||
|
qDebug() << "updateVBOs().... couldn't get _readArraysLock.tryLockForRead()";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_callsToTreesToArrays = 0; // clear it
|
_callsToTreesToArrays = 0; // clear it
|
||||||
|
@ -1143,11 +1082,11 @@ void VoxelSystem::render() {
|
||||||
|
|
||||||
updateVBOs();
|
updateVBOs();
|
||||||
|
|
||||||
if (!_usePrimitiveRenderer) {
|
if (_drawHaze) {
|
||||||
if (_drawHaze) {
|
glEnable(GL_FOG);
|
||||||
glEnable(GL_FOG);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
{
|
||||||
PerformanceWarning warn(showWarnings, "render().. TRIANGLES...");
|
PerformanceWarning warn(showWarnings, "render().. TRIANGLES...");
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -1223,16 +1162,10 @@ void VoxelSystem::render() {
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_drawHaze) {
|
|
||||||
glDisable(GL_FOG);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
applyScaleAndBindProgram(texture);
|
if (_drawHaze) {
|
||||||
_renderer->render();
|
glDisable(GL_FOG);
|
||||||
removeScaleAndReleaseProgram(texture);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1275,12 +1208,6 @@ void VoxelSystem::killLocalVoxels() {
|
||||||
_tree->getRoot()->setVoxelSystem(voxelSystem);
|
_tree->getRoot()->setVoxelSystem(voxelSystem);
|
||||||
_tree->unlock();
|
_tree->unlock();
|
||||||
clearFreeBufferIndexes();
|
clearFreeBufferIndexes();
|
||||||
if (_usePrimitiveRenderer) {
|
|
||||||
if (_renderer) {
|
|
||||||
_renderer->release();
|
|
||||||
}
|
|
||||||
clearAllNodesBufferIndex();
|
|
||||||
}
|
|
||||||
_voxelsInReadArrays = 0; // do we need to do this?
|
_voxelsInReadArrays = 0; // do we need to do this?
|
||||||
setupNewVoxelsForDrawing();
|
setupNewVoxelsForDrawing();
|
||||||
}
|
}
|
||||||
|
@ -1308,178 +1235,6 @@ void VoxelSystem::clearAllNodesBufferIndex() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VoxelSystem::inspectForInteriorOcclusionsOperation(OctreeElement* element, void* extraData) {
|
|
||||||
_nodeCount++;
|
|
||||||
VoxelTreeElement* voxel = (VoxelTreeElement*)element;
|
|
||||||
|
|
||||||
// Nothing to do at the leaf level
|
|
||||||
if (voxel->isLeaf()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bit mask of occluded shared faces indexed by child
|
|
||||||
unsigned char occludedSharedFace[NUMBER_OF_CHILDREN] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
||||||
|
|
||||||
// Traverse all pair combinations of children
|
|
||||||
for (int i = NUMBER_OF_CHILDREN; --i >= 0; ) {
|
|
||||||
|
|
||||||
VoxelTreeElement* childA = voxel->getChildAtIndex(i);
|
|
||||||
if (childA) {
|
|
||||||
|
|
||||||
// Get the child A's occluding faces, for a leaf that will be
|
|
||||||
// all six voxel faces, and for a non leaf, that will be
|
|
||||||
// all faces which are completely covered by four child octants.
|
|
||||||
unsigned char exteriorOcclusionsA = childA->getExteriorOcclusions();
|
|
||||||
|
|
||||||
for (int j = i; --j >= 0; ) {
|
|
||||||
|
|
||||||
VoxelTreeElement* childB = voxel->getChildAtIndex(j);
|
|
||||||
if (childB) {
|
|
||||||
|
|
||||||
// Get child B's occluding faces
|
|
||||||
unsigned char exteriorOcclusionsB = childB->getExteriorOcclusions();
|
|
||||||
|
|
||||||
// Determine the shared halfspace partition between siblings A and B,
|
|
||||||
// i.e., near/far, left/right, or top/bottom
|
|
||||||
unsigned char partitionA = _sOctantIndexToSharedBitMask[i][j] &
|
|
||||||
exteriorOcclusionsA;
|
|
||||||
unsigned char partitionB = _sOctantIndexToSharedBitMask[i][j] &
|
|
||||||
exteriorOcclusionsB;
|
|
||||||
|
|
||||||
// Determine which face of each sibling is occluded.
|
|
||||||
|
|
||||||
// The _sOctantIndexToBitMask is a partition occupancy mask. For
|
|
||||||
// example, if the near-left-top (NLT) and near-left-bottom (NLB) child voxels
|
|
||||||
// exist, the shared partition is top-bottom (TB), and thus the occluded
|
|
||||||
// shared face of the NLT voxel is its bottom face.
|
|
||||||
occludedSharedFace[i] |= (partitionB & _sOctantIndexToBitMask[i]);
|
|
||||||
occludedSharedFace[j] |= (partitionA & _sOctantIndexToBitMask[j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Exchange bit pairs, left to right, vice versa, etc.
|
|
||||||
occludedSharedFace[i] = _sSwizzledOcclusionBits[occludedSharedFace[i]];
|
|
||||||
// Combine this voxel's interior excluded shared face only to those children which are coincident
|
|
||||||
// with the excluded face.
|
|
||||||
occludedSharedFace[i] |= (voxel->getInteriorOcclusions() & _sOctantIndexToBitMask[i]);
|
|
||||||
|
|
||||||
// Inform the child
|
|
||||||
childA->setInteriorOcclusions(occludedSharedFace[i]);
|
|
||||||
if (occludedSharedFace[i] != OctreeElement::HalfSpace::None) {
|
|
||||||
//const glm::vec3& v = voxel->getCorner();
|
|
||||||
//float s = voxel->getScale();
|
|
||||||
|
|
||||||
//qDebug("Child %d of voxel at %f %f %f size: %f has %02x occlusions", i, v.x, v.y, v.z, s, occludedSharedFace[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VoxelSystem::inspectForExteriorOcclusionsOperation(OctreeElement* element, void* extraData) {
|
|
||||||
_nodeCount++;
|
|
||||||
VoxelTreeElement* voxel = (VoxelTreeElement*)element;
|
|
||||||
|
|
||||||
// Nothing to do at the leaf level
|
|
||||||
if (voxel->isLeaf()) {
|
|
||||||
// By definition the the exterior faces of a leaf voxel are
|
|
||||||
// always occluders.
|
|
||||||
voxel->setExteriorOcclusions(OctreeElement::HalfSpace::All);
|
|
||||||
// And the sibling occluders
|
|
||||||
voxel->setInteriorOcclusions(OctreeElement::HalfSpace::None);
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
voxel->setExteriorOcclusions(OctreeElement::HalfSpace::None);
|
|
||||||
voxel->setInteriorOcclusions(OctreeElement::HalfSpace::None);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count of exterior occluding faces of this voxel element indexed
|
|
||||||
// by half space partition
|
|
||||||
unsigned int exteriorOcclusionsCt[6] = { 0, 0, 0, 0, 0, 0 };
|
|
||||||
|
|
||||||
// Traverse all children
|
|
||||||
for (int i = NUMBER_OF_CHILDREN; --i >= 0; ) {
|
|
||||||
|
|
||||||
VoxelTreeElement* child = voxel->getChildAtIndex(i);
|
|
||||||
if (child) {
|
|
||||||
|
|
||||||
// Get the child's occluding faces, for a leaf, that will be
|
|
||||||
// all six voxel faces, and for a non leaf, that will be
|
|
||||||
// all faces which are completely covered by four child octants.
|
|
||||||
unsigned char exteriorOcclusionsOfChild = child->getExteriorOcclusions();
|
|
||||||
exteriorOcclusionsOfChild &= _sOctantIndexToBitMask[i];
|
|
||||||
|
|
||||||
for (int j = 6; --j >= 0; ) {
|
|
||||||
|
|
||||||
// Determine if the halfspace partition indexed by 1 << j is
|
|
||||||
// present in the exterior occlusions of the child.
|
|
||||||
unsigned char partition = exteriorOcclusionsOfChild & (1 << j);
|
|
||||||
|
|
||||||
if (partition) {
|
|
||||||
exteriorOcclusionsCt[j]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// Derive the exterior occlusions of the voxel elements from the exclusions
|
|
||||||
// of its children
|
|
||||||
unsigned char exteriorOcclusions = OctreeElement::HalfSpace::None;
|
|
||||||
for (int i = 6; --i >= 0; ) {
|
|
||||||
if (exteriorOcclusionsCt[i] == _sNumOctantsPerHemiVoxel) {
|
|
||||||
|
|
||||||
// Exactly four octants qualify for full exterior occlusion
|
|
||||||
exteriorOcclusions |= (1 << i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inform the voxel element
|
|
||||||
voxel->setExteriorOcclusions(exteriorOcclusions);
|
|
||||||
|
|
||||||
if (exteriorOcclusions == OctreeElement::HalfSpace::All) {
|
|
||||||
//const glm::vec3& v = voxel->getCorner();
|
|
||||||
//float s = voxel->getScale();
|
|
||||||
|
|
||||||
//qDebug("Completely occupied voxel at %f %f %f size: %f", v.x, v.y, v.z, s);
|
|
||||||
|
|
||||||
// All of the exterior faces of this voxel element are
|
|
||||||
// occluders, which means that this element is completely
|
|
||||||
// occupied. Hence, the subtree from this node could be
|
|
||||||
// pruned and replaced by a leaf voxel, if the visible
|
|
||||||
// properties of the children are the same
|
|
||||||
|
|
||||||
} else if (exteriorOcclusions != OctreeElement::HalfSpace::None) {
|
|
||||||
//const glm::vec3& v = voxel->getCorner();
|
|
||||||
//float s = voxel->getScale();
|
|
||||||
|
|
||||||
//qDebug("Partially occupied voxel at %f %f %f size: %f with %02x", v.x, v.y, v.z, s, exteriorOcclusions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VoxelSystem::inspectForOcclusions() {
|
|
||||||
|
|
||||||
if (_inOcclusions) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_inOcclusions = true;
|
|
||||||
_nodeCount = 0;
|
|
||||||
|
|
||||||
bool showDebugDetails = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
|
||||||
PerformanceWarning warn(showDebugDetails, "inspectForOcclusions()");
|
|
||||||
|
|
||||||
_tree->lockForRead();
|
|
||||||
_tree->recurseTreeWithPostOperation(inspectForExteriorOcclusionsOperation);
|
|
||||||
_nodeCount = 0;
|
|
||||||
_tree->recurseTreeWithOperation(inspectForInteriorOcclusionsOperation);
|
|
||||||
_tree->unlock();
|
|
||||||
|
|
||||||
if (showDebugDetails) {
|
|
||||||
qDebug("inspecting all occlusions of %d nodes", _nodeCount);
|
|
||||||
}
|
|
||||||
_inOcclusions = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VoxelSystem::forceRedrawEntireTreeOperation(OctreeElement* element, void* extraData) {
|
bool VoxelSystem::forceRedrawEntireTreeOperation(OctreeElement* element, void* extraData) {
|
||||||
_nodeCount++;
|
_nodeCount++;
|
||||||
element->setDirtyBit();
|
element->setDirtyBit();
|
||||||
|
@ -1904,170 +1659,3 @@ void VoxelSystem::bindPerlinModulateProgram() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Swizzle value of bit pairs of the value of index
|
|
||||||
unsigned short VoxelSystem::_sSwizzledOcclusionBits[64] = {
|
|
||||||
0x0000, // 00000000
|
|
||||||
0x0002, // 00000001
|
|
||||||
0x0001, // 00000010
|
|
||||||
0x0003, // 00000011
|
|
||||||
0x0008, // 00000100
|
|
||||||
0x000a, // 00000101
|
|
||||||
0x0009, // 00000110
|
|
||||||
0x000b, // 00000111
|
|
||||||
0x0004, // 00001000
|
|
||||||
0x0006, // 00001001
|
|
||||||
0x0005, // 00001010
|
|
||||||
0x0007, // 00001011
|
|
||||||
0x000c, // 00001100
|
|
||||||
0x000e, // 00001101
|
|
||||||
0x000d, // 00001110
|
|
||||||
0x000f, // 00001111
|
|
||||||
0x0020, // 00010000
|
|
||||||
0x0022, // 00010001
|
|
||||||
0x0021, // 00010010
|
|
||||||
0x0023, // 00010011
|
|
||||||
0x0028, // 00010100
|
|
||||||
0x002a, // 00010101
|
|
||||||
0x0029, // 00010110
|
|
||||||
0x002b, // 00010111
|
|
||||||
0x0024, // 00011000
|
|
||||||
0x0026, // 00011001
|
|
||||||
0x0025, // 00011010
|
|
||||||
0x0027, // 00011011
|
|
||||||
0x002c, // 00011100
|
|
||||||
0x002e, // 00011101
|
|
||||||
0x002d, // 00011110
|
|
||||||
0x002f, // 00011111
|
|
||||||
0x0010, // 00100000
|
|
||||||
0x0012, // 00100001
|
|
||||||
0x0011, // 00100010
|
|
||||||
0x0013, // 00100011
|
|
||||||
0x0018, // 00100100
|
|
||||||
0x001a, // 00100101
|
|
||||||
0x0019, // 00100110
|
|
||||||
0x001b, // 00100111
|
|
||||||
0x0014, // 00101000
|
|
||||||
0x0016, // 00101001
|
|
||||||
0x0015, // 00101010
|
|
||||||
0x0017, // 00101011
|
|
||||||
0x001c, // 00101100
|
|
||||||
0x001e, // 00101101
|
|
||||||
0x001d, // 00101110
|
|
||||||
0x001f, // 00101111
|
|
||||||
0x0030, // 00110000
|
|
||||||
0x0032, // 00110001
|
|
||||||
0x0031, // 00110010
|
|
||||||
0x0033, // 00110011
|
|
||||||
0x0038, // 00110100
|
|
||||||
0x003a, // 00110101
|
|
||||||
0x0039, // 00110110
|
|
||||||
0x003b, // 00110111
|
|
||||||
0x0034, // 00111000
|
|
||||||
0x0036, // 00111001
|
|
||||||
0x0035, // 00111010
|
|
||||||
0x0037, // 00111011
|
|
||||||
0x003c, // 00111100
|
|
||||||
0x003e, // 00111101
|
|
||||||
0x003d, // 00111110
|
|
||||||
0x003f, // 00111111
|
|
||||||
};
|
|
||||||
|
|
||||||
// Octant bitmask array indexed by octant. The mask value indicates the octant's halfspace partitioning. The index
|
|
||||||
// value corresponds to the voxel's octal code derived in "pointToVoxel" in SharedUtil.cpp, which, BTW, does *not*
|
|
||||||
// correspond to the "ChildIndex" enum value in OctreeElement.h
|
|
||||||
unsigned char VoxelSystem::_sOctantIndexToBitMask[8] = {
|
|
||||||
OctreeElement::HalfSpace::Bottom | OctreeElement::HalfSpace::Left | OctreeElement::HalfSpace::Near,
|
|
||||||
OctreeElement::HalfSpace::Bottom | OctreeElement::HalfSpace::Left | OctreeElement::HalfSpace::Far,
|
|
||||||
OctreeElement::HalfSpace::Top | OctreeElement::HalfSpace::Left | OctreeElement::HalfSpace::Near,
|
|
||||||
OctreeElement::HalfSpace::Top | OctreeElement::HalfSpace::Left | OctreeElement::HalfSpace::Far,
|
|
||||||
OctreeElement::HalfSpace::Bottom | OctreeElement::HalfSpace::Right | OctreeElement::HalfSpace::Near,
|
|
||||||
OctreeElement::HalfSpace::Bottom | OctreeElement::HalfSpace::Right | OctreeElement::HalfSpace::Far,
|
|
||||||
OctreeElement::HalfSpace::Top | OctreeElement::HalfSpace::Right | OctreeElement::HalfSpace::Near,
|
|
||||||
OctreeElement::HalfSpace::Top | OctreeElement::HalfSpace::Right | OctreeElement::HalfSpace::Far,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Two dimensional array map indexed by octant row and column. The mask value
|
|
||||||
// indicates the two faces shared by the octants
|
|
||||||
unsigned char VoxelSystem::_sOctantIndexToSharedBitMask[8][8] = {
|
|
||||||
{ // Index 0: Bottom-Left-Near
|
|
||||||
0, // Bottom-Left-Near
|
|
||||||
OctreeElement::HalfSpace::Near | OctreeElement::HalfSpace::Far, // Bottom-Left-Far
|
|
||||||
OctreeElement::HalfSpace::Bottom | OctreeElement::HalfSpace::Top, // Top-Left-Near
|
|
||||||
0, // Top-Left-Far
|
|
||||||
OctreeElement::HalfSpace::Right | OctreeElement::HalfSpace::Left, // Bottom-Right-Near
|
|
||||||
0, // Bottom-Right-Far
|
|
||||||
0, // Top-Right-Near
|
|
||||||
0, // Top-Right-Far
|
|
||||||
},
|
|
||||||
{ // Index 1: Bottom-Left-Far
|
|
||||||
OctreeElement::HalfSpace::Near | OctreeElement::HalfSpace::Far, // Bottom-Left-Near
|
|
||||||
0, // Bottom-Left-Far
|
|
||||||
0, // Top-Left-Near
|
|
||||||
OctreeElement::HalfSpace::Bottom | OctreeElement::HalfSpace::Top, // Top-Left-Far
|
|
||||||
0, // Bottom-Right-Near
|
|
||||||
OctreeElement::HalfSpace::Right | OctreeElement::HalfSpace::Left, // Bottom-Right-Far
|
|
||||||
0, // Top-Right-Near
|
|
||||||
0, // Top-Right-Far
|
|
||||||
},
|
|
||||||
{ // Index 2: Top-Left-Near
|
|
||||||
OctreeElement::HalfSpace::Bottom | OctreeElement::HalfSpace::Top, // Bottom-Left-Near
|
|
||||||
0, // Bottom-Left-Far
|
|
||||||
0, // Top-Left-Near
|
|
||||||
OctreeElement::HalfSpace::Near | OctreeElement::HalfSpace::Far, // Top-Left-Far
|
|
||||||
0, // Bottom-Right-Near
|
|
||||||
0, // Bottom-Right-Far
|
|
||||||
OctreeElement::HalfSpace::Right | OctreeElement::HalfSpace::Left, // Top-Right-Near
|
|
||||||
0, // Top-Right-Far
|
|
||||||
},
|
|
||||||
{ // Index 3: Top-Left-Far
|
|
||||||
0, // Bottom-Left-Near
|
|
||||||
OctreeElement::HalfSpace::Bottom | OctreeElement::HalfSpace::Top, // Bottom-Left-Far
|
|
||||||
OctreeElement::HalfSpace::Near | OctreeElement::HalfSpace::Far, // Top-Left-Near
|
|
||||||
0, // Top-Left-Far
|
|
||||||
0, // Bottom-Right-Near
|
|
||||||
0, // Bottom-Right-Far
|
|
||||||
0, // Top-Right-Near
|
|
||||||
OctreeElement::HalfSpace::Right | OctreeElement::HalfSpace::Left, // Top-Right-Far
|
|
||||||
},
|
|
||||||
{ // Index 4: Bottom-Right-Near
|
|
||||||
OctreeElement::HalfSpace::Right | OctreeElement::HalfSpace::Left, // Bottom-Left-Near
|
|
||||||
0, // Bottom-Left-Far
|
|
||||||
0, // Top-Left-Near
|
|
||||||
0, // Top-Left-Far
|
|
||||||
0, // Bottom-Right-Near
|
|
||||||
OctreeElement::HalfSpace::Near | OctreeElement::HalfSpace::Far, // Bottom-Right-Far
|
|
||||||
OctreeElement::HalfSpace::Bottom | OctreeElement::HalfSpace::Top, // Top-Right-Near
|
|
||||||
0, // Top-Right-Far
|
|
||||||
},
|
|
||||||
{ // Index 5: Bottom-Right-Far
|
|
||||||
0, // Bottom-Left-Near
|
|
||||||
OctreeElement::HalfSpace::Right | OctreeElement::HalfSpace::Left, // Bottom-Left-Far
|
|
||||||
0, // Top-Left-Near
|
|
||||||
0, // Top-Left-Far
|
|
||||||
OctreeElement::HalfSpace::Near | OctreeElement::HalfSpace::Far, // Bottom-Right-Near
|
|
||||||
0, // Bottom-Right-Far
|
|
||||||
0, // Top-Right-Near
|
|
||||||
OctreeElement::HalfSpace::Bottom | OctreeElement::HalfSpace::Top, // Top-Right-Far
|
|
||||||
},
|
|
||||||
{ // Index 6: Top-Right-Near
|
|
||||||
0, // Bottom-Left-Near
|
|
||||||
0, // Bottom-Left-Far
|
|
||||||
OctreeElement::HalfSpace::Right | OctreeElement::HalfSpace::Left, // Top-Left-Near
|
|
||||||
0, // Top-Left-Far
|
|
||||||
OctreeElement::HalfSpace::Bottom | OctreeElement::HalfSpace::Top, // Bottom-Right-Near
|
|
||||||
0, // Bottom-Right-Far
|
|
||||||
0, // Top-Right-Near
|
|
||||||
OctreeElement::HalfSpace::Near | OctreeElement::HalfSpace::Far, // Top-Right-Far
|
|
||||||
},
|
|
||||||
{ // Index 7: Top-Right-Far
|
|
||||||
0, // Bottom-Left-Near
|
|
||||||
0, // Bottom-Left-Far
|
|
||||||
0, // Top-Left-Near
|
|
||||||
OctreeElement::HalfSpace::Right | OctreeElement::HalfSpace::Left, // Top-Left-Far
|
|
||||||
0, // Bottom-Right-Near
|
|
||||||
OctreeElement::HalfSpace::Bottom | OctreeElement::HalfSpace::Top, // Bottom-Right-Far
|
|
||||||
OctreeElement::HalfSpace::Near | OctreeElement::HalfSpace::Far, // Top-Right-Near
|
|
||||||
0, // Top-Right-Far
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
#include "Camera.h"
|
#include "Camera.h"
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
#include "world.h"
|
#include "world.h"
|
||||||
#include "PrimitiveRenderer.h"
|
|
||||||
|
|
||||||
class ProgramObject;
|
class ProgramObject;
|
||||||
|
|
||||||
|
@ -71,7 +70,6 @@ public:
|
||||||
void killLocalVoxels();
|
void killLocalVoxels();
|
||||||
|
|
||||||
virtual void hideOutOfView(bool forceFullFrustum = false);
|
virtual void hideOutOfView(bool forceFullFrustum = false);
|
||||||
void inspectForOcclusions();
|
|
||||||
bool hasViewChanged();
|
bool hasViewChanged();
|
||||||
bool isViewChanging();
|
bool isViewChanging();
|
||||||
|
|
||||||
|
@ -129,8 +127,6 @@ private:
|
||||||
static bool killSourceVoxelsOperation(OctreeElement* element, void* extraData);
|
static bool killSourceVoxelsOperation(OctreeElement* element, void* extraData);
|
||||||
static bool forceRedrawEntireTreeOperation(OctreeElement* element, void* extraData);
|
static bool forceRedrawEntireTreeOperation(OctreeElement* element, void* extraData);
|
||||||
static bool clearAllNodesBufferIndexOperation(OctreeElement* element, void* extraData);
|
static bool clearAllNodesBufferIndexOperation(OctreeElement* element, void* extraData);
|
||||||
static bool inspectForExteriorOcclusionsOperation(OctreeElement* element, void* extraData);
|
|
||||||
static bool inspectForInteriorOcclusionsOperation(OctreeElement* element, void* extraData);
|
|
||||||
static bool hideOutOfViewOperation(OctreeElement* element, void* extraData);
|
static bool hideOutOfViewOperation(OctreeElement* element, void* extraData);
|
||||||
static bool hideAllSubTreeOperation(OctreeElement* element, void* extraData);
|
static bool hideAllSubTreeOperation(OctreeElement* element, void* extraData);
|
||||||
static bool showAllSubTreeOperation(OctreeElement* element, void* extraData);
|
static bool showAllSubTreeOperation(OctreeElement* element, void* extraData);
|
||||||
|
@ -241,17 +237,6 @@ private:
|
||||||
float _lastKnownVoxelSizeScale;
|
float _lastKnownVoxelSizeScale;
|
||||||
int _lastKnownBoundaryLevelAdjust;
|
int _lastKnownBoundaryLevelAdjust;
|
||||||
|
|
||||||
bool _inOcclusions;
|
|
||||||
bool _showCulledSharedFaces; ///< Flag visibility of culled faces
|
|
||||||
bool _usePrimitiveRenderer; ///< Flag primitive renderer for use
|
|
||||||
PrimitiveRenderer* _renderer; ///< Voxel renderer
|
|
||||||
|
|
||||||
static const unsigned int _sNumOctantsPerHemiVoxel = 4;
|
|
||||||
static int _sCorrectedChildIndex[8];
|
|
||||||
static unsigned short _sSwizzledOcclusionBits[64]; ///< Swizzle value of bit pairs of the value of index
|
|
||||||
static unsigned char _sOctantIndexToBitMask[8]; ///< Map octant index to partition mask
|
|
||||||
static unsigned char _sOctantIndexToSharedBitMask[8][8]; ///< Map octant indices to shared partition mask
|
|
||||||
|
|
||||||
// haze
|
// haze
|
||||||
bool _drawHaze;
|
bool _drawHaze;
|
||||||
float _farHazeDistance;
|
float _farHazeDistance;
|
||||||
|
|
Loading…
Reference in a new issue