rev |
line source |
nuclear@0
|
1 /*
|
nuclear@0
|
2 glass - (web)gl-assistant.
|
nuclear@0
|
3 Copyright (C) 2011 John Tsiombikas <nuclear@member.fsf.org>
|
nuclear@0
|
4
|
nuclear@0
|
5 This program is free software: you can redistribute it and/or modify
|
nuclear@0
|
6 it under the terms of the GNU General Public License as published by
|
nuclear@0
|
7 the Free Software Foundation, either version 3 of the License, or
|
nuclear@0
|
8 (at your option) any later version.
|
nuclear@0
|
9
|
nuclear@0
|
10 This program is distributed in the hope that it will be useful,
|
nuclear@0
|
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
nuclear@0
|
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
nuclear@0
|
13 GNU General Public License for more details.
|
nuclear@0
|
14
|
nuclear@0
|
15 You should have received a copy of the GNU General Public License
|
nuclear@0
|
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
|
nuclear@0
|
17 */
|
nuclear@0
|
18
|
nuclear@4
|
19 /* public interface
|
nuclear@4
|
20
|
nuclear@4
|
21 bool glass_init(canvas-id, optional-console-textarea-id);
|
nuclear@4
|
22 void glass_start(void);
|
nuclear@4
|
23
|
nuclear@4
|
24 void glass_display_func(callback);
|
nuclear@4
|
25 void glass_reshape_func(callback);
|
nuclear@4
|
26 void glass_mouse_func(callback);
|
nuclear@4
|
27 void glass_motion_func(callback);
|
nuclear@4
|
28
|
nuclear@4
|
29 void glass_resize(x, y);
|
nuclear@4
|
30
|
nuclear@4
|
31 WebGLTexture load_texture(uri);
|
nuclear@4
|
32
|
nuclear@4
|
33 WebGLShader load_shader(uri, type);
|
nuclear@4
|
34 WebGLShader create_shader(source, type, optional-sdrname);
|
nuclear@4
|
35 WebGLProgram load_program(vertex-uri, fragment-uri);
|
nuclear@4
|
36 WebGLProgram create_program(vertex-shader, fragment-shader);
|
nuclear@4
|
37
|
nuclear@4
|
38 void set_uniform1i(program, uniform-name, value);
|
nuclear@4
|
39 void set_uniform1f(program, uniform-name, value);
|
nuclear@4
|
40 void set_uniform2f(program, uniform-name, x, y);
|
nuclear@4
|
41 void set_uniform3f(program, uniform-name, x, y, z);
|
nuclear@4
|
42 void set_uniform4f(program, uniform-name, x, y, z, w);
|
nuclear@4
|
43 void set_uniform_matrix4fv(program, uniform-name, matrix);
|
nuclear@4
|
44
|
nuclear@4
|
45 void request_redisplay(void);
|
nuclear@4
|
46
|
nuclear@4
|
47 void logmsg(string);
|
nuclear@4
|
48 */
|
nuclear@4
|
49
|
nuclear@0
|
50 var gl;
|
nuclear@0
|
51 var canvas, console;
|
nuclear@5
|
52 var glass_orig_width, glass_orig_height;
|
nuclear@0
|
53 var glass_data_loaded, glass_data_count;
|
nuclear@0
|
54 var glass_progr_prog;
|
nuclear@0
|
55 var glass_start_time = 0;
|
nuclear@5
|
56 var glass_fullscr = false;
|
nuclear@0
|
57
|
nuclear@0
|
58 var glass_cb_disp = null;
|
nuclear@4
|
59 var glass_cb_reshape = null;
|
nuclear@2
|
60 var glass_cb_mouse = null;
|
nuclear@2
|
61 var glass_cb_motion = null;
|
nuclear@0
|
62
|
nuclear@0
|
63 function glass_init(canv_id, cons_id)
|
nuclear@0
|
64 {
|
nuclear@0
|
65 if(cons_id && (console = document.getElementById(cons_id))) {
|
nuclear@0
|
66 console.value = "";
|
nuclear@0
|
67 }
|
nuclear@0
|
68
|
nuclear@0
|
69 if(!(canvas = document.getElementById(canv_id))) {
|
nuclear@0
|
70 logmsg("canvas \"" + canv_id + "\" does not exist\n");
|
nuclear@0
|
71 return false;
|
nuclear@0
|
72 }
|
nuclear@0
|
73 try {
|
nuclear@0
|
74 gl = canvas.getContext("experimental-webgl");
|
nuclear@0
|
75 }
|
nuclear@0
|
76 catch(e) {
|
nuclear@0
|
77 logmsg("can't get an OpenGL context\n");
|
nuclear@0
|
78 return false;
|
nuclear@0
|
79 }
|
nuclear@0
|
80
|
nuclear@5
|
81 glass_orig_width = canvas.width;
|
nuclear@5
|
82 glass_orig_height = canvas.height;
|
nuclear@5
|
83
|
nuclear@0
|
84 var vs = create_shader(glass_progr_vs_src, gl.VERTEX_SHADER);
|
nuclear@0
|
85 var ps = create_shader(glass_progr_ps_src, gl.FRAGMENT_SHADER);
|
nuclear@0
|
86 if(!vs || !ps || !(glass_progr_prog = create_program(vs, ps))) {
|
nuclear@0
|
87 logmsg("internal glass error\n");
|
nuclear@0
|
88 return false;
|
nuclear@0
|
89 }
|
nuclear@0
|
90
|
nuclear@0
|
91 glass_data_loaded = glass_data_count = 0;
|
nuclear@0
|
92 return true;
|
nuclear@0
|
93 }
|
nuclear@0
|
94
|
nuclear@0
|
95 function glass_start()
|
nuclear@0
|
96 {
|
nuclear@2
|
97 if(glass_cb_motion) {
|
nuclear@2
|
98 canvas.onmousemove = function(ev) {
|
nuclear@2
|
99 glass_cb_motion(ev.clientX, ev.clientY);
|
nuclear@2
|
100 }
|
nuclear@2
|
101 }
|
nuclear@2
|
102
|
nuclear@2
|
103 if(glass_cb_mouse) {
|
nuclear@2
|
104 canvas.onmousedown = function(ev) {
|
nuclear@2
|
105 glass_cb_mouse(ev.button, true, ev.clientX, ev.clientY);
|
nuclear@2
|
106 }
|
nuclear@2
|
107 canvas.onmouseup = function(ev) {
|
nuclear@2
|
108 glass_cb_mouse(ev.button, false, ev.clientX, ev.clientY);
|
nuclear@2
|
109 }
|
nuclear@2
|
110 }
|
nuclear@2
|
111
|
nuclear@0
|
112 glass_start_time = get_msec();
|
nuclear@4
|
113
|
nuclear@4
|
114 if(glass_cb_reshape) {
|
nuclear@4
|
115 glass_cb_reshape(canvas.width, canvas.height);
|
nuclear@4
|
116 }
|
nuclear@0
|
117 glass_redraw();
|
nuclear@0
|
118 }
|
nuclear@0
|
119
|
nuclear@0
|
120 function glass_display_func(func)
|
nuclear@0
|
121 {
|
nuclear@0
|
122 glass_cb_disp = func;
|
nuclear@0
|
123 }
|
nuclear@0
|
124
|
nuclear@4
|
125 function glass_reshape_func(func)
|
nuclear@4
|
126 {
|
nuclear@4
|
127 glass_cb_reshape = func;
|
nuclear@4
|
128 }
|
nuclear@4
|
129
|
nuclear@2
|
130 function glass_mouse_func(func)
|
nuclear@2
|
131 {
|
nuclear@2
|
132 glass_cb_mouse = func;
|
nuclear@2
|
133 }
|
nuclear@2
|
134
|
nuclear@2
|
135 function glass_motion_func(func)
|
nuclear@2
|
136 {
|
nuclear@2
|
137 glass_cb_motion = func;
|
nuclear@2
|
138 }
|
nuclear@2
|
139
|
nuclear@4
|
140 function glass_resize(x, y)
|
nuclear@4
|
141 {
|
nuclear@5
|
142 canvas.width = glass_orig_width = x;
|
nuclear@5
|
143 canvas.height = glass_orig_height = y;
|
nuclear@4
|
144
|
nuclear@4
|
145 if(glass_cb_reshape) {
|
nuclear@4
|
146 glass_cb_reshape(x, y);
|
nuclear@4
|
147 }
|
nuclear@4
|
148 }
|
nuclear@4
|
149
|
nuclear@5
|
150 function glass_fullscreen(state)
|
nuclear@5
|
151 {
|
nuclear@5
|
152 if(state == null) {
|
nuclear@5
|
153 glass_fullscr = !glass_fullscr;
|
nuclear@5
|
154 } else {
|
nuclear@5
|
155 glass_fullscr = state;
|
nuclear@5
|
156 }
|
nuclear@5
|
157
|
nuclear@5
|
158 if(glass_fullscr) {
|
nuclear@5
|
159 canvas.style.position = "absolute";
|
nuclear@5
|
160 canvas.style.left = 0;
|
nuclear@5
|
161 canvas.style.top = 0;
|
nuclear@5
|
162 canvas.width = window.innerWidth;
|
nuclear@5
|
163 canvas.height = window.innerHeight;
|
nuclear@5
|
164
|
nuclear@5
|
165 window.onresize = function() {
|
nuclear@5
|
166 canvas.width = window.innerWidth;
|
nuclear@5
|
167 canvas.height = window.innerHeight;
|
nuclear@5
|
168 if(glass_cb_reshape) {
|
nuclear@5
|
169 glass_cb_reshape(canvas.width, canvas.height);
|
nuclear@5
|
170 }
|
nuclear@5
|
171 }
|
nuclear@5
|
172 } else {
|
nuclear@5
|
173 canvas.style.position = "static";
|
nuclear@5
|
174 canvas.width = glass_orig_width;
|
nuclear@5
|
175 canvas.height = glass_orig_height;
|
nuclear@5
|
176
|
nuclear@5
|
177 window.onresize = null;
|
nuclear@5
|
178 }
|
nuclear@5
|
179
|
nuclear@5
|
180 if(glass_cb_reshape) {
|
nuclear@5
|
181 glass_cb_reshape(canvas.width, canvas.height);
|
nuclear@5
|
182 }
|
nuclear@5
|
183 }
|
nuclear@5
|
184
|
nuclear@2
|
185
|
nuclear@0
|
186 function glass_redraw()
|
nuclear@0
|
187 {
|
nuclear@0
|
188 var msec = get_msec();
|
nuclear@0
|
189
|
nuclear@0
|
190 if(glass_data_loaded < glass_data_count || !glass_cb_disp) {
|
nuclear@0
|
191 glass_draw_progress(msec);
|
nuclear@0
|
192 } else {
|
nuclear@0
|
193 glass_cb_disp(msec);
|
nuclear@0
|
194 }
|
nuclear@0
|
195 request_redisplay(glass_redraw);
|
nuclear@0
|
196 }
|
nuclear@0
|
197
|
nuclear@0
|
198 function get_msec()
|
nuclear@0
|
199 {
|
nuclear@0
|
200 return new Date().getTime() - glass_start_time;
|
nuclear@0
|
201 }
|
nuclear@0
|
202
|
nuclear@0
|
203 /* ---- textures ---- */
|
nuclear@0
|
204 function load_texture(name)
|
nuclear@0
|
205 {
|
nuclear@0
|
206 glass_data_count++;
|
nuclear@0
|
207
|
nuclear@0
|
208 var tex = gl.createTexture();
|
nuclear@0
|
209 tex.image = new Image();
|
nuclear@0
|
210 tex.image.onload = function() { glass_tex_load_done(tex); }
|
nuclear@0
|
211 tex.image.onerror = glass_tex_load_failed;
|
nuclear@0
|
212 tex.image.src = name;
|
nuclear@0
|
213 return tex;
|
nuclear@0
|
214 }
|
nuclear@0
|
215
|
nuclear@0
|
216 function glass_tex_load_done(tex)
|
nuclear@0
|
217 {
|
nuclear@0
|
218 gl.bindTexture(gl.TEXTURE_2D, tex);
|
nuclear@0
|
219 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, tex.image);
|
nuclear@0
|
220 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
nuclear@0
|
221 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
nuclear@0
|
222 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
|
nuclear@0
|
223 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
|
nuclear@0
|
224 gl.bindTexture(gl.TEXTURE_2D, null);
|
nuclear@0
|
225 glass_data_loaded++;
|
nuclear@0
|
226 logmsg("loaded image: " + tex.image.src +
|
nuclear@0
|
227 " (" + glass_data_loaded + "/" + glass_data_count + ")\n");
|
nuclear@0
|
228 }
|
nuclear@0
|
229
|
nuclear@0
|
230 function glass_tex_load_failed()
|
nuclear@0
|
231 {
|
nuclear@0
|
232 logmsg("failed to load texture: " + this.src + "\n");
|
nuclear@0
|
233 }
|
nuclear@0
|
234
|
nuclear@0
|
235
|
nuclear@0
|
236 /* ---- shaders ---- */
|
nuclear@0
|
237 function load_shader(name, type)
|
nuclear@0
|
238 {
|
nuclear@0
|
239 var xhr = new XMLHttpRequest();
|
nuclear@0
|
240 xhr.open("GET", name, false);
|
nuclear@0
|
241 xhr.overrideMimeType("text/plain");
|
nuclear@0
|
242 xhr.send(null);
|
nuclear@0
|
243
|
nuclear@0
|
244 var src = xhr.responseText;
|
nuclear@2
|
245 return create_shader(src, type, name);
|
nuclear@0
|
246 }
|
nuclear@0
|
247
|
nuclear@2
|
248 function create_shader(src, type, name)
|
nuclear@0
|
249 {
|
nuclear@0
|
250 var sdr = gl.createShader(type);
|
nuclear@0
|
251 gl.shaderSource(sdr, src);
|
nuclear@0
|
252 gl.compileShader(sdr);
|
nuclear@0
|
253
|
nuclear@0
|
254 if(!gl.getShaderParameter(sdr, gl.COMPILE_STATUS)) {
|
nuclear@2
|
255 if(!name) {
|
nuclear@2
|
256 name = "<unnamed>";
|
nuclear@2
|
257 }
|
nuclear@2
|
258 logmsg("failed to compile shader: " + name + ": " + gl.getShaderInfoLog(sdr));
|
nuclear@0
|
259 return null;
|
nuclear@0
|
260 }
|
nuclear@0
|
261 return sdr;
|
nuclear@0
|
262 }
|
nuclear@0
|
263
|
nuclear@0
|
264 function load_program(vsname, psname)
|
nuclear@0
|
265 {
|
nuclear@0
|
266 var vs, ps;
|
nuclear@0
|
267
|
nuclear@0
|
268 if(!(vs = load_shader(vsname, gl.VERTEX_SHADER))) {
|
nuclear@0
|
269 return null;
|
nuclear@0
|
270 }
|
nuclear@0
|
271 if(!(ps = load_shader(psname, gl.FRAGMENT_SHADER))) {
|
nuclear@0
|
272 return null;
|
nuclear@0
|
273 }
|
nuclear@0
|
274 return create_program(vs, ps);
|
nuclear@0
|
275 }
|
nuclear@0
|
276
|
nuclear@0
|
277 function create_program(vs, ps)
|
nuclear@0
|
278 {
|
nuclear@0
|
279 var prog = gl.createProgram();
|
nuclear@0
|
280 gl.attachShader(prog, vs);
|
nuclear@0
|
281 gl.attachShader(prog, ps);
|
nuclear@0
|
282 gl.linkProgram(prog);
|
nuclear@0
|
283
|
nuclear@0
|
284 if(!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
|
nuclear@0
|
285 logmsg("Failed to link GLSL program (" + vsname + " - " + psname + ")\n");
|
nuclear@0
|
286 return null;
|
nuclear@0
|
287 }
|
nuclear@0
|
288 return prog;
|
nuclear@0
|
289 }
|
nuclear@0
|
290
|
nuclear@3
|
291 function set_uniform1i(p, name, v)
|
nuclear@3
|
292 {
|
nuclear@3
|
293 var loc = gl.getUniformLocation(p, name);
|
nuclear@3
|
294 if(loc != -1) {
|
nuclear@3
|
295 gl.useProgram(p);
|
nuclear@3
|
296 gl.uniform1i(loc, v);
|
nuclear@3
|
297 }
|
nuclear@3
|
298 }
|
nuclear@3
|
299
|
nuclear@2
|
300 function set_uniform1f(p, name, v)
|
nuclear@2
|
301 {
|
nuclear@2
|
302 var loc = gl.getUniformLocation(p, name);
|
nuclear@2
|
303 if(loc != -1) {
|
nuclear@2
|
304 gl.useProgram(p);
|
nuclear@2
|
305 gl.uniform1f(loc, v);
|
nuclear@2
|
306 }
|
nuclear@2
|
307 }
|
nuclear@2
|
308
|
nuclear@2
|
309 function set_uniform2f(p, name, x, y)
|
nuclear@2
|
310 {
|
nuclear@2
|
311 var loc = gl.getUniformLocation(p, name);
|
nuclear@2
|
312 if(loc != -1) {
|
nuclear@2
|
313 gl.useProgram(p);
|
nuclear@2
|
314 gl.uniform2f(loc, x, y);
|
nuclear@2
|
315 }
|
nuclear@2
|
316 }
|
nuclear@2
|
317
|
nuclear@2
|
318 function set_uniform3f(p, name, x, y, z)
|
nuclear@2
|
319 {
|
nuclear@2
|
320 var loc = gl.getUniformLocation(p, name);
|
nuclear@2
|
321 if(loc != -1) {
|
nuclear@2
|
322 gl.useProgram(p);
|
nuclear@2
|
323 gl.uniform3f(loc, x, y, z);
|
nuclear@2
|
324 }
|
nuclear@2
|
325 }
|
nuclear@2
|
326
|
nuclear@2
|
327 function set_uniform4f(p, name, x, y, z, w)
|
nuclear@2
|
328 {
|
nuclear@2
|
329 var loc = gl.getUniformLocation(p, name);
|
nuclear@2
|
330 if(loc != -1) {
|
nuclear@2
|
331 gl.useProgram(p);
|
nuclear@2
|
332 gl.uniform4f(loc, x, y, z, w);
|
nuclear@2
|
333 }
|
nuclear@2
|
334 }
|
nuclear@2
|
335
|
nuclear@2
|
336 function set_uniform_matrix4fv(p, name, mat)
|
nuclear@2
|
337 {
|
nuclear@2
|
338 var loc = gl.getUniformLocation(p, name);
|
nuclear@2
|
339 if(loc != -1) {
|
nuclear@2
|
340 gl.useProgram(p);
|
nuclear@2
|
341 gl.uniformMatrix4fv(loc, false, mat);
|
nuclear@2
|
342 }
|
nuclear@2
|
343 }
|
nuclear@2
|
344
|
nuclear@0
|
345 function logmsg(str)
|
nuclear@0
|
346 {
|
nuclear@0
|
347 if(console) {
|
nuclear@0
|
348 console.value += str;
|
nuclear@0
|
349 }
|
nuclear@0
|
350 }
|
nuclear@0
|
351
|
nuclear@0
|
352 window.request_redisplay = (function() {
|
nuclear@0
|
353 return window.requestAnimationFrame ||
|
nuclear@0
|
354 window.webkitRequestAnimationFrame ||
|
nuclear@0
|
355 window.mozRequestAnimationFrame ||
|
nuclear@0
|
356 window.oRequestAnimationFrame ||
|
nuclear@0
|
357 window.msRequestAnimationFrame ||
|
nuclear@0
|
358 function(callback, element) { window.setTimeout(callback, 1000/60); };
|
nuclear@0
|
359 })();
|
nuclear@0
|
360
|
nuclear@0
|
361
|
nuclear@0
|
362 var glass_progr_vs_src =
|
nuclear@0
|
363 "attribute vec4 attr_vertex, attr_color;\n" +
|
nuclear@0
|
364 "uniform mat4 mvmat, projmat;\n" +
|
nuclear@0
|
365 "varying vec4 color;\n" +
|
nuclear@0
|
366 "void main()\n{\n" +
|
nuclear@0
|
367 "\tmat4 mvp = projmat * mvmat;\n" +
|
nuclear@0
|
368 "\tgl_Position = mvp * attr_vertex;\n" +
|
nuclear@0
|
369 "\tcolor = attr_color;\n}\n";
|
nuclear@0
|
370
|
nuclear@0
|
371 var glass_progr_ps_src =
|
nuclear@0
|
372 "precision highp float;\n" +
|
nuclear@0
|
373 "varying vec4 color;\n" +
|
nuclear@0
|
374 "void main()\n{\n" +
|
nuclear@0
|
375 "\tgl_FragColor = color;\n}\n";
|
nuclear@0
|
376
|
nuclear@0
|
377 function glass_draw_progress(msec)
|
nuclear@0
|
378 {
|
nuclear@0
|
379 glMatrixMode(GL_PROJECTION);
|
nuclear@0
|
380 glPushMatrix();
|
nuclear@0
|
381 glLoadIdentity();
|
nuclear@0
|
382
|
nuclear@0
|
383 glMatrixMode(GL_MODELVIEW);
|
nuclear@0
|
384 glPushMatrix();
|
nuclear@0
|
385 glLoadIdentity();
|
nuclear@0
|
386
|
nuclear@0
|
387 gl.clearColor(0, 0, 0, 1);
|
nuclear@0
|
388 gl.clear(gl.COLOR_BUFFER_BIT);
|
nuclear@0
|
389
|
nuclear@0
|
390 var progr = glass_data_loaded / glass_data_count;
|
nuclear@0
|
391
|
nuclear@0
|
392 gl.useProgram(glass_progr_prog);
|
nuclear@0
|
393
|
nuclear@0
|
394 glBegin(GL_QUADS);
|
nuclear@0
|
395 glColor3f(0.3, 0.5, 0.8);
|
nuclear@0
|
396 glVertex2f(-0.55, -0.1);
|
nuclear@0
|
397 glVertex2f(0.55, -0.1);
|
nuclear@0
|
398 glVertex2f(0.55, 0.1);
|
nuclear@0
|
399 glVertex2f(-0.55, 0.1);
|
nuclear@0
|
400
|
nuclear@0
|
401 glColor3f(0, 0, 0);
|
nuclear@0
|
402 glVertex2f(-0.525, -0.075);
|
nuclear@0
|
403 glVertex2f(0.525, -0.075);
|
nuclear@0
|
404 glVertex2f(0.525, 0.075);
|
nuclear@0
|
405 glVertex2f(-0.525, 0.075);
|
nuclear@0
|
406
|
nuclear@0
|
407 glColor3f(1.0, 0.3, 0.2);
|
nuclear@0
|
408 glVertex2f(-0.5, -0.05);
|
nuclear@0
|
409 glVertex2f(progr - 0.5, -0.05);
|
nuclear@0
|
410 glVertex2f(progr - 0.5, 0.05);
|
nuclear@0
|
411 glVertex2f(-0.5, 0.05);
|
nuclear@0
|
412 glEnd();
|
nuclear@0
|
413
|
nuclear@0
|
414 glMatrixMode(GL_PROJECTION);
|
nuclear@0
|
415 glPopMatrix();
|
nuclear@0
|
416 glMatrixMode(GL_MODELVIEW);
|
nuclear@0
|
417 glPopMatrix();
|
nuclear@0
|
418 }
|
nuclear@0
|
419
|