1 /**
  2  * potree.js 
  3  * http://potree.org
  4  *
  5  * Copyright 2012, Markus Sch�tz
  6  * Licensed under the GPL Version 2 or later.
  7  * - http://potree.org/wp/?page_id=7
  8  * - http://www.gnu.org/licenses/gpl-3.0.html
  9  *
 10  */
 11 
 12 /**
 13  * You can call the constructor with either, width and height, or "system". When
 14  * invoking the constructor with "system", the framebuffer will refer to the
 15  * system/default framebuffer.
 16  * 
 17  * @param {int}
 18  *            width
 19  * @param {int}
 20  *            height
 21  * @class create and handle FramebufferObjects
 22  * @see <a href="http://learningwebgl.com/blog/?p=1786">WebGL Lesson 16</a>
 23  */
 24 function Framebuffer(width, height) {
 25 	if (arguments[0] === inheriting)
 26 		return;
 27 	if (arguments[0] == "system") {
 28 		this.framebuffer = null;
 29 		this.initOtherStuff();
 30 	} else {
 31 		this.initBufferStuff(width, height);
 32 		this.initOtherStuff();
 33 	}
 34 }
 35 
 36 /**
 37  * 
 38  * @returns the system framebuffer
 39  */
 40 Framebuffer.getSystemBuffer = function() {
 41 	if (Framebuffer.systemBuffer == null) {
 42 		Framebuffer.systemBuffer = new Framebuffer("system");
 43 	}
 44 
 45 	return Framebuffer.systemBuffer;
 46 };
 47 
 48 Framebuffer.getActiveBuffer = function() {
 49 	if (Framebuffer.activeBuffer == null) {
 50 		Framebuffer.activeBuffer = Framebuffer.getSystemBuffer();
 51 	}
 52 
 53 	return Framebuffer.activeBuffer;
 54 };
 55 
 56 Framebuffer.setActiveBuffer = function(buffer) {
 57 	Framebuffer.activeBuffer = buffer;
 58 };
 59 
 60 /**
 61  * change size of the framebuffer.
 62  * 
 63  * @param {int}
 64  *            width
 65  * @param {int}
 66  *            height
 67  */
 68 Framebuffer.prototype.setSize = function(width, height) {
 69 	this.initBufferStuff(width, height);
 70 };
 71 
 72 /**
 73  * Initialize stuff like a vbo for screen quads.
 74  */
 75 Framebuffer.prototype.initOtherStuff = function() {
 76 
 77 	this.vbo = gl.createBuffer();
 78 	this.texcoordvbo = gl.createBuffer();
 79 	gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo);
 80 	var vertices = new Float32Array([ 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1,
 81 			0, 0, 1, 0 ]);
 82 	gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
 83 
 84 	gl.bindBuffer(gl.ARRAY_BUFFER, this.texcoordvbo);
 85 	var vertices = new Float32Array([ 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1 ]);
 86 	gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
 87 
 88 };
 89 
 90 /**
 91  * change size of screenQuad.
 92  * 
 93  * @param {[x,y]}
 94  *            start x and y must be values between 0 and 1
 95  * @param {[x,y]}
 96  *            end x and y must be values between 0 and 1
 97  */
 98 Framebuffer.prototype.updateScreenQuad = function(start, end) {
 99 	gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo);
