]> git.tdb.fi Git - libs/gl.git/blob - source/renderpass.cpp
Give materials the capability to automatically create a suitable shader
[libs/gl.git] / source / renderpass.cpp
1 #include <msp/datafile/collection.h>
2 #include <msp/strings/format.h>
3 #include "error.h"
4 #include "material.h"
5 #include "renderpass.h"
6 #include "program.h"
7 #include "programdata.h"
8 #include "renderer.h"
9 #include "texture.h"
10 #include "texture2d.h"
11 #include "texturing.h"
12
13 using namespace std;
14
15 namespace Msp {
16 namespace GL {
17
18 RenderPass::RenderPass():
19         shprog(0),
20         shprog_from_material(false),
21         shdata(0),
22         material(0),
23         texturing(0),
24         back_faces(false)
25 { }
26
27 RenderPass::RenderPass(const RenderPass &other):
28         shprog(other.shprog),
29         shprog_from_material(other.shprog_from_material),
30         shdata(other.shdata),
31         uniform_slots(other.uniform_slots),
32         material(other.material),
33         material_slot(other.material_slot),
34         texturing(other.texturing ? new Texturing(*other.texturing) : 0),
35         tex_names(other.tex_names),
36         back_faces(other.back_faces)
37 { }
38
39 RenderPass &RenderPass::operator=(const RenderPass &other)
40 {
41         shprog = other.shprog;
42         shprog_from_material = other.shprog_from_material;
43         shdata = other.shdata;
44         uniform_slots = other.uniform_slots;
45         material = other.material;
46         material_slot = other.material_slot;
47         texturing = other.texturing ? new Texturing(*other.texturing) : 0;
48         tex_names = other.tex_names;
49         back_faces = other.back_faces;
50         return *this;
51 }
52
53 RenderPass::~RenderPass()
54 {
55         delete texturing;
56 }
57
58 void RenderPass::finalize_material(DataFile::Collection *coll)
59 {
60         maybe_create_material_shader(coll);
61         ensure_private_shader_data();
62
63         if(!texturing)
64                 texturing = new Texturing;
65         material->attach_textures_to(*texturing, *shdata);
66 }
67
68 void RenderPass::maybe_create_material_shader(DataFile::Collection *coll)
69 {
70         if(shprog && !shprog_from_material)
71                 return;
72
73         if(coll)
74         {
75                 shprog = material->create_compatible_shader(*coll);
76                 shprog.keep();
77         }
78         else
79                 shprog = material->create_compatible_shader();
80
81         if(shdata)
82                 shdata = new ProgramData(*shdata, shprog.get());
83
84         shprog_from_material = true;
85 }
86
87 void RenderPass::ensure_private_shader_data()
88 {
89         if(!shprog)
90                 throw invalid_operation("RenderPass::ensure_private_shader_data");
91
92         if(!shdata)
93                 shdata = new ProgramData(shprog.get());
94         else if(shdata.refcount()>1)
95                 shdata = new ProgramData(*shdata);
96 }
97
98 void RenderPass::set_shader_program(const Program *prog, const ProgramData *data)
99 {
100         shprog = prog;
101         shprog.keep();
102         shprog_from_material = false;
103         shdata = (data ? new ProgramData(*data) : 0);
104         if(material)
105                 finalize_material(0);
106 }
107
108 const string &RenderPass::get_slotted_uniform_name(const string &slot) const
109 {
110         map<string, string>::const_iterator i = uniform_slots.find(slot);
111         if(i==uniform_slots.end())
112         {
113                 static string empty;
114                 return empty;
115         }
116         return i->second;
117 }
118
119 void RenderPass::set_material(const Material *mat)
120 {
121         material = mat;
122         material.keep();
123         finalize_material(0);
124 }
125
126 void RenderPass::set_texture(unsigned index, const Texture *tex)
127 {
128         if(!texturing)
129                 texturing = new Texturing;
130
131         texturing->attach(index, *tex);
132 }
133
134 int RenderPass::get_texture_index(const string &n) const
135 {
136         map<string, unsigned>::const_iterator i = tex_names.find(n);
137         if(i==tex_names.end())
138                 return -1;
139         return i->second;
140 }
141
142 void RenderPass::apply(Renderer &renderer) const
143 {
144         renderer.set_texturing(texturing);
145         renderer.set_material(material.get());
146         renderer.set_shader_program(shprog.get(), shdata.get());
147         renderer.set_reverse_winding(back_faces);
148 }
149
150
151 RenderPass::Loader::Loader(RenderPass &p):
152         DataFile::CollectionObjectLoader<RenderPass>(p, 0)
153 {
154         init();
155 }
156
157 RenderPass::Loader::Loader(RenderPass &p, Collection &c):
158         DataFile::CollectionObjectLoader<RenderPass>(p, &c)
159 {
160         init();
161 }
162
163 void RenderPass::Loader::init()
164 {
165         add("shader",   &Loader::shader);
166         add("material", &Loader::material_inline);
167         add("material", &Loader::material);
168         add("material_slot", &RenderPass::material_slot);
169         add("back_faces",&RenderPass::back_faces);
170         add("texunit",  &Loader::texunit);
171         add("texunit",  &Loader::texunit_auto);
172         add("texunit",  &Loader::texunit_named);
173         add("uniforms", &Loader::uniforms);
174         add("uniform_slot", &Loader::uniform_slot);
175 }
176
177 void RenderPass::Loader::material_inline()
178 {
179         Material::GenericLoader ldr(coll);
180         load_sub_with(ldr);
181         obj.material = ldr.get_material();
182         obj.finalize_material(coll);
183 }
184
185 void RenderPass::Loader::material(const string &name)
186 {
187         obj.material = &get_collection().get<Material>(name);
188         obj.material.keep();
189         obj.finalize_material(coll);
190 }
191
192 void RenderPass::Loader::shader(const string &n)
193 {
194         obj.shprog = &get_collection().get<Program>(n);
195         obj.shprog.keep();
196         obj.shprog_from_material = false;
197         if(obj.shdata)
198                 obj.shdata = new ProgramData(*obj.shdata, obj.shprog.get());
199         if(obj.material)
200                 obj.finalize_material(coll);
201 }
202
203 void RenderPass::Loader::texunit(unsigned i)
204 {
205         if(!obj.texturing)
206                 obj.texturing = new Texturing;
207         TextureLoader ldr(*obj.texturing, i, coll);
208         load_sub_with(ldr);
209 }
210
211 void RenderPass::Loader::texunit_auto(const string &n)
212 {
213         if(!obj.texturing)
214                 obj.texturing = new Texturing;
215         int i = obj.texturing->find_free_unit(n);
216         if(i<0)
217                 throw runtime_error("no free texunit");
218         texunit_named(i, n);
219 }
220
221 void RenderPass::Loader::texunit_named(unsigned i, const string &n)
222 {
223         texunit(i);
224         obj.tex_names[n] = i;
225         obj.ensure_private_shader_data();
226         obj.shdata->uniform(n, static_cast<int>(i));
227 }
228
229 void RenderPass::Loader::uniforms()
230 {
231         obj.ensure_private_shader_data();
232         load_sub(*obj.shdata);
233 }
234
235 void RenderPass::Loader::uniform_slot(const string &name)
236 {
237         uniform_slot2(name, name);
238 }
239
240 void RenderPass::Loader::uniform_slot2(const string &name, const string &slot)
241 {
242         obj.uniform_slots[slot] = name;
243 }
244
245
246 RenderPass::TextureLoader::TextureLoader(Texturing &t, unsigned i, Collection *c):
247         DataFile::CollectionObjectLoader<Texturing>(t, c),
248         index(i)
249 {
250         add("texture",   &TextureLoader::texture);
251         add("texture2d", &TextureLoader::texture2d);
252 }
253
254 void RenderPass::TextureLoader::finish()
255 {
256         if(tex)
257         {
258                 obj.attach(index, *tex);
259                 tex.release();
260         }
261 }
262
263 void RenderPass::TextureLoader::texture(const string &name)
264 {
265         tex = &get_collection().get<Texture>(name);
266         tex.keep();
267 }
268
269 void RenderPass::TextureLoader::texture2d()
270 {
271         tex = new Texture2D;
272         if(coll)
273                 load_sub(static_cast<Texture2D &>(*tex), get_collection());
274         else
275                 load_sub(static_cast<Texture2D &>(*tex));
276 }
277
278 } // namespace GL
279 } // namespace Msp