+#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