on a428bef help sign in
303 lines 8.3 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 model(vert, frag, buffer, uniforms) {
22 return model = {
23 vert: vert,
24 frag: frag,
25 buffer: buffer,
26 uniforms: uniforms
27 };
29 }
31 function do_compile (gl, src, tipe) {
33 var shader = gl.createShader(tipe);
34 console.log("Created shader");
36 gl.shaderSource(shader, src);
37 gl.compileShader(shader);
38 var compile = gl.COMPILE_STATUS;
39 if (!gl.getShaderParameter(shader,compile)) {
40 throw gl.getShaderInfoLog(shader);
41 }
43 return shader;
45 }
47 function do_link (gl, vshader, fshader) {
49 var program = gl.createProgram();
50 console.log("Created program");
52 gl.attachShader(program, vshader);
53 gl.attachShader(program, fshader);
54 gl.linkProgram(program);
55 if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
56 throw gl.getProgramInfoLog(program);
57 }
59 return program;
61 }
63 function do_bind (gl, program, bufferElems) {
65 var buffers = {};
67 var attributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
68 for (var i = 0; i < attributes; i += 1) {
69 var attribute = gl.getActiveAttrib(program, i);
70 switch (attribute.type) {
71 case gl.FLOAT_VEC3:
73 // Might want to invert the loop
74 // to build the array buffer first
75 // and then bind each one-at-a-time
76 var data = [];
77 List.each(function(elem){
78 data.push(elem._0[attribute.name][0]);
79 data.push(elem._0[attribute.name][1]);
80 data.push(elem._0[attribute.name][2]);
81 data.push(elem._1[attribute.name][0]);
82 data.push(elem._1[attribute.name][1]);
83 data.push(elem._1[attribute.name][2]);
84 data.push(elem._2[attribute.name][0]);
85 data.push(elem._2[attribute.name][1]);
86 data.push(elem._2[attribute.name][2]);
87 }, bufferElems);
88 var array = new Float32Array(data);
90 var buffer = gl.createBuffer();
91 console.log("Created attribute buffer " + attribute.name);
92 gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
93 gl.bufferData(gl.ARRAY_BUFFER, array, gl.STATIC_DRAW);
95 buffers[attribute.name] = buffer;
97 break;
99 default:
100 console.log("Bad buffer type");
101 break;
102 }
104 }
106 var numIndices = 3 * List.length(bufferElems);
107 var indices = List.toArray(List.range(0, numIndices - 1));
108 console.log("Created index buffer");
109 var indexBuffer = gl.createBuffer();
110 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
111 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
113 var bufferObject = {
114 numIndices: numIndices,
115 indexBuffer: indexBuffer,
116 buffers: buffers
117 };
119 return bufferObject;
121 }
123 function drawGL(model) {
125 var gl = model.cache.gl;
127 gl.viewport(0, 0, model.w, model.h);
128 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
129 console.log("Drawing");
131 function drawModel(m) {
133 var program;
134 if (m.vert.id && m.frag.id) {
135 var progid = m.vert.id + '#' + m.frag.id;
136 program = model.cache.programs[progid];
137 }
139 if (!program) {
141 var vshader = undefined;
142 if (m.vert.id) {
143 vshader = model.cache.shaders[m.vert.id];
144 } else {
145 m.vert.id = Utils.guid();
146 }
148 if (!vshader) {
149 vshader = do_compile(gl, m.vert.src, gl.VERTEX_SHADER);
150 model.cache.shaders[m.vert.id] = vshader;
151 }
153 var fshader = undefined;
154 if (m.frag.id) {
155 fshader = model.cache.shaders[m.frag.id];
156 } else {
157 m.frag.id = Utils.guid();
158 }
160 if (!fshader) {
161 fshader = do_compile(gl, m.frag.src, gl.FRAGMENT_SHADER);
162 model.cache.shaders[m.frag.id] = fshader;
163 }
165 program = do_link(gl, vshader, fshader);
166 var progid = m.vert.id + '#' + m.frag.id;
167 model.cache.programs[progid] = program;
169 }
171 gl.useProgram(program);
173 var numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
174 for (var i = 0; i < numUniforms; i += 1) {
175 var uniform = gl.getActiveUniform(program, i);
176 var uniformLocation = gl.getUniformLocation(program, uniform.name);
177 switch (uniform.type) {
178 case gl.INT:
179 gl.uniform1i(uniformLocation, m.uniforms[uniform.name]);
180 break;
181 case gl.FLOAT:
182 gl.uniform1f(uniformLocation, m.uniforms[uniform.name]);
183 break;
184 case gl.FLOAT_VEC3:
185 gl.uniform3fv(uniformLocation, m.uniforms[uniform.name]);
186 break;
187 case gl.FLOAT_MAT4:
188 gl.uniformMatrix4fv(uniformLocation, false, m.uniforms[uniform.name]);
189 break;
190 default:
191 console.log("Unsupported uniform type: " + uniform.type);
192 break;
193 }
194 }
196 var buffer = model.cache.buffers[m.buffer.guid];
197 if (!buffer) {
198 buffer = do_bind(gl, program, m.buffer);
199 model.cache.buffers[m.buffer.guid] = buffer;
200 }
202 var numIndices = buffer.numIndices;
203 var indexBuffer = buffer.indexBuffer;
204 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
206 var numAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
207 for (var i = 0; i < numAttributes; i += 1) {
208 var attribute = gl.getActiveAttrib(program, i);
209 var attribLocation = gl.getAttribLocation(program, attribute.name);
210 gl.enableVertexAttribArray(attribLocation);
211 var attributeBuffer = buffer.buffers[attribute.name];
213 switch (attribute.type) {
214 case gl.FLOAT_VEC3:
215 gl.bindBuffer(gl.ARRAY_BUFFER, attributeBuffer);
216 gl.vertexAttribPointer(attribLocation, 3, gl.FLOAT, false, 0, 0);
217 break;
218 default:
219 console.log("Unsupported attribute type: " + attribute.type);
220 break;
221 }
222 }
224 gl.drawElements(gl.TRIANGLES, numIndices, gl.UNSIGNED_SHORT, 0);
226 }
228 List.each(drawModel, model.models);
230 }
232 function webgl(dimensions, models) {
234 if (!window.WebGLRenderingContext) {
235 throw new Error("It appears your browser does not support WebGL! http://get.webgl.org/troubleshooting");
236 return;
237 }
239 var w = dimensions._0;
240 var h = dimensions._1;
242 function render(model) {
244 var div = newNode('div');
245 div.style.overflow = 'hidden';
246 var canvas = newNode('canvas');
247 var gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
248 gl.enable(gl.DEPTH_TEST);
250 model.cache.gl = gl;
251 model.cache.canvas = canvas;
252 model.cache.shaders = [];
253 model.cache.programs = {};
254 model.cache.buffers = [];
256 update(div, model, model);
258 return div;
260 }
262 function update(div, oldModel, newModel) {
264 newModel.cache = oldModel.cache;
266 var canvas = newModel.cache.canvas;
268 canvas.style.width = oldModel.w + 'px';
269 canvas.style.height = oldModel.h + 'px';
270 canvas.style.display = "block";
271 canvas.style.position = "absolute";
272 canvas.width = oldModel.w;
273 canvas.height = oldModel.h;
275 drawGL(newModel);
277 div.appendChild(canvas);
279 }
281 var elem = {
282 ctor: 'Custom',
283 type: 'WebGL',
284 render: render,
285 update: update,
286 model: {
287 models: models,
288 cache: {},
289 w: w,
290 h: h,
291 }
292 };
294 return A3(newElement, w, h, elem);
296 }
298 return elm.Native.Graphics.WebGL.values = {
299 model:F4(model),
300 webgl:F2(webgl)
301 };
303};