Merge pull request #5840 from jherico/bart

Instanced rendering for Box entities
This commit is contained in:
Brad Hefta-Gaub 2015-09-18 18:06:25 -07:00
commit ec87502f49
17 changed files with 1139 additions and 898 deletions

View file

@ -16,7 +16,7 @@ var PARTICLE_MAX_SIZE = 2.50;
var LIFETIME = 600;
var boxes = [];
var ids = Entities.findEntities({ x: 512, y: 512, z: 512 }, 50);
var ids = Entities.findEntities(MyAvatar.position, 50);
for (var i = 0; i < ids.length; i++) {
var id = ids[i];
var properties = Entities.getEntityProperties(id);
@ -33,10 +33,10 @@ for (var x = 0; x < SIDE_SIZE; x++) {
var gray = Math.random() * 155;
var cube = Math.random() > 0.5;
var color = { red: 100 + gray, green: 100 + gray, blue: 100 + gray };
var position = { x: 512 + x * 0.2, y: 512 + y * 0.2, z: 512 + z * 0.2};
var position = Vec3.sum(MyAvatar.position, { x: x * 0.2, y: y * 0.2, z: z * 0.2});
var radius = Math.random() * 0.1;
boxes.push(Entities.addEntity({
type: cube ? "Box" : "Sphere",
type: cube ? "Box" : "Box",
name: "PerfTest",
position: position,
dimensions: { x: radius, y: radius, z: radius },

View file

@ -39,9 +39,6 @@ void RenderableBoxEntityItem::render(RenderArgs* args) {
PerformanceTimer perfTimer("RenderableBoxEntityItem::render");
Q_ASSERT(getType() == EntityTypes::Box);
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
batch.setModelTransform(getTransformToCenter()); // we want to include the scale as well
glm::vec4 cubeColor(toGlm(getXColor()), getLocalRenderAlpha());
if (!_procedural) {
_procedural.reset(new Procedural(this->getUserData()));
@ -54,11 +51,15 @@ void RenderableBoxEntityItem::render(RenderArgs* args) {
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
}
gpu::Batch& batch = *args->_batch;
glm::vec4 cubeColor(toGlm(getXColor()), getLocalRenderAlpha());
if (_procedural->ready()) {
batch.setModelTransform(getTransformToCenter()); // we want to include the scale as well
_procedural->prepare(batch, this->getDimensions());
DependencyManager::get<GeometryCache>()->renderSolidCube(batch, 1.0f, _procedural->getColor(cubeColor));
} else {
DependencyManager::get<DeferredLightingEffect>()->renderSolidCube(batch, 1.0f, cubeColor);
DependencyManager::get<DeferredLightingEffect>()->renderSolidCubeInstance(batch, getTransformToCenter(), cubeColor);
}
RenderableDebugableEntityItem::render(this, args);

View file

@ -8,10 +8,10 @@
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <string.h>
#include "Batch.h"
#include <string.h>
#if defined(NSIGHT_FOUND)
#include "nvToolsExt.h"
@ -302,4 +302,28 @@ void Batch::enableSkybox(bool enable) {
bool Batch::isSkyboxEnabled() const {
return _enableSkybox;
}
}
void Batch::setupNamedCalls(const std::string& instanceName, NamedBatchData::Function function) {
NamedBatchData& instance = _namedData[instanceName];
++instance._count;
instance._function = function;
}
BufferPointer Batch::getNamedBuffer(const std::string& instanceName, uint8_t index) {
NamedBatchData& instance = _namedData[instanceName];
if (instance._buffers.size() <= index) {
instance._buffers.resize(index + 1);
}
if (!instance._buffers[index]) {
instance._buffers[index].reset(new Buffer());
}
return instance._buffers[index];
}
void Batch::preExecute() {
for (auto& mapItem : _namedData) {
mapItem.second.process(*this);
}
_namedData.clear();
}

View file

@ -12,6 +12,8 @@
#define hifi_gpu_Batch_h
#include <vector>
#include <mutex>
#include <functional>
#include "Framebuffer.h"
#include "Pipeline.h"
@ -38,16 +40,42 @@ enum ReservedSlot {
TRANSFORM_CAMERA_SLOT = 7,
};
// The named batch data provides a mechanism for accumulating data into buffers over the course
// of many independent calls. For instance, two objects in the scene might both want to render
// a simple box, but are otherwise unaware of each other. The common code that they call to render
// the box can create buffers to store the rendering parameters for each box and register a function
// that will be called with the accumulated buffer data when the batch commands are finally
// executed against the backend
class Batch {
public:
typedef Stream::Slot Slot;
struct NamedBatchData {
using BufferPointers = std::vector<BufferPointer>;
using Function = std::function<void(gpu::Batch&, NamedBatchData&)>;
std::once_flag _once;
BufferPointers _buffers;
size_t _count{ 0 };
Function _function;
void process(Batch& batch) {
_function(batch, *this);
}
};
using NamedBatchDataMap = std::map<std::string, NamedBatchData>;
Batch();
explicit Batch(const Batch& batch);
~Batch();
void clear();
void preExecute();
// Batches may need to override the context level stereo settings
// if they're performing framebuffer copy operations, like the
// deferred lighting resolution mechanism
@ -67,6 +95,12 @@ public:
void drawInstanced(uint32 nbInstances, Primitive primitiveType, uint32 nbVertices, uint32 startVertex = 0, uint32 startInstance = 0);
void drawIndexedInstanced(uint32 nbInstances, Primitive primitiveType, uint32 nbIndices, uint32 startIndex = 0, uint32 startInstance = 0);
void setupNamedCalls(const std::string& instanceName, NamedBatchData::Function function);
BufferPointer getNamedBuffer(const std::string& instanceName, uint8_t index = 0);
// Input Stage
// InputFormat
// InputBuffers
@ -291,6 +325,8 @@ public:
FramebufferCaches _framebuffers;
QueryCaches _queries;
NamedBatchDataMap _namedData;
bool _enableStereo{ true };
bool _enableSkybox{ false };

View file

@ -120,6 +120,18 @@ enum Dimension {
MAT4,
NUM_DIMENSIONS,
};
// Count (of scalars) in an Element for a given Dimension
static const int LOCATION_COUNT[NUM_DIMENSIONS] = {
1,
1,
1,
1,
1,
3,
4,
};
// Count (of scalars) in an Element for a given Dimension
static const int DIMENSION_COUNT[NUM_DIMENSIONS] = {
1,
@ -127,8 +139,8 @@ static const int DIMENSION_COUNT[NUM_DIMENSIONS] = {
3,
4,
4,
9,
16,
3,
4,
};
// Semantic of an Element
@ -184,6 +196,7 @@ public:
Dimension getDimension() const { return (Dimension)_dimension; }
uint8 getDimensionCount() const { return DIMENSION_COUNT[(Dimension)_dimension]; }
uint8 getLocationCount() const { return LOCATION_COUNT[(Dimension)_dimension]; }
Type getType() const { return (Type)_type; }
bool isNormalized() const { return (getType() >= NFLOAT); }

View file

@ -191,6 +191,9 @@ void GLBackend::renderPassDraw(Batch& batch) {
}
void GLBackend::render(Batch& batch) {
// Finalize the batch by moving all the instanced rendering into the command buffer
batch.preExecute();
_stereo._skybox = batch.isSkyboxEnabled();
// Allow the batch to override the rendering stereo settings
// for things like full framebuffer copy operations (deferred lighting passes)
@ -316,7 +319,19 @@ void GLBackend::do_drawInstanced(Batch& batch, uint32 paramOffset) {
}
void GLBackend::do_drawIndexedInstanced(Batch& batch, uint32 paramOffset) {
(void) CHECK_GL_ERROR();
updateInput();
updateTransform();
updatePipeline();
GLint numInstances = batch._params[paramOffset + 4]._uint;
GLenum mode = _primitiveToGLmode[(Primitive)batch._params[paramOffset + 3]._uint];
uint32 numIndices = batch._params[paramOffset + 2]._uint;
uint32 startIndex = batch._params[paramOffset + 1]._uint;
uint32 startInstance = batch._params[paramOffset + 0]._uint;
GLenum glType = _elementTypeToGLType[_input._indexBufferType];
glDrawElementsInstanced(mode, numIndices, glType, nullptr, numInstances);
(void)CHECK_GL_ERROR();
}
void GLBackend::do_resetStages(Batch& batch, uint32 paramOffset) {

View file

@ -160,7 +160,10 @@ void GLBackend::updateInput() {
if (_input._format) {
for (auto& it : _input._format->getAttributes()) {
const Stream::Attribute& attrib = (it).second;
newActivation.set(attrib._slot);
uint8_t locationCount = attrib._element.getLocationCount();
for (int i = 0; i < locationCount; ++i) {
newActivation.set(attrib._slot + i);
}
}
}
@ -211,14 +214,19 @@ void GLBackend::updateInput() {
const Stream::Attribute& attrib = attributes.at(channel._slots[i]);
GLuint slot = attrib._slot;
GLuint count = attrib._element.getDimensionCount();
uint8_t locationCount = attrib._element.getLocationCount();
GLenum type = _elementTypeToGLType[attrib._element.getType()];
GLuint stride = strides[bufferNum];
GLenum perLocationStride = strides[bufferNum];
GLuint stride = perLocationStride * locationCount;
GLuint pointer = attrib._offset + offsets[bufferNum];
GLboolean isNormalized = attrib._element.isNormalized();
glVertexAttribPointer(slot, count, type, isNormalized, stride,
reinterpret_cast<GLvoid*>(pointer));
for (int j = 0; j < locationCount; ++j) {
glVertexAttribPointer(slot + j, count, type, isNormalized, stride,
reinterpret_cast<GLvoid*>(pointer + perLocationStride * j));
glVertexAttribDivisor(slot + j, attrib._frequency);
}
// TODO: Support properly the IAttrib version
(void) CHECK_GL_ERROR();

View file

@ -75,6 +75,11 @@ void makeBindings(GLBackend::GLShader* shader) {
glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_WEIGHT, "inSkinClusterWeight");
}
loc = glGetAttribLocation(glprogram, "inInstanceTransform");
if (loc >= 0 && loc != gpu::Stream::INSTANCE_XFM) {
glBindAttribLocation(glprogram, gpu::Stream::INSTANCE_XFM, "inInstanceTransform");
}
// Link again to take into account the assigned attrib location
glLinkProgram(glprogram);

View file

@ -18,4 +18,5 @@ in vec4 inTangent;
in vec4 inSkinClusterIndex;
in vec4 inSkinClusterWeight;
in vec4 inTexCoord1;
in mat4 inInstanceTransform;
<@endif@>

View file

@ -139,6 +139,11 @@ public:
// \return the number of bytes copied
Size append(Size size, const Byte* data);
template <typename T>
Size append(const T& t) {
return append(sizeof(t), reinterpret_cast<const Byte*>(&t));
}
// Access the sysmem object.
const Sysmem& getSysmem() const { assert(_sysmem); return (*_sysmem); }
Sysmem& editSysmem() { assert(_sysmem); return (*_sysmem); }

View file

@ -35,11 +35,12 @@ public:
SKIN_CLUSTER_INDEX = 5,
SKIN_CLUSTER_WEIGHT = 6,
TEXCOORD1 = 7,
INSTANCE_XFM = 8,
INSTANCE_SCALE = 9,
INSTANCE_TRANSLATE = 10,
INSTANCE_SCALE = 8,
INSTANCE_TRANSLATE = 9,
INSTANCE_XFM = 10,
NUM_INPUT_SLOTS,
// Instance XFM is a mat4, and as such takes up 4 slots
NUM_INPUT_SLOTS = INSTANCE_XFM + 4,
};
typedef uint8 Slot;

View file

@ -53,6 +53,15 @@ TransformCamera getTransformCamera() {
}
<@endfunc@>
<@func transformInstancedModelToClipPos(cameraTransform, objectTransform, modelPos, clipPos)@>
<!// Equivalent to the following but hoppefully a tad more accurate
//return camera._projection * camera._view * object._model * pos; !>
{ // transformModelToClipPos
vec4 _eyepos = (inInstanceTransform * <$modelPos$>) + vec4(-<$modelPos$>.w * <$cameraTransform$>._viewInverse[3].xyz, 0.0);
<$clipPos$> = <$cameraTransform$>._projectionViewUntranslated * _eyepos;
}
<@endfunc@>
<@func $transformModelToEyeAndClipPos(cameraTransform, objectTransform, modelPos, eyePos, clipPos)@>
<!// Equivalent to the following but hoppefully a tad more accurate
//return camera._projection * camera._view * object._model * pos; !>
@ -65,12 +74,31 @@ TransformCamera getTransformCamera() {
}
<@endfunc@>
<@func $transformInstancedModelToEyeAndClipPos(cameraTransform, objectTransform, modelPos, eyePos, clipPos)@>
<!// Equivalent to the following but hoppefully a tad more accurate
//return camera._projection * camera._view * object._model * pos; !>
{ // transformModelToClipPos
vec4 _worldpos = (inInstanceTransform * <$modelPos$>);
<$eyePos$> = (<$cameraTransform$>._view * _worldpos);
vec4 _eyepos =(inInstanceTransform * <$modelPos$>) + vec4(-<$modelPos$>.w * <$cameraTransform$>._viewInverse[3].xyz, 0.0);
<$clipPos$> = <$cameraTransform$>._projectionViewUntranslated * _eyepos;
// <$eyePos$> = (<$cameraTransform$>._projectionInverse * <$clipPos$>);
}
<@endfunc@>
<@func transformModelToWorldPos(objectTransform, modelPos, worldPos)@>
{ // transformModelToWorldPos
<$worldPos$> = (<$objectTransform$>._model * <$modelPos$>);
}
<@endfunc@>
<@func transformInstancedModelToWorldPos(objectTransform, modelPos, worldPos)@>
{ // transformModelToWorldPos
<$worldPos$> = (inInstanceTransform * <$modelPos$>);
}
<@endfunc@>
<@func transformModelToEyeDir(cameraTransform, objectTransform, modelDir, eyeDir)@>
{ // transformModelToEyeDir
vec3 mr0 = vec3(<$objectTransform$>._modelInverse[0].x, <$objectTransform$>._modelInverse[1].x, <$objectTransform$>._modelInverse[2].x);
@ -85,6 +113,21 @@ TransformCamera getTransformCamera() {
}
<@endfunc@>
<@func transformInstancedModelToEyeDir(cameraTransform, objectTransform, modelDir, eyeDir)@>
{ // transformModelToEyeDir
mat4 modelInverse = inverse(inInstanceTransform);
vec3 mr0 = vec3(modelInverse[0].x, modelInverse[1].x, modelInverse[2].x);
vec3 mr1 = vec3(modelInverse[0].y, modelInverse[1].y, modelInverse[2].y);
vec3 mr2 = vec3(modelInverse[0].z, modelInverse[1].z, modelInverse[2].z);
vec3 mvc0 = vec3(dot(<$cameraTransform$>._viewInverse[0].xyz, mr0), dot(<$cameraTransform$>._viewInverse[0].xyz, mr1), dot(<$cameraTransform$>._viewInverse[0].xyz, mr2));
vec3 mvc1 = vec3(dot(<$cameraTransform$>._viewInverse[1].xyz, mr0), dot(<$cameraTransform$>._viewInverse[1].xyz, mr1), dot(<$cameraTransform$>._viewInverse[1].xyz, mr2));
vec3 mvc2 = vec3(dot(<$cameraTransform$>._viewInverse[2].xyz, mr0), dot(<$cameraTransform$>._viewInverse[2].xyz, mr1), dot(<$cameraTransform$>._viewInverse[2].xyz, mr2));
<$eyeDir$> = vec3(dot(mvc0, <$modelDir$>), dot(mvc1, <$modelDir$>), dot(mvc2, <$modelDir$>));
}
<@endfunc@>
<@func transformEyeToWorldDir(cameraTransform, eyeDir, worldDir)@>
{ // transformEyeToWorldDir
<$worldDir$> = vec3(<$cameraTransform$>._viewInverse * vec4(<$eyeDir$>.xyz, 0.0));

File diff suppressed because it is too large Load diff

View file

@ -37,15 +37,22 @@ public:
void init(AbstractViewStateInterface* viewState);
/// Sets up the state necessary to render static untextured geometry with the simple program.
void bindSimpleProgram(gpu::Batch& batch, bool textured = false, bool culled = true,
gpu::PipelinePointer bindSimpleProgram(gpu::Batch& batch, bool textured = false, bool culled = true,
bool emmisive = false, bool depthBias = false);
/// Sets up the state necessary to render static untextured geometry with the simple program.
void bindInstanceProgram(gpu::Batch& batch, bool textured = false, bool culled = true,
bool emmisive = false, bool depthBias = false);
//// Renders a solid sphere with the simple program.
void renderSolidSphere(gpu::Batch& batch, float radius, int slices, int stacks, const glm::vec4& color);
//// Renders a wireframe sphere with the simple program.
void renderWireSphere(gpu::Batch& batch, float radius, int slices, int stacks, const glm::vec4& color);
//// Renders a solid cube using instancing. Transform should include scaling.
void renderSolidCubeInstance(gpu::Batch& batch, const Transform& xfm, const glm::vec4& color);
//// Renders a solid cube with the simple program.
void renderSolidCube(gpu::Batch& batch, float size, const glm::vec4& color);

View file

@ -689,28 +689,31 @@ void GeometryCache::renderVertices(gpu::Batch& batch, gpu::Primitive primitiveTy
}
}
void GeometryCache::renderSolidCube(gpu::Batch& batch, float size, const glm::vec4& color) {
Vec2Pair colorKey(glm::vec2(color.x, color.y), glm::vec2(color.z, color.y));
const int FLOATS_PER_VERTEX = 3;
const int VERTICES_PER_FACE = 4;
const int NUMBER_OF_FACES = 6;
const int TRIANGLES_PER_FACE = 2;
const int VERTICES_PER_TRIANGLE = 3;
const int vertices = NUMBER_OF_FACES * VERTICES_PER_FACE;
const int indices = NUMBER_OF_FACES * TRIANGLES_PER_FACE * VERTICES_PER_TRIANGLE;
const int vertexPoints = vertices * FLOATS_PER_VERTEX;
const int VERTEX_STRIDE = sizeof(GLfloat) * FLOATS_PER_VERTEX * 2; // vertices and normals
const int NORMALS_OFFSET = sizeof(GLfloat) * FLOATS_PER_VERTEX;
static const int FLOATS_PER_VERTEX = 3;
static const int VERTICES_PER_TRIANGLE = 3;
static const int CUBE_NUMBER_OF_FACES = 6;
static const int CUBE_VERTICES_PER_FACE = 4;
static const int CUBE_TRIANGLES_PER_FACE = 2;
static const int CUBE_VERTICES = CUBE_NUMBER_OF_FACES * CUBE_VERTICES_PER_FACE;
static const int CUBE_VERTEX_POINTS = CUBE_VERTICES * FLOATS_PER_VERTEX;
static const int CUBE_INDICES = CUBE_NUMBER_OF_FACES * CUBE_TRIANGLES_PER_FACE * VERTICES_PER_TRIANGLE;
static const gpu::Element CUBE_POSITION_ELEMENT{ gpu::VEC3, gpu::FLOAT, gpu::XYZ };
static const gpu::Element CUBE_NORMAL_ELEMENT{ gpu::VEC3, gpu::FLOAT, gpu::XYZ };
static const gpu::Element CUBE_COLOR_ELEMENT{ gpu::VEC4, gpu::NUINT8, gpu::RGBA };
static const gpu::Element INSTANCE_XFM_ELEMENT{ gpu::MAT4, gpu::FLOAT, gpu::XYZW };
gpu::BufferPointer GeometryCache::getCubeVertices(float size) {
if (!_solidCubeVertices.contains(size)) {
auto verticesBuffer = std::make_shared<gpu::Buffer>();
_solidCubeVertices[size] = verticesBuffer;
GLfloat* vertexData = new GLfloat[vertexPoints * 2]; // vertices and normals
GLfloat* vertexData = new GLfloat[CUBE_VERTEX_POINTS * 2]; // vertices and normals
GLfloat* vertex = vertexData;
float halfSize = size / 2.0f;
static GLfloat cannonicalVertices[vertexPoints] =
static GLfloat cannonicalVertices[CUBE_VERTEX_POINTS] =
{ 1, 1, 1, -1, 1, 1, -1,-1, 1, 1,-1, 1, // v0,v1,v2,v3 (front)
1, 1, 1, 1,-1, 1, 1,-1,-1, 1, 1,-1, // v0,v3,v4,v5 (right)
1, 1, 1, 1, 1,-1, -1, 1,-1, -1, 1, 1, // v0,v5,v6,v1 (top)
@ -719,7 +722,7 @@ void GeometryCache::renderSolidCube(gpu::Batch& batch, float size, const glm::ve
1,-1,-1, -1,-1,-1, -1, 1,-1, 1, 1,-1 }; // v4,v7,v6,v5 (back)
// normal array
static GLfloat cannonicalNormals[vertexPoints] =
static GLfloat cannonicalNormals[CUBE_VERTEX_POINTS] =
{ 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, // v0,v1,v2,v3 (front)
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v0,v3,v4,v5 (right)
0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, // v0,v5,v6,v1 (top)
@ -731,7 +734,7 @@ void GeometryCache::renderSolidCube(gpu::Batch& batch, float size, const glm::ve
GLfloat* cannonicalVertex = &cannonicalVertices[0];
GLfloat* cannonicalNormal = &cannonicalNormals[0];
for (int i = 0; i < vertices; i++) {
for (int i = 0; i < CUBE_VERTICES; i++) {
// vertices
*(vertex++) = halfSize * *cannonicalVertex++;
*(vertex++) = halfSize * *cannonicalVertex++;
@ -742,90 +745,121 @@ void GeometryCache::renderSolidCube(gpu::Batch& batch, float size, const glm::ve
*(vertex++) = *cannonicalNormal++;
*(vertex++) = *cannonicalNormal++;
}
verticesBuffer->append(sizeof(GLfloat) * vertexPoints * 2, (gpu::Byte*) vertexData);
verticesBuffer->append(sizeof(GLfloat) * CUBE_VERTEX_POINTS * 2, (gpu::Byte*) vertexData);
}
return _solidCubeVertices[size];
}
gpu::BufferPointer GeometryCache::getSolidCubeIndices() {
if (!_solidCubeIndexBuffer) {
static GLubyte cannonicalIndices[indices] =
{ 0, 1, 2, 2, 3, 0, // front
static GLubyte cannonicalIndices[CUBE_INDICES] = { 0, 1, 2, 2, 3, 0, // front
4, 5, 6, 6, 7, 4, // right
8, 9,10, 10,11, 8, // top
12,13,14, 14,15,12, // left
16,17,18, 18,19,16, // bottom
20,21,22, 22,23,20 }; // back
auto indexBuffer = std::make_shared<gpu::Buffer>();
_solidCubeIndexBuffer = indexBuffer;
_solidCubeIndexBuffer->append(sizeof(cannonicalIndices), (gpu::Byte*) cannonicalIndices);
}
return _solidCubeIndexBuffer;
}
void GeometryCache::setupCubeVertices(gpu::Batch& batch, gpu::BufferPointer& verticesBuffer) {
static const int VERTEX_STRIDE = sizeof(GLfloat) * FLOATS_PER_VERTEX * 2; // vertices and normals
static const int NORMALS_OFFSET = sizeof(GLfloat) * FLOATS_PER_VERTEX;
gpu::BufferView verticesView(verticesBuffer, 0, verticesBuffer->getSize(), VERTEX_STRIDE, CUBE_POSITION_ELEMENT);
gpu::BufferView normalsView(verticesBuffer, NORMALS_OFFSET, verticesBuffer->getSize(), VERTEX_STRIDE, CUBE_NORMAL_ELEMENT);
batch.setInputBuffer(gpu::Stream::POSITION, verticesView);
batch.setInputBuffer(gpu::Stream::NORMAL, normalsView);
}
void GeometryCache::renderSolidCubeInstances(gpu::Batch& batch, size_t count, gpu::BufferPointer transformBuffer, gpu::BufferPointer colorBuffer) {
static gpu::Stream::FormatPointer streamFormat;
if (!streamFormat) {
streamFormat = std::make_shared<gpu::Stream::Format>(); // 1 for everyone
streamFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, CUBE_POSITION_ELEMENT, 0);
streamFormat->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, CUBE_NORMAL_ELEMENT);
streamFormat->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, CUBE_COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE);
streamFormat->setAttribute(gpu::Stream::INSTANCE_XFM, gpu::Stream::INSTANCE_XFM, INSTANCE_XFM_ELEMENT, 0, gpu::Stream::PER_INSTANCE);
}
batch.setInputFormat(streamFormat);
gpu::BufferView colorView(colorBuffer, CUBE_COLOR_ELEMENT);
batch.setInputBuffer(gpu::Stream::COLOR, colorView);
gpu::BufferView instanceXfmView(transformBuffer, 0, transformBuffer->getSize(), INSTANCE_XFM_ELEMENT);
batch.setInputBuffer(gpu::Stream::INSTANCE_XFM, instanceXfmView);
gpu::BufferPointer verticesBuffer = getCubeVertices(1.0);
setupCubeVertices(batch, verticesBuffer);
batch.setIndexBuffer(gpu::UINT8, getSolidCubeIndices(), 0);
batch.drawIndexedInstanced(count, gpu::TRIANGLES, CUBE_INDICES);
}
void GeometryCache::renderSolidCube(gpu::Batch& batch, float size, const glm::vec4& color) {
Vec2Pair colorKey(glm::vec2(color.x, color.y), glm::vec2(color.z, color.y));
if (!_solidCubeColors.contains(colorKey)) {
auto colorBuffer = std::make_shared<gpu::Buffer>();
_solidCubeColors[colorKey] = colorBuffer;
const int NUM_COLOR_SCALARS_PER_CUBE = 24;
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_CUBE] = { compactColor, compactColor, compactColor, compactColor,
compactColor, compactColor, compactColor, compactColor,
compactColor, compactColor, compactColor, compactColor,
compactColor, compactColor, compactColor, compactColor,
compactColor, compactColor, compactColor, compactColor,
compactColor, compactColor, compactColor, compactColor };
int colors[CUBE_VERTICES] = {
compactColor, compactColor, compactColor, compactColor,
compactColor, compactColor, compactColor, compactColor,
compactColor, compactColor, compactColor, compactColor,
compactColor, compactColor, compactColor, compactColor,
compactColor, compactColor, compactColor, compactColor,
compactColor, compactColor, compactColor, compactColor
};
colorBuffer->append(sizeof(colors), (gpu::Byte*) colors);
}
gpu::BufferPointer verticesBuffer = _solidCubeVertices[size];
gpu::BufferPointer colorBuffer = _solidCubeColors[colorKey];
const int VERTICES_SLOT = 0;
const int NORMALS_SLOT = 1;
const int COLOR_SLOT = 2;
static gpu::Stream::FormatPointer streamFormat;
static gpu::Element positionElement, normalElement, colorElement;
if (!streamFormat) {
streamFormat = std::make_shared<gpu::Stream::Format>(); // 1 for everyone
streamFormat->setAttribute(gpu::Stream::POSITION, VERTICES_SLOT, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
streamFormat->setAttribute(gpu::Stream::NORMAL, NORMALS_SLOT, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ));
streamFormat->setAttribute(gpu::Stream::COLOR, COLOR_SLOT, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA));
positionElement = streamFormat->getAttributes().at(gpu::Stream::POSITION)._element;
normalElement = streamFormat->getAttributes().at(gpu::Stream::NORMAL)._element;
colorElement = streamFormat->getAttributes().at(gpu::Stream::COLOR)._element;
streamFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, CUBE_POSITION_ELEMENT, 0);
streamFormat->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, CUBE_NORMAL_ELEMENT);
streamFormat->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, CUBE_COLOR_ELEMENT);
}
gpu::BufferView verticesView(verticesBuffer, 0, verticesBuffer->getSize(), VERTEX_STRIDE, positionElement);
gpu::BufferView normalsView(verticesBuffer, NORMALS_OFFSET, verticesBuffer->getSize(), VERTEX_STRIDE, normalElement);
gpu::BufferView colorView(colorBuffer, streamFormat->getAttributes().at(gpu::Stream::COLOR)._element);
batch.setInputFormat(streamFormat);
batch.setInputBuffer(VERTICES_SLOT, verticesView);
batch.setInputBuffer(NORMALS_SLOT, normalsView);
batch.setInputBuffer(COLOR_SLOT, colorView);
batch.setIndexBuffer(gpu::UINT8, _solidCubeIndexBuffer, 0);
batch.drawIndexed(gpu::TRIANGLES, indices);
gpu::BufferView colorView(colorBuffer, CUBE_COLOR_ELEMENT);
batch.setInputBuffer(gpu::Stream::COLOR, colorView);
gpu::BufferPointer verticesBuffer = getCubeVertices(size);
setupCubeVertices(batch, verticesBuffer);
batch.setIndexBuffer(gpu::UINT8, getSolidCubeIndices(), 0);
batch.drawIndexed(gpu::TRIANGLES, CUBE_INDICES);
}
void GeometryCache::renderWireCube(gpu::Batch& batch, float size, const glm::vec4& color) {
Vec2Pair colorKey(glm::vec2(color.x, color.y),glm::vec2(color.z, color.y));
const int FLOATS_PER_VERTEX = 3;
const int VERTICES_PER_EDGE = 2;
const int TOP_EDGES = 4;
const int BOTTOM_EDGES = 4;
const int SIDE_EDGES = 4;
const int vertices = 8;
const int indices = (TOP_EDGES + BOTTOM_EDGES + SIDE_EDGES) * VERTICES_PER_EDGE;
static const int WIRE_CUBE_VERTICES_PER_EDGE = 2;
static const int WIRE_CUBE_TOP_EDGES = 4;
static const int WIRE_CUBE_BOTTOM_EDGES = 4;
static const int WIRE_CUBE_SIDE_EDGES = 4;
static const int WIRE_CUBE_VERTICES = 8;
static const int WIRE_CUBE_INDICES = (WIRE_CUBE_TOP_EDGES + WIRE_CUBE_BOTTOM_EDGES + WIRE_CUBE_SIDE_EDGES) * WIRE_CUBE_VERTICES_PER_EDGE;
if (!_cubeVerticies.contains(size)) {
auto verticesBuffer = std::make_shared<gpu::Buffer>();
_cubeVerticies[size] = verticesBuffer;
int vertexPoints = vertices * FLOATS_PER_VERTEX;
GLfloat* vertexData = new GLfloat[vertexPoints]; // only vertices, no normals because we're a wire cube
static const int WIRE_CUBE_VERTEX_POINTS = WIRE_CUBE_VERTICES * FLOATS_PER_VERTEX;
GLfloat* vertexData = new GLfloat[WIRE_CUBE_VERTEX_POINTS]; // only vertices, no normals because we're a wire cube
GLfloat* vertex = vertexData;
float halfSize = size / 2.0f;
@ -834,15 +868,15 @@ void GeometryCache::renderWireCube(gpu::Batch& batch, float size, const glm::vec
1,-1, 1, 1,-1,-1, -1,-1,-1, -1,-1, 1 // v4, v5, v6, v7 (bottom)
};
for (int i = 0; i < vertexPoints; i++) {
for (int i = 0; i < WIRE_CUBE_VERTEX_POINTS; i++) {
vertex[i] = cannonicalVertices[i] * halfSize;
}
verticesBuffer->append(sizeof(GLfloat) * vertexPoints, (gpu::Byte*) vertexData); // I'm skeptical that this is right
verticesBuffer->append(sizeof(GLfloat) * WIRE_CUBE_VERTEX_POINTS, (gpu::Byte*) vertexData); // I'm skeptical that this is right
}
if (!_wireCubeIndexBuffer) {
static GLubyte cannonicalIndices[indices] = {
static GLubyte cannonicalIndices[WIRE_CUBE_INDICES] = {
0, 1, 1, 2, 2, 3, 3, 0, // (top)
4, 5, 5, 6, 6, 7, 7, 4, // (bottom)
0, 4, 1, 5, 2, 6, 3, 7, // (side edges)
@ -890,7 +924,7 @@ void GeometryCache::renderWireCube(gpu::Batch& batch, float size, const glm::vec
batch.setInputBuffer(VERTICES_SLOT, verticesView);
batch.setInputBuffer(COLOR_SLOT, colorView);
batch.setIndexBuffer(gpu::UINT8, _wireCubeIndexBuffer, 0);
batch.drawIndexed(gpu::LINES, indices);
batch.drawIndexed(gpu::LINES, WIRE_CUBE_INDICES);
}
void GeometryCache::renderBevelCornersRect(gpu::Batch& batch, int x, int y, int width, int height, int bevelDistance, const glm::vec4& color, int id) {

View file

@ -131,6 +131,11 @@ public:
virtual QSharedPointer<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
bool delayLoad, const void* extra);
gpu::BufferPointer getCubeVertices(float size);
void setupCubeVertices(gpu::Batch& batch, gpu::BufferPointer& verticesBuffer);
gpu::BufferPointer getSolidCubeIndices();
void renderSphere(gpu::Batch& batch, float radius, int slices, int stacks, const glm::vec3& color, bool solid = true, int id = UNKNOWN_ID)
{ renderSphere(batch, radius, slices, stacks, glm::vec4(color, 1.0f), solid, id); }
@ -139,6 +144,7 @@ public:
void renderGrid(gpu::Batch& batch, int xDivisions, int yDivisions, const glm::vec4& color);
void renderGrid(gpu::Batch& batch, int x, int y, int width, int height, int rows, int cols, const glm::vec4& color, int id = UNKNOWN_ID);
void renderSolidCubeInstances(gpu::Batch& batch, size_t count, gpu::BufferPointer transformBuffer, gpu::BufferPointer colorBuffer);
void renderSolidCube(gpu::Batch& batch, float size, const glm::vec4& color);
void renderWireCube(gpu::Batch& batch, float size, const glm::vec4& color);
void renderBevelCornersRect(gpu::Batch& batch, int x, int y, int width, int height, int bevelDistance, const glm::vec4& color, int id = UNKNOWN_ID);

View file

@ -18,6 +18,7 @@
<$declareStandardTransform()$>
uniform bool Instanced = false;
// the interpolated normal
out vec3 _normal;
@ -33,6 +34,11 @@ void main(void) {
// standard transform
TransformCamera cam = getTransformCamera();
TransformObject obj = getTransformObject();
<$transformModelToClipPos(cam, obj, inPosition, gl_Position)$>
<$transformModelToEyeDir(cam, obj, inNormal.xyz, _normal)$>
if (Instanced) {
<$transformInstancedModelToClipPos(cam, obj, inPosition, gl_Position)$>
<$transformInstancedModelToEyeDir(cam, obj, inNormal.xyz, _normal)$>
} else {
<$transformModelToClipPos(cam, obj, inPosition, gl_Position)$>
<$transformModelToEyeDir(cam, obj, inNormal.xyz, _normal)$>
}
}