Merge remote-tracking branch 'glBitmap@DGL/master'
[LazOpenGLCore.git] / uglcFrameBufferObject.pas
1 unit uglcFrameBufferObject;
2
3 { Package:      OpenGLCore
4   Prefix:       glc - OpenGL Core
5   Beschreibung: diese Unit enthält eine Klassen-Kapselung der OpenGL FrameBufferObjekte
6   Beispiel:
7     var
8       fbo: TglcFrameBufferObject;
9       tex: TglcTextureBuffer;
10       buf: TglcRenderBuffer;
11
12     fbo := TglcFrameBufferObject.Create;
13     try
14       ffbo.SetSize(800, 600);
15
16       // creating texture buffer as color buffer
17       tex := TglcTextureBuffer.Create(TglcFormat.fmRGBA, TglcInternalFormat.ifRGBA16F);
18       fbo.AddBuffer(tex, TglcAttachment.atColor0, true);
19
20       // creating render buffer as depth buffer
21       buf := TglcRenderBuffer.Create(TglcInternalFormat.ifDepthComponent);
22       fbo.AddBuffer(buf, TglcAttachment.atDepth, true);
23
24       // render to frame buffer object
25       fbo.Bind;
26       // do normal rendering
27       fbo.Unbind;
28
29       // use texture buffer
30       tex.Bind;
31       // do normal rendering
32       tex.Unbind;
33     finally
34       FreeAndNil(fbo);
35     end; }
36
37 {$mode objfpc}{$H+}
38
39 interface
40
41 uses
42   Classes, SysUtils, fgl, {$IFNDEF OPENGL_ES}dglOpenGl{$ELSE}dglOpenGLES{$ENDIF}, uglcTypes;
43
44 type
45 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
46   TglcBufferType = (btRenderBuffer, btTextureBuffer);
47   TglcBuffer = class(TObject)
48   private
49     fBufferType: TglcBufferType;
50     fWidth: Integer;
51     fHeight: Integer;
52
53     procedure SetWidth(const aValue: Integer);
54     procedure SetHeight(const aValue: Integer);
55   public
56     property Width : Integer read fWidth  write SetWidth;
57     property Height: Integer read fHeight write SetHeight;
58     property BufferType: TglcBufferType read fBufferType;
59
60     procedure SetSize(const aWidth, aHeight: Integer); virtual;
61   end;
62
63 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
64   EglcRenderBuffer = class(Exception);
65   TglcRenderBuffer = class(TglcBuffer)
66   private
67     fID: gluInt;
68     fFormat: TglcInternalFormat;
69
70     procedure UpdateRenderBufferStorage;
71     procedure SetFormat(const aValue: TglcInternalFormat);
72   public
73     property ID:     gluInt             read fID;
74     property Format: TglcInternalFormat read fFormat write SetFormat;
75
76     procedure SetSize(const aWidth, aHeight: Integer); override;
77     procedure Bind;
78     procedure Unbind;
79
80     constructor Create(const aFormat: TglcInternalFormat);
81     destructor Destroy; override;
82   end;
83
84 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
85   EglcTextureBuffer = class(exception);
86   TglcTextureBuffer = class(TglcBuffer)
87   private
88     fID: GLuint;
89     fFormat: TglcFormat;
90     fInternalFormat: TglcInternalFormat;
91     fBorder: Boolean;
92
93     procedure UpdateTexImage;
94     procedure SetFormat(const aValue: TglcFormat);
95     procedure SetInternalFormat(const aValue: TglcInternalFormat);
96     procedure SetBorder(const aValue: Boolean);
97   public
98     property ID            : GLuint             read fID;
99     property Border        : Boolean            read fBorder         write SetBorder;
100     property Format        : TglcFormat         read fFormat         write SetFormat;
101     property InternalFormat: TglcInternalFormat read fInternalFormat write SetInternalFormat;
102
103     procedure SetSize(const aWidth, aHeight: Integer); override;
104     procedure Bind(const aEnableTextureUnit: Boolean = true);
105     procedure Unbind(const aDisableTextureUnit: Boolean = true);
106
107     constructor Create(const aFormat: TglcFormat; const aInternalFormat: TglcInternalFormat);
108     destructor Destroy; override;
109   end;
110
111 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
112   EglcFrameBufferObject = class(Exception);
113   TglcFrameBufferObject = class(TObject)
114   private type
115     TglcAttachmentContainer = class(TObject)
116       Buffer: TglcBuffer;
117       Attachment: TglcAttachment;
118       OwnsObject: Boolean;
119       constructor Create(const aBuffer: TglcBuffer; const aAttachment: TglcAttachment; const aOwnsObject: Boolean = true);
120       destructor Destroy; override;
121     end;
122     TglcAttachmentContainerList = specialize TFPGObjectList<TglcAttachmentContainer>;
123   private
124     fID: GLuint;
125     fOwnsObjects: Boolean;
126     fWidth: Integer;
127     fHeight: Integer;
128     fBuffers: TglcAttachmentContainerList;
129 {$IFDEF OPENGL_ES}
130     fOldViewport: array[0..3] of GLint;
131 {$ENDIF}
132
133     function GetBuffer(const aIndex: Integer): TglcBuffer;
134     procedure SetBuffer(const aIndex: Integer; const aValue: TglcBuffer);
135
136     function GetAttachment(const aIndex: Integer): TglcAttachment;
137     procedure SetAttachment(const aIndex: Integer; const aValue: TglcAttachment);
138
139     function GetBufferCount: Integer;
140
141     procedure Attach(const aIndex: Integer);
142     procedure Detach(const aIndex: Integer);
143
144     procedure SetWidth(const aValue: Integer);
145     procedure SetHeight(const aValue: Integer);
146     procedure CheckFrameBufferStatus;
147     procedure UpdateAndCheckFBO;
148   public
149     property ID         : GLuint  read fID;
150     property Count      : Integer read GetBufferCount;
151     property OwnsObjects: Boolean read fOwnsObjects;
152     property Width      : Integer read fWidth         write SetWidth;
153     property Height     : Integer read fHeight        write SetHeight;
154     property Attachments[const aIndex: Integer]: TglcAttachment read GetAttachment write SetAttachment;
155     property Buffers    [const aIndex: Integer]: TglcBuffer     read GetBuffer     write SetBuffer;
156
157     procedure AddBuffer(const aBuffer: TglcBuffer; const aAttachment: TglcAttachment; const aOwnsBuffer: Boolean = true);
158     procedure DelBuffer(const aIndex: Integer);
159     function RemBuffer(const aBuffer: TglcBuffer): Integer;
160     function IndexOfBuffer(const aBuffer: TglcBuffer): Integer;
161
162     procedure SetSize(const aWidth, aHeight: Integer);
163     function CheckAttachment(const aAttachment: TglcAttachment): Boolean;
164
165     procedure Bind(const aSetViewport: Boolean = true);
166     procedure Unbind(const aResetViewport: Boolean = true);
167
168     constructor Create(const aOwnBuffers: Boolean = true);
169     destructor Destroy; override;
170   end;
171
172 implementation
173
174 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
175 //TglcBuffer////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
176 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
177 procedure TglcBuffer.SetWidth(const aValue: Integer);
178 begin
179   SetSize(aValue, fHeight);
180 end;
181
182 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
183 procedure TglcBuffer.SetHeight(const aValue: Integer);
184 begin
185   SetSize(fWidth, aValue);
186 end;
187
188 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
189 procedure TglcBuffer.SetSize(const aWidth, aHeight: Integer);
190 begin
191   fWidth  := aWidth;
192   fHeight := aHeight;
193 end;
194
195 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
196 //TglcRenderBuffer//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
197 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
198 procedure TglcRenderBuffer.UpdateRenderBufferStorage;
199 begin
200   glGetError; //clear Erroros
201   Bind;
202   glRenderbufferStorage(GL_RENDERBUFFER, GLenum(fFormat), fWidth, fHeight);
203   Unbind;
204   glcCheckAndRaiseError;
205 end;
206
207 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
208 procedure TglcRenderBuffer.SetFormat(const aValue: TglcInternalFormat);
209 begin
210   fFormat := aValue;
211   UpdateRenderBufferStorage;
212 end;
213
214 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
215 procedure TglcRenderBuffer.SetSize(const aWidth, aHeight: Integer);
216 begin
217   if (aWidth <= 0) or (aHeight <= 0) then
218     raise EglcRenderBuffer.Create('invalid width or height');
219   if (aWidth <> fWidth) or (aHeight <> fHeight) then begin
220     inherited SetSize(aWidth, aHeight);
221     UpdateRenderBufferStorage;
222   end;
223 end;
224
225 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
226 procedure TglcRenderBuffer.Bind;
227 begin
228   glBindRenderbuffer(GL_RENDERBUFFER, fID);
229 end;
230
231 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
232 procedure TglcRenderBuffer.Unbind;
233 begin
234   glBindRenderbuffer(GL_RENDERBUFFER, 0);
235 end;
236
237 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
238 constructor TglcRenderBuffer.Create(const aFormat: TglcInternalFormat);
239 begin
240   inherited Create;
241   fBufferType := btRenderBuffer;
242   glGenRenderbuffers(1, @fID);
243   fFormat := aFormat;
244   SetSize(64, 64);
245 end;
246
247 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
248 destructor TglcRenderBuffer.Destroy;
249 begin
250   glDeleteRenderbuffers(1, @fID);
251   inherited Destroy;
252 end;
253
254 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
255 //TglcTextureBuffer/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
256 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
257 procedure TglcTextureBuffer.UpdateTexImage;
258 begin
259   glGetError;   //clear errors
260   Bind(false);
261   glTexImage2D(GL_TEXTURE_2D, 0, GLenum(fInternalFormat), fWidth, fHeight, GLint(Byte(fBorder) and Byte(1)), GLenum(fFormat), GL_UNSIGNED_BYTE, nil);
262   Unbind(false);
263   glcCheckAndRaiseError;
264 end;
265
266 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
267 procedure TglcTextureBuffer.SetFormat(const aValue: TglcFormat);
268 begin
269   if (fFormat <> aValue) then begin
270     fFormat := aValue;
271     UpdateTexImage;
272   end;
273 end;
274
275 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
276 procedure TglcTextureBuffer.SetInternalFormat(const aValue: TglcInternalFormat);
277 begin
278   if (fInternalFormat <> aValue) then begin
279     fInternalFormat := aValue;
280     UpdateTexImage;
281   end;
282 end;
283
284 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
285 procedure TglcTextureBuffer.SetBorder(const aValue: Boolean);
286 begin
287   if (fBorder <> aValue) then begin
288     fBorder := aValue;
289     UpdateTexImage;
290   end;
291 end;
292
293 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
294 procedure TglcTextureBuffer.SetSize(const aWidth, aHeight: Integer);
295 begin
296   if (aWidth <= 0) or (aHeight <= 0) then
297     raise EglcTextureBuffer.Create('invalid width or height');
298   if (aWidth <> fWidth) or (aHeight <> fHeight) then begin
299     inherited SetSize(aWidth, aHeight);
300     UpdateTexImage;
301   end;
302 end;
303
304 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
305 procedure TglcTextureBuffer.Bind(const aEnableTextureUnit: Boolean = true);
306 begin
307   if aEnableTextureUnit then
308     glEnable(GL_TEXTURE_2D);
309   glBindTexture(GL_TEXTURE_2D, fID);
310 end;
311
312 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
313 procedure TglcTextureBuffer.Unbind(const aDisableTextureUnit: Boolean = true);
314 begin
315   if aDisableTextureUnit then
316     glDisable(GL_TEXTURE_2D);
317   glBindTexture(GL_TEXTURE_2D, 0);
318 end;
319
320 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
321 constructor TglcTextureBuffer.Create(const aFormat: TglcFormat; const aInternalFormat: TglcInternalFormat);
322 begin
323   inherited Create;
324   fBufferType := btTextureBuffer;
325
326   glGenTextures(1, @fID);
327   Bind(false);
328   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, {$IFNDEF OPENGL_ES}GL_CLAMP{$ELSE}GL_CLAMP_TO_EDGE{$ENDIF});
329   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, {$IFNDEF OPENGL_ES}GL_CLAMP{$ELSE}GL_CLAMP_TO_EDGE{$ENDIF});
330   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
331   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
332   Unbind(false);
333
334   fFormat := aFormat;
335   fInternalFormat := aInternalFormat;
336   SetSize(64, 64);
337 end;
338
339 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
340 destructor TglcTextureBuffer.Destroy;
341 begin
342   glDeleteTextures(1, @fID);
343   inherited Destroy;
344 end;
345
346 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
347 //TglcAttachment////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
348 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
349 constructor TglcFrameBufferObject.TglcAttachmentContainer.Create(const aBuffer: TglcBuffer;
350   const aAttachment: TglcAttachment; const aOwnsObject: Boolean);
351 begin
352   inherited Create;
353   Buffer     := aBuffer;
354   Attachment := aAttachment;
355   OwnsObject := aOwnsObject;
356 end;
357
358 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
359 destructor TglcFrameBufferObject.TglcAttachmentContainer.Destroy;
360 begin
361   if OwnsObject then
362     Buffer.Free;
363   inherited Destroy;
364 end;
365
366 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
367 //TglcFrameBufferObject/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
368 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
369 function TglcFrameBufferObject.GetBuffer(const aIndex: Integer): TglcBuffer;
370 begin
371   if (aIndex >= 0) and (aIndex < fBuffers.Count) then
372     result := fBuffers[aIndex].Buffer
373   else
374     raise EglcFrameBufferObject.Create('Index out of Bounds: ' + IntToStr(aIndex));
375 end;
376
377 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
378 procedure TglcFrameBufferObject.SetBuffer(const aIndex: Integer; const aValue: TglcBuffer);
379 begin
380   if (aIndex < 0) or (aIndex >= fBuffers.Count) then
381     raise EglcFrameBufferObject.Create('Index out of Bounds: ' + IntToStr(aIndex));
382
383   if not Assigned(aValue) then
384     raise EglcFrameBufferObject.Create('invalid buffer');
385
386   Detach(aIndex);
387   fBuffers[aIndex].Buffer := aValue;
388   Attach(aIndex);
389   UpdateAndCheckFBO;
390 end;
391
392 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
393 function TglcFrameBufferObject.GetAttachment(const aIndex: Integer): TglcAttachment;
394 begin
395   if (aIndex >= 0) and (aIndex < fBuffers.Count) then
396     result := fBuffers[aIndex].Attachment
397   else
398     raise EglcFrameBufferObject.Create('Index out of Bounds: ' + IntToStr(aIndex));
399 end;
400
401 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
402 procedure TglcFrameBufferObject.SetAttachment(const aIndex: Integer; const aValue: TglcAttachment);
403 begin
404   if (aIndex < 0) or (aIndex >= fBuffers.Count) then
405     raise EglcFrameBufferObject.Create('Index out of Bounds: ' + IntToStr(aIndex));
406
407   if not CheckAttachment(aValue) then
408     raise EglcFrameBufferObject.Create('Attachment already assigned');
409
410   Detach(aIndex);
411   fBuffers[aIndex].Attachment := aValue;
412   Attach(aIndex);
413   UpdateAndCheckFBO;
414 end;
415
416 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
417 procedure TglcFrameBufferObject.Attach(const aIndex: Integer);
418 var
419   a: TglcAttachment;
420   b: TglcBuffer;
421 begin
422   a := Attachments[aIndex];
423   b := Buffers[aIndex];
424   Bind(false);
425   if (b.BufferType = btRenderBuffer) then
426     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GLenum(a), GL_RENDERBUFFER, (b as TglcRenderBuffer).ID)
427   else
428     glFramebufferTexture2D(GL_FRAMEBUFFER, GLenum(a), GL_TEXTURE_2D, (b as TglcTextureBuffer).ID, 0);
429   Unbind(false);
430 end;
431
432 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
433 procedure TglcFrameBufferObject.Detach(const aIndex: Integer);
434 var
435   a: TglcAttachment;
436   b: TglcBuffer;
437 begin
438   a := Attachments[aIndex];
439   b := Buffers[aIndex];
440   Bind(false);
441   if (b.BufferType = btRenderBuffer) then
442     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GLenum(a), GL_RENDERBUFFER, 0)
443   else
444     glFramebufferTexture2D(GL_FRAMEBUFFER, GLenum(a), GL_TEXTURE_2D, 0, 0);
445   Unbind(false);
446 end;
447
448 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
449 //legt die neue Breite fest
450 //@Value: Breite;
451 procedure TglcFrameBufferObject.SetWidth(const aValue: Integer);
452 begin
453   SetSize(aValue, fHeight);
454 end;
455
456 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
457 //legt die neue Höhe fest
458 //@Value: neue Höhe;
459 procedure TglcFrameBufferObject.SetHeight(const aValue: Integer);
460 begin
461   SetSize(fWidth, aValue);
462 end;
463
464 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
465 procedure TglcFrameBufferObject.CheckFrameBufferStatus;
466 begin
467   case glCheckFramebufferStatus(GL_FRAMEBUFFER) of
468     GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
469       raise EglcFrameBufferObject.Create('Incomplete attachment');
470     GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
471       raise EglcFrameBufferObject.Create('Missing attachment');
472 {$IFNDEF OPENGL_ES}
473     GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
474       raise EglcFrameBufferObject.Create('Incomplete dimensions');
475     GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
476       raise EglcFrameBufferObject.Create('Incomplete formats');
477     GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
478       raise EglcFrameBufferObject.Create('Incomplete draw buffer');
479     GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
480       raise EglcFrameBufferObject.Create('Incomplete read buffer');
481 {$ENDIF}
482     GL_FRAMEBUFFER_UNSUPPORTED:
483       raise EglcFrameBufferObject.Create('Framebufferobjects unsupported');
484   end;
485 end;
486
487 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
488 //prüft das FrameBufferObjekt auf Fehler
489 procedure TglcFrameBufferObject.UpdateAndCheckFBO;
490
491   function IsColorAttachment(const a: TglcAttachment): Boolean;
492   begin
493     result := (GLenum(a) >= GL_COLOR_ATTACHMENT0) and (GLenum(a) <= GL_COLOR_ATTACHMENT15);
494   end;
495
496 var
497   buff: array of GLenum;
498 {$IFNDEF OPENGL_ES}
499   b: GLboolean;
500 {$ENDIF}
501   i: Integer;
502 begin
503   if (fBuffers.Count = 0) then
504     exit;
505   Bind(false);
506
507   //find ColorBuffers
508   SetLength(buff, 0);
509   for i := 0 to fBuffers.Count-1 do
510     if IsColorAttachment(fBuffers[i].Attachment) then begin
511       SetLength(buff, Length(buff) + 1);
512       buff[High(buff)] := GLenum(fBuffers[i].Attachment);
513     end;
514
515   //set Read and Draw Buffer
516   if (Length(buff) = 0) then begin
517     glReadBuffer(GL_NONE);
518 {$IFNDEF OPENGL_ES}
519     glDrawBuffer(GL_NONE);
520 {$ELSE}
521     SetLength(buff, 1);
522     buff[0] := GL_NONE;
523     glDrawBuffers(1, @buff[0]);
524 {$ENDIF}
525   end else begin
526     glDrawBuffers(Length(buff), @buff[0]);
527 {$IFNDEF OPENGL_ES}
528     glGetBooleanv(GL_DOUBLEBUFFER, @b);
529     if b then
530       glReadBuffer(GL_BACK)
531     else
532       glReadBuffer(GL_FRONT);
533 {$ELSE}
534     glReadBuffer(GL_FRONT);
535 {$ENDIF}
536   end;
537   Unbind(false);
538 end;
539
540 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
541 function TglcFrameBufferObject.GetBufferCount: Integer;
542 begin
543   result := fBuffers.Count;
544 end;
545
546 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
547 procedure TglcFrameBufferObject.AddBuffer(const aBuffer: TglcBuffer;
548   const aAttachment: TglcAttachment; const aOwnsBuffer: Boolean);
549 begin
550   if not Assigned(aBuffer) then
551     raise EglcFrameBufferObject.Create('invalid buffer');
552   if not CheckAttachment(aAttachment) then
553     raise EglcFrameBufferObject.Create('attachment already assigned');
554
555   fBuffers.Add(TglcAttachmentContainer.Create(aBuffer, aAttachment, fOwnsObjects and aOwnsBuffer));
556   if OwnsObjects then
557     aBuffer.SetSize(fWidth, fHeight);
558   Attach(fBuffers.Count-1);
559
560   UpdateAndCheckFBO;
561 end;
562
563 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
564 procedure TglcFrameBufferObject.DelBuffer(const aIndex: Integer);
565 begin
566   if (aIndex >= 0) and (aIndex < fBuffers.Count) then begin
567     Detach(aIndex);
568     fBuffers.Delete(aIndex);
569     UpdateAndCheckFBO;
570   end else
571     raise EglcFrameBufferObject.Create('Index out of Bounds: ' + IntToStr(aIndex));
572 end;
573
574 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
575 function TglcFrameBufferObject.RemBuffer(const aBuffer: TglcBuffer): Integer;
576 begin
577   result := IndexOfBuffer(aBuffer);
578   if (result >= 0) then
579     DelBuffer(result);
580 end;
581
582 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
583 function TglcFrameBufferObject.IndexOfBuffer(const aBuffer: TglcBuffer): Integer;
584 var
585   i: Integer;
586 begin
587   for i := 0 to fBuffers.Count-1 do
588     if (fBuffers[i].Buffer = aBuffer) then begin
589       result := i;
590       exit;
591     end;
592   result := -1;
593 end;
594
595 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
596 //legt die Größe neu fest
597 //@Width: neue Breite;
598 //@Height: neue Höhe;
599 procedure TglcFrameBufferObject.SetSize(const aWidth, aHeight: Integer);
600 var
601   c: TglcAttachmentContainer;
602 begin
603   if (aWidth <= 0) or (aHeight <= 0) then
604     raise EglcFrameBufferObject.Create('invalid width or height');
605
606   fWidth  := aWidth;
607   fHeight := aHeight;
608   if OwnsObjects then
609     for c in fBuffers do
610       if c.OwnsObject then
611         c.Buffer.SetSize(fWidth, fHeight);
612 end;
613
614 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
615 function TglcFrameBufferObject.CheckAttachment(const aAttachment: TglcAttachment): Boolean;
616 var
617   i: Integer;
618 begin
619   result := false;
620   for i := 0 to fBuffers.Count-1 do
621     if (fBuffers[i].Attachment = aAttachment) then
622       exit;
623   result := true;
624 end;
625
626 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
627 //Bindet das FrameBufferObjekt
628 procedure TglcFrameBufferObject.Bind(const aSetViewport: Boolean = true);
629 begin
630   glBindFramebuffer(GL_FRAMEBUFFER, fID);
631   if aSetViewport then begin
632 {$IFNDEF OPENGL_ES}
633     glPushAttrib(GL_VIEWPORT_BIT);
634 {$ELSE}
635     glGetIntegerv(GL_VIEWPORT, @fOldViewport[0]);
636 {$ENDIF}
637     glViewPort(0, 0, fWidth, fHeight);
638   end;
639 end;
640
641 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
642 //Entbindet das FrameBufferObjekt
643 procedure TglcFrameBufferObject.Unbind(const aResetViewport: Boolean = true);
644 begin
645   if aResetViewport then
646 {$IFNDEF OPENGL_ES}
647     glPopAttrib;
648 {$ELSE}
649     glViewport(fOldViewport[0], fOldViewport[1], fOldViewport[2], fOldViewport[3]);
650 {$ENDIF}
651   glBindFramebuffer(GL_FRAMEBUFFER, 0);
652 end;
653
654 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
655 //erzeugt das Objekt
656 constructor TglcFrameBufferObject.Create(const aOwnBuffers: Boolean = true);
657 begin
658   inherited Create;
659
660   glGenFramebuffers(1, @fID);
661   fWidth       := 64;
662   fHeight      := 64;
663   fOwnsObjects := aOwnBuffers;
664   fBuffers     := TglcAttachmentContainerList.Create(true); //containers are always owned by this object!
665 end;
666
667 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
668 //gibt das Objekt frei
669 destructor TglcFrameBufferObject.Destroy;
670 begin
671   fBuffers.Free;
672   glDeleteFramebuffers(1, @fID);
673   inherited Destroy;
674 end;
675
676 end.
677