1 #include <msp/core/algorithm.h>
2 #include <msp/core/maputils.h>
3 #include <msp/gl/extensions/arb_draw_instanced.h>
4 #include <msp/gl/extensions/arb_instanced_arrays.h>
5 #include <msp/gl/extensions/arb_vertex_array_object.h>
6 #include <msp/gl/extensions/arb_vertex_shader.h>
9 #include "instancearray.h"
12 #include "objectinstance.h"
14 #include "technique.h"
15 #include "vertexsetup.h"
22 InstanceArray::InstanceArray(const Object &o):
30 const Technique *tech = object.get_technique();
31 for(const auto &kvp: tech->get_passes())
33 const Program *shprog = kvp.second.get_shader_program();
35 throw invalid_argument("InstanceArray::InstanceArray");
37 int loc = shprog->get_attribute_location("instance_transform");
39 matrix_location = loc;
40 else if(loc!=matrix_location)
41 throw invalid_argument("InstanceArray::InstanceArray");
44 if(ARB_vertex_array_object && ARB_instanced_arrays && ARB_draw_instanced)
46 instance_data = new VertexArray((RAW_ATTRIB4,matrix_location, RAW_ATTRIB4,matrix_location+1, RAW_ATTRIB4,matrix_location+2));
47 const VertexFormat &fmt = instance_data->get_format();
48 matrix_offset = fmt.offset((RAW_ATTRIB4,matrix_location));
50 instance_buffer = new Buffer;
51 instance_data->use_buffer(instance_buffer);
53 const Mesh *mesh = object.get_mesh();
55 vtx_setup = new VertexSetup;
56 vtx_setup->set_format_instanced(mesh->get_vertices().get_format(), fmt);
57 vtx_setup->set_vertex_array(mesh->get_vertices());
58 vtx_setup->set_index_buffer(*mesh->get_index_buffer(), mesh->get_batches().front().get_index_type());
59 vtx_setup->set_instance_array(*instance_data);
62 static Require req(ARB_vertex_shader);
65 InstanceArray::~InstanceArray()
67 for(ObjectInstance *i: instances)
71 delete instance_buffer;
74 void InstanceArray::append(ObjectInstance *inst)
76 instances.push_back(inst);
79 if(instance_data->size()<instances.size())
81 instance_data->append();
82 unsigned req_size = instance_data->get_required_buffer_size();
83 if(instance_buffer->get_size()>0 && instance_buffer->get_size()<req_size)
85 delete instance_buffer;
86 instance_buffer = new Buffer;
87 instance_data->use_buffer(instance_buffer);
90 update_instance_matrix(instances.size()-1);
94 void InstanceArray::remove(ObjectInstance &inst)
96 auto i = find(instances, &inst);
97 if(i==instances.end())
98 throw key_error(&inst);
101 *i = instances.back();
102 instances.pop_back();
105 void InstanceArray::update_instance_matrix(unsigned index)
110 const Matrix &m = *instances[index]->get_matrix();
112 float *d = reinterpret_cast<float *>(instance_data->modify(instances.size()-1)+matrix_offset);
113 for(unsigned i=0; i<12; ++i)
117 void InstanceArray::render(Renderer &renderer, Tag tag) const
119 if(instances.empty())
124 const Technique *tech = object.get_technique();
126 throw logic_error("no technique");
127 const RenderPass *pass = tech->find_pass(tag);
131 const Mesh *mesh = object.get_mesh();
132 mesh->get_vertices().refresh();
133 if(instance_buffer->get_size()==0)
134 instance_buffer->storage(instance_data->get_required_buffer_size());
135 instance_data->refresh();
137 Renderer::Push push(renderer);
138 pass->apply(renderer);
139 mesh->draw_instanced(renderer, *vtx_setup, instances.size());
143 for(ObjectInstance *i: instances)
145 const Matrix &m = *i->get_matrix();
146 for(unsigned j=0; j<3; ++j)
147 glVertexAttrib4f(matrix_location+j, m(j, 0), m(j, 1), m(j, 2), m(j, 3));
148 i->render(renderer, tag);