1 /*
2 *******************************************************************************************
3 * Dgame (a D game framework) - Copyright (c) Randy Schütt
4 * 
5 * This software is provided 'as-is', without any express or implied warranty.
6 * In no event will the authors be held liable for any damages arising from
7 * the use of this software.
8 * 
9 * Permission is granted to anyone to use this software for any purpose,
10 * including commercial applications, and to alter it and redistribute it
11 * freely, subject to the following restrictions:
12 * 
13 * 1. The origin of this software must not be misrepresented; you must not claim
14 *    that you wrote the original software. If you use this software in a product,
15 *    an acknowledgment in the product documentation would be appreciated but is
16 *    not required.
17 * 
18 * 2. Altered source versions must be plainly marked as such, and must not be
19 *    misrepresented as being the original software.
20 * 
21 * 3. This notice may not be removed or altered from any source distribution.
22 *******************************************************************************************
23 */
24 module Dgame.System.FrameBufferObject;
25 
26 private {
27 	import derelict.opengl3.gl;
28 
29 	import Dgame.Internal.Log;
30 	import Dgame.Graphics.Drawable;
31 	import Dgame.Graphics.Texture;
32 }
33 
34 /**
35  * A FrameBufferObject is usefull if you want to blit Drawables on a Texture.
36  * You create an FBO, set an Image/Texture to it and draw what you want.
37  * After drawing it is on your Texture.
38  *
39  * Author: rschuett
40  */
41 class FrameBufferObject {
42 private:
43 	GLuint _fboId;
44 	GLuint _depthBuffer;
45 
46 	Texture _tex;
47 
48 public:
49 final:
50 	/**
51 	 * CTor with a Texture and the boolean flag if you want a depthBuffer
52 	 */
53 	this(Texture tex, bool depthBuffer = false) in {
54 		assert(tex !is null && tex.isValid());
55 	} body {
56 		glGenFramebuffers(1, &this._fboId);
57 		if (!this._fboId)
58 			Log.error("Failed to create the frame buffer object.");
59 
60 		glBindFramebuffer(GL_FRAMEBUFFER, this._fboId);
61 
62 		if (depthBuffer) {
63 			glGenRenderbuffers(1, &this._depthBuffer);
64 			if (!this._depthBuffer)
65 				Log.error("Failed to create the attached depth buffer.");
66 
67 			glBindRenderbuffer(GL_RENDERBUFFER, this._depthBuffer);
68 			glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, tex.width, tex.height);
69 			glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, this._depthBuffer);
70 		}
71 
72 		this.setTexture(tex);
73 	}
74 
75 	~this() {
76 		if (this._depthBuffer != 0)
77 			glDeleteRenderbuffers(1, &this._depthBuffer);
78 		glDeleteFramebuffers(1, &this._fboId);
79 	}
80 
81 	/**
82 	 * Set a new Texture to the FBO which is now the new render target.
83 	 */
84 	void setTexture(Texture tex) in {
85 		assert(tex !is null && tex.isValid());
86 	} body {
87 		this.bind();
88 		scope(exit) this.unbind();
89 
90 		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex.Id, 0);
91 
92 		if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
93 			glBindFramebuffer(GL_FRAMEBUFFER, 0);
94 			Log.error("Failed to link the target texture to the frame buffer.");
95 		}
96 
97 		this._tex = tex;
98 	}
99 
100 	/**
101 	 * Clears the content of the FBO.
102 	 */
103 	void clear() const {
104 		this.bind();
105 		scope(exit) this.unbind();
106 
107 		glClear(GL_COLOR_BUFFER_BIT);
108 	}
109 
110 	/**
111 	 * Draw a Drawable on the current render target
112 	 */
113 	void draw(Drawable draw) in {
114 		assert(draw !is null);
115 		assert(this._tex !is null && this._tex.isValid());
116 	} body {
117 		this.bind();
118 		scope(exit) this.unbind();
119 
120 		glPushAttrib(GL_VIEWPORT_BIT | GL_CURRENT_BIT | GL_ENABLE_BIT);
121 		glPushMatrix();
122 
123 		glDisable(GL_TEXTURE_2D);
124 
125 		scope(exit) {
126 			glPopAttrib();
127 			glPopMatrix();
128 		}
129 
130 		glMatrixMode(GL_MODELVIEW);
131 		glLoadIdentity();
132 
133 		glMatrixMode(GL_PROJECTION);
134 		glLoadIdentity();
135 
136 		glViewport(0, 0, this._tex.width, this._tex.height);
137 		glOrtho(0, this._tex.width, this._tex.height, 0, 1, -1);
138 
139 		draw.render();
140 	}
141 
142 	/**
143 	 * Bind the current FBO. This is mostly done automatically.
144 	 */
145 	void bind() const {
146 		glBindFramebuffer(GL_FRAMEBUFFER, this._fboId);
147 	}
148 
149 	/**
150 	* Unbind the current FBO. This is mostly done automatically.
151 	*/
152 	void unbind() const {
153 		glBindFramebuffer(GL_FRAMEBUFFER, 0);
154 	}
155 }