diff --git a/.gitignore b/.gitignore index a502b8ec3..80715b6ed 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ cabal-dev data */ElmFiles/* .DS_Store +build +cache diff --git a/compiler/Type/Constrain/Expression.hs b/compiler/Type/Constrain/Expression.hs index 48df53307..8009d9ea6 100644 --- a/compiler/Type/Constrain/Expression.hs +++ b/compiler/Type/Constrain/Expression.hs @@ -42,7 +42,7 @@ constrain env (L span expr) tipe = Nothing -> throwError [PP.text "Some sort of GLSL parse error"] Just sourceTipe -> return . L span $ CEqual tipe (shaderTipe sourceTipe) where - shaderTipe t = Env.get env Env.types "Graphics.WebGL.GLShader" <| t + shaderTipe t = Env.get env Env.types "Graphics.WebGL.Shader" <| t glTipe = Env.get env Env.types . PH.glTipeName declsRecord :: PH.GLShaderDecls -> TermN Variable declsRecord extractedTipes = record (fields' extractedTipes) (TermN EmptyRecord1) diff --git a/libraries/Graphics/WebGL.elm b/libraries/Graphics/WebGL.elm index fc81a3e91..5a146a3a4 100644 --- a/libraries/Graphics/WebGL.elm +++ b/libraries/Graphics/WebGL.elm @@ -1,19 +1,25 @@ -module Graphics.WebGL (encapsulate, webgl) where +module Graphics.WebGL (link, bind, encapsulate, webgl) where {-| WebGL -} import Graphics.Element (Element) import Native.Graphics.WebGL -data Shader a u v = Dummy_Shader +data GL = Dummy_GL + +data Shader auv = Dummy_Shader data Program a u = Dummy_Program -type Linker = Shader a u v -> Shader {} {} v -> Program a u + +link : GL -> Shader { attribute : a, uniform : u, varying : v } -> Shader { attribute : {}, uniform : {}, varying : v } -> Program a u +link = Native.Graphics.WebGL.link -- Binder really should not need a program -- I need runtime type information from something though... data Buffer a = Dummy_Buffer -type Binder = Program a u -> [a] -> Buffer a + +bind : GL -> Program a u -> [a] -> Buffer a +bind = Native.Graphics.WebGL.bind -- Now I cheat here because elm lacks existential types or rank-n -- I'm in essense doing the following @@ -24,5 +30,5 @@ data Model = Dummy_Model encapsulate : Program a u -> Buffer a -> u -> Model encapsulate = Native.Graphics.WebGL.encapsulate -webgl : Int -> Int -> (Linker -> Binder -> [Model]) -> Element +webgl : Int -> Int -> (GL -> [Model]) -> Element webgl = Native.Graphics.WebGL.webgl diff --git a/libraries/Native/Graphics/WebGL.js b/libraries/Native/Graphics/WebGL.js index 6b56956b2..7db941240 100644 --- a/libraries/Native/Graphics/WebGL.js +++ b/libraries/Native/Graphics/WebGL.js @@ -27,190 +27,173 @@ Elm.Native.Graphics.WebGL.make = function(elm) { } - function webgl(w, h, draw) { - - var node = newNode('canvas'); - var gl = node.getContext('webgl'); - - function link (vSrc, fSrc) { + function link (gl, vSrc, fSrc) { - function createShader(str, type) { + function createShader(str, type) { - var shader = gl.createShader(type); + var shader = gl.createShader(type); - gl.shaderSource(shader, str); - gl.compileShader(shader); - var compile = gl.COMPILE_STATUS; - if (!gl.getShaderParameter(shader,compile)) { - throw gl.getShaderInfoLog(shader); - } - - return shader; - - }; + gl.shaderSource(shader, str); + gl.compileShader(shader); + var compile = gl.COMPILE_STATUS; + if (!gl.getShaderParameter(shader,compile)) { + throw gl.getShaderInfoLog(shader); + } - var vshader = createShader(vSrc, gl.VERTEX_SHADER); - var fshader = createShader(fSrc, gl.FRAGMENT_SHADER); - var program = gl.createProgram(); + return shader; - gl.attachShader(program, vshader); - gl.attachShader(program, fshader); - gl.linkProgram(program); - if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { - throw gl.getProgramInfoLog(program); - } + }; - return program; + var vshader = createShader(vSrc, gl.VERTEX_SHADER); + var fshader = createShader(fSrc, gl.FRAGMENT_SHADER); + var program = gl.createProgram(); + gl.attachShader(program, vshader); + gl.attachShader(program, fshader); + gl.linkProgram(program); + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + throw gl.getProgramInfoLog(program); } - function bind (program, bufferElems) { + return program; - var bufferObject = {}; + } - var attributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); - for (var i = 0; i < attributes; i += 1) { - var attribute = gl.getActiveAttrib(program, i); - switch (attribute.type) { - case gl.FLOAT_VEC3: + function bind (gl, program, bufferElems) { - // Might want to invert the loop - // to build the array buffer first - // and then bind each one-at-a-time - var data = []; - List.each(function(elem){ - data.push(elem[attribute.name][0]); - data.push(elem[attribute.name][1]); - data.push(elem[attribute.name][2]); - }, bufferElems); - var array = new Float32Array(data); + var buffers = {}; - var buffer = gl.createBuffer(); - gl.bindBuffer(gl.ARRAY_BUFFER, buffer); - gl.bufferData(gl.ARRAY_BUFFER, array, gl.STATIC_DRAW); + var attributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); + for (var i = 0; i < attributes; i += 1) { + var attribute = gl.getActiveAttrib(program, i); + switch (attribute.type) { + case gl.FLOAT_VEC3: - bufferObject[attribute.name] = buffer; + // Might want to invert the loop + // to build the array buffer first + // and then bind each one-at-a-time + var data = []; + List.each(function(elem){ + data.push(elem[attribute.name][0]); + data.push(elem[attribute.name][1]); + data.push(elem[attribute.name][2]); + }, bufferElems); + var array = new Float32Array(data); - break; + var buffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + gl.bufferData(gl.ARRAY_BUFFER, array, gl.STATIC_DRAW); - default: - console.log("Bad buffer type"); - break; - } + buffers[attribute.name] = buffer; - } + break; - return bufferObject; + default: + console.log("Bad buffer type"); + break; + } } - function render(model) { - drawGL(model); - return model.node; - } + var bufferObject = { + numIndices: List.length(bufferElems), + buffers: buffers + }; - function update(canvasNode, oldModel, newModel) { - drawGL(newModel) - } + return bufferObject; } - function drawGL(model) { - - var gl = model.gl; - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - - function drawModel(glModel) { - var program = glModel.program; - gl.useProgram(program); - - var activeUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); - for (var i = 0; i < activeUniforms; i += 1) { - var uniform = gl.getActiveUniform(program, i); - var uniformLocation = gl.getUniformLocation(program, uniform.name); - switch (uniform.type) { - case gl.FLOAT_MAT4: - gl.uniformMatrix4fv(uniformLocation, false, glModel.uniforms[uniform.name]); - break; - default: - console.log("Unsupported uniform type: " + uniform.type); - break; + function webgl(w, h, draw) { + + var node = newNode('canvas'); + var gl = node.getContext('webgl'); + + function drawGL(state) { + + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + + function drawModel(model) { + + var program = model.program; + gl.useProgram(program); + + var numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); + for (var i = 0; i < numUniforms; i += 1) { + var uniform = gl.getActiveUniform(program, i); + var uniformLocation = gl.getUniformLocation(program, uniform.name); + switch (uniform.type) { + case gl.FLOAT_MAT4: + gl.uniformMatrix4fv(uniformLocation, false, model.uniforms[uniform.name]); + break; + default: + console.log("Unsupported uniform type: " + uniform.type); + break; + } } - } - var numIndices = List.length(glModel.attributes); - var indices = List.toArray(List.range(0,numIndices)); - var indexBuffer = gl.createBuffer(); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW); - - var activeAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); - for (var i = 0; i < activeAttributes; i += 1) { - var attribute = gl.getActiveAttrib(program, i); - var attribLocation = gl.getAttribLocation(program, attribute.name); - gl.enableVertexAttribArray(attribLocation); - switch (attribute.type) { - case gl.FLOAT_VEC3: - var data = []; - List.each(function(elem){ - data.push(elem[attribute.name][0]); - data.push(elem[attribute.name][1]); - data.push(elem[attribute.name][2]); - }, glModel.attributes); - var attributeBuffer = gl.createBuffer(); - gl.bindBuffer(gl.ARRAY_BUFFER, attributeBuffer); - gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW); - gl.vertexAttribPointer(attribLocation, 3, gl.FLOAT, false, 0, 0); - break; - default: - console.log("Unsupported attribute type: " + attribute.type); - break; + var numIndices = model.buffer.numIndices; + var indices = List.toArray(List.range(0,numIndices)); + + var indexBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW); + + var numAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); + for (var i = 0; i < numAttributes; i += 1) { + var attribute = gl.getActiveAttrib(program, i); + var attribLocation = gl.getAttribLocation(program, attribute.name); + gl.enableVertexAttribArray(attribLocation); + attributeBuffer = model.buffer.buffers[attribute.name]; + + switch (attribute.type) { + case gl.FLOAT_VEC3: + gl.bindBuffer(gl.ARRAY_BUFFER, attributeBuffer); + gl.vertexAttribPointer(attribLocation, 3, gl.FLOAT, false, 0, 0); + break; + default: + console.log("Unsupported attribute type: " + attribute.type); + break; + } } - } - gl.drawElements(gl.TRIANGLES, numIndices, gl.UNSIGNED_SHORT, 0); + gl.drawElements(gl.TRIANGLES, numIndices, gl.UNSIGNED_SHORT, 0); - } + } - List.each(drawModel, model.glModels); + List.each(drawModel, state.models); - } + } - /* - * Called for new node, caches lots of stuff - */ - function render(model) { - drawGL(model); - return model.node; - } + function render(state) { + drawGL(state); + return node; + } - /* - * Called at existing node - */ - function update(canvasNode, oldModel, newModel) { - drawGL(newModel) - } + function update(_node, oldState, newState) { + drawGL(newState) + } - function webgl(context, glModels) { + var models = draw(gl); - return A3(newElement, context.w, context.h, { + var elem = { ctor: 'Custom', - type: 'WebGL', - render: render, - update: update, - model: { - aspect: context.w/context.h, - node: context.node, - gl: context.gl, - glModels: glModels, - }, - }); + type: 'WebGL', + render: render, + update: update, + model: { + models: models, + } + }; + return A3(newElement, w, h, elem); } return elm.Native.Graphics.WebGL.values = { - encapsulate:F3(linkModel), - webgl:F3(webgl), + link:F3(link), + bind:F3(bind), + encapsulate:F3(encapsulate), + webgl:F3(webgl) }; }; diff --git a/tests/good/.gitignore b/tests/good/.gitignore deleted file mode 100644 index ab307b242..000000000 --- a/tests/good/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -build -cache diff --git a/tests/good/Box.elm b/tests/good/Box.elm index 4ff4aabee..b411ec59a 100644 --- a/tests/good/Box.elm +++ b/tests/good/Box.elm @@ -1,59 +1,36 @@ -import open Color -import open MJS - -import Graphics.WebGL (GLContext, Vertex, Triangle, Mesh, Scene, makeGL, bufferMesh, transform, mesh, webgl) - --- Setup the GLContext - -gl : Signal GLContext -gl = makeGL 400 400 +import MJS (V3,v3) +import Graphics.WebGL (link, bind, encapsulate, webgl) -- Define what our geometry looks like +top : V3 top = v3 0 1 0 +left : V3 left = v3 -1 -1 0 +right : V3 right = v3 1 -1 0 -geometry : Triangle -geometry = (Vertex top red, Vertex left blue, Vertex right green) - --- Bind our geometry to a mesh in our GL gl - -geoMesh : Signal Mesh -geoMesh = lift (bufferMesh [geometry]) gl - --- Build two helper transforms - -perspective : M4x4 -perspective = m4x4makePerspective 45 1 0.01 100 - -camera : Float -> M4x4 -camera r = - let eye = v3 0 0 r - center = v3 0 0 0 - up = v3 0 1 0 - in m4x4makeLookAt eye center up - --- Build the scene with extra model rotate transforms +mesh : [{ pos : V3 }] +mesh = map (\vec -> { pos = vec }) [top, left, right] -scene : Float -> Mesh -> Scene -scene angle m = - let xAxis = v3 1 0 0 - yAxis = v3 0 1 0 - rotationX = m4x4makeRotate (angle / 2) xAxis - rotationY = m4x4makeRotate (angle / 3) yAxis - in transform perspective [ - transform (camera <| 5 + 3 * sin angle) [ - transform rotationX [ - transform rotationY [ - mesh m ]]]] +vert = [glShader| +attribute vec3 pos; +void main () { + gl_Position = vec4(pos, 1.0); +} +|] --- Standard accumulator and wiring it up with main +frag = [glShader| +void main () { + gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); +} +|] -angle : Signal Float -angle = foldp (\_ a -> a + 0.1) 0 (fps 25) +draw gl = + let prog = link gl vert frag + buf = bind gl prog mesh + in [ encapsulate prog buf {} ] -main : Signal Element -main = lift2 webgl gl (lift2 scene angle geoMesh) +main = webgl 400 400 draw