--- /dev/null
+extension ARB_draw_instanced
--- /dev/null
+extension ARB_instanced_arrays
layout(location=2) in vec3 normal;
layout(location=4) in vec3 tangent;
layout(location=5) in vec3 binormal;
+layout(location=12) in vec4 instance_transform[3];
#pragma MSP stage(fragment)
layout(location=0) out vec4 frag_color;
+#include <msp/gl/extensions/arb_draw_instanced.h>
#include <msp/gl/extensions/ext_draw_range_elements.h>
#include <msp/gl/extensions/msp_legacy_features.h>
#include <msp/gl/extensions/msp_primitive_restart.h>
glDrawElements(prim_type, size(), data_type, data_ptr);
}
+void Batch::draw_instanced(unsigned count) const
+{
+ static Require req(ARB_draw_instanced);
+
+ BindRestore _bind_ibuf(get_buffer(), ELEMENT_ARRAY_BUFFER);
+ const void *data_ptr = setup_draw();
+
+ glDrawElementsInstanced(prim_type, size(), data_type, data_ptr, count);
+}
+
const void *Batch::setup_draw() const
{
if(restart)
unsigned get_index(unsigned) const;
void draw() const;
+ void draw_instanced(unsigned) const;
private:
const void *setup_draw() const;
static void set_restart_index(unsigned);
--- /dev/null
+#include <msp/core/algorithm.h>
+#include <msp/core/maputils.h>
+#include <msp/gl/extensions/arb_draw_instanced.h>
+#include <msp/gl/extensions/arb_instanced_arrays.h>
+#include <msp/gl/extensions/arb_vertex_array_object.h>
+#include <msp/gl/extensions/arb_vertex_shader.h>
+#include "buffer.h"
+#include "camera.h"
+#include "instancearray.h"
+#include "mesh.h"
+#include "object.h"
+#include "objectinstance.h"
+#include "renderer.h"
+#include "technique.h"
+#include "vertexsetup.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+InstanceArray::InstanceArray(const Object &o):
+ object(o),
+ instance_data(0),
+ instance_buffer(0),
+ vtx_setup(0),
+ matrix_offset(0)
+{
+ if(ARB_vertex_array_object && ARB_instanced_arrays && ARB_draw_instanced)
+ {
+ instance_data = new VertexArray((ATTRIB4,12, ATTRIB4,13, ATTRIB4,14));
+ const VertexFormat &fmt = instance_data->get_format();
+ matrix_offset = fmt.offset(make_indexed_component(ATTRIB4, 12));
+
+ instance_buffer = new Buffer(ARRAY_BUFFER);
+ instance_data->use_buffer(instance_buffer);
+
+ vtx_setup = new VertexSetup;
+ vtx_setup->set_vertex_array(object.get_mesh()->get_vertices());
+ vtx_setup->set_index_buffer(*object.get_mesh()->get_index_buffer());
+ vtx_setup->set_instance_array(instance_data);
+ }
+ else
+ static Require req(ARB_vertex_shader);
+}
+
+InstanceArray::~InstanceArray()
+{
+ delete vtx_setup;
+ delete instance_data;
+ delete instance_buffer;
+}
+
+void InstanceArray::append(ObjectInstance *inst)
+{
+ instances.push_back(inst);
+ if(instance_data)
+ {
+ if(instance_data->size()<instances.size())
+ instance_data->append();
+ update_instance_matrix(instances.size()-1);
+ }
+}
+
+void InstanceArray::remove(ObjectInstance &inst)
+{
+ vector<ObjectInstance *>::iterator i = find(instances, &inst);
+ if(i==instances.end())
+ throw key_error(&inst);
+
+ delete *i;
+ *i = instances.back();
+ instances.pop_back();
+}
+
+void InstanceArray::update_instance_matrix(unsigned index)
+{
+ if(!instance_data)
+ return;
+
+ const Matrix &m = *instances[index]->get_matrix();
+
+ float *d = instance_data->modify(instances.size()-1);
+ for(unsigned i=0; i<12; ++i)
+ d[matrix_offset+i] = m(i/4, i%4);
+}
+
+void InstanceArray::render(Renderer &renderer, const Tag &tag) const
+{
+ if(instances.empty())
+ return;
+
+ if(instance_data)
+ {
+ const Technique *tech = object.get_technique();
+ if(!tech)
+ throw logic_error("no technique");
+ if(!tech->has_pass(tag))
+ return;
+ const RenderPass &pass = tech->get_pass(tag);
+
+ const Mesh *mesh = object.get_mesh();
+ mesh->get_vertices().refresh();
+ instance_data->refresh();
+
+ Renderer::Push push(renderer);
+ pass.apply(renderer);
+ mesh->draw_instanced(renderer, *vtx_setup, instances.size());
+ }
+ else
+ {
+ for(vector<ObjectInstance *>::const_iterator i=instances.begin(); i!=instances.end(); ++i)
+ {
+ const Matrix &m = *(*i)->get_matrix();
+ glVertexAttrib4f(12, m(0, 0), m(0, 1), m(0, 2), m(0, 3));
+ glVertexAttrib4f(13, m(1, 0), m(1, 1), m(1, 2), m(1, 3));
+ glVertexAttrib4f(14, m(2, 0), m(2, 1), m(2, 2), m(2, 3));
+ (*i)->render(renderer, tag);
+ }
+ }
+}
+
+} // namespace GL
+} // namespace Msp
--- /dev/null
+#ifndef MSP_GL_INSTANCEARRAY_H_
+#define MSP_GL_INSTANCEARRAY_H_
+
+#include <vector>
+#include "programdata.h"
+#include "renderable.h"
+
+namespace Msp {
+namespace GL {
+
+class Buffer;
+class Object;
+class ObjectInstance;
+class VertexArray;
+class VertexSetup;
+
+/**
+Renders multiple instances of an Object in an efficient manner. If instanced
+rendering is supported, only one draw call per Batch needs to be issued.
+
+Changing the Mesh of the Object while an InstanceArray exists is not supported.
+*/
+class InstanceArray: public Renderable
+{
+public:
+ template<typename T>
+ class Instance: public T
+ {
+ private:
+ InstanceArray &array;
+ unsigned index;
+
+ public:
+ Instance(const Object &o, InstanceArray &a, unsigned i): T(o), array(a), index(i) { }
+
+ virtual void set_matrix(const Matrix &);
+ };
+
+private:
+ const Object &object;
+ std::vector<ObjectInstance *> instances;
+ VertexArray *instance_data;
+ Buffer *instance_buffer;
+ VertexSetup *vtx_setup;
+ unsigned matrix_offset;
+
+public:
+ InstanceArray(const Object &);
+ ~InstanceArray();
+
+ void set_matrix_attribute(const std::string &);
+
+ template<typename T = ObjectInstance>
+ T &append();
+private:
+ void append(ObjectInstance *);
+ void update_instance_matrix(unsigned);
+public:
+ void remove(ObjectInstance &);
+
+ virtual void render(Renderer &, const Tag &) const;
+};
+
+template<typename T>
+T &InstanceArray::append()
+{
+ Instance<T> *inst = new Instance<T>(object, *this, instances.size());
+ append(inst);
+ return *inst;
+}
+
+template<typename T>
+void InstanceArray::Instance<T>::set_matrix(const Matrix &m)
+{
+ T::set_matrix(m);
+ array.update_instance_matrix(index);
+}
+
+} // namespace GL
+} // namespace Msp
+
+#endif
renderer.draw(*i);
}
+void Mesh::draw_instanced(Renderer &renderer, const VertexSetup &vs, unsigned count) const
+{
+ if(vs.get_vertex_array()!=&vertices)
+ throw invalid_argument("Mesh::draw_instanced");
+
+ if(manager)
+ {
+ manager->resource_used(*this);
+ if(disallow_rendering)
+ return;
+ }
+
+ renderer.set_vertex_setup(&vs);
+ renderer.set_winding_test(winding);
+
+ for(vector<Batch>::const_iterator i=batches.begin(); i!=batches.end(); ++i)
+ renderer.draw_instanced(*i, count);
+}
+
void Mesh::bind() const
{
/* If VAOs are not supported, vtx_setup is zero and set_current won't get
public:
const VertexArray &get_vertices() const { return vertices; }
+ const VertexSetup *get_vertex_setup() const { return vtx_setup; }
const Buffer *get_index_buffer() const { return ibuf; }
unsigned get_n_vertices() const;
float *modify_vertex(unsigned);
void draw() const;
void draw(Renderer &) const;
+ void draw_instanced(Renderer &, const VertexSetup &, unsigned) const;
/** Binds the mesh for rendering. The vertex array is applied using generic
attributes only. Uses vertex array object if possible. */
batch.draw();
}
+void Renderer::draw_instanced(const Batch &batch, unsigned count)
+{
+ apply_state();
+
+ batch.draw_instanced(count);
+}
+
void Renderer::apply_state()
{
/* We (mostly) let the objects themselves figure out if the binding has
void render(const Renderable &, const Tag & = Tag());
void draw(const Batch &);
+ void draw_instanced(const Batch &, unsigned);
private:
void apply_state();
#include <msp/core/raii.h>
#include <msp/gl/extensions/arb_direct_state_access.h>
+#include <msp/gl/extensions/arb_instanced_arrays.h>
#include <msp/gl/extensions/arb_vertex_array_object.h>
#include <msp/gl/extensions/arb_vertex_attrib_binding.h>
#include <msp/gl/extensions/arb_vertex_buffer_object.h>
VertexSetup::VertexSetup():
dirty(0),
- array(0),
+ vertex_array(0),
+ inst_array(0),
index_buffer(0)
{
static Require req(ARB_vertex_array_object);
void VertexSetup::set_vertex_array(const VertexArray &a)
{
- array = &a;
+ vertex_array = &a;
update(VERTEX_ARRAY);
}
+void VertexSetup::set_instance_array(const VertexArray *a)
+{
+ if(a)
+ static Require req(ARB_instanced_arrays);
+
+ inst_array = a;
+ update(INSTANCE_ARRAY);
+}
+
void VertexSetup::set_index_buffer(const Buffer &ibuf)
{
index_buffer = &ibuf;
}
if(mask&VERTEX_ARRAY)
- update_vertex_array(direct);
+ update_vertex_array(*vertex_array, 0, 0, direct);
+
+ if(mask&INSTANCE_ARRAY)
+ {
+ if(inst_array)
+ update_vertex_array(*inst_array, 1, 1, direct);
+ }
if(mask&INDEX_BUFFER)
{
}
}
-void VertexSetup::update_vertex_array(bool direct) const
+void VertexSetup::update_vertex_array(const VertexArray &array, unsigned binding, unsigned divisor, bool direct) const
{
- Conditional<Bind> bind_vbuf(!direct, array->get_buffer(), ARRAY_BUFFER);
+ Conditional<Bind> bind_vbuf(!direct, array.get_buffer(), ARRAY_BUFFER);
- const VertexFormat &fmt = array->get_format();
+ const VertexFormat &fmt = array.get_format();
unsigned stride = get_stride(fmt)*sizeof(float);
if(direct)
- glVertexArrayVertexBuffer(id, 0, array->get_buffer()->get_id(), 0, stride);
+ {
+ glVertexArrayVertexBuffer(id, binding, array.get_buffer()->get_id(), 0, stride);
+ glVertexArrayBindingDivisor(id, binding, divisor);
+ }
unsigned offset = 0;
for(const unsigned char *c=fmt.begin(); c!=fmt.end(); ++c)
glVertexArrayAttribFormat(id, t, 4, GL_UNSIGNED_BYTE, true, offset);
else
glVertexArrayAttribFormat(id, t, sz, GL_FLOAT, false, offset);
- glVertexArrayAttribBinding(id, t, 0);
+ glVertexArrayAttribBinding(id, t, binding);
glEnableVertexArrayAttrib(id, t);
}
else
glVertexAttribPointer(t, 4, GL_UNSIGNED_BYTE, true, stride, reinterpret_cast<unsigned char *>(offset));
else
glVertexAttribPointer(t, sz, GL_FLOAT, false, stride, reinterpret_cast<float *>(offset));
+ glVertexAttribDivisor(t, divisor);
glEnableVertexAttribArray(t);
}
offset += sz*sizeof(float);
enum ComponentMask
{
VERTEX_ARRAY = 1,
- INDEX_BUFFER = 2
+ INSTANCE_ARRAY = 2,
+ INDEX_BUFFER = 4
};
unsigned id;
mutable unsigned dirty;
- const VertexArray *array;
+ const VertexArray *vertex_array;
+ const VertexArray *inst_array;
const Buffer *index_buffer;
public:
~VertexSetup();
void set_vertex_array(const VertexArray &);
- void set_instance_array(const VertexArray &);
+ void set_instance_array(const VertexArray *);
void set_index_buffer(const Buffer &);
+ const VertexArray *get_vertex_array() const { return vertex_array; }
+ const VertexArray *get_instance_array() const { return inst_array; }
const Buffer *get_index_buffer() const { return index_buffer; }
private:
void update(unsigned) const;
- void update_vertex_array(bool) const;
+ void update_vertex_array(const VertexArray &, unsigned, unsigned, bool) const;
public:
void bind() const;