]> git.tdb.fi Git - libs/gl.git/blob - source/render/instancearray.cpp
Only allow VertexArray's format to be set once
[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         const Technique::PassMap &passes = tech->get_passes();
32         for(Technique::PassMap::const_iterator i=passes.begin(); i!=passes.end(); ++i)
33         {
34                 const Program *shprog = i->second.get_shader_program();
35                 if(!shprog)
36                         throw invalid_argument("InstanceArray::InstanceArray");
37
38                 int loc = shprog->get_attribute_location("instance_transform");
39                 if(matrix_location<0)
40                         matrix_location = loc;
41                 else if(loc!=matrix_location)
42                         throw invalid_argument("InstanceArray::InstanceArray");
43         }
44
45         if(ARB_vertex_array_object && ARB_instanced_arrays && ARB_draw_instanced)
46         {
47                 instance_data = new VertexArray((RAW_ATTRIB4,matrix_location, RAW_ATTRIB4,matrix_location+1, RAW_ATTRIB4,matrix_location+2));
48                 const VertexFormat &fmt = instance_data->get_format();
49                 matrix_offset = fmt.offset(make_indexed_attribute(RAW_ATTRIB4, matrix_location));
50
51                 instance_buffer = new Buffer(ARRAY_BUFFER);
52                 instance_data->use_buffer(instance_buffer);
53
54                 vtx_setup = new VertexSetup;
55                 vtx_setup->set_format_instanced(object.get_mesh()->get_vertices().get_format(), fmt);
56                 vtx_setup->set_vertex_array(object.get_mesh()->get_vertices());
57                 vtx_setup->set_index_buffer(*object.get_mesh()->get_index_buffer());
58                 vtx_setup->set_instance_array(*instance_data);
59         }
60         else
61                 static Require req(ARB_vertex_shader);
62 }
63
64 InstanceArray::~InstanceArray()
65 {
66         for(vector<ObjectInstance *>::iterator i=instances.begin(); i!=instances.end(); ++i)
67                 delete *i;
68         delete vtx_setup;
69         delete instance_data;
70         delete instance_buffer;
71 }
72
73 void InstanceArray::append(ObjectInstance *inst)
74 {
75         instances.push_back(inst);
76         if(instance_data)
77         {
78                 if(instance_data->size()<instances.size())
79                 {
80                         instance_data->append();
81                         unsigned req_size = instance_data->get_required_buffer_size();
82                         if(instance_buffer->get_size()>0 && instance_buffer->get_size()<req_size)
83                         {
84                                 delete instance_buffer;
85                                 instance_buffer = new Buffer(ARRAY_BUFFER);
86                                 instance_data->use_buffer(instance_buffer);
87                         }
88                 }
89                 update_instance_matrix(instances.size()-1);
90         }
91 }
92
93 void InstanceArray::remove(ObjectInstance &inst)
94 {
95         vector<ObjectInstance *>::iterator i = find(instances, &inst);
96         if(i==instances.end())
97                 throw key_error(&inst);
98
99         delete *i;
100         *i = instances.back();
101         instances.pop_back();
102 }
103
104 void InstanceArray::update_instance_matrix(unsigned index)
105 {
106         if(!instance_data)
107                 return;
108
109         const Matrix &m = *instances[index]->get_matrix();
110
111         float *d = instance_data->modify(instances.size()-1);
112         for(unsigned i=0; i<12; ++i)
113                 d[matrix_offset+i] = m(i/4, i%4);
114 }
115
116 void InstanceArray::render(Renderer &renderer, Tag tag) const
117 {
118         if(instances.empty())
119                 return;
120
121         if(instance_data)
122         {
123                 const Technique *tech = object.get_technique();
124                 if(!tech)
125                         throw logic_error("no technique");
126                 const RenderPass *pass = tech->find_pass(tag);
127                 if(!pass)
128                         return;
129
130                 const Mesh *mesh = object.get_mesh();
131                 mesh->get_vertices().refresh();
132                 if(instance_buffer->get_size()==0)
133                         instance_buffer->storage(instance_data->get_required_buffer_size());
134                 instance_data->refresh();
135
136                 Renderer::Push push(renderer);
137                 pass->apply(renderer);
138                 mesh->draw_instanced(renderer, *vtx_setup, instances.size());
139         }
140         else
141         {
142                 for(vector<ObjectInstance *>::const_iterator i=instances.begin(); i!=instances.end(); ++i)
143                 {
144                         const Matrix &m = *(*i)->get_matrix();
145                         for(unsigned j=0; j<3; ++j)
146                                 glVertexAttrib4f(matrix_location+j, m(j, 0), m(j, 1), m(j, 2), m(j, 3));
147                         (*i)->render(renderer, tag);
148                 }
149         }
150 }
151
152 } // namespace GL
153 } // namespace Msp