100 	var vertices = new Float32Array([ start[0], start[1], 0, end[0], start[1],
101 			0, end[0], end[1], 0, start[0], start[1], 0, end[0], end[1], 0,
102 			start[0], end[1], 0 ]);
103 	gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
104 };
105 
106 /**
107  * removes existing webgl color/render/frame-buffers and creates new ones
108  * 
109  * @param {int}
110  *            width
111  * @param {int}
112  *            height
113  */
114 Framebuffer.prototype.initBufferStuff = function(width, height) {
115 	if (this.width == width && this.height == height) {
116 		return;
117 	}
118 
119 	this.width = width;
120 	this.height = height;
121 
122 	// remove exiting buffers
123 	gl.bindFramebuffer(gl.FRAMEBUFFER, null);
124 	if (this.texture != null) {
125 		gl.deleteTexture(this.texture.glid);
126 		this.texture = null;
127 	}
128 	if (this.renderbuffer != null) {
129 		gl.deleteRenderbuffer(this.renderbuffer);
130 		this.renderbuffer = null;
131 	}
132 	if (this.framebuffer != null) {
133 		gl.deleteFramebuffer(this.framebuffer);
134 		this.framebuffer = null;
135 	}
136 
137 	// create new buffers
138 	this.framebuffer = gl.createFramebuffer();
139 	this.texture = new Texture();
140 	this.texture.glid = gl.createTexture();
141 	this.renderbuffer = gl.createRenderbuffer();
142 	
143 	// WEBGL_depth_texture not supported in firefox/ANGLE
144 //	this.depthTexture = new Texture();
145 //	this.depthTexture.glid = gl.createTexture();
146 
147 	// framebuffer
148 	gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
149 	this.framebuffer.width = width;
150 	this.framebuffer.height = height;
151 
152 	// colorbuffer
153 	this.initColorbuffer();
154 
155 	// depthbuffer
156 	gl.bindRenderbuffer(gl.RENDERBUFFER, this.renderbuffer);
157 	gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16,
158 			this.framebuffer.width, this.framebuffer.height);
159 	
160 	// WEBGL_depth_texture not supported in firefox/ANGLE
161 //	gl.bindTexture(gl.TEXTURE_2D, this.depthTexture.glid);
162 //	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
163 //	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
164 //	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
165 //	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
166 //	gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT, this.framebuffer.width, this.framebuffer.height, 0, gl.DEPTH_COMPONENT, gl.UNSIGNED_BYTE, null);
167 	
168 
169 	// assemble buffers
170 	gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
171 			gl.TEXTURE_2D, this.texture.glid, 0);
172 	gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT,
173 			gl.RENDERBUFFER, this.renderbuffer);
174 	
175 	// WEBGL_depth_texture not supported in firefox/ANGLE
176 //	gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, this.depthTexture.glid, 0);
177 
178 	this.checkBuffer();
179 
180 	gl.bindTexture(gl.TEXTURE_2D, null);
181 	gl.bindRenderbuffer(gl.RENDERBUFFER, null);
182 	gl.bindFramebuffer(gl.FRAMEBUFFER, null);
183 };
184 
185 /**
186  * initializes the colourbuffer that'll be used as COLOR_ATTACHMENT0
187  */
188 Framebuffer.prototype.initColorbuffer = function() {
189 	gl.bindTexture(gl.TEXTURE_2D, this.texture.glid);
190 	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
191 	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
192 	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
193 	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
194 	gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.framebuffer.width,
195 			this.framebuffer.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
196 };
197 
198 /**
199  * check if framebuffer was successfully created. Throws an exception if the
200  * buffer is invalid.
201  */
202 Framebuffer.prototype.checkBuffer = function checkBuffer() {
203 	gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
204 
205 	var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
206 	switch (status) {
207 	case gl.FRAMEBUFFER_COMPLETE:
208 		break;
209 	case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
210 		Logger
211 				.error("Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_ATTACHMENT");
212 		throw "";
213 		break;
214 	case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
215 		Logger
216 				.error("Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT");
217 		throw "";
218 		break;
219 	case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
220 		Logger
221 				.error("Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_DIMENSIONS");
222 		throw "";
223 		break;
224 	case gl.FRAMEBUFFER_UNSUPPORTED:
225 		Logger.error("Incomplete framebuffer: FRAMEBUFFER_UNSUPPORTED");
226 		throw "";
227 		break;
228 	default:
229 		Logger.error("Incomplete framebuffer: " + status);
230 		throw "";
231 	}
232 };
233 
234 Framebuffer.bindDefault = function() {
235 	Framebuffer.activeBuffer = Framebuffer.getSystemBuffer();
236 	gl.bindFramebuffer(gl.FRAMEBUFFER, null);
237 };
238 
239 /**
240  * binds this framebuffer which makes it the target for all drawCalls and
241  * framebuffer related calls.
242  */
243 Framebuffer.prototype.bind = function() {
244 	Framebuffer.activeBuffer = this;
245 	gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
246 };
247 
248 /**
249  * 
250  * @param {Texture}
251  *            texture
252  * @param {[x,y]}
253  *            start
254  * @param {[x,y]}
255  *            end
256  * 
257  * @example drawTexture(abc, [0,0], [1,1])
258  */
259 Framebuffer.prototype.drawTexture = function(texture, start, end) {
260 	this.bind();
261 
262 	var mat = ShaderManager.getShader("drawTexture");
263 	gl.useProgram(mat.program);
264 
265 	this.updateScreenQuad(start, end);
266 
267 	// uniforms
268 	if (this.framebuffer == null) {
269 		var canvas = document.getElementById("canvas");
270 		gl.uniform1f(mat.uWidth, canvas.width);
271 		gl.uniform1f(mat.uHeight, canvas.height);
272 	} else {
273 		gl.uniform1f(mat.uWidth, this.width);
274 		gl.uniform1f(mat.uHeight, this.height);
275 	}
276 	// texture
277 	gl.activeTexture(gl.TEXTURE0);
278 	gl.bindTexture(gl.TEXTURE_2D, texture.glid);
279 	gl.uniform1i(mat.uTexture, 0);
280 	// depth
281 	gl.activeTexture(gl.TEXTURE1);
282 	gl.bindTexture(gl.TEXTURE_2D, this.renderbuffer);
283 	gl.uniform1i(mat.uDepth, 0);
284 
285 	// vertex attributes
286 	gl.enableVertexAttribArray(mat.aVertexPosition);
287 	gl.enableVertexAttribArray(mat.aTexcoords);
288 
289 	gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo);
290 	gl.vertexAttribPointer(mat.aVertexPosition, 3, gl.FLOAT, false, 0, 0);
291 
292 	gl.bindBuffer(gl.ARRAY_BUFFER, this.texcoordvbo);
293 	gl.vertexAttribPointer(mat.aTexcoords, 2, gl.FLOAT, false, 0, 0);
294 
295 	gl.disable(gl.DEPTH_TEST);
296 	gl.drawArrays(gl.TRIANGLES, 0, 6);
297 
298 	gl.disableVertexAttribArray(mat.aVertexPosition);
299 	gl.disableVertexAttribArray(mat.aTexcoords);
300 };
301 
302 /**
303  * draws a quad over the whole framebuffer using the provided material.
304  * 
305  * @param {Material}
306  *            mat
307  */
308 Framebuffer.prototype.drawFullscreenQuad = function(mat) {
309 
310 	gl.useProgram(mat.program);
311 
312 	this.updateScreenQuad([ -1, -1 ], [ 1, 1 ]);
313 
314 	// vertex attributes
315 	gl.enableVertexAttribArray(mat.attributes.aVertexPosition);
316 	gl.enableVertexAttribArray(mat.attributes.aTexcoord);
317 
318 	gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo);
319 	gl.vertexAttribPointer(mat.attributes.aVertexPosition, 3, gl.FLOAT, false, 0, 0);
320 
321 	gl.bindBuffer(gl.ARRAY_BUFFER, this.texcoordvbo);
322 	gl.vertexAttribPointer(mat.attributes.aTexcoord, 2, gl.FLOAT, false, 0, 0);
323 
324 	gl.drawArrays(gl.TRIANGLES, 0, 6);
325 
326 	gl.disableVertexAttribArray(mat.attributes.aVertexPosition);
327 	gl.disableVertexAttribArray(mat.attributes.aTexcoord);
328 };
329