├── .eslintrc.json ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── elm-package.json ├── examples ├── .gitignore ├── crate.elm ├── cube.elm ├── elm-package.json ├── first-person.elm ├── firstrender_bug.elm ├── mouse_bug.elm ├── screenshots │ ├── crate.jpg │ ├── cube.jpg │ ├── first-person.jpg │ ├── thwomp.jpg │ └── triangle.jpg ├── texture │ ├── thwomp_face.jpg │ ├── thwomp_side.jpg │ └── woodCrate.jpg ├── thwomp.elm └── triangle.elm ├── gh-pages.sh ├── pipeline.png └── src ├── Native └── WebGL.js └── WebGL.elm /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true 4 | }, 5 | "extends": "eslint:recommended", 6 | "rules": { 7 | "indent": [ "error", 2, { "SwitchCase": 1 } ], 8 | "space-infix-ops": [ "error" ], 9 | "linebreak-style": [ "error", "unix" ], 10 | "comma-style": [ "error", "last" ], 11 | "brace-style": [ "error", "1tbs", { "allowSingleLine": true } ], 12 | "curly": "error", 13 | "quotes": [ "error", "single" ], 14 | "semi": [ "error", "always" ], 15 | "space-before-function-paren": [ "error", {"anonymous": "always", "named": "never"} ], 16 | "keyword-spacing": [ "error", { "before": true, "after": true } ], 17 | "space-before-blocks": [ "error", "always" ], 18 | "key-spacing": [ "error", { "beforeColon": false, "afterColon": true, "mode": "strict" } ], 19 | "semi-spacing": [ "error", {"before": false, "after": true} ], 20 | "comma-spacing": [ "error", { "before": false, "after": true } ], 21 | "comma-dangle": ["error", "never"] 22 | }, 23 | "globals" : { 24 | "_elm_community$elm_webgl$Native_WebGL": true, 25 | 26 | "_elm_lang$virtual_dom$Native_VirtualDom": false, 27 | "_elm_lang$core$List$map": false, 28 | "_elm_lang$core$List$length": false, 29 | "_elm_lang$core$Native_Utils": false, 30 | "_elm_lang$core$Native_Scheduler": false, 31 | 32 | "A2": false, 33 | "F2": false, 34 | "F3": false, 35 | "F4": false, 36 | "F5": false, 37 | "Float32Array": false, 38 | "Int32Array": false, 39 | "Uint16Array": false 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | elm-stuff 2 | index.html 3 | .DS_Store 4 | gh-pages 5 | node_modules 6 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to elm-community/elm-webgl 2 | 3 | This repository is kept for maintenance and does not accept most feature requests. In particular, [#6](https://github.com/elm-community/elm-webgl/issues/6) requires that all 4 | changes be PATCH-level, meaning that the API cannot be modified. 5 | 6 | Any contribution should: 7 | * Compile using `elm make` 8 | * Be recognized as a PATCH change using `elm package diff` 9 | * Not introduce mutable variables 10 | * Justify why the change is needed and why it won't break anything already here 11 | * JavaScript code should be validated using `eslint src` 12 | 13 | Documentation improvements are welcome. 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, John P Mayer, Jr 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the {organization} nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Moved to [elm-explorations/webgl](http://package.elm-lang.org/packages/elm-explorations/webgl/latest) 2 | -------------------------------------------------------------------------------- /elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "3.0.4", 3 | "summary": "A library for general 3D rendering with WebGL", 4 | "repository": "https://github.com/elm-community/elm-webgl.git", 5 | "license": "BSD3", 6 | "source-directories": [ 7 | "src" 8 | ], 9 | "exposed-modules": [ 10 | "WebGL" 11 | ], 12 | "native-modules": true, 13 | "dependencies": { 14 | "elm-lang/core": "4.0.0 <= v < 5.0.0", 15 | "elm-lang/html": "1.0.0 <= v < 2.0.0" 16 | }, 17 | "elm-version": "0.17.0 <= v < 0.18.0" 18 | } 19 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | elm.js 2 | -------------------------------------------------------------------------------- /examples/crate.elm: -------------------------------------------------------------------------------- 1 | import Math.Vector2 exposing (Vec2) 2 | import Math.Vector3 exposing (..) 3 | import Math.Matrix4 exposing (..) 4 | import Task 5 | import Time exposing (Time) 6 | import WebGL exposing (..) 7 | import Html exposing (Html) 8 | import Html.App as Html 9 | import AnimationFrame 10 | import Html.Attributes exposing (width, height) 11 | 12 | 13 | type alias Model = 14 | { texture : Maybe Texture 15 | , theta : Float 16 | } 17 | 18 | 19 | type Action 20 | = TextureError Error 21 | | TextureLoaded Texture 22 | | Animate Time 23 | 24 | 25 | update : Action -> Model -> (Model, Cmd Action) 26 | update action model = 27 | case action of 28 | TextureError err -> 29 | (model, Cmd.none) 30 | TextureLoaded texture -> 31 | ({model | texture = Just texture}, Cmd.none) 32 | Animate dt -> 33 | ({model | theta = model.theta + dt / 10000}, Cmd.none) 34 | 35 | 36 | init : (Model, Cmd Action) 37 | init = 38 | ( {texture = Nothing, theta = 0} 39 | , loadTexture "texture/woodCrate.jpg" 40 | |> Task.perform TextureError TextureLoaded 41 | ) 42 | 43 | 44 | main : Program Never 45 | main = 46 | Html.program 47 | { init = init 48 | , view = view 49 | , subscriptions = (\model -> AnimationFrame.diffs Animate) 50 | , update = update 51 | } 52 | 53 | 54 | -- MESHES 55 | 56 | crate : Drawable { pos:Vec3, coord:Vec3 } 57 | crate = 58 | Triangle <| 59 | List.concatMap rotatedFace [ (0,0), (90,0), (180,0), (270,0), (0,90), (0,-90) ] 60 | 61 | 62 | rotatedFace : (Float,Float) -> List ({ pos:Vec3, coord:Vec3 }, { pos:Vec3, coord:Vec3 }, { pos:Vec3, coord:Vec3 }) 63 | rotatedFace (angleX,angleY) = 64 | let 65 | x = makeRotate (degrees angleX) (vec3 1 0 0) 66 | y = makeRotate (degrees angleY) (vec3 0 1 0) 67 | t = x `mul` y `mul` makeTranslate (vec3 0 0 1) 68 | each f (a,b,c) = 69 | (f a, f b, f c) 70 | in 71 | List.map (each (\x -> {x | pos = transform t x.pos })) face 72 | 73 | 74 | face : List ({ pos:Vec3, coord:Vec3 }, { pos:Vec3, coord:Vec3 }, { pos:Vec3, coord:Vec3 }) 75 | face = 76 | let 77 | topLeft = { pos = vec3 -1 1 0, coord = vec3 0 1 0 } 78 | topRight = { pos = vec3 1 1 0, coord = vec3 1 1 0 } 79 | bottomLeft = { pos = vec3 -1 -1 0, coord = vec3 0 0 0 } 80 | bottomRight = { pos = vec3 1 -1 0, coord = vec3 1 0 0 } 81 | in 82 | [ (topLeft,topRight,bottomLeft) 83 | , (bottomLeft,topRight,bottomRight) 84 | ] 85 | 86 | 87 | -- VIEW 88 | 89 | perspective : Float -> Mat4 90 | perspective angle = 91 | List.foldr mul Math.Matrix4.identity 92 | [ perspectiveMatrix 93 | , camera 94 | , makeRotate (3*angle) (vec3 0 1 0) 95 | , makeRotate (2*angle) (vec3 1 0 0) 96 | ] 97 | 98 | 99 | perspectiveMatrix : Mat4 100 | perspectiveMatrix = 101 | makePerspective 45 1 0.01 100 102 | 103 | 104 | camera : Mat4 105 | camera = 106 | makeLookAt (vec3 0 0 5) (vec3 0 0 0) (vec3 0 1 0) 107 | 108 | 109 | view : Model -> Html Action 110 | view {texture, theta} = 111 | 112 | (case texture of 113 | Nothing -> 114 | [] 115 | Just tex -> 116 | [render vertexShader fragmentShader crate { crate = tex, perspective = perspective theta }] 117 | ) 118 | |> WebGL.toHtml [width 400, height 400] 119 | 120 | 121 | -- SHADERS 122 | 123 | vertexShader : Shader { pos:Vec3, coord:Vec3 } { u | perspective:Mat4 } { vcoord:Vec2 } 124 | vertexShader = [glsl| 125 | 126 | attribute vec3 pos; 127 | attribute vec3 coord; 128 | uniform mat4 perspective; 129 | varying vec2 vcoord; 130 | 131 | void main () { 132 | gl_Position = perspective * vec4(pos, 1.0); 133 | vcoord = coord.xy; 134 | } 135 | 136 | |] 137 | 138 | 139 | fragmentShader : Shader {} { u | crate:Texture } { vcoord:Vec2 } 140 | fragmentShader = [glsl| 141 | 142 | precision mediump float; 143 | uniform sampler2D crate; 144 | varying vec2 vcoord; 145 | 146 | void main () { 147 | gl_FragColor = texture2D(crate, vcoord); 148 | } 149 | 150 | |] 151 | -------------------------------------------------------------------------------- /examples/cube.elm: -------------------------------------------------------------------------------- 1 | import Color exposing (..) 2 | import Math.Vector3 exposing (..) 3 | import Math.Matrix4 exposing (..) 4 | import WebGL exposing (..) 5 | import Html.App as Html 6 | import AnimationFrame 7 | import Html.Attributes exposing (width, height) 8 | 9 | 10 | main : Program Never 11 | main = 12 | Html.program 13 | { init = (0, Cmd.none) 14 | , view = scene >> WebGL.toHtml [width 400, height 400] 15 | , subscriptions = (\model -> AnimationFrame.diffs Basics.identity) 16 | , update = (\dt theta -> (theta + dt / 5000, Cmd.none)) 17 | } 18 | 19 | 20 | -- MESHES - create a cube in which each vertex has a position and color 21 | 22 | type alias Vertex = 23 | { color : Vec3 24 | , position : Vec3 25 | } 26 | 27 | 28 | cube : Drawable Vertex 29 | cube = 30 | let 31 | rft = vec3 1 1 1 -- right, front, top 32 | lft = vec3 -1 1 1 -- left, front, top 33 | lbt = vec3 -1 -1 1 34 | rbt = vec3 1 -1 1 35 | rbb = vec3 1 -1 -1 36 | rfb = vec3 1 1 -1 37 | lfb = vec3 -1 1 -1 38 | lbb = vec3 -1 -1 -1 39 | in 40 | Triangle << List.concat <| 41 | [ face green rft rfb rbb rbt -- right 42 | , face blue rft rfb lfb lft -- front 43 | , face yellow rft lft lbt rbt -- top 44 | , face red rfb lfb lbb rbb -- bottom 45 | , face purple lft lfb lbb lbt -- left 46 | , face orange rbt rbb lbb lbt -- back 47 | ] 48 | 49 | 50 | face : Color -> Vec3 -> Vec3 -> Vec3 -> Vec3 -> List (Vertex, Vertex, Vertex) 51 | face rawColor a b c d = 52 | let 53 | color = 54 | let c = toRgb rawColor in 55 | vec3 56 | (toFloat c.red / 255) 57 | (toFloat c.green / 255) 58 | (toFloat c.blue / 255) 59 | 60 | vertex position = 61 | Vertex color position 62 | in 63 | [ (vertex a, vertex b, vertex c) 64 | , (vertex c, vertex d, vertex a) 65 | ] 66 | 67 | 68 | -- VIEW 69 | 70 | scene : Float -> List Renderable 71 | scene angle = 72 | [ render vertexShader fragmentShader cube (uniforms angle) ] 73 | 74 | 75 | uniforms : Float -> { rotation:Mat4, perspective:Mat4, camera:Mat4, shade:Float } 76 | uniforms t = 77 | { rotation = mul (makeRotate (3*t) (vec3 0 1 0)) (makeRotate (2*t) (vec3 1 0 0)) 78 | , perspective = makePerspective 45 1 0.01 100 79 | , camera = makeLookAt (vec3 0 0 5) (vec3 0 0 0) (vec3 0 1 0) 80 | , shade = 0.8 81 | } 82 | 83 | 84 | -- SHADERS 85 | 86 | vertexShader : Shader { attr | position:Vec3, color:Vec3 } 87 | { unif | rotation:Mat4, perspective:Mat4, camera:Mat4 } 88 | { vcolor:Vec3 } 89 | vertexShader = [glsl| 90 | 91 | attribute vec3 position; 92 | attribute vec3 color; 93 | uniform mat4 perspective; 94 | uniform mat4 camera; 95 | uniform mat4 rotation; 96 | varying vec3 vcolor; 97 | void main () { 98 | gl_Position = perspective * camera * rotation * vec4(position, 1.0); 99 | vcolor = color; 100 | } 101 | 102 | |] 103 | 104 | 105 | fragmentShader : Shader {} { u | shade:Float } { vcolor:Vec3 } 106 | fragmentShader = [glsl| 107 | 108 | precision mediump float; 109 | uniform float shade; 110 | varying vec3 vcolor; 111 | void main () { 112 | gl_FragColor = shade * vec4(vcolor, 1.0); 113 | } 114 | 115 | |] 116 | -------------------------------------------------------------------------------- /examples/elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "summary": "Examples", 4 | "repository": "https://github.com/elm-community/elm-webgl.git", 5 | "license": "BSD3", 6 | "source-directories": [ 7 | ".", 8 | "../src" 9 | ], 10 | "exposed-modules": [], 11 | "native-modules": true, 12 | "dependencies": { 13 | "elm-community/elm-linear-algebra": "2.0.1 <= v <= 2.0.1", 14 | "elm-lang/animation-frame": "1.0.0 <= v < 2.0.0", 15 | "elm-lang/core": "4.0.0 <= v <= 4.0.0", 16 | "elm-lang/html": "1.0.0 <= v < 2.0.0", 17 | "elm-lang/keyboard": "1.0.0 <= v < 2.0.0", 18 | "elm-lang/mouse": "1.0.0 <= v < 2.0.0", 19 | "elm-lang/window": "1.0.0 <= v < 2.0.0" 20 | }, 21 | "elm-version": "0.17.0 <= v < 0.18.0" 22 | } 23 | -------------------------------------------------------------------------------- /examples/first-person.elm: -------------------------------------------------------------------------------- 1 | -- Try adding the ability to crouch or to land on top of the crate. 2 | 3 | import Keyboard 4 | import Math.Vector2 exposing (Vec2) 5 | import Math.Vector3 exposing (..) 6 | import Math.Vector3 as V3 7 | import Math.Matrix4 exposing (..) 8 | import Task exposing (Task) 9 | import Time exposing (..) 10 | import WebGL exposing (..) 11 | import Html exposing (Html, text, div) 12 | import Html.App as Html 13 | import Html.Attributes exposing (width, height, style) 14 | import AnimationFrame 15 | import Window 16 | 17 | 18 | -- MODEL 19 | 20 | type alias Person = 21 | { position : Vec3 22 | , velocity : Vec3 23 | } 24 | 25 | 26 | type alias Keys = 27 | { left : Bool 28 | , right : Bool 29 | , up : Bool 30 | , down : Bool 31 | , space : Bool 32 | } 33 | 34 | 35 | type alias Model = 36 | { texture : Maybe Texture 37 | , keys : Keys 38 | , size : Window.Size 39 | , person : Person 40 | } 41 | 42 | 43 | type Action 44 | = TextureError Error 45 | | TextureLoaded Texture 46 | | KeyChange (Keys -> Keys) 47 | | Animate Time 48 | | Resize Window.Size 49 | 50 | 51 | eyeLevel : Float 52 | eyeLevel = 2 53 | 54 | 55 | defaultPerson : Person 56 | defaultPerson = 57 | { position = vec3 0 eyeLevel -10 58 | , velocity = vec3 0 0 0 59 | } 60 | 61 | 62 | update : Action -> Model -> (Model, Cmd Action) 63 | update action model = 64 | case action of 65 | TextureError err -> 66 | (model, Cmd.none) 67 | TextureLoaded texture -> 68 | ({model | texture = Just texture}, Cmd.none) 69 | KeyChange keyfunc -> 70 | ({model | keys = keyfunc model.keys}, Cmd.none) 71 | Resize size -> 72 | ({model | size = size}, Cmd.none) 73 | Animate dt -> 74 | ( { model 75 | | person = model.person 76 | |> walk (directions model.keys) 77 | |> jump model.keys.space 78 | |> gravity (dt / 500) 79 | |> physics (dt / 500) 80 | } 81 | , Cmd.none 82 | ) 83 | 84 | 85 | init : (Model, Cmd Action) 86 | init = 87 | ( { texture = Nothing 88 | , person = defaultPerson 89 | , keys = Keys False False False False False 90 | , size = Window.Size 0 0 91 | } 92 | , Cmd.batch 93 | [ loadTexture "texture/woodCrate.jpg" 94 | |> Task.perform TextureError TextureLoaded 95 | , Window.size |> Task.perform (always Resize (0, 0)) Resize 96 | ] 97 | ) 98 | 99 | 100 | subscriptions : Model -> Sub Action 101 | subscriptions _ = 102 | [ AnimationFrame.diffs Animate 103 | , Keyboard.downs (keyChange True) 104 | , Keyboard.ups (keyChange False) 105 | , Window.resizes Resize 106 | ] 107 | |> Sub.batch 108 | 109 | 110 | keyChange : Bool -> Keyboard.KeyCode -> Action 111 | keyChange on keyCode = 112 | (case keyCode of 113 | 32 -> \k -> {k | space = on} 114 | 37 -> \k -> {k | left = on} 115 | 39 -> \k -> {k | right = on} 116 | 38 -> \k -> {k | up = on} 117 | 40 -> \k -> {k | down = on} 118 | _ -> Basics.identity 119 | ) |> KeyChange 120 | 121 | 122 | main : Program Never 123 | main = 124 | Html.program 125 | { init = init 126 | , view = view 127 | , subscriptions = subscriptions 128 | , update = update 129 | } 130 | 131 | 132 | directions : Keys -> {x : Int, y : Int} 133 | directions {left, right, up, down} = 134 | let 135 | direction a b = 136 | case (a, b) of 137 | (True, False) -> -1 138 | (False, True) -> 1 139 | _ -> 0 140 | in 141 | { x = direction left right 142 | , y = direction down up 143 | } 144 | 145 | 146 | walk : { x:Int, y:Int } -> Person -> Person 147 | walk directions person = 148 | if getY person.position > eyeLevel then 149 | person 150 | else 151 | let 152 | vx = toFloat -directions.x 153 | vz = toFloat directions.y 154 | in 155 | { person | 156 | velocity = vec3 vx (getY person.velocity) vz 157 | } 158 | 159 | 160 | jump : Bool -> Person -> Person 161 | jump isJumping person = 162 | if not isJumping || getY person.position > eyeLevel then 163 | person 164 | else 165 | let 166 | (vx,_,vz) = toTuple person.velocity 167 | in 168 | { person | 169 | velocity = vec3 vx 2 vz 170 | } 171 | 172 | 173 | physics : Float -> Person -> Person 174 | physics dt person = 175 | let 176 | position = 177 | person.position `add` V3.scale dt person.velocity 178 | 179 | (x,y,z) = toTuple position 180 | in 181 | { person | 182 | position = 183 | if y < eyeLevel then vec3 x eyeLevel z else position 184 | } 185 | 186 | 187 | gravity : Float -> Person -> Person 188 | gravity dt person = 189 | if getY person.position <= eyeLevel then 190 | person 191 | else 192 | let 193 | v = toRecord person.velocity 194 | in 195 | { person | 196 | velocity = vec3 v.x (v.y - 2 * dt) v.z 197 | } 198 | 199 | 200 | world : Maybe Texture -> Mat4 -> List Renderable 201 | world maybeTexture perspective = 202 | case maybeTexture of 203 | Nothing -> 204 | [] 205 | 206 | Just tex -> 207 | [render vertexShader fragmentShader crate { crate=tex, perspective=perspective }] 208 | 209 | 210 | -- VIEW 211 | 212 | perspective : (Int,Int) -> Person -> Mat4 213 | perspective (w,h) person = 214 | mul (makePerspective 45 (toFloat w / toFloat h) 0.01 100) 215 | (makeLookAt person.position (person.position `add` k) j) 216 | 217 | 218 | view : Model -> Html Action 219 | view {size, person, texture} = 220 | let 221 | perspectiveMatrix = perspective (size.width, size.height) person 222 | entities = world texture perspectiveMatrix 223 | in 224 | div 225 | [ style 226 | [ ("width", toString size.width ++ "px") 227 | , ("height", toString size.height ++ "px") 228 | , ("position", "relative") 229 | ] 230 | ] 231 | [ WebGL.toHtml 232 | [width size.width, height size.height, style [("display", "block")]] 233 | entities 234 | , div 235 | [ style 236 | [ ("position", "absolute") 237 | , ("font-family", "monospace") 238 | , ("text-align", "center") 239 | , ("left", "20px") 240 | , ("right", "20px") 241 | , ("top", "20px") 242 | ] 243 | ] 244 | [text message] 245 | ] 246 | 247 | 248 | message : String 249 | message = 250 | "Walk around with a first person perspective.\n" 251 | ++ "Arrows keys to move, space bar to jump." 252 | 253 | 254 | -- Define the mesh for a crate 255 | 256 | type alias Vertex = 257 | { position : Vec3 258 | , coord : Vec3 259 | } 260 | 261 | 262 | crate : Drawable Vertex 263 | crate = 264 | Triangle (List.concatMap rotatedFace [ (0,0), (90,0), (180,0), (270,0), (0,90), (0,-90) ]) 265 | 266 | 267 | rotatedFace : (Float,Float) -> List (Vertex, Vertex, Vertex) 268 | rotatedFace (angleXZ,angleYZ) = 269 | let 270 | x = makeRotate (degrees angleXZ) j 271 | y = makeRotate (degrees angleYZ) i 272 | t = x `mul` y 273 | each f (a,b,c) = (f a, f b, f c) 274 | in 275 | List.map (each (\v -> {v | position = transform t v.position })) face 276 | 277 | 278 | face : List (Vertex, Vertex, Vertex) 279 | face = 280 | let 281 | topLeft = Vertex (vec3 -1 1 1) (vec3 0 1 0) 282 | topRight = Vertex (vec3 1 1 1) (vec3 1 1 0) 283 | bottomLeft = Vertex (vec3 -1 -1 1) (vec3 0 0 0) 284 | bottomRight = Vertex (vec3 1 -1 1) (vec3 1 0 0) 285 | in 286 | [ (topLeft,topRight,bottomLeft) 287 | , (bottomLeft,topRight,bottomRight) 288 | ] 289 | 290 | 291 | -- Shaders 292 | 293 | vertexShader : Shader { position:Vec3, coord:Vec3 } { u | perspective:Mat4 } { vcoord:Vec2 } 294 | vertexShader = [glsl| 295 | 296 | attribute vec3 position; 297 | attribute vec3 coord; 298 | uniform mat4 perspective; 299 | varying vec2 vcoord; 300 | 301 | void main () { 302 | gl_Position = perspective * vec4(position, 1.0); 303 | vcoord = coord.xy; 304 | } 305 | 306 | |] 307 | 308 | 309 | fragmentShader : Shader {} { u | crate:Texture } { vcoord:Vec2 } 310 | fragmentShader = [glsl| 311 | 312 | precision mediump float; 313 | uniform sampler2D crate; 314 | varying vec2 vcoord; 315 | 316 | void main () { 317 | gl_FragColor = texture2D(crate, vcoord); 318 | } 319 | 320 | |] 321 | -------------------------------------------------------------------------------- /examples/firstrender_bug.elm: -------------------------------------------------------------------------------- 1 | module Main exposing (main) 2 | 3 | -- A test case for https://github.com/elm-community/elm-webgl/issues/23 4 | -- WebGL should render before the first virtual-dom diff is applied 5 | 6 | 7 | import Math.Vector3 as Vec3 exposing (Vec3) 8 | import Html as H exposing (Html) 9 | import Html.Attributes as HA 10 | import WebGL 11 | 12 | 13 | main : Html msg 14 | main = 15 | WebGL.toHtml 16 | [ HA.width 400 17 | , HA.height 300 18 | ] 19 | [ WebGL.render vertexShader fragmentShader heroVertices {} ] 20 | 21 | 22 | type alias Vertex = 23 | { pos : Vec3 } 24 | 25 | 26 | heroVertices : WebGL.Drawable Vertex 27 | heroVertices = 28 | WebGL.Triangle 29 | [ ( { pos = Vec3.vec3 0 0 0 } 30 | , { pos = Vec3.vec3 1 1 0 } 31 | , { pos = Vec3.vec3 1 -1 0 } 32 | ) 33 | ] 34 | 35 | 36 | vertexShader : WebGL.Shader Vertex {} {} 37 | vertexShader = [glsl| 38 | precision mediump float; 39 | attribute vec3 pos; 40 | void main() { 41 | gl_Position = vec4(0.5 * pos, 1); 42 | } 43 | |] 44 | 45 | 46 | fragmentShader : WebGL.Shader {} {} {} 47 | fragmentShader = [glsl| 48 | precision mediump float; 49 | void main() { 50 | gl_FragColor = vec4(0, 0, 1, 1); 51 | } 52 | |] 53 | -------------------------------------------------------------------------------- /examples/mouse_bug.elm: -------------------------------------------------------------------------------- 1 | import Mouse 2 | import WebGL as GL 3 | import Math.Vector3 exposing (..) 4 | import Math.Vector2 exposing (..) 5 | import Math.Matrix4 as Mat4 6 | import Html.App as Html 7 | import Html exposing (Html) 8 | import Html.Attributes exposing (width, height) 9 | 10 | 11 | main : Program Never 12 | main = 13 | Html.program 14 | { init = ({x = 0, y = 0}, Cmd.none) 15 | , view = view 16 | , subscriptions = (\_ -> Mouse.moves identity) 17 | , update = (\pos _ -> (pos, Cmd.none)) 18 | } 19 | 20 | 21 | type alias Vertex = { position : Vec2, color : Vec3 } 22 | 23 | mesh : GL.Drawable Vertex 24 | mesh = GL.Triangle <| 25 | [ ( Vertex (vec2 0 0 ) (vec3 1 0 0) 26 | , Vertex (vec2 1 1 ) (vec3 0 1 0) 27 | , Vertex (vec2 1 0 ) (vec3 0 0 1) 28 | ), 29 | ( Vertex (vec2 0 0 ) (vec3 1 0 0) 30 | , Vertex (vec2 0 1 ) (vec3 0 0 1) 31 | , Vertex (vec2 1 1 ) (vec3 0 1 0) 32 | ) 33 | ] 34 | 35 | 36 | ortho2D : Float -> Float -> Mat4.Mat4 37 | ortho2D w h = Mat4.makeOrtho2D 0 w h 0 38 | 39 | 40 | view : Mouse.Position -> Html (Mouse.Position) 41 | view {x, y} = 42 | GL.toHtml 43 | [width x, height y] 44 | [ GL.render vertexShader fragmentShader mesh { mat = ortho2D 1 1 } ] 45 | 46 | 47 | -- Shaders 48 | 49 | vertexShader : GL.Shader { attr | position:Vec2, color:Vec3 } { unif | mat:Mat4.Mat4 } { vcolor:Vec3 } 50 | vertexShader = [glsl| 51 | 52 | attribute vec2 position; 53 | attribute vec3 color; 54 | uniform mat4 mat; 55 | varying vec3 vcolor; 56 | 57 | void main () { 58 | gl_Position = mat * vec4(position, 0.0, 1.0); 59 | vcolor = color; 60 | } 61 | 62 | |] 63 | 64 | 65 | fragmentShader : GL.Shader {} u { vcolor:Vec3 } 66 | fragmentShader = [glsl| 67 | 68 | precision mediump float; 69 | varying vec3 vcolor; 70 | 71 | void main () { 72 | gl_FragColor = vec4(vcolor, 1.0); 73 | } 74 | 75 | |] 76 | -------------------------------------------------------------------------------- /examples/screenshots/crate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elm-community/elm-webgl/4c0dcf44b1266fd840e4c7875337d0bfb98950ea/examples/screenshots/crate.jpg -------------------------------------------------------------------------------- /examples/screenshots/cube.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elm-community/elm-webgl/4c0dcf44b1266fd840e4c7875337d0bfb98950ea/examples/screenshots/cube.jpg -------------------------------------------------------------------------------- /examples/screenshots/first-person.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elm-community/elm-webgl/4c0dcf44b1266fd840e4c7875337d0bfb98950ea/examples/screenshots/first-person.jpg -------------------------------------------------------------------------------- /examples/screenshots/thwomp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elm-community/elm-webgl/4c0dcf44b1266fd840e4c7875337d0bfb98950ea/examples/screenshots/thwomp.jpg -------------------------------------------------------------------------------- /examples/screenshots/triangle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elm-community/elm-webgl/4c0dcf44b1266fd840e4c7875337d0bfb98950ea/examples/screenshots/triangle.jpg -------------------------------------------------------------------------------- /examples/texture/thwomp_face.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elm-community/elm-webgl/4c0dcf44b1266fd840e4c7875337d0bfb98950ea/examples/texture/thwomp_face.jpg -------------------------------------------------------------------------------- /examples/texture/thwomp_side.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elm-community/elm-webgl/4c0dcf44b1266fd840e4c7875337d0bfb98950ea/examples/texture/thwomp_side.jpg -------------------------------------------------------------------------------- /examples/texture/woodCrate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elm-community/elm-webgl/4c0dcf44b1266fd840e4c7875337d0bfb98950ea/examples/texture/woodCrate.jpg -------------------------------------------------------------------------------- /examples/thwomp.elm: -------------------------------------------------------------------------------- 1 | -- Thanks to The PaperNES Guy for the texture: 2 | -- http://the-papernes-guy.deviantart.com/art/Thwomps-Thwomps-Thwomps-186879685 3 | 4 | import Math.Vector2 exposing (Vec2) 5 | import Math.Vector3 as V3 exposing (..) 6 | import Math.Matrix4 exposing (..) 7 | import Mouse 8 | import Task exposing (Task) 9 | import WebGL exposing (..) 10 | import Window 11 | import Html.App as Html 12 | import Html exposing (Html) 13 | import Html.Attributes exposing (width, height) 14 | 15 | 16 | type alias Model = 17 | { size : Window.Size 18 | , position : Mouse.Position 19 | , textures : (Maybe Texture, Maybe Texture) 20 | } 21 | 22 | 23 | type Action 24 | = TexturesError Error 25 | | TexturesLoaded (Maybe Texture, Maybe Texture) 26 | | Resize Window.Size 27 | | MouseMove Mouse.Position 28 | 29 | 30 | update : Action -> Model -> (Model, Cmd Action) 31 | update action model = 32 | case action of 33 | TexturesError err -> 34 | (model, Cmd.none) 35 | TexturesLoaded textures -> 36 | ({model | textures = textures}, Cmd.none) 37 | Resize size -> 38 | ({model | size = size}, Cmd.none) 39 | MouseMove position -> 40 | ({model | position = position}, Cmd.none) 41 | 42 | 43 | init : (Model, Cmd Action) 44 | init = 45 | ( { size = {width = 0, height = 0} 46 | , position = {x = 0, y = 0} 47 | , textures = (Nothing, Nothing) 48 | } 49 | , Cmd.batch 50 | [ Window.size |> Task.perform (always Resize (0, 0)) Resize 51 | , fetchTextures |> Task.perform TexturesError TexturesLoaded 52 | ] 53 | ) 54 | 55 | 56 | subscriptions : Model -> Sub Action 57 | subscriptions _ = 58 | Sub.batch 59 | [ Window.resizes Resize 60 | , Mouse.moves MouseMove 61 | ] 62 | 63 | 64 | main : Program Never 65 | main = 66 | Html.program 67 | { init = init 68 | , view = view face sides 69 | , subscriptions = subscriptions 70 | , update = update 71 | } 72 | 73 | 74 | fetchTextures : Task Error (Maybe Texture, Maybe Texture) 75 | fetchTextures = 76 | loadTexture "texture/thwomp_face.jpg" `Task.andThen` \faceTexture -> 77 | loadTexture "texture/thwomp_side.jpg" `Task.andThen` \sideTexture -> 78 | Task.succeed (Just faceTexture, Just sideTexture) 79 | 80 | 81 | -- MESHES - define the mesh for a Thwomp's face 82 | 83 | type alias Vertex = 84 | { position : Vec3, coord : Vec3 } 85 | 86 | 87 | face : List (Vertex, Vertex, Vertex) 88 | face = 89 | rotatedSquare (0,0) 90 | 91 | 92 | sides : List (Vertex, Vertex, Vertex) 93 | sides = 94 | List.concatMap rotatedSquare [ (90,0), (180,0), (270,0), (0,90), (0,-90) ] 95 | 96 | 97 | rotatedSquare : (Float,Float) -> List (Vertex, Vertex, Vertex) 98 | rotatedSquare (angleXZ,angleYZ) = 99 | let x = makeRotate (degrees angleXZ) j 100 | y = makeRotate (degrees angleYZ) i 101 | t = x `mul` y 102 | each f (a,b,c) = (f a, f b, f c) 103 | in 104 | List.map (each (\v -> {v | position = transform t v.position })) square 105 | 106 | 107 | square : List (Vertex, Vertex, Vertex) 108 | square = 109 | let topLeft = Vertex (vec3 -1 1 1) (vec3 0 1 0) 110 | topRight = Vertex (vec3 1 1 1) (vec3 1 1 0) 111 | bottomLeft = Vertex (vec3 -1 -1 1) (vec3 0 0 0) 112 | bottomRight = Vertex (vec3 1 -1 1) (vec3 1 0 0) 113 | in 114 | [ (topLeft,topRight,bottomLeft) 115 | , (bottomLeft,topRight,bottomRight) 116 | ] 117 | 118 | 119 | -- VIEW 120 | 121 | perspective : Model -> Mat4 122 | perspective {size, position} = 123 | let w = toFloat size.width 124 | h = toFloat size.height 125 | x = toFloat position.x 126 | y = toFloat position.y 127 | 128 | distance = 6 129 | 130 | eyeX = distance * (w/2 - x) / w 131 | eyeY = distance * (y - h/2) / h 132 | eye = V3.scale distance (V3.normalize (vec3 eyeX eyeY distance)) 133 | in 134 | mul (makePerspective 45 (w/h) 0.01 100) 135 | (makeLookAt eye (vec3 0 0 0) j) 136 | 137 | 138 | view : List (Vertex, Vertex, Vertex) 139 | -> List (Vertex, Vertex, Vertex) 140 | -> Model 141 | -> Html Action 142 | view mesh1 mesh2 ({textures, size} as model) = 143 | let 144 | perspectiveMatrix = perspective model 145 | (texture1, texture2) = textures 146 | in 147 | WebGL.toHtml 148 | [ width size.width, height size.height ] 149 | ( toEntity mesh1 texture1 perspectiveMatrix ++ 150 | toEntity mesh2 texture2 perspectiveMatrix 151 | ) 152 | 153 | 154 | toEntity : List (Vertex, Vertex, Vertex) -> Maybe Texture -> Mat4 -> List Renderable 155 | toEntity mesh response perspective = 156 | response 157 | |> Maybe.map (\texture -> 158 | [ render vertexShader fragmentShader (Triangle mesh) { texture=texture, perspective=perspective } ]) 159 | |> Maybe.withDefault [] 160 | 161 | 162 | -- SHADERS 163 | 164 | vertexShader : Shader { position:Vec3, coord:Vec3 } { u | perspective:Mat4 } { vcoord:Vec2 } 165 | vertexShader = [glsl| 166 | 167 | attribute vec3 position; 168 | attribute vec3 coord; 169 | uniform mat4 perspective; 170 | varying vec2 vcoord; 171 | 172 | void main () { 173 | gl_Position = perspective * vec4(position, 1.0); 174 | vcoord = coord.xy; 175 | } 176 | 177 | |] 178 | 179 | 180 | fragmentShader : Shader {} { u | texture:Texture } { vcoord:Vec2 } 181 | fragmentShader = [glsl| 182 | 183 | precision mediump float; 184 | uniform sampler2D texture; 185 | varying vec2 vcoord; 186 | 187 | void main () { 188 | gl_FragColor = texture2D(texture, vcoord); 189 | } 190 | 191 | |] 192 | -------------------------------------------------------------------------------- /examples/triangle.elm: -------------------------------------------------------------------------------- 1 | import Math.Vector3 exposing (..) 2 | import Math.Matrix4 exposing (..) 3 | import WebGL exposing (..) 4 | import Html exposing (Html) 5 | import Html.App as Html 6 | import Html.Attributes exposing (width, height) 7 | import AnimationFrame 8 | 9 | -- Create a mesh with two triangles 10 | 11 | type alias Vertex = { position : Vec3, color : Vec3 } 12 | 13 | 14 | mesh : Drawable Vertex 15 | mesh = Triangle 16 | [ ( Vertex (vec3 0 0 0) (vec3 1 0 0) 17 | , Vertex (vec3 1 1 0) (vec3 0 1 0) 18 | , Vertex (vec3 1 -1 0) (vec3 0 0 1) 19 | ) 20 | ] 21 | 22 | 23 | main : Program Never 24 | main = 25 | Html.program 26 | { init = (0, Cmd.none) 27 | , view = view 28 | , subscriptions = (\model -> AnimationFrame.diffs Basics.identity) 29 | , update = (\elapsed currentTime -> (elapsed + currentTime, Cmd.none)) 30 | } 31 | 32 | 33 | view : Float -> Html msg 34 | view t = 35 | WebGL.toHtml 36 | [ width 400, height 400 ] 37 | [ render vertexShader fragmentShader mesh { perspective = perspective (t / 1000) } ] 38 | 39 | 40 | perspective : Float -> Mat4 41 | perspective t = 42 | mul (makePerspective 45 1 0.01 100) 43 | (makeLookAt (vec3 (4 * cos t) 0 (4 * sin t)) (vec3 0 0 0) (vec3 0 1 0)) 44 | 45 | 46 | -- Shaders 47 | 48 | vertexShader : Shader { attr | position:Vec3, color:Vec3 } { unif | perspective:Mat4 } { vcolor:Vec3 } 49 | vertexShader = [glsl| 50 | 51 | attribute vec3 position; 52 | attribute vec3 color; 53 | uniform mat4 perspective; 54 | varying vec3 vcolor; 55 | 56 | void main () { 57 | gl_Position = perspective * vec4(position, 1.0); 58 | vcolor = color; 59 | } 60 | 61 | |] 62 | 63 | 64 | fragmentShader : Shader {} u { vcolor:Vec3 } 65 | fragmentShader = [glsl| 66 | 67 | precision mediump float; 68 | varying vec3 vcolor; 69 | 70 | void main () { 71 | gl_FragColor = vec4(vcolor, 1.0); 72 | } 73 | 74 | |] 75 | -------------------------------------------------------------------------------- /gh-pages.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | rm -rf gh-pages || exit 0; 5 | 6 | mkdir -p gh-pages/examples 7 | 8 | # compile JS using Elm 9 | cd examples 10 | for i in crate cube first-person thwomp triangle; do 11 | elm make $i.elm --yes --output ../gh-pages/examples/$i.html 12 | done 13 | 14 | # copy the textures 15 | cp -R texture ../gh-pages/examples 16 | cp -R screenshots ../gh-pages/examples 17 | 18 | # configure domain 19 | cd ../gh-pages 20 | echo "webgl.elm-community.org" >> CNAME 21 | 22 | # init branch and commit 23 | git init 24 | git add . 25 | git commit -m "Deploying to GH Pages" 26 | git push --force "git@github.com:elm-community/elm-webgl.git" master:gh-pages 27 | -------------------------------------------------------------------------------- /pipeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elm-community/elm-webgl/4c0dcf44b1266fd840e4c7875337d0bfb98950ea/pipeline.png -------------------------------------------------------------------------------- /src/Native/WebGL.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-unused-vars 2 | var _elm_community$elm_webgl$Native_WebGL = function () { 3 | 4 | // setup logging 5 | // eslint-disable-next-line no-unused-vars 6 | function LOG(msg) { 7 | // console.log(msg); 8 | } 9 | 10 | var Utils = _elm_lang$core$Native_Utils; 11 | 12 | var rAF = typeof requestAnimationFrame !== 'undefined' ? 13 | requestAnimationFrame : 14 | function (cb) { setTimeout(cb, 1000 / 60); }; 15 | 16 | function unsafeCoerceGLSL(src) { 17 | return { src: src }; 18 | } 19 | 20 | function loadTexture(source) { 21 | return _elm_lang$core$Native_Scheduler.nativeBinding(function (callback) { 22 | var img = new Image(); 23 | img.onload = function () { 24 | return callback(_elm_lang$core$Native_Scheduler.succeed({ ctor: 'Texture', img: img })); 25 | }; 26 | img.onerror = function () { 27 | return callback(_elm_lang$core$Native_Scheduler.fail({ ctor: 'Error' })); 28 | }; 29 | img.crossOrigin = 'Anonymous'; 30 | img.src = source; 31 | }); 32 | } 33 | 34 | function loadTextureRaw(filter, source) { 35 | return _elm_lang$core$Native_Scheduler.nativeBinding(function (callback) { 36 | var img = new Image(); 37 | img.onload = function () { 38 | return callback(_elm_lang$core$Native_Scheduler.succeed({ ctor: 'RawTexture', img: img })); 39 | }; 40 | img.onerror = function () { 41 | return callback(_elm_lang$core$Native_Scheduler.fail({ ctor: 'Error' })); 42 | }; 43 | img.crossOrigin = 'Anonymous'; 44 | img.src = source; 45 | }); 46 | } 47 | 48 | function textureSize(texture) { 49 | return Utils.Tuple2(texture.img.width, texture.img.height); 50 | } 51 | 52 | function render(vert, frag, buffer, uniforms, functionCalls) { 53 | 54 | if (!buffer.guid) { 55 | buffer.guid = Utils.guid(); 56 | } 57 | 58 | return { 59 | vert: vert, 60 | frag: frag, 61 | buffer: buffer, 62 | uniforms: uniforms, 63 | functionCalls: functionCalls 64 | }; 65 | 66 | } 67 | 68 | function do_texture(gl, texture) { 69 | 70 | var tex = gl.createTexture(); 71 | LOG('Created texture'); 72 | 73 | gl.bindTexture(gl.TEXTURE_2D, tex); 74 | gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); 75 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.img); 76 | switch (texture.ctor) { 77 | case 'Texture': 78 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); 79 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 80 | break; 81 | case 'RawTexture': 82 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 83 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 84 | break; 85 | } 86 | gl.generateMipmap(gl.TEXTURE_2D); 87 | //gl.bindTexture(gl.TEXTURE0, null); 88 | return tex; 89 | 90 | } 91 | 92 | function do_compile(gl, src, tipe) { 93 | 94 | var shader = gl.createShader(tipe); 95 | LOG('Created shader'); 96 | 97 | gl.shaderSource(shader, src); 98 | gl.compileShader(shader); 99 | var compile = gl.COMPILE_STATUS; 100 | if (!gl.getShaderParameter(shader, compile)) { 101 | throw gl.getShaderInfoLog(shader); 102 | } 103 | 104 | return shader; 105 | 106 | } 107 | 108 | function do_link(gl, vshader, fshader) { 109 | 110 | var program = gl.createProgram(); 111 | LOG('Created program'); 112 | 113 | gl.attachShader(program, vshader); 114 | gl.attachShader(program, fshader); 115 | gl.linkProgram(program); 116 | if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { 117 | throw gl.getProgramInfoLog(program); 118 | } 119 | 120 | return program; 121 | 122 | } 123 | 124 | function get_render_info(gl, render_type) { 125 | switch (render_type) { 126 | case 'Triangle': 127 | return { mode: gl.TRIANGLES, elemSize: 3 }; 128 | case 'LineStrip': 129 | return { mode: gl.LINE_STRIP, elemSize: 1 }; 130 | case 'LineLoop': 131 | return { mode: gl.LINE_LOOP, elemSize: 1 }; 132 | case 'Points': 133 | return { mode: gl.POINTS, elemSize: 1 }; 134 | case 'Lines': 135 | return { mode: gl.LINES, elemSize: 2 }; 136 | case 'TriangleStrip': 137 | return { mode: gl.TRIANGLE_STRIP, elemSize: 1 }; 138 | case 'TriangleFan': 139 | return { mode: gl.TRIANGLE_FAN, elemSize: 1 }; 140 | } 141 | } 142 | 143 | function get_attribute_info(gl, type) { 144 | switch (type) { 145 | case gl.FLOAT: 146 | return { size: 1, type: Float32Array, baseType: gl.FLOAT }; 147 | case gl.FLOAT_VEC2: 148 | return { size: 2, type: Float32Array, baseType: gl.FLOAT }; 149 | case gl.FLOAT_VEC3: 150 | return { size: 3, type: Float32Array, baseType: gl.FLOAT }; 151 | case gl.FLOAT_VEC4: 152 | return { size: 4, type: Float32Array, baseType: gl.FLOAT }; 153 | case gl.INT: 154 | return { size: 1, type: Int32Array, baseType: gl.INT }; 155 | case gl.INT_VEC2: 156 | return { size: 2, type: Int32Array, baseType: gl.INT }; 157 | case gl.INT_VEC3: 158 | return { size: 3, type: Int32Array, baseType: gl.INT }; 159 | case gl.INT_VEC4: 160 | return { size: 4, type: Int32Array, baseType: gl.INT }; 161 | } 162 | } 163 | 164 | /** 165 | Form the buffer for a given attribute. 166 | 167 | @param gl gl context 168 | @param attribute the attribute to bind to. We use its name to grab the record by name and also to know 169 | how many elements we need to grab. 170 | @param bufferElems The list coming in from elm. 171 | @param elem_length The length of the number of vertices that complete one 'thing' based on the drawing mode. 172 | ie, 2 for Lines, 3 for Triangles, etc. 173 | */ 174 | function do_bind_attribute(gl, attribute, bufferElems, elem_length) { 175 | var idxKeys = []; 176 | for (var i = 0; i < elem_length; i++) { 177 | idxKeys.push('_' + i); 178 | } 179 | 180 | function dataFill(data, cnt, fillOffset, elem, key) { 181 | if (elem_length == 1) { 182 | for (var i = 0; i < cnt; i++) { 183 | data[fillOffset++] = cnt === 1 ? elem[key] : elem[key][i]; 184 | } 185 | } else { 186 | idxKeys.forEach(function (idx) { 187 | for (var i = 0; i < cnt; i++) { 188 | data[fillOffset++] = (cnt === 1 ? elem[idx][key] : elem[idx][key][i]); 189 | } 190 | }); 191 | } 192 | } 193 | 194 | var attributeInfo = get_attribute_info(gl, attribute.type); 195 | 196 | if (attributeInfo === undefined) { 197 | throw new Error('No info available for: ' + attribute.type); 198 | } 199 | 200 | var data_idx = 0; 201 | var array = new attributeInfo.type( _elm_lang$core$List$length(bufferElems) * attributeInfo.size * elem_length); 202 | 203 | A2(_elm_lang$core$List$map, function (elem) { 204 | dataFill(array, attributeInfo.size, data_idx, elem, attribute.name); 205 | data_idx += attributeInfo.size * elem_length; 206 | }, bufferElems); 207 | 208 | var buffer = gl.createBuffer(); 209 | LOG('Created attribute buffer ' + attribute.name); 210 | 211 | gl.bindBuffer(gl.ARRAY_BUFFER, buffer); 212 | gl.bufferData(gl.ARRAY_BUFFER, array, gl.STATIC_DRAW); 213 | return buffer; 214 | } 215 | 216 | /** 217 | This sets up the binding cacheing buffers. 218 | 219 | We don't actually bind any buffers now except for the indices buffer, which we fill with 0..n. The problem 220 | with filling the buffers here is that it is possible to have a buffer shared between two webgl shaders; which 221 | could have different active attributes. If we bind it here against a particular program, we might not bind 222 | them all. That final bind is now done right before drawing. 223 | 224 | @param gl gl context 225 | @param bufferElems The list coming in from elm. 226 | @param elem_length The length of the number of vertices that complete one 'thing' based on the drawing mode. 227 | ie, 2 for Lines, 3 for Triangles, etc. 228 | */ 229 | function do_bind_setup(gl, bufferElems, elem_length) { 230 | var buffers = {}; 231 | 232 | var numIndices = elem_length * _elm_lang$core$List$length(bufferElems); 233 | var indices = new Uint16Array(numIndices); 234 | for (var i = 0; i < numIndices; i += 1) { 235 | indices[i] = i; 236 | } 237 | var indexBuffer = gl.createBuffer(); 238 | LOG('Created index buffer'); 239 | 240 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); 241 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); 242 | 243 | var bufferObject = { 244 | numIndices: numIndices, 245 | indexBuffer: indexBuffer, 246 | buffers: buffers 247 | }; 248 | 249 | return bufferObject; 250 | 251 | } 252 | 253 | function getProgID(vertID, fragID) { 254 | return vertID + '#' + fragID; 255 | } 256 | 257 | function drawGL(domNode, data) { 258 | 259 | var model = data.model; 260 | var gl = model.cache.gl; 261 | 262 | if (!gl) { 263 | return domNode; 264 | } 265 | 266 | gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); 267 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 268 | LOG('Drawing'); 269 | 270 | function drawEntity(render) { 271 | if (_elm_lang$core$List$length(render.buffer._0) === 0) { 272 | return; 273 | } 274 | 275 | var progid; 276 | var program; 277 | if (render.vert.id && render.frag.id) { 278 | progid = getProgID(render.vert.id, render.frag.id); 279 | program = model.cache.programs[progid]; 280 | } 281 | 282 | if (!program) { 283 | 284 | var vshader; 285 | if (render.vert.id) { 286 | vshader = model.cache.shaders[render.vert.id]; 287 | } else { 288 | render.vert.id = Utils.guid(); 289 | } 290 | 291 | if (!vshader) { 292 | vshader = do_compile(gl, render.vert.src, gl.VERTEX_SHADER); 293 | model.cache.shaders[render.vert.id] = vshader; 294 | } 295 | 296 | var fshader; 297 | if (render.frag.id) { 298 | fshader = model.cache.shaders[render.frag.id]; 299 | } else { 300 | render.frag.id = Utils.guid(); 301 | } 302 | 303 | if (!fshader) { 304 | fshader = do_compile(gl, render.frag.src, gl.FRAGMENT_SHADER); 305 | model.cache.shaders[render.frag.id] = fshader; 306 | } 307 | 308 | program = do_link(gl, vshader, fshader); 309 | progid = getProgID(render.vert.id, render.frag.id); 310 | model.cache.programs[progid] = program; 311 | 312 | } 313 | 314 | gl.useProgram(program); 315 | 316 | progid = progid || getProgID(render.vert.id, render.frag.id); 317 | var setters = model.cache.uniformSetters[progid]; 318 | if (!setters) { 319 | setters = createUniformSetters(gl, model, program); 320 | model.cache.uniformSetters[progid] = setters; 321 | } 322 | 323 | setUniforms(setters, render.uniforms); 324 | 325 | var renderType = get_render_info(gl, render.buffer.ctor); 326 | var buffer = model.cache.buffers[render.buffer.guid]; 327 | 328 | if (!buffer) { 329 | buffer = do_bind_setup(gl, render.buffer._0, renderType.elemSize); 330 | model.cache.buffers[render.buffer.guid] = buffer; 331 | } 332 | 333 | var numIndices = buffer.numIndices; 334 | var indexBuffer = buffer.indexBuffer; 335 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); 336 | 337 | var numAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); 338 | 339 | for (var i = 0; i < numAttributes; i += 1) { 340 | var attribute = gl.getActiveAttrib(program, i); 341 | 342 | var attribLocation = gl.getAttribLocation(program, attribute.name); 343 | gl.enableVertexAttribArray(attribLocation); 344 | 345 | if (buffer.buffers[attribute.name] === undefined) { 346 | buffer.buffers[attribute.name] = do_bind_attribute(gl, attribute, render.buffer._0, renderType.elemSize); 347 | } 348 | var attributeBuffer = buffer.buffers[attribute.name]; 349 | var attributeInfo = get_attribute_info(gl, attribute.type); 350 | 351 | A2(_elm_lang$core$List$map, function (functionCall) { 352 | functionCall(gl); 353 | }, render.functionCalls); 354 | 355 | gl.bindBuffer(gl.ARRAY_BUFFER, attributeBuffer); 356 | gl.vertexAttribPointer(attribLocation, attributeInfo.size, attributeInfo.baseType, false, 0, 0); 357 | } 358 | gl.drawElements(renderType.mode, numIndices, gl.UNSIGNED_SHORT, 0); 359 | 360 | } 361 | 362 | A2(_elm_lang$core$List$map, drawEntity, model.renderables); 363 | return domNode; 364 | } 365 | 366 | function createUniformSetters(gl, model, program) { 367 | 368 | var textureCounter = 0; 369 | function createUniformSetter(program, uniform) { 370 | var uniformLocation = gl.getUniformLocation(program, uniform.name); 371 | switch (uniform.type) { 372 | case gl.INT: 373 | return function (value) { 374 | gl.uniform1i(uniformLocation, value); 375 | }; 376 | case gl.FLOAT: 377 | return function (value) { 378 | gl.uniform1f(uniformLocation, value); 379 | }; 380 | case gl.FLOAT_VEC2: 381 | return function (value) { 382 | gl.uniform2fv(uniformLocation, value); 383 | }; 384 | case gl.FLOAT_VEC3: 385 | return function (value) { 386 | gl.uniform3fv(uniformLocation, value); 387 | }; 388 | case gl.FLOAT_VEC4: 389 | return function (value) { 390 | gl.uniform4fv(uniformLocation, value); 391 | }; 392 | case gl.FLOAT_MAT4: 393 | return function (value) { 394 | gl.uniformMatrix4fv(uniformLocation, false, value); 395 | }; 396 | case gl.SAMPLER_2D: 397 | var currentTexture = textureCounter; 398 | var activeName = 'TEXTURE' + currentTexture; 399 | textureCounter += 1; 400 | return function (value) { 401 | var texture = value; 402 | var tex = undefined; 403 | if (texture.id) { 404 | tex = model.cache.textures[texture.id]; 405 | } else { 406 | texture.id = Utils.guid(); 407 | } 408 | if (!tex) { 409 | tex = do_texture(gl, texture); 410 | model.cache.textures[texture.id] = tex; 411 | } 412 | gl.activeTexture(gl[activeName]); 413 | gl.bindTexture(gl.TEXTURE_2D, tex); 414 | gl.uniform1i(uniformLocation, currentTexture); 415 | }; 416 | case gl.BOOL: 417 | return function (value) { 418 | gl.uniform1i(uniformLocation, value); 419 | }; 420 | default: 421 | LOG('Unsupported uniform type: ' + uniform.type); 422 | return function () {}; 423 | } 424 | } 425 | 426 | var uniformSetters = {}; 427 | var numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); 428 | for (var i = 0; i < numUniforms; i += 1) { 429 | var uniform = gl.getActiveUniform(program, i); 430 | uniformSetters[uniform.name] = createUniformSetter(program, uniform); 431 | } 432 | 433 | return uniformSetters; 434 | } 435 | 436 | function setUniforms(setters, values) { 437 | Object.keys(values).forEach(function (name) { 438 | var setter = setters[name]; 439 | if (setter) { 440 | setter(values[name]); 441 | } 442 | }); 443 | } 444 | 445 | function enable(capability) { 446 | return function (gl) { 447 | gl.enable(gl[capability]); 448 | }; 449 | } 450 | 451 | function disable(capability) { 452 | return function (gl) { 453 | gl.disable(gl[capability]); 454 | }; 455 | } 456 | 457 | function blendColor(r, g, b, a) { 458 | return function (gl) { 459 | gl.blendColor(r, g, b, a); 460 | }; 461 | } 462 | 463 | function blendEquation(mode) { 464 | return function (gl) { 465 | gl.blendEquation(gl[mode]); 466 | }; 467 | } 468 | 469 | function blendEquationSeparate(modeRGB, modeAlpha) { 470 | return function (gl) { 471 | gl.blendEquationSeparate(gl[modeRGB], gl[modeAlpha]); 472 | }; 473 | } 474 | 475 | function blendFunc(src, dst) { 476 | return function (gl) { 477 | gl.blendFunc(gl[src], gl[dst]); 478 | }; 479 | } 480 | 481 | function depthFunc(mode) { 482 | return function (gl) { 483 | gl.depthFunc(gl[mode]); 484 | }; 485 | } 486 | 487 | function sampleCoverage(value, invert) { 488 | return function (gl) { 489 | gl.sampleCoverage(value, invert); 490 | }; 491 | } 492 | 493 | function stencilFunc(func, ref, mask) { 494 | return function (gl) { 495 | gl.stencilFunc(gl[func], ref, mask); 496 | }; 497 | } 498 | 499 | function stencilFuncSeparate(face, func, ref, mask) { 500 | return function (gl) { 501 | gl.stencilFuncSeparate(gl[face], gl[func], ref, mask); 502 | }; 503 | } 504 | 505 | function stencilOperation(fail, zfail, zpass) { 506 | return function (gl) { 507 | gl.stencilOp(gl[fail], gl[zfail], gl[zpass]); 508 | }; 509 | } 510 | 511 | function stencilOperationSeparate(face, fail, zfail, zpass) { 512 | return function (gl) { 513 | gl.stencilOpSeparate(gl[face], gl[fail], gl[zfail], gl[zpass]); 514 | }; 515 | } 516 | 517 | 518 | // VIRTUAL-DOM WIDGETS 519 | 520 | function toHtml(functionCalls, factList, renderables) { 521 | var model = { 522 | functionCalls: functionCalls, 523 | renderables: renderables, 524 | cache: {} 525 | }; 526 | return _elm_lang$virtual_dom$Native_VirtualDom.custom(factList, model, implementation); 527 | } 528 | 529 | // WIDGET IMPLEMENTATION 530 | var implementation = { 531 | render: renderCanvas, 532 | diff: diff 533 | }; 534 | 535 | 536 | function renderCanvas(model) { 537 | 538 | LOG('Render canvas'); 539 | var canvas = document.createElement('canvas'); 540 | var gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); 541 | 542 | if (gl) { 543 | A2(_elm_lang$core$List$map, function (functionCall) { 544 | functionCall(gl); 545 | }, model.functionCalls); 546 | } else { 547 | canvas = document.createElement('div'); 548 | canvas.innerHTML = 'Enable WebGL to see this content!'; 549 | } 550 | 551 | model.cache = model.cache || {}; 552 | model.cache.gl = gl; 553 | model.cache.shaders = []; 554 | model.cache.programs = {}; 555 | model.cache.uniformSetters = {}; 556 | model.cache.buffers = []; 557 | model.cache.textures = []; 558 | 559 | // Render for the first time. 560 | // This has to be done in animation frame, 561 | // because the canvas is not in the DOM yet, 562 | // when renderCanvas is called by virtual-dom 563 | rAF(function () { 564 | drawGL(canvas, {model: model}); 565 | }); 566 | 567 | return canvas; 568 | } 569 | 570 | 571 | function diff(oldModel, newModel) { 572 | newModel.model.cache = oldModel.model.cache; 573 | return { 574 | applyPatch: drawGL, 575 | data: newModel 576 | }; 577 | } 578 | 579 | return { 580 | unsafeCoerceGLSL: unsafeCoerceGLSL, 581 | textureSize: textureSize, 582 | loadTexture: loadTexture, 583 | render: F5(render), 584 | toHtml: F3(toHtml), 585 | enable: enable, 586 | disable: disable, 587 | blendColor: F4(blendColor), 588 | blendEquation: blendEquation, 589 | blendEquationSeparate: F2(blendEquationSeparate), 590 | blendFunc: F2(blendFunc), 591 | depthFunc: depthFunc, 592 | sampleCoverage: F2(sampleCoverage), 593 | stencilFunc: F3(stencilFunc), 594 | stencilFuncSeparate: F4(stencilFuncSeparate), 595 | stencilOperation: F3(stencilOperation), 596 | stencilOperationSeparate: F4(stencilOperationSeparate), 597 | loadTextureRaw: F2(loadTextureRaw) 598 | }; 599 | 600 | }(); 601 | -------------------------------------------------------------------------------- /src/WebGL.elm: -------------------------------------------------------------------------------- 1 | module WebGL exposing (..) 2 | 3 | {-| The WebGL API is for high performance rendering. Definitely read about 4 | [how WebGL works](https://github.com/johnpmayer/elm-webgl/blob/master/README.md) 5 | and look at some examples before trying to do too much with just the 6 | documentation provided here. 7 | 8 | # Main Types 9 | @docs Texture, TextureFilter, Shader, Renderable, Error, Drawable 10 | 11 | # Entities 12 | @docs render, renderWithConfig 13 | 14 | # WebGL Html 15 | @docs toHtml, toHtmlWith, defaultConfiguration 16 | 17 | # WebGL API Calls 18 | @docs FunctionCall 19 | 20 | # WebGL API Types 21 | @docs Capability, BlendOperation, BlendMode, CompareMode, FaceMode, ZMode 22 | 23 | # Loading Textures 24 | @docs loadTexture, loadTextureWithFilter, textureSize 25 | 26 | # Unsafe Shader Creation (for library writers) 27 | @docs unsafeShader 28 | 29 | # Functions 30 | @docs computeAPICall, computeAPICalls, computeBlendModeString, computeBlendOperationString, computeCapabilityString, computeCompareModeString, computeFaceModeString, computeZModeString 31 | 32 | -} 33 | 34 | import Html exposing (Html, Attribute) 35 | import Task exposing (Task) 36 | import List 37 | import Native.WebGL 38 | 39 | {-| 40 | WebGl has a number of rendering modes available. Each of the tagged union types 41 | maps to a separate rendering mode. 42 | 43 | Triangles are the basic building blocks of a mesh. You can put them together 44 | to form any shape. Each corner of a triangle is called a *vertex* and contains a 45 | bunch of *attributes* that describe that particular corner. These attributes can 46 | be things like position and color. 47 | 48 | So when you create a `Triangle` you are really providing three sets of attributes 49 | that describe the corners of a triangle. 50 | 51 | See: [Library reference](https://msdn.microsoft.com/en-us/library/dn302395%28v=vs.85%29.aspx) for the description of each type. 52 | -} 53 | 54 | type Drawable attributes 55 | = Triangle (List (attributes, attributes, attributes)) 56 | | Lines (List (attributes, attributes) ) 57 | | LineStrip (List attributes) 58 | | LineLoop (List attributes) 59 | | Points (List attributes) 60 | | TriangleFan (List attributes) 61 | | TriangleStrip (List attributes) 62 | 63 | {-| Shader is a phantom data type. Don't instantiate it yourself. See below. 64 | -} 65 | type Shader attributes uniforms varyings = Shader 66 | 67 | 68 | {-| Shaders are programs for running many computations on the GPU in parallel. 69 | They are written in a language called 70 | [GLSL](http://en.wikipedia.org/wiki/OpenGL_Shading_Language). Read more about 71 | shaders [here](https://github.com/johnpmayer/elm-webgl/blob/master/README.md). 72 | 73 | Normally you specify a shader with a `shader` block. This is because shaders 74 | must be compiled before they are used, imposing an overhead that it is best to 75 | avoid in general. This function lets you create a shader with a raw string of 76 | GLSL. It is intended specifically for libary writers who want to create shader 77 | combinators. 78 | -} 79 | unsafeShader : String -> Shader attribute uniform varying 80 | unsafeShader = 81 | Native.WebGL.unsafeCoerceGLSL 82 | 83 | {-| A `Texture` loads a texture with linear filtering enabled. If you do not 84 | want filtering, create a `RawTexture` with `loadTextureRaw`. 85 | -} 86 | type Texture = Texture 87 | 88 | {-| Textures work in two ways when looking up a pixel value - Linear or Nearest 89 | -} 90 | type TextureFilter = Linear | Nearest 91 | 92 | {-| An error which occured in the graphics context -} 93 | type Error = Error 94 | 95 | {-| Loads a texture from the given url. PNG and JPEG are known to work, but 96 | other formats have not been as well-tested yet. 97 | -} 98 | loadTexture : String -> Task Error Texture 99 | loadTexture = loadTextureWithFilter Linear 100 | 101 | {-| Loads a texture from the given url. PNG and JPEG are known to work, but 102 | other formats have not been as well-tested yet. Configurable filter. 103 | -} 104 | loadTextureWithFilter : TextureFilter -> String -> Task Error Texture 105 | loadTextureWithFilter filter url = Native.WebGL.loadTextureRaw Linear url 106 | 107 | {-| Return the (width, height) size of a texture. Useful for sprite sheets 108 | or other times you may want to use only a potion of a texture image. 109 | -} 110 | textureSize : Texture -> (Int, Int) 111 | textureSize = 112 | Native.WebGL.textureSize 113 | 114 | {-| Conceptually, an encapsulataion of the instructions to render something -} 115 | type Renderable = Renderable 116 | 117 | {-| Packages a vertex shader, a fragment shader, a mesh, and uniform variables 118 | as an `Renderable`. This specifies a full rendering pipeline to be run on the GPU. 119 | You can read more about the pipeline 120 | [here](https://github.com/johnpmayer/elm-webgl/blob/master/README.md). 121 | 122 | Values will be cached intelligently, so if you have already sent a shader or 123 | mesh to the GPU, it will not be resent. This means it is fairly cheap to create 124 | new entities if you are reusing shaders and meshes that have been used before. 125 | -} 126 | renderWithConfig : List FunctionCall -> Shader attributes uniforms varyings -> Shader {} uniforms varyings -> (Drawable attributes) -> uniforms -> Renderable 127 | renderWithConfig functionCalls vert frag buffer uniforms = 128 | computeAPICalls functionCalls 129 | |> Native.WebGL.render vert frag buffer uniforms 130 | 131 | 132 | {-| Same as `renderWithConfig` but without using 133 | custom per-render configurations. 134 | -} 135 | render : Shader attributes uniforms varyings -> Shader {} uniforms varyings -> (Drawable attributes) -> uniforms -> Renderable 136 | render = renderWithConfig [] 137 | 138 | 139 | {-| Default configuration that is used as 140 | the implicit configurations for `webgl`. 141 | -} 142 | defaultConfiguration : List FunctionCall 143 | defaultConfiguration = 144 | [ Enable DepthTest 145 | ] 146 | 147 | 148 | {-| Same as toHtmlWith but with default configurations, 149 | implicitly configured for you. See `defaultConfiguration` for more information. 150 | -} 151 | toHtml : List (Attribute msg) -> List Renderable -> Html msg 152 | toHtml = 153 | toHtmlWith defaultConfiguration 154 | 155 | 156 | {-| Render a WebGL scene with the given dimensions and entities. Shaders and 157 | meshes are cached so that they do not get resent to the GPU, so it should be 158 | relatively cheap to create new entities out of existing values. 159 | -} 160 | toHtmlWith : List FunctionCall -> List (Attribute msg) -> List Renderable -> Html msg 161 | toHtmlWith functionCalls = 162 | Native.WebGL.toHtml (computeAPICalls functionCalls) 163 | 164 | 165 | {-| -} 166 | computeAPICalls : List FunctionCall -> List (a -> b) 167 | computeAPICalls functionCalls = 168 | List.map 169 | computeAPICall 170 | functionCalls 171 | 172 | 173 | {-| -} 174 | computeAPICall : FunctionCall -> (a -> b) 175 | computeAPICall function = 176 | case function of 177 | Enable capability -> 178 | computeCapabilityString capability 179 | |> Native.WebGL.enable 180 | 181 | Disable capability -> 182 | computeCapabilityString capability 183 | |> Native.WebGL.disable 184 | 185 | BlendColor (r, g, b, a) -> 186 | Native.WebGL.blendColor r g b a 187 | 188 | BlendEquation mode -> 189 | computeBlendModeString mode 190 | |> Native.WebGL.blendEquation 191 | 192 | BlendEquationSeparate (modeRGB', modeAlpha') -> 193 | let modeRGB = computeBlendModeString modeRGB' 194 | modeAlpha = computeBlendModeString modeAlpha' 195 | in Native.WebGL.blendEquationSeparate modeRGB modeAlpha 196 | 197 | BlendFunc (src', dst') -> 198 | let src = computeBlendOperationString src' 199 | dst = computeBlendOperationString dst' 200 | in Native.WebGL.blendFunc src dst 201 | 202 | DepthFunc mode -> 203 | computeCompareModeString mode 204 | |> Native.WebGL.depthFunc 205 | 206 | SampleCoverageFunc (value, invert) -> 207 | Native.WebGL.sampleCoverage value invert 208 | 209 | StencilFunc (func, ref, mask) -> 210 | let mode = computeCompareModeString func 211 | in Native.WebGL.stencilFunc mode ref mask 212 | 213 | StencilFuncSeparate (face', func, ref, mask) -> 214 | let face = computeFaceModeString face' 215 | mode = computeCompareModeString func 216 | in Native.WebGL.stencilFuncSeparate face mode ref mask 217 | 218 | StencilOperation (fail', zfail', zpass') -> 219 | let fail = computeZModeString fail' 220 | zfail = computeZModeString zfail' 221 | zpass = computeZModeString zpass' 222 | in Native.WebGL.stencilOperation fail zfail zpass 223 | 224 | StencilOperationSeparate (face', fail', zfail', zpass') -> 225 | let face = computeFaceModeString face' 226 | fail = computeZModeString fail' 227 | zfail = computeZModeString zfail' 228 | zpass = computeZModeString zpass' 229 | in Native.WebGL.stencilOperationSeparate face fail zfail zpass 230 | 231 | 232 | {-| The `FunctionCall` provides a typesafe way to call 233 | all pre-fragment operations and some special functions. 234 | 235 | `Enable(capability: Capability)` 236 | + enable server-side GL capabilities 237 | 238 | `Disable(cap: Capability)` 239 | + disable server-side GL capabilities 240 | 241 | `BlendColor(red: Float, green: Float, blue: Float, alpha: Float)` 242 | + set the blend color 243 | 244 | `BlendEquation(mode: BlendMode)` 245 | + specify the equation used for both the 246 | RGB blend equation and the Alpha blend equation 247 | + `mode`: specifies how source and destination colors are combined 248 | 249 | `BlendEquationSeparate(modeRGB: BlendMode, modeAlpha: BlendMode)` 250 | + set the RGB blend equation and the alpha blend equation separately 251 | + `modeRGB`: specifies the RGB blend equation, how the red, green, 252 | and blue components of the source and destination colors are combined 253 | + `modeAlpha`: specifies the alpha blend equation, how the alpha component 254 | of the source and destination colors are combined 255 | 256 | `BlendFunc(srcFactor: BlendMode, dstFactor: BlendMode)` 257 | + specify pixel arithmetic 258 | + `srcFactor`: Specifies how the red, green, blue, 259 | and alpha source blending factors are computed 260 | + `dstFactor`: Specifies how the red, green, blue, 261 | and alpha destination blending factors are computed 262 | + `SrcAlphaSaturate` should only be used for the srcFactor); 263 | + Both values may not reference a `ConstantColor` value; 264 | 265 | `SampleCoverageFunc(value: Float, invert: Bool)` 266 | + specify multisample coverage parameters 267 | + `value`: Specify a single floating-point sample coverage value. 268 | The value is clamped to the range 0 1 . The initial value is `1` 269 | + `invert`: Specify a single boolean value representing 270 | if the coverage masks should be inverted. The initial value is `False` 271 | 272 | `StencilFunc(func: CompareMode, ref: Int, mask: Int)` 273 | + set front and back function and reference value for stencil testing 274 | + `func`: Specifies the test function. The initial value is `Always` 275 | + `ref`: Specifies the reference value for the stencil test. ref is 276 | clamped to the range 0 2 n - 1 , n is the number of bitplanes 277 | in the stencil buffer. The initial value is `0`. 278 | + `mask`: Specifies a mask that is ANDed with both the reference value 279 | and the stored stencil value when the test is done. 280 | The initial value is all `1`'s. 281 | 282 | `StencilFuncSeparate(face: FaceMode, func: CompareMode, ref: Int, mask: Int)` 283 | + set front and/or back function and reference value for stencil testing 284 | + `face`: Specifies whether front and/or back stencil state is updated 285 | + see the description of `StencilFunc` for info about the other parameters 286 | 287 | `StencilOperation(fail: ZMode, zfail: ZMode, pass: ZMode)` 288 | + set front and back stencil test actions 289 | + `fail`: Specifies the action to take when the stencil test fails. 290 | The initial value is `Keep` 291 | + `zfail`: Specifies the stencil action when the stencil test passes, 292 | but the depth test fails. The initial value is `Keep` 293 | + `pass`: Specifies the stencil action when both the stencil test 294 | and the depth test pass, or when the stencil test passes and either 295 | there is no depth buffer or depth testing is not enabled. 296 | The initial value is `Keep` 297 | 298 | `StencilOperationSeparate(face: FaceMode, fail: ZMode, zfail: ZMode, pass: Zmode)` 299 | + set front and/or back stencil test actions 300 | + `face`: Specifies whether front and/or back stencil state is updated. 301 | + See the description of `StencilOperation` for info about the other parameters. 302 | -} 303 | type FunctionCall 304 | = Enable Capability 305 | | Disable Capability 306 | | BlendColor (Float, Float, Float, Float) 307 | | BlendEquation BlendMode 308 | | BlendEquationSeparate (BlendMode, BlendMode) 309 | | BlendFunc (BlendOperation, BlendOperation) 310 | | DepthFunc CompareMode 311 | | SampleCoverageFunc (Float, Bool) 312 | | StencilFunc (CompareMode, Int, Int) 313 | | StencilFuncSeparate (FaceMode, CompareMode, Int, Int) 314 | | StencilOperation (ZMode, ZMode, ZMode) 315 | | StencilOperationSeparate (FaceMode, ZMode, ZMode, ZMode) 316 | 317 | 318 | {-| -} 319 | computeCapabilityString : Capability -> String 320 | computeCapabilityString capability = 321 | case capability of 322 | Blend -> 323 | "BLEND" 324 | 325 | CullFace -> 326 | "CULL_FACE" 327 | 328 | DepthTest -> 329 | "DEPTH_TEST" 330 | 331 | Dither -> 332 | "DITHER" 333 | 334 | PolygonOffsetFill -> 335 | "POLYGON_OFFSET_FILL" 336 | 337 | SampleAlphaToCoverage -> 338 | "SAMPLE_ALPHA_TO_COVERAGE" 339 | 340 | SampleCoverage -> 341 | "SAMPLE_COVERAGE" 342 | 343 | ScissorTest -> 344 | "SCISSOR_TEST" 345 | 346 | StencilTest -> 347 | "STENCIL_TEST" 348 | 349 | 350 | {-| The `Capability` type is used to enable/disable server-side GL capabilities. 351 | 352 | + `Blend`: If enabled, blend the computed fragment color values 353 | with the values in the color buffers. 354 | + `CullFace`: If enabled, cull polygons based on their winding in window coordinates. 355 | + `DepthTest`: If enabled, do depth comparisons and update the depth buffer. 356 | + `Dither`: If enabled, dither color components. 357 | or indices before they are written to the color buffer. 358 | + `PolygonOffsetFill`: If enabled, an offset is added 359 | to depth values of a polygon's fragments produced by rasterization. 360 | + `SampleAlphaToCoverage`: If enabled, compute a temporary coverage value 361 | where each bit is determined by the alpha value at the corresponding sample location. 362 | The temporary coverage value is then ANDed with the fragment coverage value. 363 | + `SampleCoverage`: If enabled, the fragment's coverage 364 | is ANDed with the temporary coverage value. 365 | + `ScissorTest`: If enabled, discard fragments that are outside the scissor rectangle 366 | + `StencilTest`: If enabled, do stencil testing and update the stencil buffer. 367 | -} 368 | type Capability 369 | = Blend 370 | | CullFace 371 | | DepthTest 372 | | Dither 373 | | PolygonOffsetFill 374 | | SampleAlphaToCoverage 375 | | SampleCoverage 376 | | ScissorTest 377 | | StencilTest 378 | 379 | 380 | {-| -} 381 | computeBlendOperationString : BlendOperation -> String 382 | computeBlendOperationString operation = 383 | case operation of 384 | Zero -> 385 | "ZERO" 386 | 387 | One -> 388 | "ONE" 389 | 390 | SrcColor -> 391 | "SRC_COLOR" 392 | 393 | OneMinusSrcColor -> 394 | "ONE_MINUS_SRC_COLOR" 395 | 396 | DstColor -> 397 | "DST_COLOR" 398 | 399 | OneMinusDstColor -> 400 | "ONE_MINUS_DST_COLOR" 401 | 402 | SrcAlpha -> 403 | "SRC_ALPHA" 404 | 405 | OneMinusSrcAlpha -> 406 | "ONE_MINUS_SRC_ALPHA" 407 | 408 | DstAlpha -> 409 | "DST_ALPHA" 410 | 411 | OneMinusDstAlpha -> 412 | "ONE_MINUS_DST_ALPHA" 413 | 414 | ConstantColor -> 415 | "CONSTANT_COLOR" 416 | 417 | OneMinusConstantColor -> 418 | "ONE_MINUS_CONSTANT_COLOR" 419 | 420 | ConstantAlpha -> 421 | "CONSTANT_ALPHA" 422 | 423 | OneMinusConstantAlpha -> 424 | "ONE_MINUS_CONSTANT_ALPHA" 425 | 426 | SrcAlphaSaturate -> 427 | "SRC_ALPHA_SATURATE" 428 | 429 | 430 | {-| The `BlendOperation` type allows you to define which blend operation to use. 431 | -} 432 | type BlendOperation 433 | = Zero 434 | | One 435 | | SrcColor 436 | | OneMinusSrcColor 437 | | DstColor 438 | | OneMinusDstColor 439 | | SrcAlpha 440 | | OneMinusSrcAlpha 441 | | DstAlpha 442 | | OneMinusDstAlpha 443 | | ConstantColor 444 | | OneMinusConstantColor 445 | | ConstantAlpha 446 | | OneMinusConstantAlpha 447 | | SrcAlphaSaturate 448 | 449 | 450 | {-| -} 451 | computeBlendModeString : BlendMode -> String 452 | computeBlendModeString mode = 453 | case mode of 454 | Add -> 455 | "FUNC_ADD" 456 | 457 | Subtract -> 458 | "FUNC_SUBTRACT" 459 | 460 | ReverseSubtract -> 461 | "FUNC_REVERSE_SUBTRACT" 462 | 463 | 464 | {-| The `BlendMode` type allows you to define which blend mode to use. 465 | -} 466 | type BlendMode 467 | = Add 468 | | Subtract 469 | | ReverseSubtract 470 | 471 | 472 | {-| -} 473 | computeCompareModeString : CompareMode -> String 474 | computeCompareModeString mode = 475 | case mode of 476 | Never -> 477 | "NEVER" 478 | 479 | Always -> 480 | "ALWAYS" 481 | 482 | Less -> 483 | "LESS" 484 | 485 | LessOrEqual -> 486 | "LEQUAL" 487 | 488 | Equal -> 489 | "EQUAL" 490 | 491 | GreaterOrEqual -> 492 | "GEQUAL" 493 | 494 | Greater -> 495 | "Greater" 496 | 497 | NotEqual -> 498 | "NOTEQUAL" 499 | 500 | 501 | {-| The `CompareMode` type allows you to define how to compare values. 502 | -} 503 | type CompareMode 504 | = Never 505 | | Always 506 | | Less 507 | | LessOrEqual 508 | | Equal 509 | | GreaterOrEqual 510 | | Greater 511 | | NotEqual 512 | 513 | 514 | {-| -} 515 | computeFaceModeString : FaceMode -> String 516 | computeFaceModeString mode = 517 | case mode of 518 | Front -> 519 | "FRONT" 520 | 521 | Back -> 522 | "BACK" 523 | 524 | FrontAndBack -> 525 | "FRONT_AND_BACK" 526 | 527 | 528 | {-| The `FaceMode` type defines which face of the stencil state is updated. 529 | -} 530 | type FaceMode 531 | = Front 532 | | Back 533 | | FrontAndBack 534 | 535 | 536 | {-| -} 537 | computeZModeString : ZMode -> String 538 | computeZModeString mode = 539 | case mode of 540 | Keep -> 541 | "KEEP" 542 | 543 | None -> 544 | "ZERO" 545 | 546 | Replace -> 547 | "REPLACE" 548 | 549 | Increment -> 550 | "INCREMENT" 551 | 552 | Decrement -> 553 | "DECREMENT" 554 | 555 | Invert -> 556 | "INVERT" 557 | 558 | IncrementWrap -> 559 | "INCREMENT_WRAP" 560 | 561 | DecrementWrap -> 562 | "DECREMENT_WRAP" 563 | 564 | 565 | {-| The `ZMode` type allows you to define what to do with the stencil buffer value. 566 | 567 | + `Keep`: Keeps the current value. 568 | + `None`: Sets the stencil buffer value to 0. 569 | + `Replace`: Sets the stencil buffer value to `ref`, 570 | See `StencilFunc` for more information. 571 | + `Increment`: Increments the current stencil buffer value. 572 | Clamps to the maximum representable unsigned value. 573 | + `Decrement`: Decrements the current stencil buffer value. Clamps to 0. 574 | + `Invert`: Bitwise inverts the current stencil buffer value. 575 | + `IncrementWrap`: Increments the current stencil buffer value. 576 | Wraps stencil buffer value to zero when incrementing 577 | the maximum representable unsigned value. 578 | + `DecrementWrap`: Decrements the current stencil buffer value. 579 | Wraps stencil buffer value to the maximum representable unsigned 580 | value when decrementing a stencil buffer value of zero. 581 | -} 582 | type ZMode 583 | = Keep 584 | | None 585 | | Replace 586 | | Increment 587 | | Decrement 588 | | Invert 589 | | IncrementWrap 590 | | DecrementWrap 591 | --------------------------------------------------------------------------------