]> git.tdb.fi Git - libs/gl.git/blob - source/render/instancearray.cpp
a53a988105d410a3fd3d58be119876b43a58f040
[libs/gl.git] / source / render / instancearray.cpp
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>
7 #include "buffer.h"
8 #include "camera.h"
9 #include "instancearray.h"
10 #include "mesh.h"
11 #include "object.h"
12 #include "objectinstance.h"
13 #include "renderer.h"
14 #include "technique.h"
15 #include "vertexsetup.h"
16
17 using namespace std;
18
19 namespace Msp {
20 namespace GL {
21
22 InstanceArray::InstanceArray(const Object &o):
23         object(o),
24         instance_data(0),
25         instance_buffer(0),
26         vtx_setup(0),
27         matrix_location(-1),
28         matrix_offset(0)
29 {
30         const Technique *tech = object.get_technique();
31         for(const auto &kvp: tech->get_passes())
32         {
33                 const Program *shprog = kvp.second.get_shader_program();
34                 if(!shprog)
35                         throw invalid_argument("InstanceArray::InstanceArray");
36
37                 int loc = shprog->get_attribute_location("instance_transform");
38                 if(matrix_location<0)
39                         matrix_location = loc;
40                 else if(loc!=matrix_location)
41                         throw invalid_argument("InstanceArray::InstanceArray");
42         }
43
44         if(ARB_vertex_array_object && ARB_instanced_arrays && ARB_draw_instanced)
45         {
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));
49
50                 instance_buffer = new Buffer;
51                 instance_data->use_buffer(instance_buffer);
52
53                 const Mesh *mesh = object.get_mesh();
54
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);
60         }
61         else
62                 static Require req(ARB_vertex_shader);
63 }
64
65 InstanceArray::~InstanceArray()
66 {
67         for(ObjectInstance *i: instances)
68                 delete i;
69         delete vtx_setup;
70         delete instance_data;
71         delete instance_buffer;
72 }
73
74 void InstanceArray::append(ObjectInstance *inst)
75 {
76         instances.push_back(inst);
77         if(instance_data)
78         {
79                 if(instance_data->size()<instances.size())
80                 {
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)
84                         {
85                                 delete instance_buffer;
86                                 instance_buffer = new Buffer;
87                                 instance_data->use_buffer(instance_buffer);
88                         }
89                 }
90                 update_instance_matrix(instances.size()-1);
91         }
92 }
93
94 void InstanceArray::remove(ObjectInstance &inst)
95 {
96         auto i = find(instances, &inst);
97         if(i==instances.end())
98                 throw key_error(&inst);
99
100         delete *i;
101         *i = instances.back();
102         instances.pop_back();
103 }
104
105 void InstanceArray::update_instance_matrix(unsigned index)
106 {
107         if(!instance_data)
108                 return;
109
110         const Matrix &m = *instances[index]->get_matrix();
111
112         float *d = reinterpret_cast<float *>(instance_data->modify(instances.size()-1)+matrix_offset);
113         for(unsigned i=0; i<12; ++i)
114                 d[i] = m(i/4, i%4);
115 }
116
117 void InstanceArray::render(Renderer &renderer, Tag tag) const
118 {
119         if(instances.empty())
120                 return;
121
122         if(instance_data)
123         {
124                 const Technique *tech = object.get_technique();
125                 if(!tech)
126                         throw logic_error("no technique");
127                 const RenderPass *pass = tech->find_pass(tag);
128                 if(!pass)
129                         return;
130
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();
136
137                 Renderer::Push push(renderer);
138                 pass->apply(renderer);
139                 mesh->draw_instanced(renderer, *vtx_setup, instances.size());
140         }
141         else
142         {
143                 for(ObjectInstance *i: instances)
144                 {
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);
149                 }
150         }
151 }
152
153 } // namespace GL
154 } // namespace Msp