├── .eslintrc.json ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── elm-package.json ├── examples ├── .gitignore ├── crate.elm ├── cube.elm ├── elm-package.json ├── first-person.elm ├── intersection.elm ├── screenshots │ ├── crate.jpg │ ├── cube.jpg │ ├── first-person.jpg │ ├── thwomp.jpg │ └── triangle.jpg ├── texture │ ├── thwomp-face.jpg │ ├── thwomp-side.jpg │ └── wood-crate.jpg ├── thwomp.elm └── triangle.elm ├── gh-pages.sh ├── pipeline.png ├── release.sh └── src ├── Native ├── Texture.js └── WebGL.js ├── WebGL.elm └── WebGL ├── Settings.elm ├── Settings ├── Blend.elm ├── DepthTest.elm ├── Internal.elm └── StencilTest.elm └── Texture.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 | "camelcase": "error", 23 | "eqeqeq": "error" 24 | }, 25 | "globals" : { 26 | "_elm_community$webgl$Native_Texture": true, 27 | "_elm_community$webgl$Native_WebGL": true, 28 | "_elm_community$webgl$Native_Settings": true, 29 | "_elm_lang$virtual_dom$Native_VirtualDom": false, 30 | "_elm_lang$core$Native_Utils": false, 31 | "_elm_lang$core$Native_Scheduler": false, 32 | "F2": false, 33 | "F3": false, 34 | "F4": false, 35 | "F5": false, 36 | "F6": 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 | release 6 | node_modules 7 | elm.js 8 | documentation.json 9 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to elm-community/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 | * Elm code should be formatted with `elm-format` 13 | 14 | Documentation improvements are welcome. 15 | -------------------------------------------------------------------------------- /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](https://package.elm-lang.org/packages/elm-explorations/webgl/latest) 2 | -------------------------------------------------------------------------------- /elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.6", 3 | "summary": "A library for general 3D rendering with WebGL", 4 | "repository": "https://github.com/elm-community/webgl.git", 5 | "license": "BSD3", 6 | "source-directories": [ 7 | "src" 8 | ], 9 | "exposed-modules": [ 10 | "WebGL", 11 | "WebGL.Settings", 12 | "WebGL.Settings.Blend", 13 | "WebGL.Settings.DepthTest", 14 | "WebGL.Settings.StencilTest", 15 | "WebGL.Texture" 16 | ], 17 | "native-modules": true, 18 | "dependencies": { 19 | "elm-lang/core": "5.0.0 <= v < 6.0.0", 20 | "elm-lang/html": "2.0.0 <= v < 3.0.0" 21 | }, 22 | "elm-version": "0.18.0 <= v < 0.19.0" 23 | } 24 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | elm.js 2 | -------------------------------------------------------------------------------- /examples/crate.elm: -------------------------------------------------------------------------------- 1 | module Main exposing (main) 2 | 3 | {- 4 | This example was inspired by https://open.gl/depthstencils 5 | It demonstrates how to use the stencil buffer. 6 | -} 7 | 8 | import AnimationFrame 9 | import Html exposing (Html) 10 | import Html.Attributes exposing (width, height, style) 11 | import Math.Matrix4 as Mat4 exposing (Mat4) 12 | import Math.Vector2 as Vec2 exposing (Vec2, vec2) 13 | import Math.Vector3 as Vec3 exposing (Vec3, vec3) 14 | import Task 15 | import Time exposing (Time) 16 | import WebGL exposing (Mesh, Shader, Entity) 17 | import WebGL.Settings.Blend as Blend 18 | import WebGL.Settings.DepthTest as DepthTest 19 | import WebGL.Settings.StencilTest as StencilTest 20 | import WebGL.Texture as Texture exposing (Error, Texture) 21 | 22 | 23 | type alias Model = 24 | { texture : Maybe Texture 25 | , theta : Float 26 | } 27 | 28 | 29 | type Msg 30 | = TextureLoaded (Result Error Texture) 31 | | Animate Time 32 | 33 | 34 | update : Msg -> Model -> ( Model, Cmd Msg ) 35 | update action model = 36 | case action of 37 | TextureLoaded textureResult -> 38 | ( { model | texture = Result.toMaybe textureResult }, Cmd.none ) 39 | 40 | Animate dt -> 41 | ( { model | theta = model.theta + dt / 10000 }, Cmd.none ) 42 | 43 | 44 | init : ( Model, Cmd Msg ) 45 | init = 46 | ( { texture = Nothing, theta = 0 } 47 | , Task.attempt TextureLoaded (Texture.load "texture/wood-crate.jpg") 48 | ) 49 | 50 | 51 | main : Program Never Model Msg 52 | main = 53 | Html.program 54 | { init = init 55 | , view = view 56 | , subscriptions = (\model -> AnimationFrame.diffs Animate) 57 | , update = update 58 | } 59 | 60 | 61 | 62 | -- View 63 | 64 | 65 | view : Model -> Html Msg 66 | view { texture, theta } = 67 | WebGL.toHtmlWith 68 | [ WebGL.alpha True 69 | , WebGL.antialias 70 | , WebGL.depth 1 71 | , WebGL.stencil 0 72 | ] 73 | [ width 400 74 | , height 400 75 | , style [ ( "display", "block" ) ] 76 | ] 77 | (texture 78 | |> Maybe.map (scene (perspective theta)) 79 | |> Maybe.withDefault [] 80 | ) 81 | 82 | 83 | perspective : Float -> Mat4 84 | perspective angle = 85 | List.foldr Mat4.mul 86 | Mat4.identity 87 | [ Mat4.makePerspective 45 1 0.01 100 88 | , Mat4.makeLookAt (vec3 0 3 8) (vec3 0 0 0) (vec3 0 1 0) 89 | , Mat4.makeRotate (3 * angle) (vec3 0 1 0) 90 | ] 91 | 92 | 93 | scene : Mat4 -> Texture -> List Entity 94 | scene camera texture = 95 | [ WebGL.entity 96 | crateVertex 97 | crateFragment 98 | crateMesh 99 | { texture = texture 100 | , perspective = camera 101 | } 102 | , WebGL.entityWith 103 | [ DepthTest.less 104 | { write = False 105 | , near = 0 106 | , far = 1 107 | } 108 | , StencilTest.test 109 | { ref = 1 110 | , mask = 0xFF 111 | , test = StencilTest.always 112 | , fail = StencilTest.keep 113 | , zfail = StencilTest.keep 114 | , zpass = StencilTest.replace 115 | , writeMask = 0xFF 116 | } 117 | ] 118 | floorVertex 119 | floorFragment 120 | floorMesh 121 | { texture = texture 122 | , perspective = camera 123 | } 124 | , WebGL.entityWith 125 | [ StencilTest.test 126 | { ref = 1 127 | , mask = 0xFF 128 | , test = StencilTest.equal 129 | , fail = StencilTest.keep 130 | , zfail = StencilTest.keep 131 | , zpass = StencilTest.keep 132 | , writeMask = 0 133 | } 134 | , DepthTest.default 135 | , Blend.custom 136 | { r = 0 137 | , g = 0 138 | , b = 0 139 | , a = 0.5 140 | , color = Blend.customAdd Blend.constantAlpha Blend.zero 141 | , alpha = Blend.customAdd Blend.one Blend.zero 142 | } 143 | ] 144 | crateVertex 145 | crateFragment 146 | crateMesh 147 | { texture = texture 148 | , perspective = 149 | Mat4.mul camera (Mat4.makeScale (vec3 1 -1 1)) 150 | } 151 | ] 152 | 153 | 154 | 155 | -- Meshes 156 | 157 | 158 | type alias Vertex = 159 | { position : Vec3 160 | , coord : Vec2 161 | } 162 | 163 | 164 | crateMesh : Mesh Vertex 165 | crateMesh = 166 | [ ( 0, 0 ), ( 90, 0 ), ( 180, 0 ), ( 270, 0 ), ( 0, 90 ), ( 0, 270 ) ] 167 | |> List.concatMap rotatedFace 168 | |> WebGL.triangles 169 | 170 | 171 | rotatedFace : ( Float, Float ) -> List ( Vertex, Vertex, Vertex ) 172 | rotatedFace ( angleXZ, angleYZ ) = 173 | let 174 | transformMat = 175 | List.foldr Mat4.mul 176 | Mat4.identity 177 | [ Mat4.makeTranslate (vec3 0 1 0) 178 | , Mat4.makeRotate (degrees angleXZ) Vec3.j 179 | , Mat4.makeRotate (degrees angleYZ) Vec3.i 180 | , Mat4.makeTranslate (vec3 0 0 1) 181 | ] 182 | 183 | transform vertex = 184 | { vertex 185 | | position = 186 | Mat4.transform 187 | transformMat 188 | vertex.position 189 | } 190 | 191 | transformTriangle ( a, b, c ) = 192 | ( transform a, transform b, transform c ) 193 | in 194 | List.map transformTriangle square 195 | 196 | 197 | square : List ( Vertex, Vertex, Vertex ) 198 | square = 199 | let 200 | topLeft = 201 | { position = vec3 -1 1 0, coord = vec2 0 1 } 202 | 203 | topRight = 204 | { position = vec3 1 1 0, coord = vec2 1 1 } 205 | 206 | bottomLeft = 207 | { position = vec3 -1 -1 0, coord = vec2 0 0 } 208 | 209 | bottomRight = 210 | { position = vec3 1 -1 0, coord = vec2 1 0 } 211 | in 212 | [ ( topLeft, topRight, bottomLeft ) 213 | , ( bottomLeft, topRight, bottomRight ) 214 | ] 215 | 216 | 217 | floorMesh : Mesh { position : Vec3 } 218 | floorMesh = 219 | let 220 | topLeft = 221 | { position = vec3 -2 0 -2 } 222 | 223 | topRight = 224 | { position = vec3 2 0 -2 } 225 | 226 | bottomLeft = 227 | { position = vec3 -2 0 2 } 228 | 229 | bottomRight = 230 | { position = vec3 2 0 2 } 231 | in 232 | WebGL.triangles 233 | [ ( topLeft, topRight, bottomLeft ) 234 | , ( bottomLeft, topRight, bottomRight ) 235 | ] 236 | 237 | 238 | 239 | -- Shaders 240 | 241 | 242 | type alias Uniforms = 243 | { perspective : Mat4 244 | , texture : Texture 245 | } 246 | 247 | 248 | crateVertex : Shader Vertex Uniforms { vcoord : Vec2 } 249 | crateVertex = 250 | [glsl| 251 | 252 | attribute vec3 position; 253 | attribute vec2 coord; 254 | uniform mat4 perspective; 255 | varying vec2 vcoord; 256 | 257 | void main () { 258 | gl_Position = perspective * vec4(position, 1.0); 259 | vcoord = coord; 260 | } 261 | 262 | |] 263 | 264 | 265 | crateFragment : Shader {} { u | texture : Texture } { vcoord : Vec2 } 266 | crateFragment = 267 | [glsl| 268 | 269 | precision mediump float; 270 | uniform sampler2D texture; 271 | varying vec2 vcoord; 272 | 273 | void main () { 274 | gl_FragColor = texture2D(texture, vcoord); 275 | } 276 | 277 | |] 278 | 279 | 280 | floorVertex : Shader { position : Vec3 } Uniforms {} 281 | floorVertex = 282 | [glsl| 283 | 284 | attribute vec3 position; 285 | uniform mat4 perspective; 286 | 287 | void main () { 288 | gl_Position = perspective * vec4(position, 1.0); 289 | } 290 | 291 | |] 292 | 293 | 294 | floorFragment : Shader attributes Uniforms {} 295 | floorFragment = 296 | [glsl| 297 | 298 | precision mediump float; 299 | 300 | void main () { 301 | gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); 302 | } 303 | 304 | |] 305 | -------------------------------------------------------------------------------- /examples/cube.elm: -------------------------------------------------------------------------------- 1 | module Main exposing (main) 2 | 3 | {- 4 | Rotating cube with colored sides. 5 | -} 6 | 7 | import AnimationFrame 8 | import Color exposing (Color) 9 | import Html exposing (Html) 10 | import Html.Attributes exposing (width, height, style) 11 | import Math.Matrix4 as Mat4 exposing (Mat4) 12 | import Math.Vector3 as Vec3 exposing (vec3, Vec3) 13 | import Time exposing (Time) 14 | import WebGL exposing (Mesh, Shader) 15 | 16 | 17 | main : Program Never Float Time 18 | main = 19 | Html.program 20 | { init = ( 0, Cmd.none ) 21 | , view = view 22 | , subscriptions = (\_ -> AnimationFrame.diffs Basics.identity) 23 | , update = (\dt theta -> ( theta + dt / 5000, Cmd.none )) 24 | } 25 | 26 | 27 | view : Float -> Html Time 28 | view theta = 29 | WebGL.toHtml 30 | [ width 400 31 | , height 400 32 | , style [ ( "display", "block" ) ] 33 | ] 34 | [ WebGL.entity 35 | vertexShader 36 | fragmentShader 37 | cubeMesh 38 | (uniforms theta) 39 | ] 40 | 41 | 42 | type alias Uniforms = 43 | { rotation : Mat4 44 | , perspective : Mat4 45 | , camera : Mat4 46 | , shade : Float 47 | } 48 | 49 | 50 | uniforms : Float -> Uniforms 51 | uniforms theta = 52 | { rotation = 53 | Mat4.mul 54 | (Mat4.makeRotate (3 * theta) (vec3 0 1 0)) 55 | (Mat4.makeRotate (2 * theta) (vec3 1 0 0)) 56 | , perspective = Mat4.makePerspective 45 1 0.01 100 57 | , camera = Mat4.makeLookAt (vec3 0 0 5) (vec3 0 0 0) (vec3 0 1 0) 58 | , shade = 0.8 59 | } 60 | 61 | 62 | 63 | -- Mesh 64 | 65 | 66 | type alias Vertex = 67 | { color : Vec3 68 | , position : Vec3 69 | } 70 | 71 | 72 | cubeMesh : Mesh Vertex 73 | cubeMesh = 74 | let 75 | rft = 76 | vec3 1 1 1 77 | 78 | lft = 79 | vec3 -1 1 1 80 | 81 | lbt = 82 | vec3 -1 -1 1 83 | 84 | rbt = 85 | vec3 1 -1 1 86 | 87 | rbb = 88 | vec3 1 -1 -1 89 | 90 | rfb = 91 | vec3 1 1 -1 92 | 93 | lfb = 94 | vec3 -1 1 -1 95 | 96 | lbb = 97 | vec3 -1 -1 -1 98 | in 99 | [ face Color.green rft rfb rbb rbt 100 | , face Color.blue rft rfb lfb lft 101 | , face Color.yellow rft lft lbt rbt 102 | , face Color.red rfb lfb lbb rbb 103 | , face Color.purple lft lfb lbb lbt 104 | , face Color.orange rbt rbb lbb lbt 105 | ] 106 | |> List.concat 107 | |> WebGL.triangles 108 | 109 | 110 | face : Color -> Vec3 -> Vec3 -> Vec3 -> Vec3 -> List ( Vertex, Vertex, Vertex ) 111 | face rawColor a b c d = 112 | let 113 | color = 114 | let 115 | c = 116 | Color.toRgb rawColor 117 | in 118 | vec3 119 | (toFloat c.red / 255) 120 | (toFloat c.green / 255) 121 | (toFloat c.blue / 255) 122 | 123 | vertex position = 124 | Vertex color position 125 | in 126 | [ ( vertex a, vertex b, vertex c ) 127 | , ( vertex c, vertex d, vertex a ) 128 | ] 129 | 130 | 131 | 132 | -- Shaders 133 | 134 | 135 | vertexShader : Shader Vertex Uniforms { vcolor : Vec3 } 136 | vertexShader = 137 | [glsl| 138 | 139 | attribute vec3 position; 140 | attribute vec3 color; 141 | uniform mat4 perspective; 142 | uniform mat4 camera; 143 | uniform mat4 rotation; 144 | varying vec3 vcolor; 145 | void main () { 146 | gl_Position = perspective * camera * rotation * vec4(position, 1.0); 147 | vcolor = color; 148 | } 149 | 150 | |] 151 | 152 | 153 | fragmentShader : Shader {} Uniforms { vcolor : Vec3 } 154 | fragmentShader = 155 | [glsl| 156 | 157 | precision mediump float; 158 | uniform float shade; 159 | varying vec3 vcolor; 160 | void main () { 161 | gl_FragColor = shade * vec4(vcolor, 1.0); 162 | } 163 | 164 | |] 165 | -------------------------------------------------------------------------------- /examples/elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "summary": "Examples", 4 | "repository": "https://github.com/elm-community/webgl.git", 5 | "license": "BSD3", 6 | "source-directories": [ 7 | ".", 8 | "../src" 9 | ], 10 | "exposed-modules": [], 11 | "native-modules": true, 12 | "dependencies": { 13 | "elm-community/linear-algebra": "1.0.0 <= v < 3.0.0", 14 | "elm-lang/animation-frame": "1.0.1 <= v < 2.0.0", 15 | "elm-lang/core": "5.0.0 <= v < 6.0.0", 16 | "elm-lang/html": "2.0.0 <= v < 3.0.0", 17 | "elm-lang/keyboard": "1.0.1 <= v < 2.0.0", 18 | "elm-lang/mouse": "1.0.1 <= v < 2.0.0", 19 | "elm-lang/window": "1.0.1 <= v < 2.0.0" 20 | }, 21 | "elm-version": "0.18.0 <= v < 0.19.0" 22 | } 23 | -------------------------------------------------------------------------------- /examples/first-person.elm: -------------------------------------------------------------------------------- 1 | module Main exposing (main) 2 | 3 | {- 4 | Try adding the ability to crouch or to land on top of the crate. 5 | -} 6 | 7 | import AnimationFrame 8 | import Html exposing (Html, text, div) 9 | import Html.Attributes exposing (width, height, style) 10 | import Keyboard 11 | import Math.Matrix4 as Mat4 exposing (Mat4) 12 | import Math.Vector2 as Vec2 exposing (Vec2, vec2) 13 | import Math.Vector3 as Vec3 exposing (Vec3, vec3) 14 | import Task exposing (Task) 15 | import Time exposing (Time) 16 | import WebGL exposing (Mesh, Shader, Entity) 17 | import WebGL.Texture as Texture exposing (Texture, Error) 18 | import Window 19 | 20 | 21 | type alias Model = 22 | { texture : Maybe Texture 23 | , keys : Keys 24 | , size : Window.Size 25 | , person : Person 26 | } 27 | 28 | 29 | type alias Person = 30 | { position : Vec3 31 | , velocity : Vec3 32 | } 33 | 34 | 35 | type Msg 36 | = TextureLoaded (Result Error Texture) 37 | | KeyChange Bool Keyboard.KeyCode 38 | | Animate Time 39 | | Resize Window.Size 40 | 41 | 42 | type alias Keys = 43 | { left : Bool 44 | , right : Bool 45 | , up : Bool 46 | , down : Bool 47 | , space : Bool 48 | } 49 | 50 | 51 | main : Program Never Model Msg 52 | main = 53 | Html.program 54 | { init = init 55 | , view = view 56 | , subscriptions = subscriptions 57 | , update = update 58 | } 59 | 60 | 61 | eyeLevel : Float 62 | eyeLevel = 63 | 2 64 | 65 | 66 | init : ( Model, Cmd Msg ) 67 | init = 68 | ( { texture = Nothing 69 | , person = Person (vec3 0 eyeLevel -10) (vec3 0 0 0) 70 | , keys = Keys False False False False False 71 | , size = Window.Size 0 0 72 | } 73 | , Cmd.batch 74 | [ Task.attempt TextureLoaded (Texture.load "texture/wood-crate.jpg") 75 | , Task.perform Resize Window.size 76 | ] 77 | ) 78 | 79 | 80 | subscriptions : Model -> Sub Msg 81 | subscriptions _ = 82 | Sub.batch 83 | [ AnimationFrame.diffs Animate 84 | , Keyboard.downs (KeyChange True) 85 | , Keyboard.ups (KeyChange False) 86 | , Window.resizes Resize 87 | ] 88 | 89 | 90 | update : Msg -> Model -> ( Model, Cmd Msg ) 91 | update action model = 92 | case action of 93 | TextureLoaded textureResult -> 94 | ( { model | texture = Result.toMaybe textureResult }, Cmd.none ) 95 | 96 | KeyChange on code -> 97 | ( { model | keys = keyFunc on code model.keys }, Cmd.none ) 98 | 99 | Resize size -> 100 | ( { model | size = size }, Cmd.none ) 101 | 102 | Animate dt -> 103 | ( { model 104 | | person = 105 | model.person 106 | |> move model.keys 107 | |> gravity (dt / 500) 108 | |> physics (dt / 500) 109 | } 110 | , Cmd.none 111 | ) 112 | 113 | 114 | keyFunc : Bool -> Keyboard.KeyCode -> Keys -> Keys 115 | keyFunc on keyCode keys = 116 | case keyCode of 117 | 32 -> 118 | { keys | space = on } 119 | 120 | 37 -> 121 | { keys | left = on } 122 | 123 | 39 -> 124 | { keys | right = on } 125 | 126 | 38 -> 127 | { keys | up = on } 128 | 129 | 40 -> 130 | { keys | down = on } 131 | 132 | _ -> 133 | keys 134 | 135 | 136 | move : Keys -> Person -> Person 137 | move { left, right, up, down, space } person = 138 | let 139 | direction a b = 140 | if a == b then 141 | 0 142 | else if a then 143 | 1 144 | else 145 | -1 146 | 147 | vy = 148 | if space then 149 | 2 150 | else 151 | Vec3.getY person.velocity 152 | in 153 | if Vec3.getY person.position <= eyeLevel then 154 | { person 155 | | velocity = 156 | vec3 (direction left right) vy (direction up down) 157 | } 158 | else 159 | person 160 | 161 | 162 | physics : Float -> Person -> Person 163 | physics dt person = 164 | let 165 | position = 166 | Vec3.add person.position (Vec3.scale dt person.velocity) 167 | in 168 | { person 169 | | position = 170 | if Vec3.getY position < eyeLevel then 171 | Vec3.setY eyeLevel position 172 | else 173 | position 174 | } 175 | 176 | 177 | gravity : Float -> Person -> Person 178 | gravity dt person = 179 | if Vec3.getY person.position > eyeLevel then 180 | { person 181 | | velocity = 182 | Vec3.setY 183 | (Vec3.getY person.velocity - 2 * dt) 184 | person.velocity 185 | } 186 | else 187 | person 188 | 189 | 190 | 191 | -- View 192 | 193 | 194 | view : Model -> Html Msg 195 | view { size, person, texture } = 196 | div 197 | [ style 198 | [ ( "width", toString size.width ++ "px" ) 199 | , ( "height", toString size.height ++ "px" ) 200 | , ( "position", "relative" ) 201 | ] 202 | ] 203 | [ WebGL.toHtmlWith 204 | [ WebGL.depth 1 205 | ] 206 | [ width size.width 207 | , height size.height 208 | , style [ ( "display", "block" ) ] 209 | ] 210 | (texture 211 | |> Maybe.map (scene size person) 212 | |> Maybe.withDefault [] 213 | ) 214 | , div 215 | [ style 216 | [ ( "position", "absolute" ) 217 | , ( "font-family", "monospace" ) 218 | , ( "color", "white" ) 219 | , ( "text-align", "center" ) 220 | , ( "left", "20px" ) 221 | , ( "right", "20px" ) 222 | , ( "top", "20px" ) 223 | ] 224 | ] 225 | [ text message ] 226 | ] 227 | 228 | 229 | message : String 230 | message = 231 | "Walk around with a first person perspective.\n" 232 | ++ "Arrows keys to move, space bar to jump." 233 | 234 | 235 | scene : Window.Size -> Person -> Texture -> List Entity 236 | scene { width, height } person texture = 237 | let 238 | perspective = 239 | Mat4.mul 240 | (Mat4.makePerspective 45 (toFloat width / toFloat height) 0.01 100) 241 | (Mat4.makeLookAt person.position (Vec3.add person.position Vec3.k) Vec3.j) 242 | in 243 | [ WebGL.entity 244 | vertexShader 245 | fragmentShader 246 | crate 247 | { texture = texture 248 | , perspective = perspective 249 | } 250 | ] 251 | 252 | 253 | 254 | -- Mesh 255 | 256 | 257 | type alias Vertex = 258 | { position : Vec3 259 | , coord : Vec2 260 | } 261 | 262 | 263 | crate : Mesh Vertex 264 | crate = 265 | [ ( 0, 0 ), ( 90, 0 ), ( 180, 0 ), ( 270, 0 ), ( 0, 90 ), ( 0, -90 ) ] 266 | |> List.concatMap rotatedSquare 267 | |> WebGL.triangles 268 | 269 | 270 | rotatedSquare : ( Float, Float ) -> List ( Vertex, Vertex, Vertex ) 271 | rotatedSquare ( angleXZ, angleYZ ) = 272 | let 273 | transformMat = 274 | Mat4.mul 275 | (Mat4.makeRotate (degrees angleXZ) Vec3.j) 276 | (Mat4.makeRotate (degrees angleYZ) Vec3.i) 277 | 278 | transform vertex = 279 | { vertex 280 | | position = 281 | Mat4.transform transformMat vertex.position 282 | } 283 | 284 | transformTriangle ( a, b, c ) = 285 | ( transform a, transform b, transform c ) 286 | in 287 | List.map transformTriangle square 288 | 289 | 290 | square : List ( Vertex, Vertex, Vertex ) 291 | square = 292 | let 293 | topLeft = 294 | Vertex (vec3 -1 1 1) (vec2 0 1) 295 | 296 | topRight = 297 | Vertex (vec3 1 1 1) (vec2 1 1) 298 | 299 | bottomLeft = 300 | Vertex (vec3 -1 -1 1) (vec2 0 0) 301 | 302 | bottomRight = 303 | Vertex (vec3 1 -1 1) (vec2 1 0) 304 | in 305 | [ ( topLeft, topRight, bottomLeft ) 306 | , ( bottomLeft, topRight, bottomRight ) 307 | ] 308 | 309 | 310 | 311 | -- Shaders 312 | 313 | 314 | type alias Uniforms = 315 | { texture : Texture 316 | , perspective : Mat4 317 | } 318 | 319 | 320 | vertexShader : Shader Vertex Uniforms { vcoord : Vec2 } 321 | vertexShader = 322 | [glsl| 323 | 324 | attribute vec3 position; 325 | attribute vec2 coord; 326 | uniform mat4 perspective; 327 | varying vec2 vcoord; 328 | 329 | void main () { 330 | gl_Position = perspective * vec4(position, 1.0); 331 | vcoord = coord; 332 | } 333 | 334 | |] 335 | 336 | 337 | fragmentShader : Shader {} Uniforms { vcoord : Vec2 } 338 | fragmentShader = 339 | [glsl| 340 | 341 | precision mediump float; 342 | uniform sampler2D texture; 343 | varying vec2 vcoord; 344 | 345 | void main () { 346 | gl_FragColor = texture2D(texture, vcoord); 347 | } 348 | 349 | |] 350 | -------------------------------------------------------------------------------- /examples/intersection.elm: -------------------------------------------------------------------------------- 1 | module Main exposing (main) 2 | 3 | {- 4 | Draws a red and a green triangles, where the green triangle is only 5 | visible in the intercection with the red triangle. 6 | A green outline marks a hidden area of the green triangle. 7 | 8 | This example helps to understand the separate stencil test. 9 | -} 10 | 11 | import Html exposing (Html) 12 | import Html.Attributes exposing (width, height, style) 13 | import Math.Vector3 as Vec3 exposing (Vec3, vec3) 14 | import WebGL exposing (Shader, Mesh) 15 | import WebGL.Settings exposing (Setting) 16 | import WebGL.Settings.StencilTest as StencilTest 17 | 18 | 19 | main : Html () 20 | main = 21 | WebGL.toHtmlWith 22 | [ WebGL.stencil 0 ] 23 | [ width 400 24 | , height 400 25 | , style [ ( "display", "block" ) ] 26 | ] 27 | [ WebGL.entityWith 28 | [ stencilTest ] 29 | vertexShader 30 | fragmentShader 31 | redAndGreenTriangles 32 | {} 33 | , WebGL.entity 34 | vertexShader 35 | fragmentShader 36 | greenOutline 37 | {} 38 | ] 39 | 40 | 41 | {-| Rendering with the following setting will write 1's to the stencil buffer 42 | for all front-facing triangles, and then test the subsequent back-facing 43 | triangles against the stencil buffer, so they be rendered only for the areas 44 | that are marked with 1's in the stencil buffer. 45 | 46 | `StencilTest.testSeparate` takes two options, one for front-, and another for 47 | back-facing triangles: 48 | 49 | * The front-facing stencil test always passes, and replaces the stencil buffer 50 | with 1. 51 | * The back-facing stencil test only passes when the value in the stencil buffer 52 | is equal to 1. It does not modify the stencil buffer. 53 | -} 54 | stencilTest : Setting 55 | stencilTest = 56 | StencilTest.testSeparate 57 | { ref = 1 58 | , mask = 0xFF 59 | , writeMask = 0xFF 60 | } 61 | { test = StencilTest.always 62 | , fail = StencilTest.keep 63 | , zfail = StencilTest.keep 64 | , zpass = StencilTest.replace 65 | } 66 | { test = StencilTest.equal 67 | , fail = StencilTest.keep 68 | , zfail = StencilTest.keep 69 | , zpass = StencilTest.keep 70 | } 71 | 72 | 73 | 74 | -- Mesh 75 | 76 | 77 | type alias Vertex = 78 | { position : Vec3 79 | , color : Vec3 80 | } 81 | 82 | 83 | redAndGreenTriangles : Mesh Vertex 84 | redAndGreenTriangles = 85 | let 86 | -- the red triangle is front-facing 87 | redTriangle = 88 | ( Vertex (vec3 -1 0.5 0) (vec3 1 0 0) 89 | , Vertex (vec3 0 -0.5 0) (vec3 1 0 0) 90 | , Vertex (vec3 1 0.5 0) (vec3 1 0 0) 91 | ) 92 | 93 | -- the green triangle is back-facing 94 | greenTriangle = 95 | ( Vertex (vec3 -1 -0.5 0) (vec3 0 1 0) 96 | , Vertex (vec3 0 0.5 0) (vec3 0 1 0) 97 | , Vertex (vec3 1 -0.5 0) (vec3 0 1 0) 98 | ) 99 | in 100 | WebGL.triangles 101 | [ redTriangle 102 | , greenTriangle 103 | ] 104 | 105 | 106 | greenOutline : Mesh Vertex 107 | greenOutline = 108 | WebGL.lineLoop 109 | [ Vertex (vec3 -1 -0.5 0) (vec3 0 1 0) 110 | , Vertex (vec3 0 0.5 0) (vec3 0 1 0) 111 | , Vertex (vec3 1 -0.5 0) (vec3 0 1 0) 112 | ] 113 | 114 | 115 | 116 | -- Shaders 117 | 118 | 119 | vertexShader : Shader Vertex {} { vcolor : Vec3 } 120 | vertexShader = 121 | [glsl| 122 | 123 | attribute vec3 position; 124 | attribute vec3 color; 125 | varying vec3 vcolor; 126 | 127 | void main () { 128 | gl_Position = vec4(position, 1.0); 129 | vcolor = color; 130 | } 131 | 132 | |] 133 | 134 | 135 | fragmentShader : Shader {} {} { vcolor : Vec3 } 136 | fragmentShader = 137 | [glsl| 138 | 139 | precision mediump float; 140 | varying vec3 vcolor; 141 | 142 | void main () { 143 | gl_FragColor = vec4(vcolor, 1.0); 144 | } 145 | 146 | |] 147 | -------------------------------------------------------------------------------- /examples/screenshots/crate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elm-community/webgl/ac927d98153552e4584ebff803703e4b4d684ca2/examples/screenshots/crate.jpg -------------------------------------------------------------------------------- /examples/screenshots/cube.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elm-community/webgl/ac927d98153552e4584ebff803703e4b4d684ca2/examples/screenshots/cube.jpg -------------------------------------------------------------------------------- /examples/screenshots/first-person.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elm-community/webgl/ac927d98153552e4584ebff803703e4b4d684ca2/examples/screenshots/first-person.jpg -------------------------------------------------------------------------------- /examples/screenshots/thwomp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elm-community/webgl/ac927d98153552e4584ebff803703e4b4d684ca2/examples/screenshots/thwomp.jpg -------------------------------------------------------------------------------- /examples/screenshots/triangle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elm-community/webgl/ac927d98153552e4584ebff803703e4b4d684ca2/examples/screenshots/triangle.jpg -------------------------------------------------------------------------------- /examples/texture/thwomp-face.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elm-community/webgl/ac927d98153552e4584ebff803703e4b4d684ca2/examples/texture/thwomp-face.jpg -------------------------------------------------------------------------------- /examples/texture/thwomp-side.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elm-community/webgl/ac927d98153552e4584ebff803703e4b4d684ca2/examples/texture/thwomp-side.jpg -------------------------------------------------------------------------------- /examples/texture/wood-crate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elm-community/webgl/ac927d98153552e4584ebff803703e4b4d684ca2/examples/texture/wood-crate.jpg -------------------------------------------------------------------------------- /examples/thwomp.elm: -------------------------------------------------------------------------------- 1 | module Main exposing (main) 2 | 3 | {- 4 | Thanks to The PaperNES Guy for the texture: 5 | http://the-papernes-guy.deviantart.com/art/Thwomps-Thwomps-Thwomps-186879685 6 | -} 7 | 8 | import Html exposing (Html, text) 9 | import Html.Attributes exposing (width, height, style) 10 | import Math.Matrix4 as Mat4 exposing (Mat4) 11 | import Math.Vector2 as Vec2 exposing (Vec2, vec2) 12 | import Math.Vector3 as Vec3 exposing (Vec3, vec3) 13 | import Mouse 14 | import Task exposing (Task) 15 | import WebGL exposing (Mesh, Shader, Entity) 16 | import WebGL.Texture as Texture exposing (Texture, defaultOptions, Error) 17 | import Window 18 | 19 | 20 | type alias Model = 21 | { size : Window.Size 22 | , position : Mouse.Position 23 | , textures : Maybe ( Texture, Texture ) 24 | } 25 | 26 | 27 | type Action 28 | = TexturesError Error 29 | | TexturesLoaded ( Texture, Texture ) 30 | | Resize Window.Size 31 | | MouseMove Mouse.Position 32 | 33 | 34 | main : Program Never Model Action 35 | main = 36 | Html.program 37 | { init = init 38 | , view = view 39 | , subscriptions = subscriptions 40 | , update = update 41 | } 42 | 43 | 44 | init : ( Model, Cmd Action ) 45 | init = 46 | ( { size = Window.Size 0 0 47 | , position = Mouse.Position 0 0 48 | , textures = Nothing 49 | } 50 | , Cmd.batch 51 | [ Task.perform Resize Window.size 52 | , fetchTextures 53 | ] 54 | ) 55 | 56 | 57 | subscriptions : Model -> Sub Action 58 | subscriptions _ = 59 | Sub.batch 60 | [ Window.resizes Resize 61 | , Mouse.moves MouseMove 62 | ] 63 | 64 | 65 | update : Action -> Model -> ( Model, Cmd Action ) 66 | update action model = 67 | case action of 68 | TexturesError err -> 69 | ( model, Cmd.none ) 70 | 71 | TexturesLoaded textures -> 72 | ( { model | textures = Just textures }, Cmd.none ) 73 | 74 | Resize size -> 75 | ( { model | size = size }, Cmd.none ) 76 | 77 | MouseMove position -> 78 | ( { model | position = position }, Cmd.none ) 79 | 80 | 81 | fetchTextures : Cmd Action 82 | fetchTextures = 83 | [ "texture/thwomp-face.jpg" 84 | , "texture/thwomp-side.jpg" 85 | ] 86 | |> List.map 87 | (Texture.loadWith 88 | { defaultOptions 89 | | magnify = Texture.nearest 90 | , minify = Texture.nearest 91 | } 92 | ) 93 | |> Task.sequence 94 | |> Task.andThen 95 | (\textures -> 96 | case textures of 97 | face :: side :: _ -> 98 | Task.succeed ( face, side ) 99 | 100 | _ -> 101 | Task.fail Texture.LoadError 102 | ) 103 | |> Task.attempt 104 | (\result -> 105 | case result of 106 | Err error -> 107 | TexturesError error 108 | 109 | Ok textures -> 110 | TexturesLoaded textures 111 | ) 112 | 113 | 114 | 115 | -- Meshes 116 | 117 | 118 | type alias Vertex = 119 | { position : Vec3 120 | , coord : Vec2 121 | } 122 | 123 | 124 | faceMesh : Mesh Vertex 125 | faceMesh = 126 | WebGL.triangles square 127 | 128 | 129 | sidesMesh : Mesh Vertex 130 | sidesMesh = 131 | [ ( 90, 0 ), ( 180, 0 ), ( 270, 0 ), ( 0, 90 ), ( 0, 270 ) ] 132 | |> List.concatMap rotatedSquare 133 | |> WebGL.triangles 134 | 135 | 136 | rotatedSquare : ( Float, Float ) -> List ( Vertex, Vertex, Vertex ) 137 | rotatedSquare ( angleXZ, angleYZ ) = 138 | let 139 | transformMat = 140 | Mat4.mul 141 | (Mat4.makeRotate (degrees angleXZ) Vec3.j) 142 | (Mat4.makeRotate (degrees angleYZ) Vec3.i) 143 | 144 | transform vertex = 145 | { vertex 146 | | position = 147 | Mat4.transform transformMat vertex.position 148 | } 149 | 150 | transformTriangle ( a, b, c ) = 151 | ( transform a, transform b, transform c ) 152 | in 153 | List.map transformTriangle square 154 | 155 | 156 | square : List ( Vertex, Vertex, Vertex ) 157 | square = 158 | let 159 | topLeft = 160 | Vertex (vec3 -1 1 1) (vec2 0 1) 161 | 162 | topRight = 163 | Vertex (vec3 1 1 1) (vec2 1 1) 164 | 165 | bottomLeft = 166 | Vertex (vec3 -1 -1 1) (vec2 0 0) 167 | 168 | bottomRight = 169 | Vertex (vec3 1 -1 1) (vec2 1 0) 170 | in 171 | [ ( topLeft, topRight, bottomLeft ) 172 | , ( bottomLeft, topRight, bottomRight ) 173 | ] 174 | 175 | 176 | 177 | -- VIEW 178 | 179 | 180 | view : Model -> Html Action 181 | view { textures, size, position } = 182 | case textures of 183 | Just ( faceTexture, sideTexture ) -> 184 | WebGL.toHtml 185 | [ width size.width 186 | , height size.height 187 | , style [ ( "display", "block" ) ] 188 | ] 189 | [ toEntity faceMesh faceTexture size position 190 | , toEntity sidesMesh sideTexture size position 191 | ] 192 | 193 | Nothing -> 194 | text "Loading textures..." 195 | 196 | 197 | toEntity : Mesh Vertex -> Texture -> Window.Size -> Mouse.Position -> Entity 198 | toEntity mesh texture { width, height } { x, y } = 199 | WebGL.entity 200 | vertexShader 201 | fragmentShader 202 | mesh 203 | { texture = texture 204 | , perspective = 205 | perspective 206 | (toFloat width) 207 | (toFloat height) 208 | (toFloat x) 209 | (toFloat y) 210 | } 211 | 212 | 213 | perspective : Float -> Float -> Float -> Float -> Mat4 214 | perspective width height x y = 215 | let 216 | eye = 217 | vec3 (0.5 - x / width) -(0.5 - y / height) 1 218 | |> Vec3.normalize 219 | |> Vec3.scale 6 220 | in 221 | Mat4.mul 222 | (Mat4.makePerspective 45 (width / height) 0.01 100) 223 | (Mat4.makeLookAt eye (vec3 0 0 0) Vec3.j) 224 | 225 | 226 | 227 | -- SHADERS 228 | 229 | 230 | type alias Uniforms = 231 | { perspective : Mat4 232 | , texture : Texture 233 | } 234 | 235 | 236 | vertexShader : Shader Vertex Uniforms { vcoord : Vec2 } 237 | vertexShader = 238 | [glsl| 239 | 240 | attribute vec3 position; 241 | attribute vec2 coord; 242 | uniform mat4 perspective; 243 | varying vec2 vcoord; 244 | 245 | void main () { 246 | gl_Position = perspective * vec4(position, 1.0); 247 | vcoord = coord.xy; 248 | } 249 | 250 | |] 251 | 252 | 253 | fragmentShader : Shader {} Uniforms { vcoord : Vec2 } 254 | fragmentShader = 255 | [glsl| 256 | 257 | precision mediump float; 258 | uniform sampler2D texture; 259 | varying vec2 vcoord; 260 | 261 | void main () { 262 | gl_FragColor = texture2D(texture, vcoord); 263 | } 264 | 265 | |] 266 | -------------------------------------------------------------------------------- /examples/triangle.elm: -------------------------------------------------------------------------------- 1 | module Main exposing (main) 2 | 3 | {- 4 | Rotating triangle, that is a "hello world" of the WebGL 5 | -} 6 | 7 | import AnimationFrame 8 | import Html exposing (Html) 9 | import Html.Attributes exposing (width, height, style) 10 | import Math.Matrix4 as Mat4 exposing (Mat4) 11 | import Math.Vector3 as Vec3 exposing (vec3, Vec3) 12 | import Time exposing (Time) 13 | import WebGL exposing (Mesh, Shader) 14 | 15 | 16 | main : Program Never Time Time 17 | main = 18 | Html.program 19 | { init = ( 0, Cmd.none ) 20 | , view = view 21 | , subscriptions = (\model -> AnimationFrame.diffs Basics.identity) 22 | , update = (\elapsed currentTime -> ( elapsed + currentTime, Cmd.none )) 23 | } 24 | 25 | 26 | view : Float -> Html msg 27 | view t = 28 | WebGL.toHtml 29 | [ width 400 30 | , height 400 31 | , style [ ( "display", "block" ) ] 32 | ] 33 | [ WebGL.entity 34 | vertexShader 35 | fragmentShader 36 | mesh 37 | { perspective = perspective (t / 1000) } 38 | ] 39 | 40 | 41 | perspective : Float -> Mat4 42 | perspective t = 43 | Mat4.mul 44 | (Mat4.makePerspective 45 1 0.01 100) 45 | (Mat4.makeLookAt (vec3 (4 * cos t) 0 (4 * sin t)) (vec3 0 0 0) (vec3 0 1 0)) 46 | 47 | 48 | 49 | -- Mesh 50 | 51 | 52 | type alias Vertex = 53 | { position : Vec3 54 | , color : Vec3 55 | } 56 | 57 | 58 | mesh : Mesh Vertex 59 | mesh = 60 | WebGL.triangles 61 | [ ( Vertex (vec3 0 0 0) (vec3 1 0 0) 62 | , Vertex (vec3 1 1 0) (vec3 0 1 0) 63 | , Vertex (vec3 1 -1 0) (vec3 0 0 1) 64 | ) 65 | ] 66 | 67 | 68 | 69 | -- Shaders 70 | 71 | 72 | type alias Uniforms = 73 | { perspective : Mat4 } 74 | 75 | 76 | vertexShader : Shader Vertex Uniforms { vcolor : Vec3 } 77 | vertexShader = 78 | [glsl| 79 | 80 | attribute vec3 position; 81 | attribute vec3 color; 82 | uniform mat4 perspective; 83 | varying vec3 vcolor; 84 | 85 | void main () { 86 | gl_Position = perspective * vec4(position, 1.0); 87 | vcolor = color; 88 | } 89 | 90 | |] 91 | 92 | 93 | fragmentShader : Shader {} Uniforms { vcolor : Vec3 } 94 | fragmentShader = 95 | [glsl| 96 | 97 | precision mediump float; 98 | varying vec3 vcolor; 99 | 100 | void main () { 101 | gl_FragColor = vec4(vcolor, 1.0); 102 | } 103 | 104 | |] 105 | -------------------------------------------------------------------------------- /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/webgl.git" master:gh-pages 27 | -------------------------------------------------------------------------------- /pipeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elm-community/webgl/ac927d98153552e4584ebff803703e4b4d684ca2/pipeline.png -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euxo pipefail 3 | 4 | rm -rf release || exit 0; 5 | 6 | elm-package bump 7 | 8 | version=$(grep -m1 version elm-package.json | awk -F: '{ print $2 }' | sed 's/[", ]//g') 9 | 10 | git commit -a -m "Bump to $version" 11 | git push 12 | 13 | cleanup="examples gh-pages.sh pipeline.png CONTRIBUTING.md .eslintrc.json release.sh" 14 | last_commit=$(git rev-parse HEAD) 15 | 16 | git clone --reference . git@github.com:elm-community/webgl.git release 17 | ( 18 | cd release 19 | git checkout $last_commit 20 | git rm -rf --ignore-unmatch $cleanup 21 | git commit -m "Cleanup and release $version" 22 | git tag -a $version -m "Release $version" 23 | git push origin $version 24 | elm-package publish 25 | ) 26 | -------------------------------------------------------------------------------- /src/Native/Texture.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-unused-vars, camelcase 2 | var _elm_community$webgl$Native_Texture = function () { 3 | 4 | var NEAREST = 9728; 5 | var LINEAR = 9729; 6 | var CLAMP_TO_EDGE = 33071; 7 | 8 | function guid() { 9 | // eslint-disable-next-line camelcase 10 | return _elm_lang$core$Native_Utils.guid(); 11 | } 12 | 13 | function load(magnify, mininify, horizontalWrap, verticalWrap, flipY, url) { 14 | // eslint-disable-next-line camelcase 15 | var Scheduler = _elm_lang$core$Native_Scheduler; 16 | var isMipmap = mininify !== NEAREST && mininify !== LINEAR; 17 | return Scheduler.nativeBinding(function (callback) { 18 | var img = new Image(); 19 | function createTexture(gl) { 20 | var tex = gl.createTexture(); 21 | gl.bindTexture(gl.TEXTURE_2D, tex); 22 | gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); 23 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img); 24 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magnify); 25 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, mininify); 26 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, horizontalWrap); 27 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, verticalWrap); 28 | if (isMipmap) { 29 | gl.generateMipmap(gl.TEXTURE_2D); 30 | } 31 | gl.bindTexture(gl.TEXTURE_2D, null); 32 | return tex; 33 | } 34 | img.onload = function () { 35 | var width = img.width; 36 | var height = img.height; 37 | var widthPowerOfTwo = (width & (width - 1)) === 0; 38 | var heightPowerOfTwo = (height & (height - 1)) === 0; 39 | var isSizeValid = (widthPowerOfTwo && heightPowerOfTwo) || ( 40 | !isMipmap 41 | && horizontalWrap === CLAMP_TO_EDGE 42 | && verticalWrap === CLAMP_TO_EDGE 43 | ); 44 | if (isSizeValid) { 45 | callback(Scheduler.succeed({ 46 | ctor: 'Texture', 47 | id: guid(), 48 | createTexture: createTexture, 49 | width: width, 50 | height: height 51 | })); 52 | } else { 53 | callback(Scheduler.fail({ 54 | ctor: 'SizeError', 55 | _0: width, 56 | _1: height 57 | })); 58 | } 59 | }; 60 | img.onerror = function () { 61 | callback(Scheduler.fail({ ctor: 'LoadError' })); 62 | }; 63 | if (url.slice(0, 5) !== 'data:') { 64 | img.crossOrigin = 'Anonymous'; 65 | } 66 | img.src = url; 67 | }); 68 | } 69 | 70 | function size(texture) { 71 | // eslint-disable-next-line camelcase 72 | return _elm_lang$core$Native_Utils.Tuple2(texture.width, texture.height); 73 | } 74 | 75 | return { 76 | size: size, 77 | load: F6(load) 78 | }; 79 | 80 | }(); 81 | -------------------------------------------------------------------------------- /src/Native/WebGL.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-unused-vars, camelcase 2 | var _elm_community$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 | function guid() { 11 | // eslint-disable-next-line camelcase 12 | return _elm_lang$core$Native_Utils.guid(); 13 | } 14 | function listEach(fn, list) { 15 | while (list.ctor !== '[]') { 16 | fn(list._0); 17 | list = list._1; 18 | } 19 | } 20 | function listLength(list) { 21 | var length = 0; 22 | while (list.ctor !== '[]') { 23 | length++; 24 | list = list._1; 25 | } 26 | return length; 27 | } 28 | 29 | var rAF = typeof requestAnimationFrame !== 'undefined' ? 30 | requestAnimationFrame : 31 | function (cb) { setTimeout(cb, 1000 / 60); }; 32 | 33 | function unsafeCoerceGLSL(src) { 34 | return { src: src }; 35 | } 36 | 37 | function entity(settings, vert, frag, buffer, uniforms) { 38 | 39 | if (!buffer.guid) { 40 | buffer.guid = guid(); 41 | } 42 | 43 | return { 44 | ctor: 'Entity', 45 | vert: vert, 46 | frag: frag, 47 | buffer: buffer, 48 | uniforms: uniforms, 49 | settings: settings 50 | }; 51 | 52 | } 53 | 54 | /** 55 | * Apply setting to the gl context 56 | * 57 | * @param {WebGLRenderingContext} gl context 58 | * @param {Setting} setting coming in from Elm 59 | */ 60 | function applySetting(gl, setting) { 61 | switch (setting.ctor) { 62 | case 'Blend': 63 | gl.enable(gl.BLEND); 64 | // eq1 f11 f12 eq2 f21 f22 r g b a 65 | gl.blendEquationSeparate(setting._0, setting._3); 66 | gl.blendFuncSeparate(setting._1, setting._2, setting._4, setting._5); 67 | gl.blendColor(setting._6, setting._7, setting._8, setting._9); 68 | break; 69 | case 'DepthTest': 70 | gl.enable(gl.DEPTH_TEST); 71 | // func mask near far 72 | gl.depthFunc(setting._0); 73 | gl.depthMask(setting._1); 74 | gl.depthRange(setting._2, setting._3); 75 | break; 76 | case 'StencilTest': 77 | gl.enable(gl.STENCIL_TEST); 78 | // ref mask writeMask test1 fail1 zfail1 zpass1 test2 fail2 zfail2 zpass2 79 | gl.stencilFuncSeparate(gl.FRONT, setting._3, setting._0, setting._1); 80 | gl.stencilOpSeparate(gl.FRONT, setting._4, setting._5, setting._6); 81 | gl.stencilMaskSeparate(gl.FRONT, setting._2); 82 | gl.stencilFuncSeparate(gl.BACK, setting._7, setting._0, setting._1); 83 | gl.stencilOpSeparate(gl.BACK, setting._8, setting._9, setting._10); 84 | gl.stencilMaskSeparate(gl.BACK, setting._2); 85 | break; 86 | case 'Scissor': 87 | gl.enable(gl.SCISSOR_TEST); 88 | gl.scissor(setting._0, setting._1, setting._2, setting._3); 89 | break; 90 | case 'ColorMask': 91 | gl.colorMask(setting._0, setting._1, setting._2, setting._3); 92 | break; 93 | case 'CullFace': 94 | gl.enable(gl.CULL_FACE); 95 | gl.cullFace(setting._0); 96 | break; 97 | case 'PolygonOffset': 98 | gl.enable(gl.POLYGON_OFFSET_FILL); 99 | gl.polygonOffset(setting._0, setting._1); 100 | break; 101 | case 'SampleCoverage': 102 | gl.enable(gl.SAMPLE_COVERAGE); 103 | gl.sampleCoverage(setting._0, setting._1); 104 | break; 105 | case 'SampleAlphaToCoverage': 106 | gl.enable(gl.SAMPLE_ALPHA_TO_COVERAGE); 107 | break; 108 | } 109 | } 110 | 111 | /** 112 | * Revert setting that was applied to the gl context 113 | * 114 | * @param {WebGLRenderingContext} gl context 115 | * @param {Setting} setting coming in from Elm 116 | */ 117 | function revertSetting(gl, setting) { 118 | switch (setting.ctor) { 119 | case 'Blend': 120 | gl.disable(gl.BLEND); 121 | break; 122 | case 'DepthTest': 123 | gl.disable(gl.DEPTH_TEST); 124 | break; 125 | case 'StencilTest': 126 | gl.disable(gl.STENCIL_TEST); 127 | break; 128 | case 'Scissor': 129 | gl.disable(gl.SCISSOR_TEST); 130 | break; 131 | case 'ColorMask': 132 | gl.colorMask(true, true, true, true); 133 | break; 134 | case 'CullFace': 135 | gl.disable(gl.CULL_FACE); 136 | break; 137 | case 'PolygonOffset': 138 | gl.disable(gl.POLYGON_OFFSET_FILL); 139 | break; 140 | case 'SampleCoverage': 141 | gl.disable(gl.SAMPLE_COVERAGE); 142 | break; 143 | case 'SampleAlphaToCoverage': 144 | gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE); 145 | break; 146 | } 147 | } 148 | 149 | function doCompile(gl, src, type) { 150 | 151 | var shader = gl.createShader(type); 152 | LOG('Created shader'); 153 | 154 | gl.shaderSource(shader, src); 155 | gl.compileShader(shader); 156 | if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { 157 | throw gl.getShaderInfoLog(shader); 158 | } 159 | 160 | return shader; 161 | 162 | } 163 | 164 | function doLink(gl, vshader, fshader) { 165 | 166 | var program = gl.createProgram(); 167 | LOG('Created program'); 168 | 169 | gl.attachShader(program, vshader); 170 | gl.attachShader(program, fshader); 171 | gl.linkProgram(program); 172 | if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { 173 | throw gl.getProgramInfoLog(program); 174 | } 175 | 176 | return program; 177 | 178 | } 179 | 180 | function getRenderInfo(gl, renderType) { 181 | switch (renderType) { 182 | case 'Triangles': 183 | return { mode: gl.TRIANGLES, elemSize: 3, indexSize: 0 }; 184 | case 'LineStrip': 185 | return { mode: gl.LINE_STRIP, elemSize: 1, indexSize: 0 }; 186 | case 'LineLoop': 187 | return { mode: gl.LINE_LOOP, elemSize: 1, indexSize: 0 }; 188 | case 'Points': 189 | return { mode: gl.POINTS, elemSize: 1, indexSize: 0 }; 190 | case 'Lines': 191 | return { mode: gl.LINES, elemSize: 2, indexSize: 0 }; 192 | case 'TriangleStrip': 193 | return { mode: gl.TRIANGLE_STRIP, elemSize: 1, indexSize: 0 }; 194 | case 'TriangleFan': 195 | return { mode: gl.TRIANGLE_FAN, elemSize: 1, indexSize: 0 }; 196 | case 'IndexedTriangles': 197 | return { mode: gl.TRIANGLES, elemSize: 1, indexSize: 3 }; 198 | } 199 | } 200 | 201 | function getAttributeInfo(gl, type) { 202 | switch (type) { 203 | case gl.FLOAT: 204 | return { size: 1, type: Float32Array, baseType: gl.FLOAT }; 205 | case gl.FLOAT_VEC2: 206 | return { size: 2, type: Float32Array, baseType: gl.FLOAT }; 207 | case gl.FLOAT_VEC3: 208 | return { size: 3, type: Float32Array, baseType: gl.FLOAT }; 209 | case gl.FLOAT_VEC4: 210 | return { size: 4, type: Float32Array, baseType: gl.FLOAT }; 211 | case gl.INT: 212 | return { size: 1, type: Int32Array, baseType: gl.INT }; 213 | case gl.INT_VEC2: 214 | return { size: 2, type: Int32Array, baseType: gl.INT }; 215 | case gl.INT_VEC3: 216 | return { size: 3, type: Int32Array, baseType: gl.INT }; 217 | case gl.INT_VEC4: 218 | return { size: 4, type: Int32Array, baseType: gl.INT }; 219 | } 220 | } 221 | 222 | /** 223 | * Form the buffer for a given attribute. 224 | * 225 | * @param {WebGLRenderingContext} gl context 226 | * @param {WebGLActiveInfo} attribute the attribute to bind to. 227 | * We use its name to grab the record by name and also to know 228 | * how many elements we need to grab. 229 | * @param {List} bufferElems The list coming in from Elm. 230 | * @param {Number} elemSize The length of the number of vertices that 231 | * complete one 'thing' based on the drawing mode. 232 | * ie, 2 for Lines, 3 for Triangles, etc. 233 | * @return {WebGLBuffer} 234 | */ 235 | function doBindAttribute(gl, attribute, bufferElems, elemSize) { 236 | var idxKeys = []; 237 | for (var i = 0; i < elemSize; i++) { 238 | idxKeys.push('_' + i); 239 | } 240 | 241 | function dataFill(data, cnt, fillOffset, elem, key) { 242 | if (elemSize === 1) { 243 | for (var i = 0; i < cnt; i++) { 244 | data[fillOffset++] = cnt === 1 ? elem[key] : elem[key][i]; 245 | } 246 | } else { 247 | idxKeys.forEach(function (idx) { 248 | for (var i = 0; i < cnt; i++) { 249 | data[fillOffset++] = cnt === 1 ? elem[idx][key] : elem[idx][key][i]; 250 | } 251 | }); 252 | } 253 | } 254 | 255 | var attributeInfo = getAttributeInfo(gl, attribute.type); 256 | 257 | if (attributeInfo === undefined) { 258 | throw new Error('No info available for: ' + attribute.type); 259 | } 260 | 261 | var dataIdx = 0; 262 | var array = new attributeInfo.type(listLength(bufferElems) * attributeInfo.size * elemSize); 263 | 264 | listEach(function (elem) { 265 | dataFill(array, attributeInfo.size, dataIdx, elem, attribute.name); 266 | dataIdx += attributeInfo.size * elemSize; 267 | }, bufferElems); 268 | 269 | var buffer = gl.createBuffer(); 270 | LOG('Created attribute buffer ' + attribute.name); 271 | 272 | gl.bindBuffer(gl.ARRAY_BUFFER, buffer); 273 | gl.bufferData(gl.ARRAY_BUFFER, array, gl.STATIC_DRAW); 274 | return buffer; 275 | } 276 | 277 | /** 278 | * This sets up the binding caching buffers. 279 | * 280 | * We don't actually bind any buffers now except for the indices buffer. 281 | * The problem with filling the buffers here is that it is possible to 282 | * have a buffer shared between two webgl shaders; 283 | * which could have different active attributes. If we bind it here against 284 | * a particular program, we might not bind them all. That final bind is now 285 | * done right before drawing. 286 | * 287 | * @param {WebGLRenderingContext} gl context 288 | * @param {Object} renderType 289 | * @param {Number} renderType.indexSize size of the index 290 | * @param {Number} renderType.elemSize size of the element 291 | * @param {Drawable} drawable a drawable object from Elm 292 | * that contains elements and optionally indices 293 | * @return {Object} buffer - an object with the following properties 294 | * @return {Number} buffer.numIndices 295 | * @return {WebGLBuffer} buffer.indexBuffer 296 | * @return {Object} buffer.buffers - will be used to buffer attributes 297 | */ 298 | function doBindSetup(gl, renderType, drawable) { 299 | LOG('Created index buffer'); 300 | var indexBuffer = gl.createBuffer(); 301 | var indices = (renderType.indexSize === 0) 302 | ? makeSequentialBuffer(renderType.elemSize * listLength(drawable._0)) 303 | : makeIndexedBuffer(drawable._1, renderType.indexSize); 304 | 305 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); 306 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); 307 | 308 | return { 309 | numIndices: indices.length, 310 | indexBuffer: indexBuffer, 311 | buffers: {} 312 | }; 313 | } 314 | 315 | /** 316 | * Create an indices array and fill it with 0..n 317 | * 318 | * @param {Number} numIndices The number of indices 319 | * @return {Uint16Array} indices 320 | */ 321 | function makeSequentialBuffer(numIndices) { 322 | var indices = new Uint16Array(numIndices); 323 | for (var i = 0; i < numIndices; i++) { 324 | indices[i] = i; 325 | } 326 | return indices; 327 | } 328 | 329 | /** 330 | * Create an indices array and fill it from indices 331 | * based on the size of the index 332 | * 333 | * @param {List} indicesList the list of indices 334 | * @param {Number} indexSize the size of the index 335 | * @return {Uint16Array} indices 336 | */ 337 | function makeIndexedBuffer(indicesList, indexSize) { 338 | var indices = new Uint16Array(listLength(indicesList) * indexSize); 339 | var fillOffset = 0; 340 | var i; 341 | listEach(function (elem) { 342 | if (indexSize === 1) { 343 | indices[fillOffset++] = elem; 344 | } else { 345 | for (i = 0; i < indexSize; i++) { 346 | indices[fillOffset++] = elem['_' + i.toString()]; 347 | } 348 | } 349 | }, indicesList); 350 | return indices; 351 | } 352 | 353 | function getProgID(vertID, fragID) { 354 | return vertID + '#' + fragID; 355 | } 356 | 357 | function drawGL(domNode, data) { 358 | 359 | var model = data.model; 360 | var gl = model.cache.gl; 361 | 362 | if (!gl) { 363 | return domNode; 364 | } 365 | 366 | gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); 367 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); 368 | LOG('Drawing'); 369 | 370 | function drawEntity(entity) { 371 | if (entity.buffer._0.ctor === '[]') { 372 | return; 373 | } 374 | 375 | var progid; 376 | var program; 377 | if (entity.vert.id && entity.frag.id) { 378 | progid = getProgID(entity.vert.id, entity.frag.id); 379 | program = model.cache.programs[progid]; 380 | } 381 | 382 | if (!program) { 383 | 384 | var vshader; 385 | if (entity.vert.id) { 386 | vshader = model.cache.shaders[entity.vert.id]; 387 | } else { 388 | entity.vert.id = guid(); 389 | } 390 | 391 | if (!vshader) { 392 | vshader = doCompile(gl, entity.vert.src, gl.VERTEX_SHADER); 393 | model.cache.shaders[entity.vert.id] = vshader; 394 | } 395 | 396 | var fshader; 397 | if (entity.frag.id) { 398 | fshader = model.cache.shaders[entity.frag.id]; 399 | } else { 400 | entity.frag.id = guid(); 401 | } 402 | 403 | if (!fshader) { 404 | fshader = doCompile(gl, entity.frag.src, gl.FRAGMENT_SHADER); 405 | model.cache.shaders[entity.frag.id] = fshader; 406 | } 407 | 408 | program = doLink(gl, vshader, fshader); 409 | progid = getProgID(entity.vert.id, entity.frag.id); 410 | model.cache.programs[progid] = program; 411 | 412 | } 413 | 414 | gl.useProgram(program); 415 | 416 | progid = progid || getProgID(entity.vert.id, entity.frag.id); 417 | var setters = model.cache.uniformSetters[progid]; 418 | if (!setters) { 419 | setters = createUniformSetters(gl, model, program); 420 | model.cache.uniformSetters[progid] = setters; 421 | } 422 | 423 | setUniforms(setters, entity.uniforms); 424 | 425 | var entityType = getRenderInfo(gl, entity.buffer.ctor); 426 | var buffer = model.cache.buffers[entity.buffer.guid]; 427 | 428 | if (!buffer) { 429 | buffer = doBindSetup(gl, entityType, entity.buffer); 430 | model.cache.buffers[entity.buffer.guid] = buffer; 431 | } 432 | 433 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer.indexBuffer); 434 | 435 | var numAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); 436 | 437 | for (var i = 0; i < numAttributes; i++) { 438 | var attribute = gl.getActiveAttrib(program, i); 439 | 440 | var attribLocation = gl.getAttribLocation(program, attribute.name); 441 | gl.enableVertexAttribArray(attribLocation); 442 | 443 | if (buffer.buffers[attribute.name] === undefined) { 444 | buffer.buffers[attribute.name] = doBindAttribute(gl, attribute, entity.buffer._0, entityType.elemSize); 445 | } 446 | var attributeBuffer = buffer.buffers[attribute.name]; 447 | var attributeInfo = getAttributeInfo(gl, attribute.type); 448 | 449 | gl.bindBuffer(gl.ARRAY_BUFFER, attributeBuffer); 450 | gl.vertexAttribPointer(attribLocation, attributeInfo.size, attributeInfo.baseType, false, 0, 0); 451 | } 452 | 453 | listEach(function (setting) { 454 | applySetting(gl, setting); 455 | }, entity.settings); 456 | 457 | gl.drawElements(entityType.mode, buffer.numIndices, gl.UNSIGNED_SHORT, 0); 458 | 459 | listEach(function (setting) { 460 | revertSetting(gl, setting); 461 | }, entity.settings); 462 | 463 | } 464 | 465 | listEach(drawEntity, model.entities); 466 | return domNode; 467 | } 468 | 469 | function createUniformSetters(gl, model, program) { 470 | var textureCounter = 0; 471 | function createUniformSetter(program, uniform) { 472 | var uniformLocation = gl.getUniformLocation(program, uniform.name); 473 | switch (uniform.type) { 474 | case gl.INT: 475 | return function (value) { 476 | gl.uniform1i(uniformLocation, value); 477 | }; 478 | case gl.FLOAT: 479 | return function (value) { 480 | gl.uniform1f(uniformLocation, value); 481 | }; 482 | case gl.FLOAT_VEC2: 483 | return function (value) { 484 | gl.uniform2fv(uniformLocation, new Float32Array(value)); 485 | }; 486 | case gl.FLOAT_VEC3: 487 | return function (value) { 488 | gl.uniform3fv(uniformLocation, new Float32Array(value)); 489 | }; 490 | case gl.FLOAT_VEC4: 491 | return function (value) { 492 | gl.uniform4fv(uniformLocation, new Float32Array(value)); 493 | }; 494 | case gl.FLOAT_MAT4: 495 | return function (value) { 496 | gl.uniformMatrix4fv(uniformLocation, false, new Float32Array(value)); 497 | }; 498 | case gl.SAMPLER_2D: 499 | var currentTexture = textureCounter++; 500 | return function (texture) { 501 | gl.activeTexture(gl.TEXTURE0 + currentTexture); 502 | var tex = model.cache.textures[texture.id]; 503 | if (!tex) { 504 | LOG('Created texture'); 505 | tex = texture.createTexture(gl); 506 | model.cache.textures[texture.id] = tex; 507 | } 508 | gl.bindTexture(gl.TEXTURE_2D, tex); 509 | gl.uniform1i(uniformLocation, currentTexture); 510 | }; 511 | case gl.BOOL: 512 | return function (value) { 513 | gl.uniform1i(uniformLocation, value); 514 | }; 515 | default: 516 | LOG('Unsupported uniform type: ' + uniform.type); 517 | return function () {}; 518 | } 519 | } 520 | 521 | var uniformSetters = {}; 522 | var numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); 523 | for (var i = 0; i < numUniforms; i++) { 524 | var uniform = gl.getActiveUniform(program, i); 525 | uniformSetters[uniform.name] = createUniformSetter(program, uniform); 526 | } 527 | 528 | return uniformSetters; 529 | } 530 | 531 | function setUniforms(setters, values) { 532 | Object.keys(values).forEach(function (name) { 533 | var setter = setters[name]; 534 | if (setter) { 535 | setter(values[name]); 536 | } 537 | }); 538 | } 539 | 540 | // VIRTUAL-DOM WIDGET 541 | 542 | function toHtml(options, factList, entities) { 543 | var model = { 544 | entities: entities, 545 | cache: {}, 546 | options: options 547 | }; 548 | // eslint-disable-next-line camelcase 549 | return _elm_lang$virtual_dom$Native_VirtualDom.custom(factList, model, implementation); 550 | } 551 | 552 | var implementation = { 553 | render: render, 554 | diff: diff 555 | }; 556 | 557 | /** 558 | * Creates canvas and schedules initial drawGL 559 | * @param {Object} model 560 | * @param {Object} model.cache that may contain the following properties: 561 | gl, shaders, programs, uniformSetters, buffers, textures 562 | * @param {List