]> git.tdb.fi Git - libs/gl.git/blobdiff - source/instancearray.cpp
Initial support for instanced rendering
[libs/gl.git] / source / instancearray.cpp
diff --git a/source/instancearray.cpp b/source/instancearray.cpp
new file mode 100644 (file)
index 0000000..4156c6f
--- /dev/null
@@ -0,0 +1,124 @@
+#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