on 115773f help sign in
366 lines 10 kB history raw
1Elm.Native.Graphics.WebGL = {};
2Elm.Native.Graphics.WebGL.make = function(elm) {
4 // LOG LEVEL
6 elm.Native = elm.Native || {};
7 elm.Native.Graphics = elm.Native.Graphics || {};
8 elm.Native.Graphics.WebGL = elm.Native.Graphics.WebGL || {};
9 if (elm.Native.Graphics.WebGL.values) return elm.Native.Graphics.WebGL.values;
11 var newNode = ElmRuntime.use(ElmRuntime.Render.Utils).newElement;
12 var newElement = Elm.Graphics.Element.make(elm).newElement;
14 var List = Elm.Native.List.make(elm);
15 var MJS = Elm.Native.MJS.make(elm);
16 var Utils = Elm.Native.Utils.make(elm);
17 var Signal = Elm.Signal.make(elm);
18 var Tuple2 = Utils.Tuple2;
20 function unsafeCoerceGLSL(src) {
21 return { src : src };
22 }
24 function loadTex(source) {
26 var response = Signal.constant(elm.Http.values.Waiting);
28 var img = new Image();
30 img.onload = function() {
31 var success = elm.Http.values.Success({img:img});
32 elm.notify(response.id, success);
33 }
35 img.onerror = function(e) {
36 var failure = A2(elm.Http.values.Failure,0,"Failed");
37 elm.notify(response.id, failure);
38 }
40 img.src = source;
42 return response;
44 }
46 function model(vert, frag, buffer, uniforms) {
48 return model = {
49 vert: vert,
50 frag: frag,
51 buffer: buffer,
52 uniforms: uniforms
53 };
55 }
57 function do_texture (gl, img) {
59 var tex = gl.createTexture();
60 console.log("Created texture");
61 gl.bindTexture(gl.TEXTURE_2D, tex);
62 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
63 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
64 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
65 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
66 gl.generateMipmap(gl.TEXTURE_2D);
67 //gl.bindTexture(gl.TEXTURE0, null);
68 return tex;
70 }
72 function do_compile (gl, src, tipe) {
74 var shader = gl.createShader(tipe);
75 console.log("Created shader");
77 gl.shaderSource(shader, src);
78 gl.compileShader(shader);
79 var compile = gl.COMPILE_STATUS;
80 if (!gl.getShaderParameter(shader,compile)) {
81 throw gl.getShaderInfoLog(shader);
82 }
84 return shader;
86 }
88 function do_link (gl, vshader, fshader) {
90 var program = gl.createProgram();
91 console.log("Created program");
93 gl.attachShader(program, vshader);
94 gl.attachShader(program, fshader);
95 gl.linkProgram(program);
96 if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
97 throw gl.getProgramInfoLog(program);
98 }
100 return program;
102 }
104 function do_bind (gl, program, bufferElems) {
106 var buffers = {};
108 var attributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
109 for (var i = 0; i < attributes; i += 1) {
110 var attribute = gl.getActiveAttrib(program, i);
111 switch (attribute.type) {
112 case gl.FLOAT_VEC3:
114 // Might want to invert the loop
115 // to build the array buffer first
116 // and then bind each one-at-a-time
117 var data = [];
118 List.each(function(elem){
119 data.push(elem._0[attribute.name][0]);
120 data.push(elem._0[attribute.name][1]);
121 data.push(elem._0[attribute.name][2]);
122 data.push(elem._1[attribute.name][0]);
123 data.push(elem._1[attribute.name][1]);
124 data.push(elem._1[attribute.name][2]);
125 data.push(elem._2[attribute.name][0]);
126 data.push(elem._2[attribute.name][1]);
127 data.push(elem._2[attribute.name][2]);
128 }, bufferElems);
129 var array = new Float32Array(data);
131 var buffer = gl.createBuffer();
132 console.log("Created attribute buffer " + attribute.name);
133 gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
134 gl.bufferData(gl.ARRAY_BUFFER, array, gl.STATIC_DRAW);
136 buffers[attribute.name] = buffer;
138 break;
140 default:
141 console.log("Bad buffer type");
142 break;
143 }
145 }
147 var numIndices = 3 * List.length(bufferElems);
148 var indices = List.toArray(List.range(0, numIndices - 1));
149 console.log("Created index buffer");
150 var indexBuffer = gl.createBuffer();
151 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
152 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
154 var bufferObject = {
155 numIndices: numIndices,
156 indexBuffer: indexBuffer,
157 buffers: buffers
158 };
160 return bufferObject;
162 }
164 function drawGL(model) {
166 var gl = model.cache.gl;
168 gl.viewport(0, 0, model.w, model.h);
169 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
170 console.log("Drawing");
172 function drawModel(m) {
174 var program;
175 if (m.vert.id && m.frag.id) {
176 var progid = m.vert.id + '#' + m.frag.id;
177 program = model.cache.programs[progid];
178 }
180 if (!program) {
182 var vshader = undefined;
183 if (m.vert.id) {
184 vshader = model.cache.shaders[m.vert.id];
185 } else {
186 m.vert.id = Utils.guid();
187 }
189 if (!vshader) {
190 vshader = do_compile(gl, m.vert.src, gl.VERTEX_SHADER);
191 model.cache.shaders[m.vert.id] = vshader;
192 }
194 var fshader = undefined;
195 if (m.frag.id) {
196 fshader = model.cache.shaders[m.frag.id];
197 } else {
198 m.frag.id = Utils.guid();
199 }
201 if (!fshader) {
202 fshader = do_compile(gl, m.frag.src, gl.FRAGMENT_SHADER);
203 model.cache.shaders[m.frag.id] = fshader;
204 }
206 program = do_link(gl, vshader, fshader);
207 var progid = m.vert.id + '#' + m.frag.id;
208 model.cache.programs[progid] = program;
210 }
212 gl.useProgram(program);
214 var numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
215 var textureCounter = 0;
216 for (var i = 0; i < numUniforms; i += 1) {
217 var uniform = gl.getActiveUniform(program, i);
218 var uniformLocation = gl.getUniformLocation(program, uniform.name);
219 switch (uniform.type) {
220 case gl.INT:
221 gl.uniform1i(uniformLocation, m.uniforms[uniform.name]);
222 break;
223 case gl.FLOAT:
224 gl.uniform1f(uniformLocation, m.uniforms[uniform.name]);
225 break;
226 case gl.FLOAT_VEC3:
227 gl.uniform3fv(uniformLocation, m.uniforms[uniform.name]);
228 break;
229 case gl.FLOAT_MAT4:
230 gl.uniformMatrix4fv(uniformLocation, false, m.uniforms[uniform.name]);
231 break;
232 case gl.SAMPLER_2D:
233 var texture = m.uniforms[uniform.name];
234 var tex = undefined;
235 if (texture.id) {
236 tex = model.cache.textures[texture.id];
237 } else {
238 texture.id = Utils.guid();
239 }
240 if (!tex) {
241 tex = do_texture(gl, texture.img);
242 model.cache.textures[texture.id] = tex;
243 }
244 var activeName = 'TEXTURE' + textureCounter;
245 gl.activeTexture(gl[activeName]);
246 gl.bindTexture(gl.TEXTURE_2D,tex);
247 gl.uniform1i(uniformLocation, textureCounter);
248 textureCounter += 1;
249 break;
250 default:
251 console.log("Unsupported uniform type: " + uniform.type);
252 break;
253 }
254 }
256 var buffer = model.cache.buffers[m.buffer.guid];
257 if (!buffer) {
258 buffer = do_bind(gl, program, m.buffer);
259 model.cache.buffers[m.buffer.guid] = buffer;
260 }
262 var numIndices = buffer.numIndices;
263 var indexBuffer = buffer.indexBuffer;
264 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
266 var numAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
267 for (var i = 0; i < numAttributes; i += 1) {
268 var attribute = gl.getActiveAttrib(program, i);
269 var attribLocation = gl.getAttribLocation(program, attribute.name);
270 gl.enableVertexAttribArray(attribLocation);
271 var attributeBuffer = buffer.buffers[attribute.name];
273 switch (attribute.type) {
274 case gl.FLOAT_VEC3:
275 gl.bindBuffer(gl.ARRAY_BUFFER, attributeBuffer);
276 gl.vertexAttribPointer(attribLocation, 3, gl.FLOAT, false, 0, 0);
277 break;
278 default:
279 console.log("Unsupported attribute type: " + attribute.type);
280 break;
281 }
282 }
284 gl.drawElements(gl.TRIANGLES, numIndices, gl.UNSIGNED_SHORT, 0);
286 }
288 List.each(drawModel, model.models);
290 }
292 function webgl(dimensions, models) {
294 if (!window.WebGLRenderingContext) {
295 throw new Error("It appears your browser does not support WebGL! http://get.webgl.org/troubleshooting");
296 return;
297 }
299 var w = dimensions._0;
300 var h = dimensions._1;
302 function render(model) {
304 var div = newNode('div');
305 div.style.overflow = 'hidden';
306 var canvas = newNode('canvas');
307 var gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
308 gl.enable(gl.DEPTH_TEST);
310 model.cache.gl = gl;
311 model.cache.canvas = canvas;
312 model.cache.shaders = [];
313 model.cache.programs = {};
314 model.cache.buffers = [];
315 model.cache.textures = [];
317 update(div, model, model);
319 return div;
321 }
323 function update(div, oldModel, newModel) {
325 newModel.cache = oldModel.cache;
327 var canvas = newModel.cache.canvas;
329 canvas.style.width = oldModel.w + 'px';
330 canvas.style.height = oldModel.h + 'px';
331 canvas.style.display = "block";
332 canvas.style.position = "absolute";
333 canvas.width = oldModel.w;
334 canvas.height = oldModel.h;
336 drawGL(newModel);
338 div.appendChild(canvas);
340 }
342 var elem = {
343 ctor: 'Custom',
344 type: 'WebGL',
345 render: render,
346 update: update,
347 model: {
348 models: models,
349 cache: {},
350 w: w,
351 h: h,
352 }
353 };
355 return A3(newElement, w, h, elem);
357 }
359 return elm.Native.Graphics.WebGL.values = {
360 unsafeCoerceGLSL:unsafeCoerceGLSL,
361 loadTex:loadTex,
362 model:F4(model),
363 webgl:F2(webgl)
364 };
366};