on 9efd173 help sign in
290 lines 8.9 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;
13 var Signal = Elm.Signal.make(elm);
14 var newElement = Elm.Graphics.Element.make(elm).newElement;
15 var Utils = Elm.Native.Utils.make(elm);
16 var Tuple2 = Utils.Tuple2;
17 var MJS = Elm.Native.MJS.make(elm);
18 var List = Elm.Native.List.make(elm);
20 function encapsulate(program, buffer, uniforms) {
22 return model = {
23 program: program,
24 buffer: buffer,
25 uniforms: uniforms
26 };
28 }
30 function link (vSrc, fSrc) {
32 var guid = Utils.guid();
34 console.log("Wrapped program #" + guid);
36 return program = {
37 guid: guid,
38 vSrc: vSrc,
39 fSrc: fSrc
40 };
42 }
44 function do_link (gl, vSrc, fSrc) {
46 function createShader(str, type) {
48 var shader = gl.createShader(type);
50 gl.shaderSource(shader, str);
51 gl.compileShader(shader);
52 var compile = gl.COMPILE_STATUS;
53 if (!gl.getShaderParameter(shader,compile)) {
54 throw gl.getShaderInfoLog(shader);
55 }
57 return shader;
59 };
61 var vshader = createShader(vSrc, gl.VERTEX_SHADER);
62 var fshader = createShader(fSrc, gl.FRAGMENT_SHADER);
63 var program = gl.createProgram();
64 console.log("Created shaders and program");
66 gl.attachShader(program, vshader);
67 gl.attachShader(program, fshader);
68 gl.linkProgram(program);
69 if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
70 throw gl.getProgramInfoLog(program);
71 }
73 return program;
75 }
77 function bind (bufferElems) {
79 var guid = Utils.guid();
81 console.log("Wrapped buffer #" + guid);
83 return buffer = {
84 guid: guid,
85 bufferElems: bufferElems
86 };
88 }
90 function do_bind (gl, program, bufferElems) {
92 var buffers = {};
94 var attributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
95 for (var i = 0; i < attributes; i += 1) {
96 var attribute = gl.getActiveAttrib(program, i);
97 switch (attribute.type) {
98 case gl.FLOAT_VEC3:
100 // Might want to invert the loop
101 // to build the array buffer first
102 // and then bind each one-at-a-time
103 var data = [];
104 List.each(function(elem){
105 data.push(elem._0[attribute.name][0]);
106 data.push(elem._0[attribute.name][1]);
107 data.push(elem._0[attribute.name][2]);
108 data.push(elem._1[attribute.name][0]);
109 data.push(elem._1[attribute.name][1]);
110 data.push(elem._1[attribute.name][2]);
111 data.push(elem._2[attribute.name][0]);
112 data.push(elem._2[attribute.name][1]);
113 data.push(elem._2[attribute.name][2]);
114 }, bufferElems);
115 var array = new Float32Array(data);
117 var buffer = gl.createBuffer();
118 console.log("Created attribute buffer " + attribute.name);
119 gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
120 gl.bufferData(gl.ARRAY_BUFFER, array, gl.STATIC_DRAW);
122 buffers[attribute.name] = buffer;
124 break;
126 default:
127 console.log("Bad buffer type");
128 break;
129 }
131 }
133 var numIndices = 3 * List.length(bufferElems);
134 var indices = List.toArray(List.range(0, numIndices - 1));
135 console.log("Created index buffer");
136 var indexBuffer = gl.createBuffer();
137 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
138 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
140 var bufferObject = {
141 numIndices: numIndices,
142 indexBuffer: indexBuffer,
143 buffers: buffers
144 };
146 return bufferObject;
148 }
150 function drawGL(model) {
152 var gl = model.cache.gl;
154 gl.viewport(0, 0, model.w, model.h);
155 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
156 console.log("Drawing");
158 function drawModel(m) {
160 var program = model.cache.programs[m.program.guid];
161 if (!program) {
162 program = do_link(gl, m.program.vSrc, m.program.fSrc);
163 model.cache.programs[m.program.guid] = program;
164 }
166 gl.useProgram(program);
168 var numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
169 for (var i = 0; i < numUniforms; i += 1) {
170 var uniform = gl.getActiveUniform(program, i);
171 var uniformLocation = gl.getUniformLocation(program, uniform.name);
172 switch (uniform.type) {
173 case gl.FLOAT_MAT4:
174 gl.uniformMatrix4fv(uniformLocation, false, m.uniforms[uniform.name]);
175 break;
176 default:
177 console.log("Unsupported uniform type: " + uniform.type);
178 break;
179 }
180 }
182 var buffer = model.cache.buffers[m.buffer.guid];
183 if (!buffer) {
184 buffer = do_bind(gl, program, m.buffer.bufferElems);
185 model.cache.buffers[m.buffer.guid] = buffer;
186 }
188 var numIndices = buffer.numIndices;
189 var indexBuffer = buffer.indexBuffer;
190 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
192 var numAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
193 for (var i = 0; i < numAttributes; i += 1) {
194 var attribute = gl.getActiveAttrib(program, i);
195 var attribLocation = gl.getAttribLocation(program, attribute.name);
196 gl.enableVertexAttribArray(attribLocation);
197 attributeBuffer = buffer.buffers[attribute.name];
199 switch (attribute.type) {
200 case gl.FLOAT_VEC3:
201 gl.bindBuffer(gl.ARRAY_BUFFER, attributeBuffer);
202 gl.vertexAttribPointer(attribLocation, 3, gl.FLOAT, false, 0, 0);
203 break;
204 default:
205 console.log("Unsupported attribute type: " + attribute.type);
206 break;
207 }
208 }
210 gl.drawElements(gl.TRIANGLES, numIndices, gl.UNSIGNED_SHORT, 0);
212 }
214 List.each(drawModel, model.models);
216 }
218 function webgl(dimensions, models) {
220 if (!window.WebGLRenderingContext) {
221 throw new Error("It appears your browser does not support WebGL! http://get.webgl.org/troubleshooting");
222 return;
223 }
225 var w = dimensions._0;
226 var h = dimensions._1;
228 function render(model) {
230 var div = newNode('div');
231 div.style.overflow = 'hidden';
232 var canvas = newNode('canvas');
233 var gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
234 gl.enable(gl.DEPTH_TEST);
236 model.cache.gl = gl;
237 model.cache.canvas = canvas;
238 model.cache.programs = [];
239 model.cache.buffers = [];
241 update(div, model, model);
243 return div;
245 }
247 function update(div, oldModel, newModel) {
249 newModel.cache = oldModel.cache;
251 var canvas = newModel.cache.canvas;
253 canvas.style.width = oldModel.w + 'px';
254 canvas.style.height = oldModel.h + 'px';
255 canvas.style.display = "block";
256 canvas.style.position = "absolute";
257 canvas.width = oldModel.w;
258 canvas.height = oldModel.h;
260 drawGL(newModel);
262 div.appendChild(canvas);
264 }
266 var elem = {
267 ctor: 'Custom',
268 type: 'WebGL',
269 render: render,
270 update: update,
271 model: {
272 models: models,
273 cache: {},
274 w: w,
275 h: h,
276 }
277 };
279 return A3(newElement, w, h, elem);
281 }
283 return elm.Native.Graphics.WebGL.values = {
284 link:F2(link),
285 bind:bind,
286 encapsulate:F3(encapsulate),
287 webgl:F2(webgl)
288 };
290};