]> git.tdb.fi Git - libs/gl.git/blob - source/pipeline.cpp
8517805e23197aef724029280d0684cdadde2ec6
[libs/gl.git] / source / pipeline.cpp
1 /* $Id$
2
3 This file is part of libmspgl
4 Copyright © 2009-2011  Mikko Rasa, Mikkosoft Productions
5 Distributed under the LGPL
6 */
7
8 #include "blend.h"
9 #include "camera.h"
10 #include "effect.h"
11 #include "except.h"
12 #include "framebuffer.h"
13 #include "lighting.h"
14 #include "pipeline.h"
15 #include "postprocessor.h"
16 #include "renderbuffer.h"
17 #include "renderer.h"
18 #include "tests.h"
19 #include "texture2d.h"
20
21 using namespace std;
22
23 namespace Msp {
24 namespace GL {
25
26 Pipeline::Pipeline(unsigned w, unsigned h, bool d):
27         camera(0),
28         width(w),
29         height(h),
30         hdr(d),
31         samples(0),
32         fbo(0),
33         color_buf(0),
34         depth_buf(0),
35         fbo_ms(0),
36         color_buf_ms(0),
37         depth_buf_ms(0)
38 { }
39
40 Pipeline::~Pipeline()
41 {
42         delete fbo;
43         delete color_buf;
44         delete depth_buf;
45 }
46
47 void Pipeline::set_hdr(bool h)
48 {
49         hdr = h;
50         if(!postproc.empty())
51                 create_fbos();
52 }
53
54 void Pipeline::set_multisample(unsigned s)
55 {
56         samples = s;
57         if(!postproc.empty())
58                 create_fbos();
59 }
60
61 void Pipeline::set_camera(const Camera *c)
62 {
63         camera = c;
64 }
65
66 PipelinePass &Pipeline::add_pass(const Tag &tag)
67 {
68         if(passes.count(tag))
69                 throw KeyError("Pass already exists");
70
71         PipelinePass &pass = passes[tag];
72         pass_order.push_back(tag);
73         return pass;
74 }
75
76 PipelinePass &Pipeline::get_pass(const Tag &tag)
77 {
78         PassMap::iterator i = passes.find(tag);
79         if(i==passes.end())
80                 throw KeyError("Unknown pass");
81         return i->second;
82 }
83
84 const PipelinePass &Pipeline::get_pass(const Tag &tag) const
85 {
86         PassMap::const_iterator i = passes.find(tag);
87         if(i==passes.end())
88                 throw KeyError("Unknown pass");
89         return i->second;
90 }
91
92 void Pipeline::add_renderable(const Renderable &r)
93 {
94         for(vector<Slot>::iterator i=renderables.begin(); i!=renderables.end(); ++i)
95                 if(i->renderable==&r)
96                 {
97                         i->passes.clear();
98                         return;
99                 }
100
101         renderables.push_back(&r);
102 }
103
104 void Pipeline::add_renderable_for_pass(const Renderable &r, const Tag &tag)
105 {
106         for(vector<Slot>::iterator i=renderables.begin(); i!=renderables.end(); ++i)
107                 if(i->renderable==&r)
108                 {
109                         i->passes.insert(tag);
110                         return;
111                 }
112
113         renderables.push_back(&r);
114         renderables.back().passes.insert(tag);
115 }
116
117 void Pipeline::remove_renderable(const Renderable &r)
118 {
119         for(vector<Slot>::iterator i=renderables.begin(); i!=renderables.end(); ++i)
120                 if(i->renderable==&r)
121                 {
122                         renderables.erase(i);
123                         return;
124                 }
125 }
126
127 void Pipeline::add_effect(Effect &e)
128 {
129         effects.push_back(&e);
130 }
131
132 void Pipeline::add_postprocessor(PostProcessor &pp)
133 {
134         postproc.push_back(&pp);
135         if(!fbo)
136                 create_fbos();
137         {
138         }
139 }
140
141 void Pipeline::render(Renderer &renderer, const Tag &tag) const
142 {
143         const PipelinePass &pass = get_pass(tag);
144
145         Bind bind_depth_test(pass.depth_test);
146         Bind bind_blend(pass.blend);
147         Bind bind_lighting(pass.lighting);
148
149         for(vector<Effect *>::const_iterator i=pass.effects.begin(); i!=pass.effects.end(); ++i)
150                 (*i)->prepare();
151
152         for(vector<Slot>::const_iterator i=renderables.begin(); i!=renderables.end(); ++i)
153                 if(i->passes.empty() || i->passes.count(tag))
154                         i->renderable->render(renderer, tag);
155
156         for(vector<Effect *>::const_iterator i=pass.effects.end(); i!=pass.effects.begin();)
157                 (*--i)->cleanup();
158 }
159
160 void Pipeline::render_all() const
161 {
162         if(camera)
163                 camera->apply();
164
165         if(fbo)
166         {
167                 Framebuffer *f = (fbo_ms ? fbo_ms : fbo);
168                 f->bind();
169                 f->clear(COLOR_BUFFER_BIT|DEPTH_BUFFER_BIT);
170         }
171
172         for(vector<Effect *>::const_iterator i=effects.begin(); i!=effects.end(); ++i)
173                 (*i)->prepare();
174
175         Renderer renderer(camera);
176         for(vector<Tag>::const_iterator i=pass_order.begin(); i!=pass_order.end(); ++i)
177                 render(renderer, *i);
178
179         for(vector<Effect *>::const_iterator i=effects.end(); i!=effects.begin();)
180                 (*--i)->cleanup();
181
182         if(fbo)
183         {
184                 if(fbo_ms)
185                         fbo->blit_from(*fbo_ms, COLOR_BUFFER_BIT|DEPTH_BUFFER_BIT, false);
186                 Framebuffer::unbind();
187         }
188
189         // XXX Need two color buffer textures to handle multiple post-processors correctly
190         for(vector<PostProcessor *>::const_iterator i=postproc.begin(); i!=postproc.end(); ++i)
191                 (*i)->render(*color_buf, *depth_buf);
192 }
193
194 void Pipeline::create_fbos()
195 {
196         delete fbo;
197         delete color_buf;
198         delete depth_buf;
199
200         delete fbo_ms;
201         fbo_ms = 0;
202         delete color_buf_ms;
203         color_buf_ms = 0;
204         delete depth_buf_ms;
205         depth_buf_ms = 0;
206
207         fbo = new Framebuffer;
208
209         color_buf = new Texture2D;
210         color_buf->set_min_filter(NEAREST);
211         color_buf->set_mag_filter(NEAREST);
212         color_buf->set_wrap(CLAMP_TO_EDGE);
213         color_buf->storage((hdr ? RGB16F : RGB), width, height);
214         fbo->attach(COLOR_ATTACHMENT0, *color_buf, 0);
215
216         depth_buf = new Texture2D;
217         depth_buf->set_min_filter(NEAREST);
218         depth_buf->set_mag_filter(NEAREST);
219         depth_buf->set_wrap(CLAMP_TO_EDGE);
220         depth_buf->storage(DEPTH_COMPONENT, width, height);
221         fbo->attach(DEPTH_ATTACHMENT, *depth_buf, 0);
222
223         if(samples)
224         {
225                 fbo_ms = new Framebuffer;
226
227                 color_buf_ms = new Renderbuffer;
228                 color_buf_ms->storage_multisample(samples, (hdr ? RGB16F : RGB), width, height);
229                 fbo_ms->attach(COLOR_ATTACHMENT0, *color_buf_ms);
230
231                 depth_buf_ms = new Renderbuffer;
232                 depth_buf_ms->storage_multisample(samples, DEPTH_COMPONENT, width, height);
233                 fbo_ms->attach(DEPTH_ATTACHMENT, *depth_buf_ms);
234         }
235 }
236
237
238 Pipeline::Slot::Slot(const Renderable *r):
239         renderable(r)
240 { }
241
242 } // namespace GL
243 } // namespace Msp