on 5c830e1 help sign in
361 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 loadTex(source) {
22 var response = Signal.constant(elm.Http.values.Waiting);
24 var img = new Image();
26 img.onload = function() {
27 var success = elm.Http.values.Success({img:img});
28 elm.notify(response.id, success);
29 }
31 img.onerror = function(e) {
32 var failure = A2(elm.Http.values.Failure,0,"Failed");
33 elm.notify(response.id, failure);
34 }
36 img.src = source;
38 return response;
40 }
42 function model(vert, frag, buffer, uniforms) {
44 return model = {
45 vert: vert,
46 frag: frag,
47 buffer: buffer,
48 uniforms: uniforms
49 };
51 }
53 function do_texture (gl, img) {
55 var tex = gl.createTexture();
56 console.log("Created texture");
57 gl.bindTexture(gl.TEXTURE_2D, tex);
58 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
59 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
60 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
61 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
62 gl.generateMipmap(gl.TEXTURE_2D);
63 //gl.bindTexture(gl.TEXTURE0, null);
64 return tex;
66 }
68 function do_compile (gl, src, tipe) {
70 var shader = gl.createShader(tipe);
71 console.log("Created shader");
73 gl.shaderSource(shader, src);
74 gl.compileShader(shader);
75 var compile = gl.COMPILE_STATUS;
76 if (!gl.getShaderParameter(shader,compile)) {
77 throw gl.getShaderInfoLog(shader);
78 }
80 return shader;
82 }
84 function do_link (gl, vshader, fshader) {
86 var program = gl.createProgram();
87 console.log("Created program");
89 gl.attachShader(program, vshader);
90 gl.attachShader(program, fshader);
91 gl.linkProgram(program);
92 if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
93 throw gl.getProgramInfoLog(program);
94 }
96 return program;
98 }
100 function do_bind (gl, program, bufferElems) {
102 var buffers = {};
104 var attributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
105 for (var i = 0; i < attributes; i += 1) {
106 var attribute = gl.getActiveAttrib(program, i);
107 switch (attribute.type) {
108 case gl.FLOAT_VEC3:
110 // Might want to invert the loop
111 // to build the array buffer first
112 // and then bind each one-at-a-time
113 var data = [];
114 List.each(function(elem){
115 data.push(elem._0[attribute.name][0]);
116 data.push(elem._0[attribute.name][1]);
117 data.push(elem._0[attribute.name][2]);
118 data.push(elem._1[attribute.name][0]);
119 data.push(elem._1[attribute.name][1]);
120 data.push(elem._1[attribute.name][2]);
121 data.push(elem._2[attribute.name][0]);
122 data.push(elem._2[attribute.name][1]);
123 data.push(elem._2[attribute.name][2]);
124 }, bufferElems);
125 var array = new Float32Array(data);
127 var buffer = gl.createBuffer();
128 console.log("Created attribute buffer " + attribute.name);
129 gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
130 gl.bufferData(gl.ARRAY_BUFFER, array, gl.STATIC_DRAW);
132 buffers[attribute.name] = buffer;
134 break;
136 default:
137 console.log("Bad buffer type");
138 break;
139 }
141 }
143 var numIndices = 3 * List.length(bufferElems);
144 var indices = List.toArray(List.range(0, numIndices - 1));
145 console.log("Created index buffer");
146 var indexBuffer = gl.createBuffer();
147 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
148 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
150 var bufferObject = {
151 numIndices: numIndices,
152 indexBuffer: indexBuffer,
153 buffers: buffers
154 };
156 return bufferObject;
158 }
160 function drawGL(model) {
162 var gl = model.cache.gl;
164 gl.viewport(0, 0, model.w, model.h);
165 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
166 console.log("Drawing");
168 function drawModel(m) {
170 var program;
171 if (m.vert.id && m.frag.id) {
172 var progid = m.vert.id + '#' + m.frag.id;
173 program = model.cache.programs[progid];
174 }
176 if (!program) {
178 var vshader = undefined;
179 if (m.vert.id) {
180 vshader = model.cache.shaders[m.vert.id];
181 } else {
182 m.vert.id = Utils.guid();
183 }
185 if (!vshader) {
186 vshader = do_compile(gl, m.vert.src, gl.VERTEX_SHADER);
187 model.cache.shaders[m.vert.id] = vshader;
188 }
190 var fshader = undefined;
191 if (m.frag.id) {
192 fshader = model.cache.shaders[m.frag.id];
193 } else {
194 m.frag.id = Utils.guid();
195 }
197 if (!fshader) {
198 fshader = do_compile(gl, m.frag.src, gl.FRAGMENT_SHADER);
199 model.cache.shaders[m.frag.id] = fshader;
200 }
202 program = do_link(gl, vshader, fshader);
203 var progid = m.vert.id + '#' + m.frag.id;
204 model.cache.programs[progid] = program;
206 }
208 gl.useProgram(program);
210 var numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
211 var textureCounter = 0;
212 for (var i = 0; i < numUniforms; i += 1) {
213 var uniform = gl.getActiveUniform(program, i);
214 var uniformLocation = gl.getUniformLocation(program, uniform.name);
215 switch (uniform.type) {
216 case gl.INT:
217 gl.uniform1i(uniformLocation, m.uniforms[uniform.name]);
218 break;
219 case gl.FLOAT:
220 gl.uniform1f(uniformLocation, m.uniforms[uniform.name]);
221 break;
222 case gl.FLOAT_VEC3:
223 gl.uniform3fv(uniformLocation, m.uniforms[uniform.name]);
224 break;
225 case gl.FLOAT_MAT4:
226 gl.uniformMatrix4fv(uniformLocation, false, m.uniforms[uniform.name]);
227 break;
228 case gl.SAMPLER_2D:
229 var texture = m.uniforms[uniform.name];
230 var tex = undefined;
231 if (texture.id) {
232 tex = model.cache.textures[texture.id];
233 } else {
234 texture.id = Utils.guid();
235 }
236 if (!tex) {
237 tex = do_texture(gl, texture.img);
238 model.cache.textures[texture.id] = tex;
239 }
240 var activeName = 'TEXTURE' + textureCounter;
241 gl.activeTexture(gl[activeName]);
242 gl.bindTexture(gl.TEXTURE_2D,tex);
243 gl.uniform1i(uniformLocation, textureCounter);
244 textureCounter += 1;
245 break;
246 default:
247 console.log("Unsupported uniform type: " + uniform.type);
248 break;
249 }
250 }
252 var buffer = model.cache.buffers[m.buffer.guid];
253 if (!buffer) {
254 buffer = do_bind(gl, program, m.buffer);
255 model.cache.buffers[m.buffer.guid] = buffer;
256 }
258 var numIndices = buffer.numIndices;
259 var indexBuffer = buffer.indexBuffer;
260 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
262 var numAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
263 for (var i = 0; i < numAttributes; i += 1) {
264 var attribute = gl.getActiveAttrib(program, i);
265 var attribLocation = gl.getAttribLocation(program, attribute.name);
266 gl.enableVertexAttribArray(attribLocation);
267 var attributeBuffer = buffer.buffers[attribute.name];
269 switch (attribute.type) {
270 case gl.FLOAT_VEC3:
271 gl.bindBuffer(gl.ARRAY_BUFFER, attributeBuffer);
272 gl.vertexAttribPointer(attribLocation, 3, gl.FLOAT, false, 0, 0);
273 break;
274 default:
275 console.log("Unsupported attribute type: " + attribute.type);
276 break;
277 }
278 }
280 gl.drawElements(gl.TRIANGLES, numIndices, gl.UNSIGNED_SHORT, 0);
282 }
284 List.each(drawModel, model.models);
286 }
288 function webgl(dimensions, models) {
290 if (!window.WebGLRenderingContext) {
291 throw new Error("It appears your browser does not support WebGL! http://get.webgl.org/troubleshooting");
292 return;
293 }
295 var w = dimensions._0;
296 var h = dimensions._1;
298 function render(model) {
300 var div = newNode('div');
301 div.style.overflow = 'hidden';
302 var canvas = newNode('canvas');
303 var gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
304 gl.enable(gl.DEPTH_TEST);
306 model.cache.gl = gl;
307 model.cache.canvas = canvas;
308 model.cache.shaders = [];
309 model.cache.programs = {};
310 model.cache.buffers = [];
311 model.cache.textures = [];
313 update(div, model, model);
315 return div;
317 }
319 function update(div, oldModel, newModel) {
321 newModel.cache = oldModel.cache;
323 var canvas = newModel.cache.canvas;
325 canvas.style.width = oldModel.w + 'px';
326 canvas.style.height = oldModel.h + 'px';
327 canvas.style.display = "block";
328 canvas.style.position = "absolute";
329 canvas.width = oldModel.w;
330 canvas.height = oldModel.h;
332 drawGL(newModel);
334 div.appendChild(canvas);
336 }
338 var elem = {
339 ctor: 'Custom',
340 type: 'WebGL',
341 render: render,
342 update: update,
343 model: {
344 models: models,
345 cache: {},
346 w: w,
347 h: h,
348 }
349 };
351 return A3(newElement, w, h, elem);
353 }
355 return elm.Native.Graphics.WebGL.values = {
356 loadTex:loadTex,
357 model:F4(model),
358 webgl:F2(webgl)
359 };
361};