├── .gitignore ├── Engine.elm ├── Engine ├── Camera │ └── Camera.elm ├── Light │ └── Light.elm ├── Material │ └── Material.elm ├── Math │ └── Utils.elm ├── Mesh │ ├── Cube.elm │ ├── Mesh.elm │ ├── Pyramid.elm │ ├── Rectangle.elm │ ├── Sphere.elm │ └── Triangle.elm ├── Render │ ├── DefaultRenderable.elm │ ├── Render.elm │ └── Renderable.elm ├── Scene │ └── Scene.elm ├── Shader │ ├── Attribute.elm │ ├── Boilerplate.elm │ ├── FragmentShader.elm │ ├── GouraudShader.elm │ ├── Library.elm │ ├── Shader.elm │ ├── Uniform.elm │ ├── Utils.elm │ └── VertexShader.elm ├── Transform │ └── Transform.elm └── Viewport │ └── Viewport.elm ├── PyramidCube.png ├── README.md ├── elm-package.json ├── example.elm ├── pong.elm └── test.elm /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore the auto created elm.js file 2 | .elm.js 3 | 4 | # Ignore the run-time created stuff 5 | elm-stuff/ 6 | 7 | elm.js 8 | 9 | *.elmi 10 | 11 | *.elmo 12 | 13 | elm-stuff/documentation.json 14 | 15 | elm-stuff/exact-dependencies.json 16 | 17 | *.yml 18 | 19 | elm-stuff/packages/elm-lang/core/1.1.0/README.md 20 | -------------------------------------------------------------------------------- /Engine.elm: -------------------------------------------------------------------------------- 1 | module Engine where 2 | 3 | {-| The Graphics Engine Library contains several functions and types to 4 | make constructing 3D Scenes more fun and easy to use. 5 | 6 | The simplest possible program to construct a scene is: 7 | 8 | main = render scene 9 | 10 | This program renders a default Scene that simply displays a red cube. 11 | 12 | `scene` is just shorthand for the following record: 13 | 14 | scene = { 15 | camera = camera, 16 | light = light, 17 | objects = fromList [cube], 18 | viewport = viewport 19 | } 20 | 21 | As you can see, a scene contains a camera, a light, a list of objects, and 22 | a viewport. 23 | 24 | Lets modify the scene to display a pyramid instead 25 | 26 | main = render { scene | objects <- fromList [pyramid]} 27 | 28 | As you can see, all it took was changing the value of the objects property. 29 | 30 | This logic is applicable almost everywhere in the library. So, you could 31 | construct arbitrary scenes like. 32 | 33 | myCube = { 34 | cube | position <- vec3 1 1 0, 35 | rotation <- vec3 45 45 0, 36 | scale <- vec3 0.5 0.5 0.5 } 37 | 38 | myPyramid = { 39 | pyramid | scale <- vec3 0.3 2.0 1.0 } 40 | 41 | myCamera = { 42 | camera | fieldOfView <- 125 } 43 | 44 | myScene = { 45 | scene | camera <- myCamera, 46 | objects <- fromList [myCube, myPyramid] } 47 | 48 | main = render myScene 49 | 50 | As you can see, the record syntax allows to change only what needs to be 51 | changed and removes any unnecessary boilerplate. 52 | 53 | # Scene 54 | @docs Scene, scene 55 | 56 | # Camera 57 | @docs Camera, camera 58 | 59 | # Light 60 | @docs Light, light 61 | 62 | # Material 63 | @docs MaterialProperty, Material, material 64 | 65 | # Mesh 66 | @docs Mesh, triangleMesh, rectangleMesh, pyramidMesh, cubeMesh, sphereMesh 67 | 68 | # Renderable 69 | @docs Renderable, renderable, triangle, rectangle, pyramid, cube, sphere 70 | 71 | # Render Function 72 | @docs render 73 | 74 | # Transform 75 | @docs Transform, transform 76 | 77 | # Viewport 78 | @docs Viewport, viewport 79 | 80 | # Shading 81 | @docs Attribute, Uniform 82 | 83 | -} 84 | 85 | -------------- IMPORTED MODULES JUST FOR TYPE ANNOTATIONS ------------------- 86 | 87 | import Math.Vector3 (Vec3) 88 | import Graphics.Element (Element) 89 | 90 | ----------------- IMPORTED MODULES TO BE RE-EXPORTED ------------------------ 91 | 92 | import Engine.Scene.Scene as Scene 93 | import Engine.Camera.Camera as Camera 94 | import Engine.Light.Light as Light 95 | import Engine.Material.Material as Material 96 | import Engine.Mesh.Mesh as Mesh 97 | import Engine.Mesh.Triangle as Triangle 98 | import Engine.Mesh.Rectangle as Rectangle 99 | import Engine.Mesh.Cube as Cube 100 | import Engine.Mesh.Pyramid as Pyramid 101 | import Engine.Mesh.Sphere as Sphere 102 | import Engine.Shader.Attribute as Attribute 103 | import Engine.Shader.Uniform as Uniform 104 | import Engine.Render.Render as Render 105 | import Engine.Render.Renderable as Renderable 106 | import Engine.Render.DefaultRenderable as DefaultRenderable 107 | import Engine.Transform.Transform as Transform 108 | import Engine.Viewport.Viewport as Viewport 109 | 110 | ----------------- Re-export Engine.Scene.Scene ------------------------------ 111 | {-| Represents a scene. A scene contains a list of objects such that 112 | calling `render` on a scene will render all the objects in a webgl context. 113 | 114 | A scene contains a camera to define the viewer's viewing point, a light to 115 | illuminate the scene, and a viewport to describe the context on which the 116 | scene will be drawn. 117 | 118 | -} 119 | type alias Scene = Scene.Scene 120 | 121 | {-| Default scene object. Draws a red cube in the middle of the default context. 122 | -} 123 | scene : Scene 124 | scene = Scene.scene 125 | 126 | ----------------------------------------------------------------------------- 127 | 128 | 129 | ----------------- Re-export Engine.Camera.Camera ---------------------------- 130 | {-| Represent a perspective camera. As a transform, a camera has a 131 | position, a rotation, and a scale. A camera also has an aspect ratio, 132 | a field of view, a near clipping plane, and a far clipping plane. In essence, 133 | a camera describes a viewing frustrum. 134 | 135 | Note : By convention, aspectRatio is a ratio (so a 4 : 3 aspect ratio 136 | can be achieved by setting aspectRatio to 4 / 3 or 1.3), fieldOfView is 137 | set in degrees (45 being usually a good safe bet), nearClipping and 138 | farClipping are set in world coordinates. 139 | 140 | Example 1 : (A typical 4 : 3 camera) 141 | 142 | fourThirdsCamera = { 143 | position = vec3 0 0 0, 144 | rotation = vec3 0 0 0, 145 | scale = vec3 1 1 1, 146 | aspectRatio = 4 / 3, 147 | fieldOfView = 45, 148 | nearClipping = 1, 149 | farClipping = 80000 } 150 | 151 | Example 2 : (A 16 : 9 camera that cannot see far and warped field of view) 152 | 153 | weirdSixteenNinthsCamera = { 154 | fourThirdsCamera | aspectRatio <- 16 / 9, 155 | fieldOfView <- 125, 156 | farClipping <- 10 } 157 | 158 | -} 159 | type alias Camera = Camera.Camera 160 | 161 | 162 | {-| Default camera on a scene. Defines a camera at position x = 0, y = 0, 163 | z = -10, with a square aspect ratio (aspectRatio == 1), a field of view of 164 | 45 degrees (fieldOfView == 45), a near clipping plane at 1 and a far 165 | clipping plane at 80000. 166 | 167 | Example : Constructing a standard widescreen cinema camera 168 | 169 | widescreenCamera = { camera | aspectRatio <- 2.39 } 170 | 171 | -} 172 | camera : Camera 173 | camera = Camera.camera 174 | 175 | ---------------------------------------------------------------------------- 176 | 177 | 178 | ----------------- Re-export Engine.Light.Light ----------------------------- 179 | 180 | {-| Represent a light in a scene. As a transform, a light has a position, 181 | a rotation, and a scale. A light also has an intensity property 182 | to control the strength of the influence of the light source in the scene, 183 | a color property, and a visibility property to act as a flag to toggle 184 | a light on and off. 185 | 186 | Note: By convention, full intensity is set a 1 and no intensity is 0, 187 | color values are values between 0 and 1 (not 0 - 255). 188 | 189 | Example 1 : (A red light at full intensity) 190 | 191 | redLight = { 192 | position = vec3 0 0 0, 193 | rotation = vec3 0 0 0, 194 | scale = vec3 1 1 1, 195 | intensity = 1, 196 | color = vec3 1 0 0, 197 | visibility = True } 198 | 199 | Example 2 : (A white light at half intensity) 200 | 201 | whiteLight = 202 | { redLight | color <- vec3 1 1 1, 203 | intensity <- 0.5} 204 | 205 | -} 206 | type alias Light = Light.Light 207 | 208 | 209 | {-| Default light on a scene. Defines a white light at full intensity 210 | at position x = 1, y = 1 , z = 3. 211 | 212 | This is ideal for constructing custom lights just by updating the properties 213 | you wish to update. 214 | 215 | Example : Constructing a green light 216 | 217 | greenLight = { light | color <- vec3 0 1 0 } 218 | -} 219 | light : Light 220 | light = Light.light 221 | 222 | ---------------------------------------------------------------------------- 223 | 224 | ----------------- Re-export Engine.Material.Material ----------------------- 225 | 226 | {-| Represent a property of a material. Contains a color and a strength. 227 | By convention, full strength is set at 1 an no strength is 0, 228 | color values are between 0 and 1 (not 0 - 255). 229 | 230 | Example (creating a white specular property at full strength): 231 | 232 | specularProperty = MaterialProperty (vec3 1 1 1) 1 233 | 234 | From the above, the specularProperty variable is given a white color and 235 | full strength. If this property is used to represent specular highlights, 236 | then this means that these highlights will appear white and very visible. 237 | 238 | -} 239 | --type alias MaterialProperty = Material.MaterialProperty 240 | 241 | 242 | {-| Represent a material. A Material has properties to help it define 243 | and adapt how it reacts to light. 244 | 245 | Emissive usually models light that seems to emanate from the object itself. 246 | 247 | Ambient usually represents some ambient term so that objects may be somewhat 248 | visible in the dark. (to compensate for not using a ray tracer) 249 | 250 | Diffuse usually models the scatter of light on a surface. Rough objects 251 | tend to have a high diffuse strength as the light's reflection does not 252 | seem to focus on a small area but rather "diffuses" or spreads on the entire 253 | surface. 254 | 255 | Specular usually models specular highlights, or shinyness. Metallic objects 256 | tend to have a high specular strength as they seem to almost act as a 257 | mirror and a light's reflection seems to focus on a small area. 258 | 259 | Note: Diffuse and Specular are completely independent, they seem 260 | to be opposites but you can perfectly have a material with both high diffuse 261 | and specular strengths and you can also perfectly have a material with both 262 | low diffuse and specular strengths. 263 | 264 | A Material also has a vertex shader and a fragment shader. A Shader is a 265 | program that is sent to the GPU (Graphics Processing Unit). 266 | 267 | The vertex shader controls where a point is displayed on the screen. Usually, 268 | it suffices to just have a vertex shader that converts a position from world 269 | coordinates to screen coodinates. 270 | 271 | The fragment shader controls what color a given fragment (just think pixel) has. 272 | Fragment shaders can often get very involved as they often calculate 273 | the contributions due to all the light sources in a scene and somehow mix 274 | this with the position, normal, and material properties of an object to 275 | finally get a pixel color. 276 | 277 | Note: Both the vertex and fragment shaders are written in the GLSL 278 | programming language. To use your own shaders simply make sure to pass them 279 | to a material as a String. 280 | -} 281 | type alias Material = Material.Material 282 | 283 | 284 | {-| Default material. Defines a material with a weak white ambient and no 285 | emissive, diffuse, or specular terms. (i.e. a simple flat material) 286 | 287 | The current default shaders are a standard vertex shader that converts from 288 | world to screen coordinates and a fragment shader that just returns a red pixel. 289 | 290 | This is ideal for creating your own materials and to just use a simple 291 | default material. 292 | -} 293 | material : Material 294 | material = Material.material 295 | 296 | ---------------------------------------------------------------------------- 297 | 298 | ----------------- Re-export Engine.Mesh.Mesh ------------------------------- 299 | 300 | {-| Mesh type. A mesh is simply a list of triangles where each vertex 301 | has some property (in this case, just the position property). This type is 302 | used to construct arbitrary geometry that can be sent to the GPU to be 303 | rendered to the screen. 304 | 305 | For reference, Triangle just a 3-tuple: 306 | 307 | type alias Triangle a = (a, a, a) 308 | 309 | and Attribute is just a record type with a position field: 310 | 311 | type alias Attribute = { 312 | position : Vec3 313 | } 314 | 315 | -} 316 | type alias Mesh = Mesh.Mesh 317 | 318 | ---------------------------------------------------------------------------- 319 | 320 | ----------------- Re-export Engine.Mesh.Triangle --------------------------- 321 | 322 | {-| Default triangle renderable object 323 | -} 324 | triangle : Renderable 325 | triangle = Triangle.triangle 326 | 327 | {-| Function to construct a triangle mesh from three points. 328 | -} 329 | triangleMesh : Vec3 -> Vec3 -> Vec3 -> Mesh 330 | triangleMesh = Triangle.triangleMesh 331 | 332 | ---------------------------------------------------------------------------- 333 | 334 | ----------------- Re-export Engine.Mesh.Rectangle -------------------------- 335 | 336 | {-| Default rectangle renderable object 337 | -} 338 | rectangle : Renderable 339 | rectangle = Rectangle.rectangle 340 | 341 | 342 | {-| Function that takes 4 points and construct a rectangle mesh. 343 | 344 | Note: Actually, this function constructs two triangles. This means that 345 | it does not explicitly check if all 4 points are co-planar. 346 | 347 | Furthermore, the function does not enforce a rectangle's property that 348 | opposite sides be of equal length and that adjacent sides be perpendicular. 349 | -} 350 | rectangleMesh : Vec3 -> Vec3 -> Vec3 -> Vec3 -> Mesh 351 | rectangleMesh = Rectangle.rectangleMesh 352 | 353 | ---------------------------------------------------------------------------- 354 | 355 | ----------------- Re-export Engine.Mesh.Cube ------------------------------- 356 | 357 | {-| Default cube renderable object 358 | -} 359 | cube : Renderable 360 | cube = Cube.cube 361 | 362 | 363 | {-| Function that takes a center point/vector and a size and returns a 364 | cube mesh. 365 | 366 | cube center size 367 | -} 368 | cubeMesh : Vec3 -> Float -> Mesh 369 | cubeMesh = Cube.cubeMesh 370 | 371 | ---------------------------------------------------------------------------- 372 | 373 | ----------------- Re-export Engine.Mesh.Pyramid ---------------------------- 374 | 375 | {-| Default pyramid renderable object 376 | -} 377 | pyramid : Renderable 378 | pyramid = Pyramid.pyramid 379 | 380 | {-| Function that takes a center point/vector, a height and a width and 381 | returns a pyramid mesh. 382 | 383 | pyramidMesh center height width 384 | -} 385 | pyramidMesh : Vec3 -> Float -> Float -> Mesh 386 | pyramidMesh = Pyramid.pyramidMesh 387 | 388 | ---------------------------------------------------------------------------- 389 | 390 | ----------------- Re-export Engine.Mesh.Sphere ----------------------------- 391 | 392 | {-| Default sphere renderable object. Located at the origin with radius of 0.5. 393 | -} 394 | 395 | sphere : Renderable 396 | sphere = Sphere.sphere 397 | 398 | {-| Function that takes a center point/vector, the radius, the number of 399 | segments around the sphere radially (like longitude), the number of segments up 400 | and down (like latitude), and returns a mesh that approximates a sphere. 401 | 402 | sphereMesh center radius segmentsR segmentsY 403 | -} 404 | sphereMesh : Vec3 -> Float -> Float -> Float -> Mesh 405 | sphereMesh = Sphere.sphereMesh 406 | 407 | ---------------------------------------------------------------------------- 408 | 409 | ----------------- Re-export Engine.Shader.Attribute ------------------------ 410 | 411 | {-| Attribute type. This type, from its fields, captures all of the 412 | attributes that are passed to the vertex shader. 413 | 414 | Currenly only contains a position field. 415 | -} 416 | type alias Attribute = Attribute.Attribute 417 | 418 | ---------------------------------------------------------------------------- 419 | 420 | ----------------- Re-export Engine.Shader.Uniform -------------------------- 421 | 422 | {-| Container type to hold all of the properties that are passed to GLSL 423 | as uniforms. 424 | -} 425 | type alias Uniform = Uniform.Uniform 426 | 427 | ---------------------------------------------------------------------------- 428 | 429 | ----------------- Re-export Engine.Render.Render --------------------------- 430 | 431 | {-| Function to render a scene to a WebGL canvas context. This function takes 432 | in a Scene and returns the WebGL canvas context. 433 | 434 | Note: The function renders only the objects in the objects list of the scene. 435 | -} 436 | render : Scene -> Element 437 | render = Render.render 438 | 439 | ---------------------------------------------------------------------------- 440 | 441 | ----------------- Re-export Engine.Render.Renderable ----------------------- 442 | 443 | {-| Represent a renderable object. A renderable object is an Object with a 444 | Transform properties (position, rotation, and scale) in order to locate it 445 | physically in world space. A renderable object also comes with a Material 446 | and a Mesh. 447 | 448 | A renderable uses its position, rotation, and scale to set its position, 449 | rotation and scale in world space respectively. The material describes how 450 | the renderable object responds to light and draws itself. The mesh describes 451 | the geometry of the renderable object (if it is a cube, a pyramid, etc...). 452 | 453 | -} 454 | type alias Renderable = Renderable.Renderable 455 | 456 | 457 | {-| Default renderable object. Alias for the default cube object. 458 | -} 459 | renderable : Renderable 460 | renderable = DefaultRenderable.renderable 461 | 462 | ---------------------------------------------------------------------------- 463 | 464 | ----------------- Re-export Engine.Transform.Transform --------------------- 465 | 466 | {-| A transform is an object with a position, a rotation, and a scale. 467 | This is mean to be able to represent linear transformations in space. As 468 | such, one can model where an object is (position), what is its orientation 469 | (rotation), and how large is it (scale). 470 | -} 471 | type alias Transform a = Transform.Transform a 472 | 473 | 474 | {-| Default transform. 475 | 476 | Defined as follows: 477 | 478 | transform = { 479 | position = vec3 0 0 0, 480 | rotation = vec3 0 0 0, 481 | scale = vec3 1 1 1 } 482 | 483 | -} 484 | transform : Transform {} 485 | transform = Transform.transform 486 | 487 | ---------------------------------------------------------------------------- 488 | 489 | ----------------- Re-export Engine.Viewport.Viewport ----------------------- 490 | 491 | {-| A Viewport is an object that is supposed to describe the target "screen" 492 | or "context" on to which a scene is rendered. 493 | 494 | A Viewport has dimensions consisting of a width and a height. 495 | -} 496 | type alias Viewport = Viewport.Viewport 497 | 498 | 499 | {-| Default Viewport. Sets dimensions.width = 400 and dimensions.height = 400. 500 | (In essence, with the default viewport object your scene will appear in 501 | a 400 by 400 canvas or webgl context). 502 | -} 503 | viewport : Viewport 504 | viewport = Viewport.viewport 505 | -------------------------------------------------------------------------------- /Engine/Camera/Camera.elm: -------------------------------------------------------------------------------- 1 | module Engine.Camera.Camera where 2 | 3 | {-| This module defines the Camera type and the default camera object. 4 | A Camera is a record type made to be the viewing point of a scene and thus 5 | enable the drawing of a scene to the screen. A Camera represents 6 | a perspective camera. 7 | 8 | # Definition 9 | @docs Camera 10 | 11 | # Default Camera 12 | @docs camera 13 | 14 | -} 15 | 16 | import Math.Vector3 (vec3) 17 | import Engine.Transform.Transform (Transform, transform) 18 | 19 | {-| Represent a perspective camera. As a transform, a camera has a 20 | position, a rotation, and a scale. A camera also has an aspect ratio, 21 | a field of view, a near clipping plane, and a far clipping plane. In essence, 22 | a camera describes a viewing frustrum. 23 | 24 | Note : By convention, aspectRatio is a ratio (so a 4 : 3 aspect ratio 25 | can be achieved by setting aspectRatio to 4 / 3 or 1.3), fieldOfView is 26 | set in degrees (45 being usually a good safe bet), nearClipping and 27 | farClipping are set in world coordinates. 28 | 29 | Example 1 : (A typical 4 : 3 camera) 30 | 31 | fourThirdsCamera = { 32 | position = vec3 0 0 0, 33 | rotation = vec3 0 0 0, 34 | scale = vec3 1 1 1, 35 | aspectRatio = 4 / 3, 36 | fieldOfView = 45, 37 | nearClipping = 1, 38 | farClipping = 80000 } 39 | 40 | Example 2 : (A 16 : 9 camera that cannot see far and warped field of view) 41 | 42 | weirdSixteenNinthsCamera = { 43 | fourThirdsCamera | aspectRatio <- 16 / 9, 44 | fieldOfView <- 125, 45 | farClipping <- 10 } 46 | 47 | -} 48 | type alias Camera = Transform { 49 | aspectRatio : Float, 50 | fieldOfView : Float, 51 | nearClipping : Float, 52 | farClipping : Float 53 | } 54 | 55 | 56 | {-| Default camera on a scene. Defines a camera at position x = 0, y = 0, 57 | z = -10, with a square aspect ratio (aspectRatio == 1), a field of view of 58 | 45 degrees (fieldOfView == 45), a near clipping plane at 1 and a far 59 | clipping plane at 80000. 60 | 61 | Example : Constructing a standard widescreen cinema camera 62 | 63 | widescreenCamera = { camera | aspectRatio <- 2.39 } 64 | 65 | -} 66 | camera : Camera 67 | camera = 68 | Transform (vec3 0 0 -10) 69 | transform.rotation 70 | transform.scale 71 | { aspectRatio = 1, 72 | fieldOfView = 45, 73 | nearClipping = 1, 74 | farClipping = 80000 } 75 | -------------------------------------------------------------------------------- /Engine/Light/Light.elm: -------------------------------------------------------------------------------- 1 | module Engine.Light.Light where 2 | 3 | {-| This module defines the Light type and the default light object. 4 | A Light is a record type made to illuminate scenes and objects in these 5 | scenes. 6 | 7 | # Definition 8 | @docs Light 9 | 10 | # Default Light 11 | @docs light 12 | 13 | -} 14 | 15 | import Math.Vector3 (Vec3, vec3) 16 | import Engine.Transform.Transform (Transform, transform) 17 | 18 | -- TODO: FIX VISIBILITY TOGGLE 19 | {-| Represent a light in a scene. As a transform, a light has a position, 20 | a rotation, and a scale. A light also has an intensity property 21 | to control the strength of the influence of the light source in the scene, 22 | a color property, and a visibility property to act as a flag to toggle 23 | a light on and off. 24 | 25 | Note: By convention, full intensity is set a 1 and no intensity is 0, 26 | color values are values between 0 and 1 (not 0 - 255). 27 | 28 | Example 1 : (A red light at full intensity) 29 | 30 | redLight = { 31 | position = vec3 0 0 0, 32 | rotation = vec3 0 0 0, 33 | scale = vec3 1 1 1, 34 | intensity = 1, 35 | color = vec3 1 0 0, 36 | visibility = True } 37 | 38 | Example 2 : (A white light at half intensity) 39 | 40 | whiteLight = 41 | { redLight | color <- vec3 1 1 1, 42 | intensity <- 0.5} 43 | 44 | -} 45 | type alias Light = Transform { 46 | intensity : Float, 47 | color : Vec3, 48 | visibility : Bool 49 | } 50 | 51 | 52 | 53 | {-| Default light on a scene. Defines a white light at full intensity 54 | at position x = 1, y = 1 , z = 3. 55 | 56 | This is ideal for constructing custom lights just by updating the properties 57 | you wish to update. 58 | 59 | Example : Constructing a green light 60 | 61 | greenLight = { light | color <- vec3 0 1 0 } 62 | -} 63 | light : Light 64 | light = 65 | Transform (vec3 1 1 3) 66 | (transform.rotation) 67 | (transform.scale) 68 | { intensity = 1, 69 | color = vec3 1 1 1, 70 | visibility = True } 71 | -------------------------------------------------------------------------------- /Engine/Material/Material.elm: -------------------------------------------------------------------------------- 1 | module Engine.Material.Material where 2 | 3 | {-| This module defined the Material type and the default material object. 4 | A Material is a record type made to define how an object reacts to light 5 | in a scene and draws itself. 6 | 7 | # Definition 8 | @docs Material 9 | 10 | # Default Material 11 | @docs material 12 | 13 | # Convenient Helper Type 14 | @docs MaterialProperty 15 | 16 | -} 17 | 18 | import Math.Vector3 (Vec3, vec3) 19 | import Engine.Shader.VertexShader (vertexShader) 20 | import Engine.Shader.FragmentShader (fragmentShader) 21 | 22 | {-| Represent a property of a material. Contains a color and a strength. 23 | By convention, full strength is set at 1 an no strength is 0, 24 | color values are between 0 and 1 (not 0 - 255). 25 | 26 | Example (creating a white specular property at full strength): 27 | 28 | specularProperty = MaterialProperty (vec3 1 1 1) 1 29 | 30 | From the above, the specularProperty variable is given a white color and 31 | full strength. If this property is used to represent specular highlights, 32 | then this means that these highlights will appear white and very visible. 33 | 34 | -} 35 | type alias MaterialProperty = { 36 | color : Vec3, 37 | strength : Float 38 | } 39 | 40 | {-| Represent a material. A Material has properties to help it define 41 | and adapt how it reacts to light. 42 | 43 | Emissive usually models light that seems to emanate from the object itself. 44 | 45 | Ambient usually represents some ambient term so that objects may be somewhat 46 | visible in the dark. (to compensate for not using a ray tracer) 47 | 48 | Diffuse usually models the scatter of light on a surface. Rough objects 49 | tend to have a high diffuse strength as the light's reflection does not 50 | seem to focus on a small area but rather "diffuses" or spreads on the entire 51 | surface. 52 | 53 | Specular usually models specular highlights, or shinyness. Metallic objects 54 | tend to have a high specular strength as they seem to almost act as a 55 | mirror and a light's reflection seems to focus on a small area. 56 | 57 | Note: Diffuse and Specular are completely independent, they seem 58 | to be opposites but you can perfectly have a material with both high diffuse 59 | and specular strengths and you can also perfectly have a material with both 60 | low diffuse and specular strengths. 61 | 62 | A Material also has a vertex shader and a fragment shader. A Shader is a 63 | program that is sent to the GPU (Graphics Processing Unit). 64 | 65 | The vertex shader controls where a point is displayed on the screen. Usually, 66 | it suffices to just have a vertex shader that converts a position from world 67 | coordinates to screen coodinates. 68 | 69 | The fragment shader controls what color a given fragment (just think pixel) has. 70 | Fragment shaders can often get very involved as they often calculate 71 | the contributions due to all the light sources in a scene and somehow mix 72 | this with the position, normal, and material properties of an object to 73 | finally get a pixel color. 74 | 75 | Note: Both the vertex and fragment shaders are written in the GLSL 76 | programming language. To use your own shaders simply make sure to pass them 77 | to a material as a String. 78 | -} 79 | type alias Material = { 80 | emissive : MaterialProperty, 81 | ambient : MaterialProperty, 82 | diffuse : MaterialProperty, 83 | specular : MaterialProperty, 84 | vertexShader : String, 85 | fragmentShader : String 86 | } 87 | 88 | {-| Default material. Defines a material with a weak white ambient and no 89 | emissive, diffuse, or specular terms. (i.e. a simple flat material) 90 | 91 | The current default shaders are a standard vertex shader that converts from 92 | world to screen coordinates and a fragment shader that just returns a red pixel. 93 | 94 | This is ideal for creating your own materials and to just use a simple 95 | default material. 96 | -} 97 | material : Material 98 | material = { 99 | emissive = MaterialProperty (vec3 0 0 0) 0, 100 | ambient = MaterialProperty (vec3 1 1 1) 0.2, 101 | diffuse = MaterialProperty (vec3 0 0 0) 0, 102 | specular = MaterialProperty (vec3 0 0 0) 0, 103 | vertexShader = vertexShader, 104 | fragmentShader = fragmentShader } 105 | -------------------------------------------------------------------------------- /Engine/Math/Utils.elm: -------------------------------------------------------------------------------- 1 | module Engine.Math.Utils where 2 | 3 | {-| This module is just a simple collection of mathematical operations 4 | used repeatedly in several areas in the Graphics Engine codebase. 5 | 6 | # "Safe" versions of Linear Algbera operations 7 | @docs safeNormalize, safeMakeRotate 8 | 9 | # Common Camera operations 10 | @docs getSideVector, getUpVector, getForwardVector, getTargetPosition 11 | 12 | # Model View Projection 13 | @docs modelMatrix, viewMatrix, projectionMatrix 14 | 15 | # Renaming Functions to avoid Namespace clashes 16 | @docs matrixIdentity 17 | 18 | -} 19 | 20 | import Math.Vector3 as Vector3 21 | import Math.Matrix4 as Matrix4 22 | import Engine.Transform.Transform (Transform) 23 | import Engine.Render.Renderable (Renderable) 24 | import Engine.Camera.Camera (Camera) 25 | 26 | -- Generic Math functions 27 | 28 | {-| Version of Vector3.normalize which simply returns the vector to be 29 | normalized untouched in the case its length is equal to zero 30 | 31 | In essence, 32 | 33 | safeNormalize (vec3 0 0 0) == vec3 0 0 0 34 | 35 | -} 36 | safeNormalize : Vector3.Vec3 -> Vector3.Vec3 37 | safeNormalize vector = 38 | if (Vector3.lengthSquared vector == 0) 39 | then vector 40 | else Vector3.normalize vector 41 | 42 | {-| Version of Matrix4.makeRotate which simply returns the identity 43 | matrix if the input rotation vector is the zero vector 44 | 45 | In essence, 46 | 47 | safeMakeRotate (vec3 0 0 0) == Matrix4.identity 48 | 49 | -} 50 | safeMakeRotate : Vector3.Vec3 -> Matrix4.Mat4 51 | safeMakeRotate vector = 52 | if (Vector3.lengthSquared vector == 0) 53 | then Matrix4.identity 54 | else Matrix4.makeRotate (sqrt <| Vector3.lengthSquared vector) (Vector3.normalize vector) 55 | 56 | {-| Renaming of the identity matrix because it clashes with the identity 57 | function in Basics which defines the function that just returns its input 58 | 59 | -} 60 | matrixIdentity : Matrix4.Mat4 61 | matrixIdentity = Matrix4.identity 62 | 63 | -- Camera Helpers 64 | 65 | {-| Calculate the right-handed side vector of a transform. This is mainly 66 | used by cameras to help orient themselves. 67 | 68 | -} 69 | getSideVector : Transform a -> Vector3.Vec3 70 | getSideVector transform = 71 | let yaw = Vector3.getX transform.rotation 72 | pitch = Vector3.getY transform.rotation 73 | roll = Vector3.getZ transform.rotation 74 | sx = sin yaw 75 | cx = cos yaw 76 | sy = sin pitch 77 | cy = cos pitch 78 | sz = sin roll 79 | cz = cos roll 80 | vx = cy * cz - sy * sx * sz 81 | vy = cy * sz + sy * sx * cz 82 | vz = -sy * cx 83 | in Vector3.vec3 vx vy vz 84 | 85 | 86 | {-| Calculate the right-handed up vector of a transform. This is mainly 87 | used by cameras to help orient themselves and create the view matrix. 88 | 89 | -} 90 | getUpVector : Transform a -> Vector3.Vec3 91 | getUpVector transform = 92 | let yaw = Vector3.getX transform.rotation 93 | pitch = Vector3.getY transform.rotation 94 | roll = Vector3.getZ transform.rotation 95 | sx = sin yaw 96 | cx = cos yaw 97 | sy = sin pitch 98 | cy = cos pitch 99 | sz = sin roll 100 | cz = cos roll 101 | vx = -cx * sz 102 | vy = cx * cz 103 | vz = sx 104 | in Vector3.vec3 vx vy vz 105 | 106 | {-| Calculate the vector pointing outward of a transform given a position 107 | and rotation. This is mainly used by cameras to help orient themselves 108 | and create the view matrix. 109 | 110 | -} 111 | getForwardVector : Transform a -> Vector3.Vec3 112 | getForwardVector transform = 113 | let yaw = Vector3.getX transform.rotation 114 | pitch = Vector3.getY transform.rotation 115 | roll = Vector3.getZ transform.rotation 116 | sx = sin yaw 117 | cx = cos yaw 118 | sy = sin pitch 119 | cy = cos pitch 120 | sz = sin roll 121 | cz = cos roll 122 | vx = sy * cz + cy * sx * sz 123 | vy = sy * sz - cy * sx * cz 124 | vz = cy * cx 125 | in Vector3.vec3 vx vy vz 126 | 127 | {-| Calculate the target position of a transform (i.e. where the transform 128 | points at). This is mainly used to figure out what a camera points at. 129 | 130 | -} 131 | getTargetPosition : Transform a -> Vector3.Vec3 132 | getTargetPosition transform = 133 | transform.position `Vector3.add` (getForwardVector transform) 134 | 135 | 136 | 137 | -- Model View Projection Matrices 138 | 139 | {-| The model matrix. Encodes the transformation of a transform as a matrix. 140 | This allows to efficiently apply such a transformation to a point to move it 141 | in world space with a given position, rotation, and scale. 142 | -} 143 | modelMatrix : Transform a -> Matrix4.Mat4 144 | modelMatrix transform = 145 | let translationMatrix = Matrix4.makeTranslate transform.position 146 | rotationMatrix = safeMakeRotate transform.rotation 147 | scaleMatrix = Matrix4.makeScale transform.scale 148 | in translationMatrix `Matrix4.mul` (rotationMatrix `Matrix4.mul` scaleMatrix) 149 | 150 | {-| The view matrix. Encodes the Look At matrix of a transform. 151 | This allows to calculate the Look At matrix of a camera to then multiply 152 | a position by the view matrix in order to convert it from world space to 153 | camera space. 154 | -} 155 | viewMatrix : Transform a -> Matrix4.Mat4 156 | viewMatrix transform = 157 | Matrix4.makeLookAt transform.position 158 | (getTargetPosition transform) 159 | (getUpVector transform) 160 | 161 | {-| The projection matrix. Encodes the perspective matrix of a camera. 162 | This allows to map a position from camera space to screen space. 163 | -} 164 | projectionMatrix : Camera -> Matrix4.Mat4 165 | projectionMatrix camera = 166 | Matrix4.makePerspective camera.fieldOfView 167 | camera.aspectRatio 168 | camera.nearClipping 169 | camera.farClipping 170 | 171 | {-| Shorthand for modelViewMatrix. Faster to calculate once in CPU. 172 | -} 173 | modelViewMatrix : Transform a -> Transform b -> Matrix4.Mat4 174 | modelViewMatrix object camera = 175 | (viewMatrix camera) `Matrix4.mul` (modelMatrix object) 176 | 177 | 178 | modelViewProjectionMatrix : Transform a -> Camera -> Matrix4.Mat4 179 | modelViewProjectionMatrix object camera = 180 | (projectionMatrix camera) `Matrix4.mul` (modelViewMatrix object camera) 181 | 182 | normalMatrix : Transform a -> Transform b -> Matrix4.Mat4 183 | normalMatrix object camera = 184 | Matrix4.inverseOrthonormal (Matrix4.transpose (modelViewMatrix object camera)) 185 | -------------------------------------------------------------------------------- /Engine/Mesh/Cube.elm: -------------------------------------------------------------------------------- 1 | module Engine.Mesh.Cube where 2 | 3 | {-| This module contains the definition of a cube mesh and of a cube 4 | renderable object. 5 | 6 | # Cube Mesh 7 | @docs cubeMesh 8 | 9 | # Cube (Renderable) 10 | @docs cube 11 | 12 | -} 13 | 14 | import Math.Vector3 (Vec3, add, vec3) 15 | import Engine.Mesh.Mesh (Mesh) 16 | import Engine.Mesh.Rectangle (rectangle, rectangleMesh) 17 | import Engine.Render.Renderable (Renderable) 18 | 19 | 20 | {-| Function that takes a center point/vector and a size and returns a 21 | cube mesh. 22 | 23 | cube center size 24 | -} 25 | cubeMesh : Vec3 -> Float -> Mesh 26 | cubeMesh center size = 27 | let hs = size / 2 28 | ftl = center `add` vec3 -hs hs -hs 29 | ftr = center `add` vec3 hs hs -hs 30 | fbr = center `add` vec3 hs -hs -hs 31 | fbl = center `add` vec3 -hs -hs -hs 32 | btl = center `add` vec3 -hs hs hs 33 | btr = center `add` vec3 hs hs hs 34 | bbr = center `add` vec3 hs -hs hs 35 | bbl = center `add` vec3 -hs -hs hs 36 | in (rectangleMesh ftl ftr btr btl) ++ 37 | (rectangleMesh ftl fbl fbr ftr) ++ 38 | (rectangleMesh fbl fbr bbr bbl) ++ 39 | (rectangleMesh btr bbr bbl btl) ++ 40 | (rectangleMesh ftl fbl bbl btl) ++ 41 | (rectangleMesh ftr fbr bbr btr) 42 | 43 | {-| Default cube renderable object 44 | -} 45 | cube : Renderable 46 | cube = { 47 | rectangle | mesh <- cubeMesh (vec3 0 0 0) 1 } 48 | -------------------------------------------------------------------------------- /Engine/Mesh/Mesh.elm: -------------------------------------------------------------------------------- 1 | module Engine.Mesh.Mesh where 2 | 3 | {-| This module contains the definition of the mesh type 4 | 5 | # Mesh Type 6 | @docs Mesh 7 | 8 | -} 9 | 10 | import WebGL (Triangle) 11 | import Engine.Shader.Attribute (Attribute) 12 | 13 | {-| Mesh type. A mesh is simply a list of triangles where each vertex 14 | has some property (in this case, just the position property). This type is 15 | used to construct arbitrary geometry that can be sent to the GPU to be 16 | rendered to the screen. 17 | 18 | For reference, Triangle just a 3-tuple: 19 | 20 | type alias Triangle a = (a, a, a) 21 | 22 | and Attribute is just a record type with a position field: 23 | 24 | type alias Attribute = { 25 | position : Vec3 26 | } 27 | 28 | -} 29 | type alias Mesh = List (Triangle Attribute) 30 | -------------------------------------------------------------------------------- /Engine/Mesh/Pyramid.elm: -------------------------------------------------------------------------------- 1 | module Engine.Mesh.Pyramid where 2 | 3 | {-| This module contains the definition of a pyramid mesh and of a pyramid 4 | renderable object. 5 | 6 | # Pyramid Mesh 7 | @docs pyramidMesh 8 | 9 | # Pyramid (Renderable) 10 | @docs pyramid 11 | 12 | -} 13 | 14 | import Math.Vector3 (Vec3, add, vec3) 15 | import Engine.Mesh.Mesh (Mesh) 16 | import Engine.Mesh.Rectangle (rectangleMesh) 17 | import Engine.Mesh.Triangle (triangle, triangleMesh) 18 | import Engine.Render.Renderable (Renderable) 19 | 20 | 21 | {-| Function that takes a center point/vector, a height and a width and 22 | returns a pyramid mesh. 23 | 24 | pyramidMesh center height width 25 | -} 26 | pyramidMesh : Vec3 -> Float -> Float -> Mesh 27 | pyramidMesh center height width = 28 | let halfHeight = height / 2 29 | halfWidth = width / 2 30 | top = center `add` (vec3 0 halfHeight 0) 31 | bfl = center `add` (vec3 -halfWidth -halfHeight -halfWidth) 32 | bfr = center `add` (vec3 halfWidth -halfHeight -halfWidth) 33 | bbl = center `add` (vec3 -halfWidth -halfHeight halfWidth) 34 | bbr = center `add` (vec3 halfWidth -halfHeight halfWidth) 35 | in (rectangleMesh bbr bbl bfl bfr) ++ 36 | (triangleMesh bfl bfr top) ++ 37 | (triangleMesh bfr bbr top) ++ 38 | (triangleMesh bbr bbl top) ++ 39 | (triangleMesh bbl bfl top) 40 | 41 | 42 | {-| Default pyramid renderable object 43 | -} 44 | pyramid : Renderable 45 | pyramid = { 46 | triangle | mesh <- pyramidMesh (vec3 0 0 0) 1 1 } 47 | -------------------------------------------------------------------------------- /Engine/Mesh/Rectangle.elm: -------------------------------------------------------------------------------- 1 | module Engine.Mesh.Rectangle where 2 | 3 | {-| This module contains the definition of a rectangle mesh and of a rectangle 4 | renderable object. 5 | 6 | # Rectangle Mesh 7 | @docs rectangleMesh 8 | 9 | # Rectangle (Renderable) 10 | @docs rectangle 11 | -} 12 | 13 | import Math.Vector3 (Vec3, vec3) 14 | import Engine.Mesh.Mesh (Mesh) 15 | import Engine.Mesh.Triangle (triangle, triangleMesh) 16 | import Engine.Render.Renderable (Renderable) 17 | 18 | {-| Function that takes 4 points and construct a rectangle mesh. 19 | 20 | Note: Actually, this function constructs two triangles. This means that 21 | it does not explicitly check if all 4 points are co-planar. 22 | 23 | Furthermore, the function does not enforce a rectangle's property that 24 | opposite sides be of equal length and that adjacent sides be perpendicular. 25 | -} 26 | rectangleMesh : Vec3 -> Vec3 -> Vec3 -> Vec3 -> Mesh 27 | rectangleMesh p q r s = 28 | (triangleMesh p q r) ++ (triangleMesh r s p) 29 | 30 | 31 | {-| Default rectangle renderable object 32 | -} 33 | rectangle : Renderable 34 | rectangle = { 35 | triangle | mesh <- rectangleMesh (vec3 -0.5 -0.5 0) (vec3 0.5 -0.5 0) (vec3 0.5 0.5 0) (vec3 -0.5 0.5 0) } 36 | -------------------------------------------------------------------------------- /Engine/Mesh/Sphere.elm: -------------------------------------------------------------------------------- 1 | module Engine.Mesh.Sphere where 2 | 3 | {-| This module contains the definition of a sphere mesh and of a sphere 4 | renderable object. 5 | 6 | # Sphere Mesh 7 | @docs sphereMesh 8 | 9 | # Sphere (Renderable) 10 | @docs sphere 11 | 12 | -} 13 | 14 | import List 15 | 16 | import Math.Vector3 (Vec3, add, vec3) 17 | import Engine.Mesh.Mesh (Mesh) 18 | import Engine.Mesh.Rectangle (rectangleMesh) 19 | import Engine.Mesh.Triangle (triangleMesh, triangle) 20 | import Engine.Render.Renderable (Renderable) 21 | 22 | 23 | {-| Function that takes a center point/vector, the radius, the number of 24 | segments around the sphere radially (like longitude), the number of segments up 25 | and down (like latitude), and returns a mesh that approximates a sphere. 26 | 27 | sphere center segmentsR segmentsY 28 | -} 29 | sphereMesh : Vec3 -> Float -> Float -> Float -> Mesh 30 | sphereMesh center radius segmentsR segmentsY = 31 | let dt = 2 * pi / segmentsR 32 | dy = 1 / segmentsY 33 | halfRadius = radius / 2 34 | getRadius y = sqrt (max 0 (halfRadius - y*y)) 35 | in [0..segmentsR-1] |> List.concatMap (\i -> 36 | let theta = i * dt 37 | x0 = cos theta 38 | x1 = cos (theta + dt) 39 | z0 = sin theta 40 | z1 = sin (theta + dt) 41 | in [0..segmentsY-1] |> List.concatMap (\j -> 42 | let y0 = j*dy - radius 43 | y1 = y0+dy 44 | r0 = getRadius y0 45 | r1 = getRadius y1 46 | bl = center `add` (vec3 (x0*r0) y0 (z0*r0)) 47 | br = center `add` (vec3 (x1*r0) y0 (z1*r0)) 48 | tl = center `add` (vec3 (x0*r1) y1 (z0*r1)) 49 | tr = center `add` (vec3 (x1*r1) y1 (z1*r1)) 50 | in triangleMesh bl br tr ++ triangleMesh bl tr tl)) 51 | 52 | 53 | {-| Default sphere renderable object. Located at the origin with radius of 0.5. 54 | -} 55 | sphere : Renderable 56 | sphere = { 57 | triangle | mesh <- sphereMesh (vec3 0 0 0) 0.5 20 20 } 58 | -------------------------------------------------------------------------------- /Engine/Mesh/Triangle.elm: -------------------------------------------------------------------------------- 1 | module Engine.Mesh.Triangle where 2 | 3 | {-| This module contains the definition of a triangle mesh and of a triangle 4 | renderable object. The triangle is the main building block of all geometry in 5 | modern computer graphics. Use these functions if you want to create your own 6 | custom geometry. 7 | 8 | # Triangle Mesh 9 | @docs triangleMesh 10 | 11 | # Triangle (Renderable) 12 | @docs triangle 13 | 14 | -} 15 | 16 | import WebGL (Triangle, map) 17 | import Math.Vector3 (Vec3, vec3, add, normalize, cross, sub) 18 | import Engine.Mesh.Mesh (Mesh) 19 | import Engine.Shader.Attribute (Attribute) 20 | import Engine.Material.Material (material) 21 | import Engine.Render.Renderable (Renderable) 22 | 23 | 24 | {-| Function to construct a triangle mesh from three points. 25 | -} 26 | triangleMesh : Vec3 -> Vec3 -> Vec3 -> Mesh 27 | triangleMesh p q r = [map Attribute (p,q,r)] 28 | 29 | 30 | {-| Default triangle renderable object 31 | -} 32 | triangle : Renderable 33 | triangle = { 34 | material = material, 35 | mesh = triangleMesh (vec3 -0.5 -0.5 0) (vec3 0.5 -0.5 0) (vec3 0 0.5 0), 36 | position = vec3 0 0 0, 37 | rotation = vec3 0 0 0, 38 | scale = vec3 1 1 1} 39 | -------------------------------------------------------------------------------- /Engine/Render/DefaultRenderable.elm: -------------------------------------------------------------------------------- 1 | module Engine.Render.DefaultRenderable where 2 | 3 | {-| This module contains the definition for the default Renderable renderable. 4 | This definition is separated from the Renderable module in order to avoid 5 | circular dependencies. 6 | 7 | 8 | # Default Renderable 9 | @docs renderable 10 | -} 11 | 12 | import Engine.Render.Renderable (Renderable) 13 | import Engine.Mesh.Cube (cube) 14 | 15 | {-| Default renderable object. Alias for the default cube object. 16 | -} 17 | renderable : Renderable 18 | renderable = cube 19 | -------------------------------------------------------------------------------- /Engine/Render/Render.elm: -------------------------------------------------------------------------------- 1 | module Engine.Render.Render where 2 | 3 | {-| This module contains functions to render objects and scenes onto 4 | a WebGL canvas context. 5 | 6 | # Render Functions 7 | @docs renderObject, render 8 | 9 | -} 10 | 11 | import WebGL (Entity, entity, webgl) 12 | import Graphics.Element (Element) 13 | 14 | import Engine.Render.Renderable (Renderable) 15 | import Engine.Scene.Scene (Scene) 16 | import Engine.Shader.Uniform (constructUniform) 17 | import Engine.Shader.Shader (constructVertexShader, constructFragmentShader) 18 | 19 | import Array (map, toList) 20 | 21 | {-| Function to render an object onto a scene. This function returns an 22 | Entity object which is what the webgl function from the WebGL library requires 23 | to draw anything onto a WebGL canvas context. 24 | 25 | Note: This function is mainly used as a helper function to render. 26 | -} 27 | renderObject : Scene -> Renderable -> Entity 28 | renderObject scene object = 29 | entity (constructVertexShader object.material.vertexShader) 30 | (constructFragmentShader object.material.fragmentShader) 31 | object.mesh 32 | (constructUniform scene object) 33 | 34 | 35 | {-| Function to render a scene to a WebGL canvas context. This function takes 36 | in a Scene and returns the WebGL canvas context. 37 | 38 | Note: The function renders only the objects in the objects list of the scene. 39 | -} 40 | render : Scene -> Element 41 | render scene = 42 | webgl (floor scene.viewport.dimensions.width, floor scene.viewport.dimensions.height) <| 43 | toList <| map (renderObject scene) scene.objects 44 | -------------------------------------------------------------------------------- /Engine/Render/Renderable.elm: -------------------------------------------------------------------------------- 1 | module Engine.Render.Renderable where 2 | 3 | {-| This module contains the definition of the Renderable type. This is 4 | separated from the definition of the default renderable object in order to 5 | avoid circular dependencies. 6 | 7 | # Definition 8 | @docs Renderable 9 | 10 | -} 11 | 12 | import Engine.Transform.Transform (Transform) 13 | import Engine.Material.Material (Material) 14 | import Engine.Mesh.Mesh (Mesh) 15 | 16 | {-| Represent a renderable object. A renderable object is an Object with a 17 | Transform properties (position, rotation, and scale) in order to locate it 18 | physically in world space. A renderable object also comes with a Material 19 | and a Mesh. 20 | 21 | A renderable uses its position, rotation, and scale to set its position, 22 | rotation and scale in world space respectively. The material describes how 23 | the renderable object responds to light and draws itself. The mesh describes 24 | the geometry of the renderable object (if it is a cube, a pyramid, etc...). 25 | 26 | -} 27 | type alias Renderable = Transform { 28 | material : Material, 29 | mesh : Mesh 30 | } 31 | -------------------------------------------------------------------------------- /Engine/Scene/Scene.elm: -------------------------------------------------------------------------------- 1 | module Engine.Scene.Scene where 2 | 3 | {-| This module defines the Scene type and the default Scene object. 4 | A Scene contains a camera (the viewer's window to the scene), a list of 5 | Renderable objects (the stuff that gets drawn to the scene), a light 6 | (something to illuminate the scene), and a Viewport (a description of 7 | the context the scene gets drawn to). 8 | 9 | Note: Currently, Graphics Engine only supports having one light. In future 10 | releases, this will most certainly change in order to support having multiple 11 | lights in a scene. 12 | 13 | Note: Currently, Graphics Engine onlu supports having one camera. While this 14 | may be all you need in most cases, it may be valuable to allow for multiple 15 | cameras. Currently, the only way to have multiple cameras is to have 16 | multiple scenes. It is still an open question on which approach is better. 17 | 18 | 19 | # Definition 20 | @docs Scene 21 | 22 | # Default Scene 23 | @docs scene 24 | -} 25 | 26 | import Engine.Camera.Camera (Camera, camera) 27 | import Engine.Render.Renderable (Renderable) 28 | import Engine.Light.Light (Light, light) 29 | import Engine.Render.DefaultRenderable (renderable) 30 | import Engine.Viewport.Viewport (Viewport, viewport) 31 | 32 | import Array (Array, fromList) 33 | 34 | -- TODO: Find a strategy to deal with multiple lights 35 | 36 | -- TODO: Consider a strategy for dealing with multiple cameras 37 | -- -- Perhaps by using multiple webgl contexts?? 38 | 39 | -- TODO: Find a strategy to deal with multiple materials 40 | 41 | {-| Represents a scene. A scene contains an array of objects such that 42 | calling `render` on a scene will render all the objects in a webgl context. 43 | 44 | A scene contains a camera to define the viewer's viewing point, a light to 45 | illuminate the scene, and a viewport to describe the context on which the 46 | scene will be drawn. 47 | 48 | -} 49 | type alias Scene = { 50 | camera : Camera, 51 | objects : Array Renderable, 52 | light : Light, 53 | viewport : Viewport 54 | } 55 | 56 | {-| Default scene object. Draws a red cube in the middle of the default context. 57 | -} 58 | scene : Scene 59 | scene = { 60 | camera = camera, 61 | objects = fromList [renderable], 62 | light = light, 63 | viewport = viewport } 64 | -------------------------------------------------------------------------------- /Engine/Shader/Attribute.elm: -------------------------------------------------------------------------------- 1 | module Engine.Shader.Attribute where 2 | 3 | {-| This module contains the definition of the Attribute type. 4 | A vertex shader takes a set of attributes as input for each vertex. 5 | The Attibute type is made to capture all of these attributes. 6 | 7 | Currently, we only pass the position to the vertex shader as an attibute. 8 | 9 | Typically, a color and a normal is also passed as an attribute but for 10 | simplicity it is highly encouraged to set the color at the material level 11 | and to retrieve the normal from the normal matrix (which is automatically 12 | passed as a uniform). 13 | 14 | # Definition 15 | @docs Attribute 16 | 17 | -} 18 | 19 | import Math.Vector3 (Vec3) 20 | 21 | -- TODO: Reconsider the properties of the Attribute type 22 | -- -- The only mandatory property here is position 23 | -- -- One could pass a color : Vec3 in order not to require Materials 24 | -- -- Normals can be calculated by the vertex shader 25 | -- -- from the model and view matrices 26 | 27 | {-| Attribute type. This type, from its fields, captures all of the 28 | attributes that are passed to the vertex shader. 29 | 30 | Currenly only contains a position field. 31 | -} 32 | type alias Attribute = { 33 | position : Vec3 34 | } 35 | -------------------------------------------------------------------------------- /Engine/Shader/Boilerplate.elm: -------------------------------------------------------------------------------- 1 | module Engine.Shader.Boilerplate where 2 | 3 | {-| This module exists to automatically handle some of the boilerplate 4 | that comes with writing shaders. 5 | 6 | # Struct Definitions 7 | @docs materialPropertyStructTypeDefinition, 8 | materialStructTypeDefinition, 9 | lightStructTypeDefinition 10 | 11 | # Automatic Setup of Variables 12 | @docs setupMaterial, setupLight 13 | 14 | # Uniform Declarations 15 | @docs uniformDeclarations 16 | 17 | # Attribute Declarations 18 | @docs attributeDeclarations 19 | 20 | 21 | # Boilerplate 22 | @docs vertexShaderBoilerplate, fragmentShaderBoilerplate 23 | 24 | -} 25 | 26 | import Engine.Shader.Utils ( 27 | declareUniform, 28 | declareAttribute, 29 | groupStatements, 30 | setFloatPrecision, 31 | newLine, 32 | declareStructType, 33 | declareInitializedVariable, 34 | callFunctionExpression) 35 | import Engine.Shader.Library (libraryVariables, libraryFunctions) 36 | import List (intersperse, map) 37 | 38 | 39 | 40 | {-| Definition of the struct MaterialProperty 41 | 42 | //GLSL CODE: 43 | 44 | struct MaterialProperty { 45 | vec3 color; 46 | float strength; 47 | }; 48 | 49 | -} 50 | materialPropertyStructTypeDefinition : String 51 | materialPropertyStructTypeDefinition = declareStructType "MaterialProperty" 52 | [ ("vec3" , "color") , 53 | ("float", "strength") ] 54 | 55 | 56 | 57 | 58 | 59 | 60 | {-| Definition of the struct Material 61 | 62 | //GLSL CODE: 63 | 64 | struct Material { 65 | MaterialProperty emissive; 66 | MaterialProperty ambient; 67 | MaterialProperty diffuse; 68 | MaterialProperty specular; 69 | }; 70 | 71 | -} 72 | materialStructTypeDefinition : String 73 | materialStructTypeDefinition = declareStructType "Material" 74 | [ ("MaterialProperty", "emissive") , 75 | ("MaterialProperty", "ambient") , 76 | ("MaterialProperty", "diffuse") , 77 | ("MaterialProperty", "specular") ] 78 | 79 | 80 | 81 | 82 | 83 | 84 | {-| The setup for material. This is due to the fact that currently you 85 | cannot yet pass structs as uniforms with elm-webgl. As such, each underlying 86 | property is passed as a primitive type and then the struct is constructed 87 | from each of the properties. 88 | 89 | Thanks to this process, you now have access to the variable : 90 | 91 | Material material; 92 | 93 | And it contains all of the data you have defined in the Elm code usable in 94 | the same way as in Elm. 95 | 96 | -} 97 | setupMaterial : String 98 | setupMaterial = 99 | let makeMaterialColor = callFunctionExpression "MaterialProperty" 100 | materialValue = callFunctionExpression "Material" <| map makeMaterialColor 101 | [ ["materialEmissiveColor", "materialEmissiveStrength"], 102 | ["materialAmbientColor" , "materialAmbientStrength" ], 103 | ["materialDiffuseColor" , "materialDiffuseStrength" ], 104 | ["materialSpecularColor", "materialSpecularStrength"]] 105 | in declareInitializedVariable "Material" "material" materialValue 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | -- TODO: Add property visibility : Bool 114 | -- TODO: Add property scale : Vec3 115 | {-| Definition of the struct Light. 116 | 117 | Note: Currently, this struct does not have the visibility property. This is 118 | due to the fact that you cannot yet pass bools as uniforms with elm-webgl. As 119 | such it is ignored. Please take notice! 120 | 121 | Note: Currently, this struct does not contain the scale property. Given the 122 | last refractoring, Lights are now defined as Transforms and as such contain 123 | a scale property. This will be amended in a future release. Please take notice! 124 | 125 | //GLSL CODE: 126 | 127 | struct Light { 128 | vec3 position; 129 | vec3 rotation; 130 | vec3 color; 131 | float intensity; 132 | }; 133 | 134 | -} 135 | lightStructTypeDefinition : String 136 | lightStructTypeDefinition = declareStructType "Light" 137 | [ ("vec3" , "position") , 138 | ("vec3" , "rotation") , 139 | ("vec3" , "color") , 140 | ("float", "intensity") ] 141 | 142 | 143 | 144 | 145 | 146 | 147 | -- TODO: Add support for visibility 148 | -- TODO: Add support for scale 149 | {-| The setup for light. This is due to the fact that currently you 150 | cannot yet pass structs as uniforms with elm-webgl. As such, each underlying 151 | property is passed as a primitive type and then the struct is constructed 152 | from each of the properties. 153 | 154 | Thanks to this process, you now have access to the variable : 155 | 156 | Light light; 157 | 158 | And it contains all of the data you have defined in the Elm code usable in 159 | the same way as in Elm. 160 | 161 | Note: Currently visibility and scale are unsupported! 162 | 163 | Note: Currently only one light is supported. This is due to the fact that 164 | currently you cannot yet pass arrays as uniforms with elm-wegl. I haven't 165 | thought of a good hack around this, sorry. 166 | 167 | -} 168 | setupLight : String 169 | setupLight = 170 | let lightValue = callFunctionExpression "Light" 171 | [ "lightPosition" , 172 | "lightRotation" , 173 | "lightColor" , 174 | "lightIntensity" ] 175 | 176 | in declareInitializedVariable "Light" "light" lightValue 177 | 178 | 179 | 180 | 181 | 182 | 183 | -- TODO : Add property lightVisibility : Bool 184 | -- TODO : Add property lightScale : Vec3 185 | {-| Set of uniform declarations. (Available in both vertex and fragment shaders) 186 | 187 | Currently, the available uniforms are: 188 | 189 | uniform mat4 modelMatrix; 190 | uniform mat4 viewMatrix; 191 | uniform mat4 projectionMatrix; 192 | 193 | uniform vec3 lightPosition; 194 | uniform vec3 lightRotation; 195 | uniform vec3 lightColor; 196 | uniform float lightIntensity; 197 | 198 | uniform vec3 materialEmissiveColor; 199 | uniform float materialEmissiveStrength; 200 | 201 | uniform vec3 materialAmbientColor; 202 | uniform float materialAmbientStrength; 203 | 204 | uniform vec3 materialDiffuseColor; 205 | uniform float materialDiffuseStrength; 206 | 207 | uniform vec3 materialSpecularColor; 208 | uniform float materialSpecularStrength; 209 | 210 | -} 211 | uniformDeclarations : String 212 | uniformDeclarations = 213 | groupStatements <| map (uncurry declareUniform) 214 | [ ("mat4" , "modelMatrix") , 215 | ("mat4" , "viewMatrix") , 216 | ("mat4" , "projectionMatrix") , 217 | ("mat4" , "modelViewMatrix") , 218 | ("mat4" , "modelViewProjectionMatrix"), 219 | ("mat4" , "normalMatrix") , 220 | ("vec3" , "lightPosition") , 221 | ("vec3" , "lightRotation") , 222 | ("vec3" , "lightColor") , 223 | ("float", "lightIntensity") , 224 | ("vec3" , "materialEmissiveColor") , 225 | ("float", "materialEmissiveStrength") , 226 | ("vec3" , "materialAmbientColor") , 227 | ("float", "materialAmbientStrength") , 228 | ("vec3" , "materialDiffuseColor") , 229 | ("float", "materialDiffuseStrength") , 230 | ("vec3" , "materialSpecularColor") , 231 | ("float", "materialSpecularStrength") ] 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | {-| Set of attribute declarations. (available in vertex shader only) 240 | 241 | Currently, the available attributes are: 242 | 243 | attribute vec3 position; 244 | 245 | -} 246 | attributeDeclarations : String 247 | attributeDeclarations = groupStatements <| map (uncurry declareAttribute) 248 | [ ("vec3", "position")] 249 | 250 | 251 | 252 | {-| Shorthand for the model view matrix. Available in both shaders. 253 | 254 | mat4 modelViewMatrix = viewMatrix * modelMatrix; 255 | 256 | -} 257 | {-} 258 | modelViewMatrix : String 259 | modelViewMatrix = 260 | declareInitializedVariable "mat4" "modelViewMatrix" 261 | "viewMatrix * modelMatrix" 262 | -} 263 | 264 | {-| Shorthand for the model view projection matrix. Available in both shaders. 265 | 266 | mat4 modelViewProjectionMatrix = projectionMatrix * viewMatrix * modelMatrix; 267 | 268 | -}{-} 269 | modelViewProjectionMatrix : String 270 | modelViewProjectionMatrix = 271 | declareInitializedVariable "mat4" "modelViewProjectionMatrix" 272 | "projectionMatrix * modelViewMatrix" 273 | -} 274 | 275 | {-| The normal matrix. Convenient to calculate surface normals. 276 | 277 | mat4 normalMatrix = transpose(inverse(modelViewMatrix)); 278 | 279 | -} 280 | --normalMatrix : String 281 | --normalMatrix = 282 | -- declareInitializedVariable "mat4" "normalMatrix" 283 | -- "transpose(inverse(modelViewMatrix))" 284 | 285 | 286 | 287 | {-| Set of useful variables for programming convenience. 288 | 289 | Currently, the available useful variables are : 290 | 291 | mat4 modelViewMatrix; 292 | mat4 modelViewProjectionMatrix; 293 | mat4 normalMatrix; 294 | 295 | -} 296 | {-usefulVariables : String 297 | usefulVariables = 298 | modelViewMatrix ++ newLine ++ 299 | modelViewProjectionMatrix 300 | -} 301 | 302 | {-| Details the common boilerplate shared in both vertex and fragment shaders. 303 | 304 | -} 305 | commonShaderBoilerplate : String 306 | commonShaderBoilerplate = 307 | materialPropertyStructTypeDefinition ++ newLine ++ newLine ++ 308 | materialStructTypeDefinition ++ newLine ++ newLine ++ 309 | lightStructTypeDefinition ++ newLine ++ newLine ++ 310 | uniformDeclarations ++ newLine ++ newLine ++ 311 | setupLight ++ newLine ++ newLine ++ 312 | setupMaterial ++ newLine ++ newLine ++ 313 | libraryVariables ++ newLine ++ newLine ++ 314 | libraryFunctions 315 | 316 | 317 | 318 | {-| The boilerplate code found in the vertex shader. 319 | 320 | -} 321 | vertexShaderBoilerplate : String 322 | vertexShaderBoilerplate = 323 | setFloatPrecision ++ newLine ++ newLine ++ 324 | attributeDeclarations ++ newLine ++ newLine ++ 325 | commonShaderBoilerplate 326 | 327 | 328 | {-| The boilerplate code found in the fragment shader. 329 | 330 | -} 331 | fragmentShaderBoilerplate : String 332 | fragmentShaderBoilerplate = 333 | setFloatPrecision ++ newLine ++ newLine ++ commonShaderBoilerplate 334 | -------------------------------------------------------------------------------- /Engine/Shader/FragmentShader.elm: -------------------------------------------------------------------------------- 1 | module Engine.Shader.FragmentShader where 2 | 3 | {-| This module contains the definition of the default fragment shader. 4 | 5 | # Default Fragment Shader 6 | @docs fragmentShader 7 | 8 | -} 9 | 10 | 11 | {-| Default fragment shader 12 | 13 | Currently, the fragment shader just sets the fragment color to red. 14 | 15 | -} 16 | fragmentShader : String 17 | fragmentShader = """ 18 | varying vec3 vPosition; 19 | 20 | void main(){ 21 | vec3 outputColor = normalize(vPosition) * sqrt(3.0); 22 | gl_FragColor = vec4(outputColor,1.0); 23 | } 24 | 25 | """ 26 | -------------------------------------------------------------------------------- /Engine/Shader/GouraudShader.elm: -------------------------------------------------------------------------------- 1 | module Engine.Shader.GouraudShader where 2 | 3 | gouraudShader : String 4 | gouraudShader = """ 5 | varying vec3 vPosition; 6 | varying vec3 vNormal; 7 | varying vec3 vViewPosition; 8 | 9 | void main(){ 10 | vec3 normal = normalize(vNormal); 11 | vec3 viewVector = normalize(vViewPosition); 12 | vec4 lightDirection = viewMatrix * vec4(light.position, 1.0); 13 | vec3 lightVector = normalize(lightDirection.xyz); 14 | vec3 pointHalfVector = normalize(lightVector + viewVector); 15 | float pointDotHalfNormal = max(dot(normal, pointHalfVector), 0.0); 16 | 17 | 18 | vec3 lightContribution = light.color * max(min(light.intensity, 1.0), 0.5); 19 | 20 | vec3 emissiveContribution = material.emissive.color * material.emissive.strength; 21 | vec3 ambientContribution = material.ambient.color * material.ambient.strength; 22 | 23 | float diffuseFactor = max( dot(normal, lightVector), 0.0) / 2.0; 24 | vec3 diffuseContribution = material.diffuse.color * material.diffuse.strength * diffuseFactor; 25 | vec3 outputColor = vec3(0.0,0.0,0.0); 26 | 27 | outputColor += 0.25 * ambientContribution; 28 | 29 | outputColor += 0.25 * emissiveContribution; 30 | 31 | outputColor += 0.25 * diffuseContribution; 32 | 33 | float shininess = material.specular.strength * 100.0; 34 | float specularFactor = material.specular.strength * pow( pointDotHalfNormal, shininess); 35 | specularFactor *= diffuseFactor * (2.0 + shininess) / 8.0; 36 | 37 | if (diffuseFactor <= 0.0){ 38 | specularFactor = 0.0; 39 | } 40 | 41 | vec3 specularContribution = material.specular.color * specularFactor; 42 | 43 | outputColor += 0.25 * specularContribution; 44 | 45 | //outputColor *= lightContribution; 46 | 47 | gl_FragColor = vec4(outputColor, 1.0); 48 | 49 | /*vec3 materialColor = normalize( 50 | emissiveContribution + ambientContribution + 51 | diffuseContribution + specularContribution 52 | ) * sqrt(3.0); 53 | 54 | vec3 outputColor = normalize( 55 | lightContribution * materialColor 56 | ) * sqrt(3.0); 57 | 58 | gl_FragColor = vec4(outputColor, 1.0);*/ 59 | 60 | } 61 | 62 | """ 63 | -------------------------------------------------------------------------------- /Engine/Shader/Library.elm: -------------------------------------------------------------------------------- 1 | module Engine.Shader.Library where 2 | 3 | {-| This module contains a set of strings that represent GLSL functions 4 | that constitute a GLSL library to add common operations missing from 5 | WebGL's version of GLSL. 6 | 7 | # Matrix Functions 8 | @docs transposeFunction, determinantFunction, traceFunction, inverseFunction 9 | 10 | -} 11 | 12 | import Engine.Shader.Utils (newLine) 13 | 14 | {-| GLSL Function that computes the transpose of a 2-dimensional matrix. 15 | 16 | // GLSL CODE: 17 | mat2 matrix = mat2( 18 | 1.0, 2.0, 19 | 3.0, 4.0 20 | ); 21 | mat2 transposedMatrix = transpose(matrix); 22 | // transposedMatrix == mat2( 23 | // 1.0, 3.0, 24 | // 2.0, 4.0) 25 | 26 | -} 27 | mat2Transpose : String 28 | mat2Transpose = """ 29 | mat2 transpose(mat2 matrix){ 30 | return mat2( 31 | matrix[0][0], matrix[1][0], 32 | matrix[0][1], matrix[1][1] 33 | ); 34 | } 35 | """ 36 | 37 | 38 | {-| GLSL Function that computes the transpose of a 3-dimensional matrix. 39 | 40 | // GLSL CODE: 41 | mat3 matrix = mat3( 42 | 1.0, 2.0, 3.0, 43 | 4.0, 5.0, 6.0, 44 | 7.0, 8.0, 9.0 45 | ); 46 | mat3 transposedMatrix = transpose(matrix); 47 | // transposedMatrix == mat3( 48 | // 1.0, 4.0, 7.0, 49 | // 2.0, 5.0, 8.0, 50 | // 3.0, 6.0, 9.0) 51 | 52 | -} 53 | mat3Transpose : String 54 | mat3Transpose = """ 55 | mat3 transpose(mat3 matrix){ 56 | return mat3( 57 | matrix[0][0], matrix[1][0], matrix[2][0], 58 | matrix[0][1], matrix[1][1], matrix[2][1], 59 | matrix[0][2], matrix[1][2], matrix[2][2] 60 | ); 61 | } 62 | """ 63 | 64 | 65 | {-| GLSL Function that computes the transpose of a 4-dimensional matrix. 66 | 67 | // GLSL CODE: 68 | mat4 matrix = mat4( 69 | 1.0, 2.0, 3.0, 4.0, 70 | 5.0, 6.0, 7.0, 8.0, 71 | 9.0, 10.0, 11.0, 12.0, 72 | 13.0, 14.0, 15.0, 16.0 73 | ); 74 | mat4 transposedMatrix = transpose(matrix); 75 | // transposedMatrix == mat4( 76 | // 1.0, 5.0, 9.0, 13.0, 77 | // 2.0, 6.0, 10.0, 14.0, 78 | // 3.0, 7.0, 11.0, 15.0, 79 | // 4.0, 8.0, 12.0, 16.0); 80 | 81 | -} 82 | mat4Transpose : String 83 | mat4Transpose = """ 84 | mat4 transpose(mat4 matrix){ 85 | return mat4( 86 | matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0], 87 | matrix[0][1], matrix[1][1], matrix[2][1], matrix[3][1], 88 | matrix[0][2], matrix[1][2], matrix[2][2], matrix[3][2], 89 | matrix[0][3], matrix[1][3], matrix[2][3], matrix[3][3] 90 | ); 91 | } 92 | """ 93 | 94 | {-| GLSL Function that computes the determinant of a 2-dimensional matrix 95 | 96 | // GLSL CODE: 97 | mat2 matrix = mat2( 98 | 1.0, 3.0, 99 | 2.0, 12.0 100 | ); 101 | float matrixDeterminant = determinant(matrix); 102 | // matrixDeterminant == 6.0 103 | 104 | -} 105 | mat2Determinant : String 106 | mat2Determinant = """ 107 | float determinant(mat2 matrix){ 108 | return matrix[0][0] * matrix[1][1] - matrix[1][0] * matrix[0][1]; 109 | } 110 | """ 111 | 112 | 113 | {-| GLSL Function that computes the determinant of a 3-dimensional matrix 114 | 115 | // GLSL CODE: 116 | mat3 matrix = mat3( 117 | 1.0, 0.0, 0.0, 118 | 0.0, 4.0, 0.0, 119 | 0.0, 0.0, 20.0 120 | ); 121 | float matrixDeterminant = determinant(matrix); 122 | // matrixDeterminant == 80.0 123 | 124 | -} 125 | mat3Determinant : String 126 | mat3Determinant = """ 127 | float determinant(mat3 matrix){ 128 | return ( 129 | matrix[0][0] * determinant(mat2( 130 | matrix[1][1], matrix[1][2], 131 | matrix[2][1], matrix[2][2] 132 | )) - 133 | matrix[0][1] * determinant(mat2( 134 | matrix[1][0], matrix[1][2], 135 | matrix[2][0], matrix[2][2] 136 | )) + 137 | matrix[0][2] * determinant(mat2( 138 | matrix[1][0], matrix[1][1], 139 | matrix[2][0], matrix[2][1])) 140 | ); 141 | } 142 | """ 143 | 144 | 145 | {-| GLSL Function that computes the determinant of a 4-dimensional matrix 146 | 147 | //GLSL CODE: 148 | mat4 matrix = matrix4( 149 | 2.0, 0.0, 0.0, 0.0, 150 | 3.0, 4.0, 0.0, 0.0, 151 | 1.0, 5.0, 9.0, 0.0, 152 | 0.0, 0.0, 4.0, 2.0 153 | ); 154 | float matrixDeterminant = determinant(matrix); 155 | // matrixDeterminant == 72.0 156 | 157 | -} 158 | mat4Determinant : String 159 | mat4Determinant = """ 160 | float determinant(mat4 matrix){ 161 | return ( 162 | matrix[0][0] * determinant(mat3( 163 | matrix[1][1], matrix[1][2], matrix[1][3], 164 | matrix[2][1], matrix[2][2], matrix[2][3], 165 | matrix[3][1], matrix[3][2], matrix[3][3] 166 | )) - 167 | matrix[0][1] * determinant(mat3( 168 | matrix[1][0], matrix[1][2], matrix[1][3], 169 | matrix[2][0], matrix[2][2], matrix[2][3], 170 | matrix[3][0], matrix[3][2], matrix[3][3] 171 | )) + 172 | matrix[0][2] * determinant(mat3( 173 | matrix[1][0], matrix[1][1], matrix[1][3], 174 | matrix[2][0], matrix[2][1], matrix[2][3], 175 | matrix[3][0], matrix[3][1], matrix[3][3] 176 | )) - 177 | matrix[0][3] * determinant(mat3( 178 | matrix[1][0], matrix[1][1], matrix[1][2], 179 | matrix[2][0], matrix[2][1], matrix[2][2], 180 | matrix[3][0], matrix[3][1], matrix[3][2] 181 | )) 182 | ); 183 | } 184 | """ 185 | 186 | {-| GLSL Function that computes the trace of a 2-dimensional matrix 187 | 188 | // GLSL CODE: 189 | mat2 matrix = mat2( 190 | 2.0, 3.0, 191 | 5.0, 1.0 192 | ); 193 | float matrixTrace = trace(matrix); 194 | // matrixTrace == 3.0 195 | 196 | -} 197 | mat2Trace : String 198 | mat2Trace = """ 199 | float trace(mat2 matrix){ 200 | return matrix[0][0] + matrix[1][1]; 201 | } 202 | """ 203 | 204 | {-| GLSL Function that computes the trace of a 3-dimensional matrix 205 | 206 | // GLSL CODE: 207 | mat3 matrix = mat3( 208 | 3.0, 9.0, 1.0, 209 | 6.0, 8.0, 0.0, 210 | 1.0, 2.0, 1.0 211 | ); 212 | float matrixTrace = trace(matrix); 213 | // matrixTrace == 12.0 214 | 215 | -} 216 | mat3Trace : String 217 | mat3Trace = """ 218 | float trace(mat3 matrix){ 219 | return matrix[0][0] + matrix[1][1] + matrix[2][2]; 220 | } 221 | """ 222 | 223 | 224 | {-| GLSL Function that computes the trace of a 4-dimensional matrix 225 | 226 | // GLSL CODE: 227 | mat4 matrix = mat4( 228 | 2.0, 9.0, 9.0, 1.0, 229 | 8.0, 7.0, 5.0, 1.0, 230 | 1.0, 2.0, 3.0, 1.0, 231 | 1.0, 2.0, 1.0, -12.0 232 | ); 233 | float matrixTrace = trace(matrix); 234 | // matrixTrace == 0.0 235 | 236 | -} 237 | mat4Trace : String 238 | mat4Trace = """ 239 | float trace(mat4 matrix){ 240 | return matrix[0][0] + matrix[1][1] + matrix[2][2] + matrix[3][3]; 241 | } 242 | """ 243 | 244 | 245 | {-| GLSL 2-Dimensional Identity Matrix 246 | 247 | //GLSL CODE: 248 | 249 | //identity2 == mat2( 250 | // 1.0, 0.0, 251 | // 0.0, 1.0 252 | //); 253 | 254 | -} 255 | mat2Identity : String 256 | mat2Identity = """ 257 | const mat2 identity2 = mat2( 258 | 1.0, 0.0, 259 | 0.0, 1.0 260 | ); 261 | """ 262 | 263 | {-| GLSL 3-Dimensional Identity Matrix 264 | 265 | //GLSL CODE: 266 | 267 | //identity3 == mat3( 268 | // 1.0, 0.0, 0.0, 269 | // 0.0, 1.0, 0.0, 270 | // 0.0, 0.0, 1.0 271 | //); 272 | 273 | -} 274 | mat3Identity : String 275 | mat3Identity = """ 276 | const mat3 identity3 = mat3( 277 | 1.0, 0.0, 0.0, 278 | 0.0, 1.0, 0.0, 279 | 0.0, 0.0, 1.0 280 | ); 281 | """ 282 | 283 | {-| GLSL 4-Dimensional Identity Matrix 284 | 285 | // GLSL CODE: 286 | 287 | //identity4 == mat4( 288 | // 1.0, 0.0, 0.0, 0.0, 289 | // 0.0, 1.0, 0.0, 0.0, 290 | // 0.0, 0.0, 1.0, 0.0, 291 | // 0.0, 0.0, 0.0, 1.0 292 | //); 293 | 294 | -} 295 | mat4Identity : String 296 | mat4Identity = """ 297 | const mat4 identity4 = mat4( 298 | 1.0, 0.0, 0.0, 0.0, 299 | 0.0, 1.0, 0.0, 0.0, 300 | 0.0, 0.0, 1.0, 0.0, 301 | 0.0, 0.0, 0.0, 1.0 302 | ); 303 | """ 304 | 305 | 306 | {-| GLSL Function that computes the inverse of a 2-Dimensional matrix. 307 | 308 | The 2-Dimensional Matrix inverse function is implemented directly 309 | by computing the determinant and hardcoding the matrix scaled 310 | by the inverse of the determinant. 311 | 312 | // GLSL CODE: 313 | mat2 matrix = mat2( 314 | 1.0, 2.0, 315 | 3.0, 4.0 316 | ); 317 | mat2 matrixInverse = inverse(matrix); 318 | //matrixInverse == mat2( 319 | // -2.0, 1.0, 320 | // 1.5,-0.5 321 | //); 322 | 323 | -} 324 | mat2Inverse : String 325 | mat2Inverse = """ 326 | mat2 inverse(mat2 matrix){ 327 | return (1.0 / determinant(matrix)) * mat2( 328 | matrix[1][1], -matrix[0][1], 329 | -matrix[1][0], matrix[0][0] 330 | ); 331 | } 332 | """ 333 | 334 | {-| GLSL Function that computes the inverse of a 3-Dimensional matrix. 335 | 336 | The 3-Dimensional Matrix inverse function is implemented using 337 | Cayley-Hamilton decomposition (mostly just for convenience). 338 | 339 | // GLSL CODE: 340 | mat3 matrix = mat3( 341 | 1.0, 2.0, 3.0, 342 | 0.0, 1.0, 4.0, 343 | 5.0, 6.0, 0.0 344 | ); 345 | mat3 matrixInverse = inverse(matrix); 346 | //matrixInverse == mat3( 347 | // -24.0, 18.0, 5.0, 348 | // 20.0,-15.0,-4.0, 349 | // -5.0, 4.0, 1.0 350 | //); 351 | 352 | -} 353 | mat3Inverse : String 354 | mat3Inverse = """ 355 | mat3 inverse(mat3 matrix){ 356 | return (1.0 / determinant(matrix)) * ( 357 | 0.5 * (pow(trace(matrix), 2.0) - trace(matrix * matrix)) * identity3 - 358 | matrix * trace(matrix) + 359 | matrix * matrix 360 | ); 361 | } 362 | """ 363 | 364 | {-| GLSL Function that computes the inverse of a 4-Dimensional matrix. 365 | 366 | The 4-Dimensional Matrix inverse function is implemented using 367 | Cayley-Hamilton decomposition (mostly just for convenience). 368 | 369 | //GLSL CODE: 370 | mat4 matrix = mat4( 371 | 4.0, 0.0, 0.0, 0.0, 372 | 0.0, 0.0, 2.0, 0.0, 373 | 0.0, 1.0, 2.0, 0.0, 374 | 1.0, 0.0, 0.0, 1.0 375 | ); 376 | mat4 matrixInverse = inverse(matrix); 377 | //matrixInverse == mat4( 378 | // 0.25 , 0.0, 0.0, 0.0, 379 | // 0.0 ,-1.0, 1.0, 0.0, 380 | // 0.0 , 0.5, 0.0, 0.0, 381 | // -0.25, 0.0, 0.0, 1.0 382 | //); 383 | 384 | -} 385 | mat4Inverse : String 386 | mat4Inverse = """ 387 | mat4 inverse(mat4 matrix){ 388 | return (1.0 / determinant(matrix)) * ( 389 | (1.0 / 6.0) * (pow(trace(matrix), 3.0) - 3.0 * trace(matrix) * trace(matrix * matrix) * identity4) - 390 | 0.5 * (pow(trace(matrix), 2.0) - trace(matrix * matrix)) + 391 | matrix * matrix * trace(matrix) - matrix * matrix * matrix 392 | ); 393 | } 394 | """ 395 | 396 | {-| GLSL Function to calculate the transpose of a 2,3 or 4-Dimensional matrix. 397 | 398 | The transpose of a matrix is simply the result of switching the rows 399 | and columns of the input matrix. 400 | 401 | transpose(mat2(1.0, 2.0, 3.0, 4.0)) == mat2(1.0, 3.0, 2.0, 4.0); 402 | 403 | transpose(transpose(matrix)) == matrix; 404 | -} 405 | transposeFunction : String 406 | transposeFunction = 407 | mat2Transpose ++ newLine ++ 408 | mat3Transpose ++ newLine ++ 409 | mat4Transpose 410 | 411 | {-| GLSL Function to calculate the transpose of a 2, 3, or 4-Dimensional matrix 412 | 413 | The determinant of a matrix represents the area of a parallelogram 414 | (in 2 dimensions), the volume of a parallelepiped (in 3 dimensions), 415 | or the hypervolume of a hyperrectangle. 416 | 417 | One of the most common uses of a determinant is as a crucial step in calculating 418 | the inverse of a matrix. The determiant can also be used as an efficient 419 | test for the invertability of a matrix. A matrix is invertible if and 420 | only if the determinant of the matrix is nonzero. 421 | 422 | determinant(mat2(1.0, 3.0, 2.0, 12.0)) == 6.0; 423 | 424 | determinant(identity4) == 1.0; 425 | -} 426 | determinantFunction : String 427 | determinantFunction = 428 | mat2Determinant ++ newLine ++ 429 | mat3Determinant ++ newLine ++ 430 | mat4Determinant 431 | 432 | {-| GLSL Function to calculate the trace of a 2, 3, or 4-Dimensional matrix. 433 | 434 | The trace of a matrix represents the derivative of the determinant 435 | (i.e. the infinitesimal change in area, volume, or hypervolume). 436 | 437 | One of the most common uses of the trace of a matrix is to compute an axis 438 | and angle from a rotation matrix. 439 | 440 | trace(mat2(1.0, 3.0, 2.0, 12.0)) == 12.0; 441 | 442 | trace(identity4) == 2.0 443 | 444 | -} 445 | traceFunction : String 446 | traceFunction = 447 | mat2Trace ++ newLine ++ 448 | mat3Trace ++ newLine ++ 449 | mat4Trace 450 | 451 | {-| GLSL Function to calculate the inverse of a 2, 3, or 4-Dimensional matrix. 452 | Note: This function does not test for invertability. So, buyer beware. 453 | 454 | The inverse of a matrix is basically like an undo matrix. A matrix 455 | multiplied by its inverse yields the identity matrix and thus undoing 456 | a matrix transformation on a vector is as simple as multiplying the 457 | transformed vector by the inverse of that matrix transformation. 458 | 459 | A common use case for the inverse of a matrix is to convert screen 460 | coordinates to world coordinates (by inverting modelViewProjectionMatrix). 461 | This is useful screen-to-world raycasting (i.e. clicking on a 3-d object). 462 | 463 | inverse(inverse(invertibleMatrix)) == invertibleMatrix; 464 | 465 | inverse(identity3) == identity3; 466 | 467 | inverse(projectionMatrix) * modelViewProjectionMatrix == modelViewMatrix; 468 | -} 469 | inverseFunction : String 470 | inverseFunction = 471 | mat2Inverse ++ newLine ++ 472 | mat3Inverse ++ newLine ++ 473 | mat4Inverse 474 | 475 | {-| GLSL 2, 3, and 4 Identity Matrices 476 | 477 | The Identity Matrix is the matrix that maps a vector to itself. 478 | 479 | identity2 * vector2 == vector2; 480 | 481 | identity3 * vector3 == vector3; 482 | 483 | identity4 * modelMatrix == modelMatrix; 484 | 485 | -} 486 | identityMatrix : String 487 | identityMatrix = 488 | mat2Identity ++ newLine ++ 489 | mat3Identity ++ newLine ++ 490 | mat4Identity 491 | 492 | 493 | {-| GLSL Set of variables included in the library 494 | 495 | mat2 identity2; 496 | mat3 identity3; 497 | mat4 identity4; 498 | 499 | -} 500 | libraryVariables : String 501 | libraryVariables = 502 | identityMatrix 503 | 504 | {-| Set of functions included in the library 505 | 506 | mat2 transpose(mat2); 507 | mat3 transpose(mat3); 508 | mat4 transpose(mat4); 509 | 510 | float determinant(mat2); 511 | float determinant(mat3); 512 | float determinant(mat4); 513 | 514 | mat2 trace(mat2); 515 | mat3 trace(mat3); 516 | mat4 trace(mat4); 517 | 518 | mat2 inverse(mat2); 519 | mat3 inverse(mat3); 520 | mat4 inverse(mat4); 521 | -} 522 | libraryFunctions : String 523 | libraryFunctions = 524 | transposeFunction ++ newLine ++ 525 | determinantFunction ++ newLine ++ 526 | traceFunction ++ newLine ++ 527 | inverseFunction 528 | -------------------------------------------------------------------------------- /Engine/Shader/Shader.elm: -------------------------------------------------------------------------------- 1 | module Engine.Shader.Shader where 2 | 3 | {-| This module constains functions to construct shaders as well as 4 | functions to return shaders as strings (mostly for debugging purposes) 5 | 6 | # Construct Shaders 7 | @docs constructVertexShader, constructFragmentShader 8 | 9 | # Construct Shader Strings 10 | @docs showVertexShader, showFragmentShader 11 | -} 12 | 13 | import WebGL (Shader, unsafeShader) 14 | import Engine.Shader.Boilerplate (vertexShaderBoilerplate, fragmentShaderBoilerplate) 15 | import Engine.Shader.Utils (newLine) 16 | import Engine.Shader.Attribute (Attribute) 17 | import Engine.Shader.Uniform (Uniform) 18 | 19 | {-| Function to construct a vertex shader as a String. Useful for debugging 20 | purposes. 21 | -} 22 | showVertexShader : String -> String 23 | showVertexShader shaderString = 24 | vertexShaderBoilerplate ++ newLine ++ shaderString 25 | 26 | 27 | {-| Function to construct a fragment shader as a String. Useful for debugging 28 | purposes. 29 | -} 30 | showFragmentShader : String -> String 31 | showFragmentShader shaderString = 32 | fragmentShaderBoilerplate ++ newLine ++ shaderString 33 | 34 | 35 | {-| Construct a Vertex Shader from a String. 36 | The input string contains a GLSL program. This function enables to automatically 37 | add a whole bunch of boilerplate code, like setting the float precision, or 38 | declaring the uniforms and attributes. 39 | -} 40 | constructVertexShader : String -> Shader Attribute Uniform varying 41 | constructVertexShader = unsafeShader << showVertexShader 42 | 43 | 44 | {-| Construct a Fragment Shader from a String. 45 | The input string contains a GLSL program. This function enables to automatically 46 | add a whole bunch of boilerplate code, like setting the float precision, or 47 | declaring the structs and uniforms. 48 | -} 49 | constructFragmentShader : String -> Shader {} Uniform varying 50 | constructFragmentShader = unsafeShader << showFragmentShader 51 | -------------------------------------------------------------------------------- /Engine/Shader/Uniform.elm: -------------------------------------------------------------------------------- 1 | module Engine.Shader.Uniform where 2 | 3 | {-| This module contains the definition of the Uniform type and 4 | a function to construct a uniform from a scene and a renderable object 5 | 6 | # Definition 7 | @docs Uniform 8 | 9 | # Construct a Uniform 10 | @docs constructUniform 11 | 12 | -} 13 | 14 | import Math.Vector3 (Vec3) 15 | import Math.Matrix4 (Mat4) 16 | import Engine.Render.Renderable (Renderable) 17 | import Engine.Scene.Scene (Scene) 18 | import Engine.Math.Utils ( 19 | modelMatrix, 20 | viewMatrix, 21 | projectionMatrix, 22 | modelViewMatrix, 23 | modelViewProjectionMatrix, 24 | normalMatrix) 25 | 26 | 27 | -- TODO: Find a different strategy for dealing with Uniforms 28 | -- -- Note, only the first three are truly necessary 29 | -- -- Consider joining the model and view matrices into a modelViewMatrix 30 | {-| Container type to hold all of the properties that are passed to GLSL 31 | as uniforms. 32 | -} 33 | type alias Uniform = { 34 | modelMatrix : Mat4, 35 | viewMatrix : Mat4, 36 | projectionMatrix : Mat4, 37 | modelViewMatrix : Mat4, 38 | modelViewProjectionMatrix : Mat4, 39 | normalMatrix : Mat4, 40 | lightPosition : Vec3, 41 | lightRotation : Vec3, 42 | lightColor : Vec3, 43 | lightIntensity : Float, 44 | materialEmissiveColor : Vec3, 45 | materialEmissiveStrength : Float, 46 | materialAmbientColor : Vec3, 47 | materialAmbientStrength : Float, 48 | materialDiffuseColor : Vec3, 49 | materialDiffuseStrength : Float, 50 | materialSpecularColor : Vec3, 51 | materialSpecularStrength : Float 52 | } 53 | 54 | 55 | {-| Constructs a record of type Uniform from a scene and a renderable object 56 | to be sent to GLSL as uniforms. 57 | -} 58 | constructUniform : Scene -> Renderable -> Uniform 59 | constructUniform scene object = { 60 | modelMatrix = modelMatrix object, 61 | viewMatrix = viewMatrix scene.camera, 62 | projectionMatrix = projectionMatrix scene.camera, 63 | modelViewMatrix = modelViewMatrix object scene.camera, 64 | modelViewProjectionMatrix = modelViewProjectionMatrix object scene.camera, 65 | normalMatrix = normalMatrix object scene.camera, 66 | lightPosition = scene.light.position, 67 | lightRotation = scene.light.rotation, 68 | lightColor = scene.light.color, 69 | lightIntensity = scene.light.intensity, 70 | materialEmissiveColor = object.material.emissive.color, 71 | materialEmissiveStrength = object.material.emissive.strength, 72 | materialAmbientColor = object.material.ambient.color, 73 | materialAmbientStrength = object.material.ambient.strength, 74 | materialDiffuseColor = object.material.diffuse.color, 75 | materialDiffuseStrength = object.material.diffuse.strength, 76 | materialSpecularColor = object.material.specular.color, 77 | materialSpecularStrength = object.material.specular.strength } 78 | -------------------------------------------------------------------------------- /Engine/Shader/Utils.elm: -------------------------------------------------------------------------------- 1 | module Engine.Shader.Utils where 2 | 3 | {-| This module contains useful values and functions for constructing 4 | shaders. This includes common shorthands for statements and symbols as well 5 | as methods to construct attributes, uniforms, structs, or functions. 6 | 7 | # Useful Strings 8 | @docs newLine, whiteSpace, tab, equalSign, comma, semiColon, 9 | openParenthesis, closeParenthesis, openCurlyBrace, closeCurlyBrace, 10 | commentStart, commentEnd, setFloatPrecision 11 | 12 | # Useful Functions 13 | @docs groupStrings, groupStatements, groupProperties 14 | 15 | # Functions to construct GLSL statements 16 | @docs declareVariable, declareInitializedVariable, declareConstVariable, 17 | declareParameter, declareAttribute, declareVarying, declareUniform, 18 | declareStructType, callFunctionExpression 19 | 20 | -} 21 | 22 | import String (join) 23 | import List (map, intersperse) 24 | 25 | {-| The new line string : "\n" 26 | -} 27 | newLine : String 28 | newLine = "\n" 29 | 30 | 31 | {-| The whitespace string : " " 32 | -} 33 | whiteSpace : String 34 | whiteSpace = " " 35 | 36 | 37 | {-| The tab string : "\t" 38 | -} 39 | tab : String 40 | tab = "\t" 41 | 42 | {-| The equal sign string : "=" 43 | -} 44 | equalSign : String 45 | equalSign = "=" 46 | 47 | 48 | {-| The comma string : "," 49 | -} 50 | comma : String 51 | comma = "," 52 | 53 | 54 | {-| The semicolon string : ";" 55 | -} 56 | semiColon : String 57 | semiColon = ";" 58 | 59 | 60 | {-| The open parenthesis string : "(" 61 | -} 62 | openParenthesis : String 63 | openParenthesis = "(" 64 | 65 | 66 | {-| The close parenthesis string : ")" 67 | -} 68 | closeParenthesis : String 69 | closeParenthesis = ")" 70 | 71 | 72 | {-| The open curly brace string : "{" 73 | -} 74 | openCurlyBrace : String 75 | openCurlyBrace = "{" 76 | 77 | 78 | {-| The close curly brace string : "}" 79 | -} 80 | closeCurlyBrace : String 81 | closeCurlyBrace = "}" 82 | 83 | 84 | {-| The GLSL multiline comment start string : "/*" 85 | -} 86 | commentStart : String 87 | commentStart = "/*" 88 | 89 | 90 | {-| The GLSL multiline comment end string : "*/" 91 | -} 92 | commentEnd : String 93 | commentEnd = "*/" 94 | 95 | 96 | {-| The string : "precision mediump float;" 97 | -} 98 | setFloatPrecision : String 99 | setFloatPrecision = "precision mediump float;" 100 | 101 | 102 | {-| Constructs a comment from a string 103 | 104 | comment "hello" == "/*\nhello\n*/" 105 | -} 106 | comment : String -> String 107 | comment commentString = 108 | commentStart ++ newLine ++ commentString ++ newLine ++ commentEnd 109 | 110 | 111 | {-| Groups a list of string into a single string 112 | 113 | groupString ["Hello", "World"] == "HelloWorld" 114 | -} 115 | groupStrings : List String -> String 116 | groupStrings = join "" 117 | 118 | 119 | {-| Groups a list of statements into a single string by interspersing 120 | new lines. 121 | 122 | groupStatements ["x = 1;", "y = 2;"] == "x = 1;\ny = 2;" 123 | -} 124 | groupStatements : List String -> String 125 | groupStatements = join "" << intersperse newLine 126 | 127 | 128 | {-| Groups a list of properties into a single string by interspersing 129 | new lines and tabs. (mainly used to construct GLSL struct definitions) 130 | 131 | groupProperties ["vec3 position;", "vec3 rotation;"] == 132 | "vec3 position;\n\tvec3 rotation;" 133 | -} 134 | groupProperties : List String -> String 135 | groupProperties = join "" << intersperse (newLine ++ tab) 136 | 137 | 138 | {-| Function to construct a variable declaration in GLSL 139 | with a given type and name 140 | 141 | declareVariable "vec3" "position" == "vec3 position;" 142 | -} 143 | declareVariable : String -> String -> String 144 | declareVariable variableType variableName = 145 | variableType ++ whiteSpace ++ variableName ++ semiColon 146 | 147 | 148 | {-| Function to construct an initialized variable in GLSL 149 | 150 | declareInitializedVariable "float" "x" "3.0" == "float x = 3.0;" 151 | -} 152 | declareInitializedVariable : String -> String -> String -> String 153 | declareInitializedVariable variableType variableName initialValue = 154 | variableType ++ whiteSpace ++ variableName ++ whiteSpace ++ equalSign ++ whiteSpace ++ initialValue ++ semiColon 155 | 156 | 157 | {-| Function to construct a const variable declaration in GLSL 158 | 159 | declareConstVariable "float" "x" "3.0" == "const float x = 3.0;" 160 | -} 161 | declareConstVariable : String -> String -> String -> String 162 | declareConstVariable variableType variableName initialValue = 163 | "const" ++ whiteSpace ++ declareInitializedVariable variableType variableName initialValue 164 | 165 | 166 | {-| Function to construct an input parameter declaration in GLSL 167 | 168 | declareParameter "uniform" "float" "border" == "uniform float border;" 169 | -} 170 | declareParameter : String -> String -> String -> String 171 | declareParameter parameterType variableType variableName = 172 | parameterType ++ whiteSpace ++ variableType ++ whiteSpace ++ variableName ++ semiColon 173 | 174 | 175 | {-| Function to construct an attribute parameter declaration in GLSL 176 | 177 | declareAttribute "vec3" "position" == "attribute vec3 position;" 178 | -} 179 | declareAttribute : String -> String -> String 180 | declareAttribute = declareParameter "attribute" 181 | 182 | 183 | {-| Function to construct a varying parameter declaration in GLSL 184 | 185 | declareVarying "vec3" "vPosition" == "varying vec3 vPosition;" 186 | -} 187 | declareVarying : String -> String -> String 188 | declareVarying = declareParameter "varying" 189 | 190 | 191 | {-| Function to construct a uniform parameter declaration in GLSL 192 | 193 | declareUniform "mat4" "modelMatrix" == "uniform mat4 modelMatrix;" 194 | -} 195 | declareUniform : String -> String -> String 196 | declareUniform = declareParameter "uniform" 197 | 198 | 199 | {-| Function to construct a struct type declaration in GLSL 200 | 201 | declareStructType "Point" [("float", "x"), ("float", "y")] == 202 | "struct Point {\n\tfloat x;\n\tfloat y;\n};" 203 | -} 204 | declareStructType : String -> List (String, String) -> String 205 | declareStructType structTypeName structProperties = 206 | let propertyToDeclaration (propertyType, propertyName) = 207 | declareVariable propertyType propertyName 208 | structBody = 209 | openCurlyBrace ++ newLine ++ tab ++ 210 | (groupProperties <| map propertyToDeclaration structProperties) ++ 211 | newLine ++ closeCurlyBrace 212 | in declareParameter "struct" structTypeName structBody 213 | 214 | 215 | 216 | {-| Function to construct a function call expression in GLSL 217 | 218 | callFunctionExpression "add" ["x", "y"] == "add(x, y)" 219 | -} 220 | callFunctionExpression : String -> List String -> String 221 | callFunctionExpression functionName parameterList = 222 | functionName ++ openParenthesis ++ 223 | (groupStrings <| intersperse (comma ++ whiteSpace) parameterList) ++ 224 | closeParenthesis 225 | -------------------------------------------------------------------------------- /Engine/Shader/VertexShader.elm: -------------------------------------------------------------------------------- 1 | module Engine.Shader.VertexShader where 2 | 3 | {-| This module contains the definition of the default vertex shader. 4 | 5 | # Default Vertex Shader 6 | @docs vertexShader 7 | 8 | -} 9 | 10 | 11 | 12 | {-| Default Vertex Shader. 13 | 14 | Currently, the vertex shader just applied the model view projection 15 | transformation onto the vertex position and passes the new position 16 | as a varying to the fragment shader. 17 | 18 | -} 19 | vertexShader : String 20 | vertexShader = """ 21 | varying vec3 vPosition; 22 | varying vec3 vNormal; 23 | varying vec3 vViewPosition; 24 | 25 | void main (){ 26 | vec4 outputPosition = modelViewProjectionMatrix * vec4(position, 1.0); 27 | vec4 modelViewPosition = modelViewMatrix * vec4(position, 1.0); 28 | gl_Position = outputPosition; 29 | vPosition = outputPosition.xyz; 30 | vNormal = normalize(mat3(normalMatrix) * position); 31 | vViewPosition = -modelViewPosition.xyz; 32 | } 33 | 34 | """ 35 | -------------------------------------------------------------------------------- /Engine/Transform/Transform.elm: -------------------------------------------------------------------------------- 1 | module Engine.Transform.Transform where 2 | 3 | {-| This module contains the definition for the Transform type and 4 | the default transform object. 5 | 6 | # Definition 7 | @docs Transform 8 | 9 | # Default Transform 10 | @docs transform 11 | 12 | -} 13 | 14 | import Math.Vector3 (Vec3, vec3) 15 | 16 | {-| A transform is an object with a position, a rotation, and a scale. 17 | This is mean to be able to represent linear transformations in space. As 18 | such, one can model where an object is (position), what is its orientation 19 | (rotation), and how large is it (scale). 20 | -} 21 | type alias Transform a = { a | 22 | position : Vec3, 23 | rotation : Vec3, 24 | scale : Vec3 25 | } 26 | 27 | 28 | {-| Default transform. 29 | 30 | Defined as follows: 31 | 32 | transform = { 33 | position = vec3 0 0 0, 34 | rotation = vec3 0 0 0, 35 | scale = vec3 1 1 1 } 36 | 37 | -} 38 | transform : Transform {} 39 | transform = { 40 | position = vec3 0 0 0, 41 | rotation = vec3 0 0 0, 42 | scale = vec3 1 1 1 } 43 | -------------------------------------------------------------------------------- /Engine/Viewport/Viewport.elm: -------------------------------------------------------------------------------- 1 | module Engine.Viewport.Viewport where 2 | 3 | {-| This module contains the definition for the Viewport type and the 4 | default viewport object 5 | 6 | # Definition 7 | @docs Viewport 8 | 9 | # Default Viewport 10 | @docs viewport 11 | 12 | -} 13 | 14 | 15 | 16 | {-| A Viewport is an object that is supposed to describe the target "screen" 17 | or "context" on to which a scene is rendered. 18 | 19 | A Viewport has dimensions consisting of a width and a height. 20 | -} 21 | type alias Viewport = { 22 | dimensions : { 23 | width : Float, 24 | height : Float 25 | } 26 | } 27 | 28 | 29 | 30 | {-| Default Viewport. Sets dimensions.width = 400 and dimensions.height = 400. 31 | (In essence, with the default viewport object your scene will appear in 32 | a 400 by 400 canvas or webgl context). 33 | -} 34 | viewport : Viewport 35 | viewport = { 36 | dimensions = { 37 | width = 400, 38 | height = 400 } } 39 | -------------------------------------------------------------------------------- /PyramidCube.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheSeamau5/GraphicsEngine/123e196c4e024fc50ce2da8dd7421bdb092190b3/PyramidCube.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ##Introduction## 2 | Graphics Engine is a 3D Graphics Library backed by [elm-webgl](https://github.com/johnpmayer/elm-webgl) made for the simple construction of 3D scenes. 3 | 4 | Example: 5 | 6 | 7 | ```elm 8 | import Engine (render, cube, pyramid, scene) 9 | import Math.Vector3 (vec3) 10 | 11 | myCube = { 12 | cube | position <- vec3 0 0 0, 13 | rotation <- vec3 45 0 45, 14 | scale <- vec3 1.5 1.5 1.5 } 15 | 16 | myPyramid = { 17 | pyramid | position <- vec3 2 0 0, 18 | scale <- vec3 0.5 1 0.5 } 19 | 20 | myScene = { 21 | scene | objects <- [myCube, myPyramid] } 22 | 23 | main = render myScene 24 | 25 | ``` 26 | 27 | ![Image of Cube and Pyramid] 28 | (/PyramidCube.png) 29 | 30 | As you can see, we just created a cube and a pyramid. We modified the properties of each one. We then added these objects to a scene. And then render the scene. 31 | 32 | -------------------------------- 33 | ##How It Works## 34 | 35 | The simplest code to render a scene is the following: 36 | 37 | ```elm 38 | import Engine (render, scene) 39 | 40 | main = render scene 41 | ``` 42 | 43 | ## Scene ## 44 | `scene` is an object of type `Scene` 45 | ```elm 46 | type alias Scene = { 47 | camera : Camera, 48 | objects : List Renderable, 49 | light : Light, 50 | viewport : Viewport 51 | } 52 | ``` 53 | 54 | A Scene contains a Camera, a list of renderable objects, a light, and a viewport. 55 | 56 | *Note : In future releases, support will be added for multiple lights. Currently, only one light per scene is possible.* 57 | 58 | The default scene is defined as follows : 59 | 60 | ```elm 61 | scene : Scene 62 | scene = { 63 | camera = camera, 64 | objects = [cube], 65 | light = light, 66 | viewport = viewport } 67 | ``` 68 | --------------------------------- 69 | ##Camera## 70 | The default camera is `camera` which is an object of type `Camera` 71 | 72 | ```elm 73 | type alias Camera = Transform { 74 | aspectRatio : Float, 75 | fieldOfView : Float, 76 | nearClipping : Float, 77 | farClipping : Float 78 | } 79 | ``` 80 | 81 | A `Camera` is a `Transform` with an aspect ratio, a field of view, a near clipping plane, and a far clipping plane. In essence, a `Camera` describes a [viewing frustrum](http://en.wikipedia.org/wiki/Viewing_frustum). 82 | 83 | The default camera is defined as follows: 84 | ```elm 85 | camera : Camera 86 | camera = { 87 | position = vec3 0 0 -10, 88 | rotation = vec3 0 0 0, 89 | scale = vec3 1 1 1, 90 | aspectRatio = 1, 91 | fieldOfView = 45, 92 | nearClipping = 1, 93 | farClipping = 80000 } 94 | 95 | ``` 96 | 97 | ------------------------------------ 98 | ##Light## 99 | The default light is `light` which is a transform of type `Light` 100 | 101 | ```elm 102 | type alias Light = Transform { 103 | color : Vec3, 104 | intensity : Float, 105 | visibility : Bool 106 | } 107 | ``` 108 | 109 | A `Light` is a `Transform` with a color, an intensity, and a visibility (i.e. a flag to turn the light on or off). A `Light` is a transform because it is a light source and must have a physical location in world coordinates in order to affect the world appropriately. 110 | 111 | 112 | The default light is defined as follows : 113 | ```elm 114 | light : Light 115 | light = { 116 | position = vec3 1 1 3, 117 | rotation = vec3 0 0 0, 118 | scale = vec3 1 1 1, 119 | intensity = 1, 120 | color = vec3 1 1 1, 121 | visibility = True } 122 | 123 | ``` 124 | 125 | ---------------------------- 126 | ##Transform## 127 | The type `Transform` is used to store position, rotation, and scale. 128 | 129 | ```elm 130 | type alias Transform = { 131 | position : Vec3, 132 | rotation : Vec3, 133 | scale : Vec3 134 | } 135 | ``` 136 | 137 | --------------------------------- 138 | ##Renderable## 139 | 140 | The type `Renderable` is used to specify that an object may be rendered onto a screen. 141 | 142 | ```elm 143 | type alias Renderable = Transform { 144 | mesh : Mesh 145 | material : Material 146 | } 147 | ``` 148 | 149 | In order for an object to be rendered it must have a physical location in world space (the Transform part), it must have a shape (the mesh property), and it must define how it reacts to light (the material property) 150 | 151 | -------------------------------- 152 | 153 | ##Mesh## 154 | A `Mesh` is simply a `List(Triangle Attribute)` 155 | 156 | ```elm 157 | type alias Mesh = List (Triangle Attribute) 158 | ``` 159 | 160 | where `Attribute` is the type of the objects that are sent to the GPU as attributes 161 | 162 | Currently, the only attribute sent to the GPU as attribute is `position` 163 | 164 | ```elm 165 | type alias Attribute = { 166 | position : Vec3 167 | } 168 | ``` 169 | 170 | Typically, one would send a color and a normal attribute to the GPU. But, the choice made by the library is that normals can be calculated by passing in a normal matrix and color can be set on the `Material` 171 | 172 | The library offers some basic meshes to construct your objects. In 2D: 173 | 174 | * `rectangleMesh` 175 | * `triangleMesh` 176 | 177 | And in 3D: 178 | 179 | * `cubeMesh` 180 | * `pyramidMesh` 181 | * `sphereMesh` 182 | 183 | ----------------------------------- 184 | ##Material## 185 | A `Material` is an object that defines how an object reacts to light and draws itself. 186 | 187 | ```elm 188 | type alias MaterialProperty = { 189 | color : Vec3, 190 | strength : Float 191 | } 192 | 193 | type alias Material = { 194 | emissive : MaterialProperty, 195 | ambient : MaterialProperty, 196 | diffuse : MaterialProperty, 197 | specular : MaterialProperty, 198 | vertexShader : String, 199 | fragmentShader : String 200 | } 201 | ``` 202 | 203 | A material has emissive, ambient, diffuse, and specular components in order to address the vast majority of possible use cases. These are standard properties to have to represent illumination models such as the [Phong Illumination Model](http://en.wikipedia.org/wiki/Phong_reflection_model). 204 | 205 | A material also has a vertex shader and a fragment shader. Default shaders are provided by the library, but you can easily write your own and pass it to the material. 206 | 207 | The default Material `material` is defined as follows: 208 | 209 | ```elm 210 | material : Material 211 | material = { 212 | emissive = MaterialProperty (vec3 0 0 0) 0, 213 | ambient = MaterialProperty (vec3 0 0 0) 0, 214 | diffuse = MaterialProperty (vec3 0 0 0) 0, 215 | specular = MaterialProperty (vec3 0 0 0) 0, 216 | vertexShader = vertexShader, 217 | fragmentShader = fragmentShader } 218 | ``` 219 | 220 | ----------------------- 221 | ##Shaders## 222 | 223 | Shaders are simply passed as strings. While this may lead to some unsafe behavior that might go unnoticed by the Elm compiler, this design choice leads to two important benefits. 224 | 225 | 1. You can write your own shaders (or even import them from .glsl files) and easily create custom materials with custom behavior. 226 | 2. The shaders you write have access to all sorts of default values and GLSL can be easily extended with a simple base library. Therefore, you do not need to manually pass or declare any attributes or uniforms as they are all given. 227 | 228 | Currently, the default vertex shader, `vertexShader` just simply applies the model-view-projection to the vertex's position. 229 | 230 | *Vertex Shader:* 231 | ```glsl 232 | varying vec3 vPosition; 233 | 234 | void main (){ 235 | vec4 outputPosition = modelViewProjectionMatrix * vec4(position, 1.0); 236 | gl_Position = outputPosition; 237 | vPosition = outputPosition.xyz; 238 | } 239 | ``` 240 | 241 | Currently, the default fragment shader, `fragmentShader` just simply returns a red color. 242 | 243 | *Fragment Shader:* 244 | ```glsl 245 | void main(){ 246 | gl_FragColor = vec4(1.0,0.0,0.0,1.0); 247 | } 248 | ``` 249 | 250 | *Note: In future releases, more default fragment shaders will be provided including a standard [Gouraud shader](http://en.wikipedia.org/wiki/Gouraud_shading), a standard [Phong shader](http://en.wikipedia.org/wiki/Phong_shading) and a simple [Toon shader](http://en.wikipedia.org/wiki/Cel_shading).* 251 | 252 | ---------------------------- 253 | ##Writing your own shaders## 254 | 255 | If you want to write your own materials, you have to know that the libraries in its design has made certain tradeoffs (which are constantly being reviewed) and that these tradeoffs have influenced how to write and structure GLSL code. 256 | 257 | First of all, you **cannot pass** attributes and uniforms yourself. 258 | 259 | Currently, the available attributes are : 260 | 261 | ```glsl 262 | attribute vec3 position; 263 | ``` 264 | 265 | The available uniforms are : 266 | ```glsl 267 | uniform mat4 modelMatrix; 268 | uniform mat4 viewMatrix; 269 | uniform mat4 projectionMatrix; 270 | uniform vec3 lightPosition; 271 | uniform vec3 lightRotation; 272 | uniform vec3 lightColor; 273 | uniform float lightIntensity; 274 | uniform vec3 materialEmissiveColor; 275 | uniform float materialEmissiveStrength; 276 | uniform vec3 materialAmbientColor; 277 | uniform float materialAmbientStrength; 278 | uniform vec3 materialDiffuseColor; 279 | uniform float materialDiffuseStrength; 280 | uniform vec3 materialSpecularColor; 281 | uniform float materialSpecularStrength; 282 | ``` 283 | 284 | In order to more easily work with materials and lights and also to allow for future improvements to the library, a few structs are made available: 285 | 286 | ```glsl 287 | struct MaterialProperty { 288 | vec3 color; 289 | float strength; 290 | }; 291 | 292 | struct Material { 293 | MaterialProperty emissive; 294 | MaterialProperty ambient; 295 | MaterialProperty diffuse; 296 | MaterialProperty specular; 297 | }; 298 | 299 | struct Light { 300 | vec3 position; 301 | vec3 rotation; 302 | vec3 color; 303 | float intensity; 304 | }; 305 | ``` 306 | 307 | As you may notice, these structs somewhat match their Elm equivalents. 308 | *Note: currently, the visibility property for Light struct is not supported but will be in future releases* 309 | 310 | Furthermore, Graphics Engine comes packaged with a small GLSL library with common functions not provided by GLSL (or at least, not by WebGL's version of GLSL) 311 | 312 | The functions are : 313 | 314 | * `float trace(mat2)` 315 | * `float trace(mat3)` 316 | * `float trace(mat4)` 317 | * `float determinant(mat2)` 318 | * `float determinant(mat3)` 319 | * `float determinant(mat4)` 320 | * `mat2 transpose(mat2)` 321 | * `mat3 transpose(mat3)` 322 | * `mat4 transpose(mat4)` 323 | * `mat2 inverse(mat2)` 324 | * `mat3 inverse(mat3)` 325 | * `mat4 inverse(mat4)` 326 | 327 | 328 | Additionally, a set of helpful variables are provided (mostly as a convenience): 329 | 330 | * `Light light;` *The light in the scene* 331 | * `Material material;` *The object's material* 332 | * `mat4 modelViewProjectionMatrix;` 333 | * `mat4 modelViewMatrix;` 334 | * `mat4 normalMatrix;` 335 | 336 | ----------------------- 337 | ##How the render function works:## 338 | 339 | You might have noticed from above that the simplest example is just 340 | 341 | ```elm 342 | import Engine(render,scene) 343 | 344 | main = render scene 345 | ``` 346 | 347 | This code is so simple that it may make the `render` function seem almost magical. But this function is actually very simple and not magical at all. 348 | 349 | `render` is defined as follows: 350 | 351 | ```elm 352 | render : Scene -> Element 353 | render scene = 354 | webgl (floor scene.viewport.dimensions.width, 355 | floor scene.viewport.dimensions.height) <| 356 | map (renderObject scene) scene.objects 357 | ``` 358 | 359 | Basically, render just calls `webgl` from [the elm-webgl library](https://github.com/johnpmayer/elm-webgl), sets up the size of the canvas from the viewport dimensions of the scene (*this is exposed so you can easily implement fullscreen or make arbitrarily sized scenes*). 360 | 361 | The `render` function then constructs the list of Entities that `webgl` wants by calling `renderObject` on all the objects in the scene. 362 | 363 | `renderObject` is not directly exposed by the `Engine` module as it is not intended to be used by the users of the Library. 364 | 365 | `renderObject` is defined as follows: 366 | 367 | ```elm 368 | renderObject : Scene -> Renderable -> Entity 369 | renderObject scene object = 370 | entity (constructVertexShader object.material.vertexShader) 371 | (constructFragmentShader object.material.fragmentShader) 372 | object.mesh 373 | (constructUniform scene object) 374 | ``` 375 | 376 | As, you can see, `renderObject` does nothing fancy. All it does is construct the vertex shader from the object's vertex shader, construct the fragment shader from the object's fragment shader, construct the necessary uniforms and then pass the vertex shader, fragment shader, the object's mesh, and the uniforms to `entity`. 377 | 378 | Please refer to [the elm-webgl library](https://github.com/johnpmayer/elm-webgl) for more details on how the `entity` and `webgl` functions work. 379 | 380 | So, basically, the real magic behind the `render` function is in the data. You just set up the scene as if you were writing a .json file and you pass that data onto `render`. That's it! 381 | 382 | ----------------------- 383 | 384 | ##Note on Dependencies## 385 | 386 | Graphics Engine depends heavily on two libraries: 387 | * [elm-webgl](https://github.com/johnpmayer/elm-webgl) 388 | * [elm-linear-algebra](https://github.com/johnpmayer/elm-linear-algebra) 389 | 390 | The dependency on [elm-webgl](https://github.com/johnpmayer/elm-webgl) is such that Graphics Engine does not actually require you to import [elm-webgl](https://github.com/johnpmayer/elm-webgl) in order to use Graphics Engine to its fullest. 391 | 392 | On the other hand, importing [elm-linear-algebra](https://github.com/johnpmayer/elm-linear-algebra) is a must to do almost anything interesting because all the vectors and matrices are stored as `Vec3` and `Mat4` types. 393 | 394 | Therefore, it is highly recommended to also download [elm-linear-algebra](https://github.com/johnpmayer/elm-linear-algebra) when using Graphics Library. 395 | -------------------------------------------------------------------------------- /elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "summary": "Graphics Engine: 3D Graphics Library", 4 | "repository": "https://github.com/TheSeamau5/GraphicsEngine.git", 5 | "license": "BSD3", 6 | "source-directories": [ 7 | "." 8 | ], 9 | "exposed-modules": [ 10 | "Engine", 11 | "Engine.Camera.Camera", 12 | "Engine.Light.Light", 13 | "Engine.Material.Material", 14 | "Engine.Math.Utils", 15 | "Engine.Mesh.Mesh", 16 | "Engine.Mesh.Triangle", 17 | "Engine.Mesh.Rectangle", 18 | "Engine.Mesh.Pyramid", 19 | "Engine.Mesh.Cube", 20 | "Engine.Mesh.Sphere", 21 | "Engine.Render.Renderable", 22 | "Engine.Render.DefaultRenderable", 23 | "Engine.Render.Render", 24 | "Engine.Scene.Scene", 25 | "Engine.Shader.Shader", 26 | "Engine.Shader.Attribute", 27 | "Engine.Shader.Uniform", 28 | "Engine.Shader.Boilerplate", 29 | "Engine.Shader.Library", 30 | "Engine.Shader.Utils", 31 | "Engine.Shader.VertexShader", 32 | "Engine.Shader.FragmentShader", 33 | "Engine.Transform.Transform", 34 | "Engine.Viewport.Viewport" 35 | ], 36 | "dependencies": { 37 | "elm-lang/core": "1.0.0 <= v < 2.0.0", 38 | "johnpmayer/elm-linear-algebra": "2.0.0 <= v < 3.0.0", 39 | "johnpmayer/elm-webgl": "1.0.0 <= v < 2.0.0" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /example.elm: -------------------------------------------------------------------------------- 1 | import Engine (..) 2 | import Engine.Material.Material (MaterialProperty) 3 | import Math.Vector3 (vec3, Vec3) 4 | import Engine.Shader.GouraudShader (gouraudShader) 5 | 6 | import Array (fromList) 7 | 8 | 9 | 10 | white = vec3 1 1 1 11 | blue = vec3 0 0 1 12 | green = vec3 0 1 0 13 | red = vec3 1 0 0 14 | purple = vec3 (155 / 255) (89 / 255) (182 / 255) 15 | 16 | gouraudMaterial : Vec3 -> Material 17 | gouraudMaterial color = { 18 | material | fragmentShader <- gouraudShader, 19 | emissive <- MaterialProperty color 1.0, 20 | ambient <- MaterialProperty white 0.4, 21 | diffuse <- MaterialProperty white 0.4, 22 | specular <- MaterialProperty white 0.5 } 23 | 24 | floor = { 25 | cube | scale <- vec3 10 0.2 10, 26 | material <- gouraudMaterial blue } 27 | 28 | redBall = { 29 | sphere | position <- vec3 0 2 0, 30 | scale <- vec3 2 2 2, 31 | material <- gouraudMaterial red } 32 | 33 | 34 | greenBox = { 35 | cube | position <- vec3 -2 2 -2, 36 | material <- gouraudMaterial green } 37 | 38 | purplePyramid = { 39 | pyramid | position <- vec3 3 2 -4, 40 | scale <- vec3 2.5 2.5 2.5, 41 | material <- gouraudMaterial purple } 42 | 43 | myCamera = {camera | position <- vec3 2 5 -12, 44 | rotation <- vec3 0.3 -0.15 0, 45 | fieldOfView <- 40, 46 | aspectRatio <- 16 / 9 } 47 | 48 | 49 | myLight = { light | position <- vec3 0 20 -50} 50 | 51 | myViewport = 52 | let myDimensions = {width = 800, height = 450} 53 | in {viewport | dimensions <- myDimensions} 54 | 55 | myScene = { 56 | scene | objects <- fromList [floor, redBall, greenBox, purplePyramid], 57 | camera <- myCamera, 58 | light <- myLight, 59 | viewport <- myViewport} 60 | 61 | main = render myScene 62 | -------------------------------------------------------------------------------- /pong.elm: -------------------------------------------------------------------------------- 1 | import Engine (..) 2 | import Engine.Material.Material (MaterialProperty) 3 | import Math.Vector3 (vec3, Vec3) 4 | import Engine.Shader.GouraudShader (gouraudShader) 5 | 6 | import Time (..) 7 | import Signal (..) 8 | import Keyboard 9 | import Window 10 | 11 | import Array (fromList) 12 | 13 | import Graphics.Element (Element) 14 | 15 | -- INPUT 16 | 17 | type alias Input = { 18 | space : Bool, 19 | paddle1 : Int, 20 | paddle2 : Int, 21 | delta : Time 22 | } 23 | 24 | 25 | delta : Signal Time 26 | delta = inSeconds <~ fps 60 27 | 28 | input : Signal Input 29 | input = sampleOn delta <| Input <~ Keyboard.space 30 | ~ map .y Keyboard.wasd 31 | ~ map .y Keyboard.arrows 32 | ~ delta 33 | 34 | 35 | 36 | -- MODEL 37 | 38 | (gameWidth, gameHeight) = (600, 400) 39 | (halfWidth, halfHeight) = (300, 200) 40 | 41 | 42 | type alias Object a = { a | 43 | x : Float, 44 | y : Float, 45 | vx : Float, 46 | vy : Float 47 | } 48 | 49 | type alias Ball = Object {} 50 | 51 | type alias Player = Object { score : Int } 52 | 53 | type State = Play | Pause 54 | 55 | type alias Game = { 56 | state : State, 57 | ball : Ball, 58 | player1 : Player, 59 | player2 : Player 60 | } 61 | 62 | player : Float -> Player 63 | player x = { x = x, y = 0, vx = 0, vy = 0, score = 0 } 64 | 65 | defaultGame : Game 66 | defaultGame = { 67 | state = Pause, 68 | ball = { x = 0, y = 0, vx = 200, vy = 200 }, 69 | player1 = player (20 - halfWidth), 70 | player2 = player (halfWidth - 20) } 71 | 72 | 73 | -- UPDATE 74 | 75 | near : Float -> Float -> Float -> Bool 76 | near n c m = m >= n - c && m <= n + c 77 | 78 | within : Ball -> Player -> Bool 79 | within ball player = 80 | (ball.x |> near player.x 8) && (ball.y |> near player.y 20) 81 | 82 | 83 | stepV : Float -> Bool -> Bool -> Float 84 | stepV v lowerCollision upperCollision = 85 | if | lowerCollision -> abs v 86 | | upperCollision -> 0 - abs v 87 | | otherwise -> v 88 | 89 | 90 | stepObj : Time -> Object a -> Object a 91 | stepObj t ({x,y,vx,vy} as obj) = 92 | { obj | x <- x + vx * t, 93 | y <- y + vy * t } 94 | 95 | stepBall : Time -> Ball -> Player -> Player -> Ball 96 | stepBall t ({x,y,vx,vy} as ball) player1 player2 = 97 | if not (ball.x |> near 0 halfWidth) 98 | then { ball | x <- 0, y <- 0} 99 | else 100 | let vx' = stepV vx (ball `within` player1) (ball `within` player2) 101 | vy' = stepV vy (y < 7 - halfHeight) (y > halfHeight - 7) 102 | in 103 | stepObj t { ball | vx <- vx', vy <- vy' } 104 | 105 | stepPlyr : Time -> Int -> Int -> Player -> Player 106 | stepPlyr t dir points player = 107 | let player' = stepObj t { player | vy <- toFloat dir * 200 } 108 | y' = clamp (22 - halfHeight) (halfHeight - 22) player'.y 109 | score' = player.score + points 110 | in 111 | { player' | y <- y', score <- score' } 112 | 113 | stepGame : Input -> Game -> Game 114 | stepGame {space, paddle1, paddle2, delta} 115 | ({state, ball, player1, player2} as game) = 116 | let score1 = if ball.x > halfWidth then 1 else 0 117 | score2 = if ball.x < -halfWidth then 1 else 0 118 | 119 | state' = if | space -> Play 120 | | score1 /= score2 -> Pause 121 | | otherwise -> state 122 | 123 | ball' = if state == Pause then ball 124 | else stepBall delta ball player1 player2 125 | 126 | player1' = stepPlyr delta paddle1 score1 player1 127 | player2' = stepPlyr delta paddle2 score2 player2 128 | 129 | in 130 | { game | state <- state', 131 | ball <- ball', 132 | player1 <- player1, 133 | player2 <- player2'} 134 | 135 | 136 | gameState : Signal Game 137 | gameState = foldp stepGame defaultGame input 138 | 139 | 140 | -- VIEW 141 | pongGreen = vec3 (60 / 255) (100 / 255) (60 / 255) 142 | white = vec3 1 1 1 143 | blue = vec3 0 0 1 144 | red = vec3 1 0 0 145 | 146 | gouraudMaterial : Vec3 -> Material 147 | gouraudMaterial color = { 148 | material | fragmentShader <- gouraudShader, 149 | emissive <- MaterialProperty color 1.0, 150 | ambient <- MaterialProperty white 0.4, 151 | diffuse <- MaterialProperty white 0.4, 152 | specular <- MaterialProperty white 0.5 } 153 | 154 | 155 | 156 | displayObj : Object a -> Renderable -> Renderable 157 | displayObj object renderable = 158 | {renderable | position <- vec3 object.x object.y 0, 159 | material <- gouraudMaterial blue } 160 | 161 | ballShape : Float -> Renderable 162 | ballShape radius = 163 | { sphere | scale <- vec3 radius radius radius, 164 | material <- gouraudMaterial red } 165 | 166 | background : Vec3 -> Float -> Float -> Renderable 167 | background color width height = 168 | { cube | position <- vec3 0 0 1, 169 | scale <- vec3 width height 0.5, 170 | material <- gouraudMaterial color } 171 | 172 | paddleShape : Float -> Float -> Renderable 173 | paddleShape width height = 174 | { cube | scale <- vec3 width height 1 } 175 | 176 | display : (Int, Int) -> Game -> Element 177 | display (w,h) {state, ball, player1, player2} = 178 | let gameCamera = { camera | 179 | position <- vec3 0 0 -550, 180 | aspectRatio <- gameWidth / gameHeight } 181 | 182 | gameLight = { light | 183 | position <- vec3 -3 5 -4 } 184 | 185 | gameDimensions = { width = gameWidth, height = gameHeight } 186 | 187 | gameViewport = { viewport | 188 | dimensions <- gameDimensions } 189 | 190 | gameScene = { scene | 191 | objects <- fromList [ 192 | background pongGreen gameWidth gameHeight, 193 | displayObj ball (ballShape 15), 194 | displayObj player1 (paddleShape 10 40), 195 | displayObj player2 (paddleShape 10 40) 196 | ], 197 | camera <- gameCamera, 198 | light <- gameLight, 199 | viewport <- gameViewport } 200 | in render gameScene 201 | 202 | main = map2 display Window.dimensions gameState 203 | -------------------------------------------------------------------------------- /test.elm: -------------------------------------------------------------------------------- 1 | import Engine (..) 2 | import Engine.Material.Material (MaterialProperty) 3 | import Math.Vector3 (vec3) 4 | import Engine.Shader.GouraudShader (gouraudShader) 5 | 6 | import Array (fromList) 7 | 8 | myObject = 9 | let gouraudMaterial = { 10 | material | fragmentShader <- gouraudShader, 11 | emissive <- MaterialProperty (vec3 0 0 1) 0.7, 12 | ambient <- MaterialProperty (vec3 1 1 1) 0.3, 13 | diffuse <- MaterialProperty (vec3 1 1 1) 0.5, 14 | specular <- MaterialProperty (vec3 1 1 1) 0.8 } 15 | in { sphere | material <- gouraudMaterial } 16 | 17 | 18 | myCamera = { 19 | camera | position <- vec3 -1.7 1.7 -3, 20 | rotation <- vec3 0.4 0.5 0 } 21 | 22 | myLight = { 23 | light | position <- vec3 -3 5 -4 } 24 | 25 | myScene = { 26 | scene | objects <- fromList [myObject], 27 | camera <- myCamera, 28 | light <- myLight } 29 | 30 | main = render myScene 31 | --------------------------------------------------------------------------------