bjorn 7 lat temu
rodzic
commit
061d775b50
78 zmienionych plików z 2157 dodań i 1401 usunięć
  1. 533 299
      api/init.lua
  2. 63 0
      api/lovr/callbacks/conf.lua
  3. 60 0
      api/lovr/enet/init.lua
  4. 1 1
      api/lovr/getOS.lua
  5. 15 4
      api/lovr/graphics/FilterMode.lua
  6. 25 0
      api/lovr/graphics/MatrixType.lua
  7. 2 0
      api/lovr/graphics/Shader/init.lua
  8. 8 5
      api/lovr/graphics/Texture/getFilter.lua
  9. 3 1
      api/lovr/graphics/Texture/init.lua
  10. 14 11
      api/lovr/graphics/Texture/setFilter.lua
  11. 48 0
      api/lovr/graphics/createWindow.lua
  12. 0 32
      api/lovr/graphics/getColorMask.lua
  13. 28 0
      api/lovr/graphics/getDefaultFilter.lua
  14. 1 1
      api/lovr/graphics/getDepthTest.lua
  15. 0 31
      api/lovr/graphics/getScissor.lua
  16. 0 0
      api/lovr/graphics/getWinding.lua
  17. 13 8
      api/lovr/graphics/plane.lua
  18. 6 0
      api/lovr/graphics/print.lua
  19. 19 8
      api/lovr/graphics/rotate.lua
  20. 18 6
      api/lovr/graphics/scale.lua
  21. 0 8
      api/lovr/graphics/setColor.lua
  22. 0 33
      api/lovr/graphics/setColorMask.lua
  23. 28 0
      api/lovr/graphics/setDefaultFilter.lua
  24. 1 1
      api/lovr/graphics/setDepthTest.lua
  25. 2 1
      api/lovr/graphics/setFont.lua
  26. 0 31
      api/lovr/graphics/setScissor.lua
  27. 0 0
      api/lovr/graphics/setWinding.lua
  28. 15 0
      api/lovr/graphics/transform.lua
  29. 18 6
      api/lovr/graphics/translate.lua
  30. 15 0
      api/lovr/headset/Controller/getHand.lua
  31. 2 1
      api/lovr/headset/Controller/isDown.lua
  32. 26 0
      api/lovr/headset/Controller/isTouched.lua
  33. 4 0
      api/lovr/headset/ControllerAxis.lua
  34. 24 0
      api/lovr/headset/ControllerButton.lua
  35. 17 0
      api/lovr/headset/ControllerHand.lua
  36. 19 0
      api/lovr/headset/HeadsetOrigin.lua
  37. 18 0
      api/lovr/headset/HeadsetType.lua
  38. 19 0
      api/lovr/headset/getOriginType.lua
  39. 24 0
      api/lovr/math/RandomGenerator/getSeed.lua
  40. 19 0
      api/lovr/math/RandomGenerator/getState.lua
  41. 9 0
      api/lovr/math/RandomGenerator/init.lua
  42. 43 0
      api/lovr/math/RandomGenerator/random.lua
  43. 35 0
      api/lovr/math/RandomGenerator/randomNormal.lua
  44. 26 0
      api/lovr/math/RandomGenerator/setSeed.lua
  45. 19 0
      api/lovr/math/RandomGenerator/setState.lua
  46. 12 0
      api/lovr/math/getRandomSeed.lua
  47. 1 4
      api/lovr/math/init.lua
  48. 96 0
      api/lovr/math/lookAt.lua
  49. 48 0
      api/lovr/math/newRandomGenerator.lua
  50. 46 0
      api/lovr/math/random.lua
  51. 35 0
      api/lovr/math/randomNormal.lua
  52. 16 0
      api/lovr/math/setRandomSeed.lua
  53. BIN
      examples/Billboard/eye.png
  54. 12 0
      examples/Billboard/main.lua
  55. 0 8
      examples/Cube/main.lua
  56. 3 0
      examples/Lighting/conf.lua
  57. 49 0
      examples/Lighting/main.lua
  58. 0 0
      examples/Panorama/equirectangular.jpg
  59. 0 0
      examples/Panorama/main.lua
  60. 54 0
      examples/Physics/main.lua
  61. 39 0
      examples/Physics/shader.lua
  62. 78 0
      examples/Primitives/main.lua
  63. 39 0
      examples/Primitives/shader.lua
  64. 5 2
      examples/init.lua
  65. 0 74
      guides/3D_Models.md
  66. 0 110
      guides/Callbacks.md
  67. 249 0
      guides/Callbacks_and_Modules.md
  68. 0 141
      guides/Controllers.md
  69. 51 0
      guides/Distribution.md
  70. 0 37
      guides/Game_Distribution.md
  71. 39 42
      guides/Getting_Started.md
  72. 0 222
      guides/How_to_Lua.md
  73. 0 46
      guides/Introduction.md
  74. 42 0
      guides/Libraries.md
  75. 0 115
      guides/Simple_Shapes.md
  76. 0 73
      guides/Sound.md
  77. 0 30
      guides/WebVR.md
  78. 3 9
      guides/init.lua

Plik diff jest za duży
+ 533 - 299
api/init.lua


+ 63 - 0
api/lovr/callbacks/conf.lua

@@ -28,6 +28,11 @@ return {
               description = [[
                 Whether the desktop window should display a mirror of what's in the headset.
               ]]
+            },
+            {
+              name = 'offset',
+              type = 'number',
+              description = 'The vertical offset for seated experiences.'
             }
           }
         },
@@ -77,6 +82,43 @@ return {
               description = 'Whether the timer module should be enabled.'
             }
           }
+        },
+        {
+          name = 'window',
+          type = 'table',
+          description = 'Configuration for the window.',
+          table = {
+            {
+              name = 'width',
+              type = 'number',
+              description = 'The width of the window.'
+            },
+            {
+              name = 'height',
+              type = 'number',
+              description = 'The height of the window.'
+            },
+            {
+              name = 'fullscreen',
+              type = 'boolean',
+              description = 'Whether the window is fullscreen.'
+            },
+            {
+              name = 'msaa',
+              type = 'number',
+              description = 'The number of antialiasing samples to use.'
+            },
+            {
+              name = 'title',
+              type = 'string',
+              description = 'The window title.'
+            },
+            {
+              name = 'icon',
+              type = 'string',
+              description = 'The path to the window icon file.'
+            }
+          }
         }
       }
     }
@@ -85,16 +127,29 @@ return {
   notes = [[
     Disabling the `headset` module can improve startup time a lot if you aren't intending to use
     `lovr.headset`.
+
+    You can set `t.window` to nil to avoid creating the window. You can do it yourself later by
+    using `lovr.graphics.createWindow`.
+
+    If the `lovr.graphics` module is disabled or the window isn't created, attempting to use any
+    functionality requiring graphics may cause a crash.
+
+    The `headset.offset` field is a vertical offset applied to the scene for headsets that do not
+    center their tracking origin on the floor.  This can be thought of as a "default user height".
+    Setting this offset makes it easier to design experiences that work in both seated and standing
+    VR configurations.
   ]],
   example = {
     description = 'A noop conf.lua that sets all configuration settings to their defaults:',
     code = [[
         function lovr.conf(t)
+
           -- Set the project identity
           t.identity = 'default'
 
           -- Headset settings
           t.headset.mirror = true
+          t.headset.offset = 1.7
 
           -- Enable or disable different modules
           t.modules.audio = true
@@ -104,6 +159,14 @@ return {
           t.modules.math = true
           t.modules.physics = true
           t.modules.timer = true
+
+          -- Configure the desktop window
+          t.window.width = 800
+          t.window.height = 600
+          t.window.fullscreen = false
+          t.window.msaa = 0
+          t.window.title = 'LÖVR'
+          t.window.icon = nil
         end
     ]]
   },

+ 60 - 0
api/lovr/enet/init.lua

@@ -0,0 +1,60 @@
+return {
+  tag = 'library',
+  summary = 'Multiplayer utilities.',
+  description = [[
+    ENet is a UDP networking library bundled with LÖVR that allows you to create multiplayer
+    experiences.
+
+    To use it, `require` the `enet` module.
+
+    More information, including full documentation and examples can be found on the
+    [lua-enet](http://leafo.net/lua-enet/) page.
+  ]],
+  external = true,
+  example = {
+    description = [[
+      Here's a simple echo server example. The client sends a message to the server and waits for a
+      response. The server waits for a message and sends it back to the client.
+    ]],
+    code = [[
+      -- client/main.lua
+      local enet = require 'enet'
+
+      function lovr.load()
+        local host = enet.host_create()
+        local server = host:connect('localhost:6789')
+
+        local done = false
+        while not done do
+          local event = host:service(100)
+          if event then
+            if event.type == 'connect' then
+              print('Connected to', event.peer)
+              event.peer:send('hello world')
+            elseif event.type == 'receive' then
+              print('Got message: ', event.data, event.peer)
+              done = true
+            end
+          end
+        end
+
+        server:disconnect()
+        host:flush()
+      end
+
+      -- server/main.lua
+      local enet = require 'enet'
+
+      function lovr.load()
+        local host = enet.host_create('localhost:6789')
+        while true do
+          local event = host:service(100)
+          if event and event.type == 'receive' then
+            print('Got message: ', event.data, event.peer)
+            event.peer:send(event.data)
+          end
+        end
+      end
+    ]]
+  }
+}

+ 1 - 1
api/lovr/getOS.lua

@@ -7,7 +7,7 @@ return {
     {
       name = 'os',
       type = 'string',
-      description = 'Either "windows" or "macOS".'
+      description = 'Either "windows", "macOS", or "linux".'
     }
   }
 }

+ 15 - 4
api/lovr/graphics/FilterMode.lua

@@ -6,12 +6,23 @@ return {
   ]],
   values = {
     {
-      name = 'linear',
-      description = 'The texture will be smoothed.'
+      name = 'nearest',
+      description = 'Fast nearest-neighbor sampling.  Leads to a pixelated style.'
     },
     {
-      name = 'nearest',
-      description = 'The texture will be pixelated.'
+      name = 'bilinear',
+      description = 'Smooth pixel sampling.'
+    },
+    {
+      name = 'trilinear',
+      description = 'Smooth pixel sampling, with smooth sampling across mipmap levels.'
+    },
+    {
+      name = 'anisotropic',
+      description = [[
+        Anisotropic texture filtering.  The level of anisotropy can also be specified when setting
+        this filter mode.  Gives the best results but is also slower.
+      ]]
     }
   }
 }

+ 25 - 0
api/lovr/graphics/MatrixType.lua

@@ -0,0 +1,25 @@
+return {
+  summary = 'Types of matrix on the transform stack.',
+  description = [[
+    When modifying the coordinate system using functions like `lovr.graphics.translate`, you can
+    modify either the model matrix or the view matrix.  The model matrix is meant to represent the
+    transform of the object being rendered, whereas the view matrix is meant to represent the
+    transform of the camera.  By default, the model matrix is manipulated.
+  ]],
+  values = {
+    {
+      name = 'model',
+      description = 'The model matrix.'
+    },
+    {
+      name = 'view',
+      description = 'The view matrix.'
+    }
+  },
+  related = {
+    'lovr.graphics.translate',
+    'lovr.graphics.rotate',
+    'lovr.graphics.scale',
+    'lovr.graphics.transform'
+  }
+}

+ 2 - 0
api/lovr/graphics/Shader/init.lua

@@ -26,6 +26,8 @@ return {
 
     Vertex shader header:
 
+        uniform mat4 lovrModel;
+        uniform mat4 lovrView;
         uniform mat4 lovrTransform;
         uniform mat4 lovrNormalMatrix;
         uniform mat4 lovrProjection;

+ 8 - 5
api/lovr/graphics/Texture/getFilter.lua

@@ -4,14 +4,17 @@ return {
   arguments = {},
   returns = {
     {
-      name = 'min',
+      name = 'mode',
       type = 'FilterMode',
-      description = 'The filter mode used for minification.'
+      description = 'The filter mode for the Texture.'
     },
     {
-      name = 'mag',
-      type = 'FilterMode',
-      description = 'The filter mode used for magnification.'
+      name = 'anisotropy',
+      type = 'number',
+      description = [[
+        If the filtering mode is "anisotropic", returns the level of anisotropy.  Otherwise, this
+        will be nil.
+      ]]
     }
   }
 }

+ 3 - 1
api/lovr/graphics/Texture/init.lua

@@ -2,7 +2,9 @@ return {
   summary = 'An image that can be applied to Meshes and Models.',
   description = [[
     A Texture is an image that can be applied to `Model`s and `Mesh`s.  Supported file formats
-    include `.png`, `.jpg`, `.tga`, and `.bmp`.
+    include `.png`, `.jpg`, `.tga`, and `.bmp`.  Additionally, three compressed formats are
+    supported: DXT1, DXT3, and DXT5 (all have the `.dds` extension).  Compressed textures are
+    generally recommended as they use less video memory and usually improve performance.
   ]],
   constructor = 'lovr.graphics.newTexture'
 }

+ 14 - 11
api/lovr/graphics/Texture/setFilter.lua

@@ -1,21 +1,24 @@
 return {
   summary = 'Set the FilterMode for the Texture.',
-  description = [[
-    Sets the `FilterMode` used by the texture when upsampling or downsampling.  The default mode is
-    `linear`.
-  ]],
+  description = 'Sets the `FilterMode` used by the texture.',
   arguments = {
     {
-      name = 'min',
+      name = 'mode',
       type = 'FilterMode',
-      description = 'The filter mode used for minification.'
+      description = 'The filter mode.'
     },
     {
-      name = 'mag',
-      default = 'min',
-      type = 'FilterMode',
-      description = 'The filter mode used for magnification.'
+      name = 'anisotropy',
+      type = 'number',
+      description = 'The level of anisotropy to use when using anisotropic filtering.'
     }
   },
-  returns = {}
+  returns = {},
+  notes = [[
+    The default setting for new textures can be set with `lovr.graphics.setDefaultFilter`.
+  ]],
+  related = {
+    'lovr.graphics.getDefaultFilter',
+    'lovr.graphics.setDefaultFilter'
+  }
 }

+ 48 - 0
api/lovr/graphics/createWindow.lua

@@ -0,0 +1,48 @@
+return {
+  tag = 'window',
+  summary = 'Creates the window.',
+  description = 'Create the desktop window, usually used to mirror the headset display.',
+  arguments = {
+    {
+      name = 'width',
+      type = 'number',
+      default = '800',
+      description = 'The width of the window.'
+    },
+    {
+      name = 'height',
+      type = 'number',
+      default = '600',
+      description = 'The height of the window.'
+    },
+    {
+      name = 'fullscreen',
+      type = 'boolean',
+      default = 'false',
+      description = 'Whether the window should be fullscreen.'
+    },
+    {
+      name = 'msaa',
+      type = 'number',
+      default = '0',
+      description = 'The number of samples to use for multisample antialiasing.'
+    },
+    {
+      name = 'title',
+      type = 'string',
+      default = 'nil',
+      description = 'The window title.'
+    },
+    {
+      name = 'icon',
+      type = 'string',
+      default = 'nil',
+      description = 'A path to an image to use for the window icon.'
+    }
+  },
+  returns = {},
+  notes = [[
+    This function can only be called once. It is normally called internally, but you can override
+    this by setting window to `nil` in conf.lua.  See `lovr.conf` for more information.
+  ]]
+}

+ 0 - 32
api/lovr/graphics/getColorMask.lua

@@ -1,32 +0,0 @@
-return {
-  tag = 'graphicsState',
-  summary = 'Get the color mask.',
-  description = [[
-    Returns the active color channels.  If a color channel is active, then drawing operations will
-    affect that particular channel.
-  ]],
-  arguments = {},
-  returns = {
-    {
-      name = 'r',
-      type = 'boolean',
-      description = 'Whether or not the red channel is enabled.'
-    },
-    {
-      name = 'g',
-      type = 'boolean',
-      description = 'Whether or not the green channel is enabled.'
-    },
-    {
-      name = 'b',
-      type = 'boolean',
-      description = 'Whether or not the blue channel is enabled.'
-    },
-    {
-      name = 'a',
-      type = 'boolean',
-      description = 'Whether or not the alpha channel is enabled.'
-    }
-  },
-  notes = 'Initially, all color channels are enabled.'
-}

+ 28 - 0
api/lovr/graphics/getDefaultFilter.lua

@@ -0,0 +1,28 @@
+return {
+  tag = 'graphicsState',
+  summary = 'Get the default filter mode for Textures.',
+  description = [[
+    Returns the default filter mode for new Textures.  This controls how textures are sampled when
+    they are minified, magnified, or stretched.
+  ]],
+  arguments = {},
+  returns = {
+    {
+      name = 'mode',
+      type = 'FilterMode',
+      description = 'The filter mode.'
+    },
+    {
+      name = 'anisotropy',
+      type = 'number',
+      description = [[
+        If the filtering mode is "anisotropic", returns the level of anisotropy.  Otherwise, this
+        will be nil.
+      ]]
+    }
+  },
+  related = {
+    'Texture:getFilter',
+    'Texture:setFilter'
+  }
+}

+ 1 - 1
api/lovr/graphics/getDepthTest.lua

@@ -12,5 +12,5 @@ return {
       description = 'The current depth test.'
     }
   },
-  notes = 'The default depth test is `less`.'
+  notes = 'The default depth test is `lequal`.'
 }

+ 0 - 31
api/lovr/graphics/getScissor.lua

@@ -1,31 +0,0 @@
-return {
-  tag = 'graphicsState',
-  summary = 'Get the scissor rectangle.',
-  description = [[
-    Returns the current scissor.  The scissor is a rectangular area of the screen.  Any pixels
-    outside the scissor region will be unaffected by drawing operations.
-  ]],
-  arguments = {},
-  returns = {
-    {
-      name = 'x',
-      type = 'number',
-      description = 'The x position of the upper left corner of the scissor.'
-    },
-    {
-      name = 'y',
-      type = 'number',
-      description = 'The y position of the upper left corner of the scissor.'
-    },
-    {
-      name = 'w',
-      type = 'number',
-      description = 'The width of the scissor in pixels.'
-    },
-    {
-      name = 'h',
-      type = 'number',
-      description = 'The height of the scissor in pixels.'
-    }
-  }
-}

+ 0 - 0
api/lovr/graphics/getPolygonWinding.lua → api/lovr/graphics/getWinding.lua


+ 13 - 8
api/lovr/graphics/plane.lua

@@ -32,31 +32,36 @@ return {
       default = '1',
       description = 'The size of the plane, in meters.'
     },
-    nx = {
+    angle = {
       type = 'number',
       default = '0',
-      description = 'The x coordinate of the normal vector of the plane.'
+      description = 'The number of radians to rotate around the rotation axis.'
     },
-    ny = {
+    ax = {
+      type = 'number',
+      default = '0',
+      description = 'The x component of the rotation axis.'
+    },
+    ay = {
       type = 'number',
       default = '1',
-      description = 'The y coordinate of the normal vector of the plane.'
+      description = 'The y component of the rotation axis.'
     },
-    nz = {
+    az = {
       type = 'number',
       default = '0',
-      description = 'The z coordinate of the normal vector of the plane.'
+      description = 'The z component of the rotation axis.'
     }
   },
   returns = {},
   variants = {
     {
-      arguments = { 'mode', 'x', 'y', 'z', 'size', 'nx', 'ny', 'nz' },
+      arguments = { 'mode', 'x', 'y', 'z', 'size', 'angle', 'ax', 'ay', 'az' },
       returns = {}
     },
     {
       description = 'Draw a textured plane.',
-      arguments = { 'texture', 'x', 'y', 'z', 'size', 'nx', 'ny', 'nz' },
+      arguments = { 'texture', 'x', 'y', 'z', 'size', 'angle', 'ax', 'ay', 'az' },
       returns = {}
     },
     {

+ 6 - 0
api/lovr/graphics/print.lua

@@ -83,6 +83,12 @@ return {
     Unicode text is supported.
 
     Use `\n` to add line breaks.
+
+    LÖVR uses a fancy technique for font rendering called multichanel signed distance fields.  This
+    leads to crisp text in VR, but requires a special shader to use.  LÖVR internally switches to
+    the appropriate shader, but only when the default shader is already set.  If you see strange
+    font artifacts, make sure you switch back to the default shader by using
+    `lovr.graphics.setShader()` before you draw text.
   ]],
   related = {
     'lovr.graphics.getFont',

+ 19 - 8
api/lovr/graphics/rotate.lua

@@ -8,31 +8,42 @@ return {
     transformation stack.
   ]],
   arguments = {
-    {
-      name = 'angle',
+    angle = {
       type = 'number',
       description = 'The amount to rotate the coordinate system by, in radians.'
     },
-    {
-      name = 'ax',
+    ax = {
       type = 'number',
       default = '0',
       description = 'The x component of the axis of rotation.'
     },
-    {
-      name = 'ay',
+    ay = {
       type = 'number',
       default = '1',
       description = 'The y component of the axis of rotation.'
     },
-    {
-      name = 'az',
+    az = {
       type = 'number',
       default = '0',
       description = 'The z component of the axis of rotation.'
+    },
+    matrix = {
+      type = 'MatrixType',
+      default = [['model']],
+      description = 'The matrix to modify.'
     }
   },
   returns = {},
+  variants = {
+    {
+      arguments = { 'angle', 'ax', 'ay', 'az' },
+      returns = {}
+    },
+    {
+      arguments = { 'matrix', 'angle', 'ax', 'ay', 'az' },
+      returns = {}
+    }
+  },
   notes = 'Order matters when scaling, translating, and rotating the coordinate system.',
   related = {
     'lovr.graphics.scale',

+ 18 - 6
api/lovr/graphics/scale.lua

@@ -9,25 +9,37 @@ return {
     transformation stack.
   ]],
   arguments = {
-    {
-      name = 'x',
+    x = {
       type = 'number',
       description = 'The amount to scale on the x axis.'
     },
-    {
-      name = 'y',
+    y = {
       type = 'number',
       default = 'x',
       description = 'The amount to scale on the y axis.'
     },
-    {
-      name = 'z',
+    z = {
       type = 'number',
       default = 'x',
       description = 'The amount to scale on the z axis.'
+    },
+    matrix = {
+      type = 'MatrixType',
+      default = [['model']],
+      description = 'The matrix to modify.'
     }
   },
   returns = {},
+  variants = {
+    {
+      arguments = { 'x', 'y', 'z' },
+      returns = {}
+    },
+    {
+      arguments = { 'matrix', 'x', 'y', 'z' },
+      returns = {}
+    }
+  },
   notes = 'Order matters when scaling, translating, and rotating the coordinate system.',
   related = {
     'lovr.graphics.rotate',

+ 0 - 8
api/lovr/graphics/setColor.lua

@@ -27,10 +27,6 @@ return {
     color = {
       type = 'table',
       description = 'A table containing the color components.'
-    },
-    hex = {
-      type = 'number',
-      description = 'An integer containing all four color components.'
     }
   },
   returns = {},
@@ -42,10 +38,6 @@ return {
     {
       arguments = { 'color' },
       returns = {}
-    },
-    {
-      arguments = { 'hex' },
-      returns = {}
     }
   },
   notes = 'The default color is white.',

+ 0 - 33
api/lovr/graphics/setColorMask.lua

@@ -1,33 +0,0 @@
-return {
-  tag = 'graphicsState',
-  summary = 'Set the color mask.',
-  description = 'Sets which color channels are affected by drawing operations.',
-  arguments = {
-    {
-      name = 'r',
-      type = 'boolean',
-      default = 'true',
-      description = 'Whether or not the red channel should be enabled.'
-    },
-    {
-      name = 'g',
-      type = 'boolean',
-      default = 'true',
-      description = 'Whether or not the green channel should be enabled.'
-    },
-    {
-      name = 'b',
-      type = 'boolean',
-      default = 'true',
-      description = 'Whether or not the blue channel should be enabled.'
-    },
-    {
-      name = 'a',
-      type = 'boolean',
-      default = 'true',
-      description = 'Whether or not the alpha channel should be enabled.'
-    }
-  },
-  returns = {},
-  notes = 'Initially, all color channels are enabled.'
-}

+ 28 - 0
api/lovr/graphics/setDefaultFilter.lua

@@ -0,0 +1,28 @@
+return {
+  tag = 'graphicsState',
+  summary = 'Set the default filter mode for Textures.',
+  description = [[
+    Sets the default filter mode for new Textures.  This controls how textures are sampled when they
+    are minified, magnified, or stretched.
+  ]],
+  arguments = {
+    {
+      name = 'mode',
+      type = 'FilterMode',
+      description = 'The filter mode.'
+    },
+    {
+      name = 'anisotropy',
+      type = 'number',
+      description = [[
+        If the filtering mode is "anisotropic", returns the level of anisotropy.  Otherwise, this
+        will be nil.
+      ]]
+    }
+  },
+  returns = {},
+  related = {
+    'Texture:getFilter',
+    'Texture:setFilter'
+  }
+}

+ 1 - 1
api/lovr/graphics/setDepthTest.lua

@@ -23,5 +23,5 @@ return {
       returns = {}
     }
   },
-  notes = 'The default depth test is `less`.'
+  notes = 'The default depth test is `lequal`.'
 }

+ 2 - 1
api/lovr/graphics/setFont.lua

@@ -6,7 +6,8 @@ return {
     {
       name = 'font',
       type = 'Font',
-      description = 'The font to use.'
+      default = 'nil',
+      description = 'The font to use.  If nil, the default font is set.'
     }
   },
   returns = {},

+ 0 - 31
api/lovr/graphics/setScissor.lua

@@ -1,31 +0,0 @@
-return {
-  tag = 'graphicsState',
-  summary = 'Set the scissor rectangle.',
-  description = [[
-    Sets the scissor region.  The scissor is a rectangular area of the screen.  Any pixels outside
-    the scissor region will be unaffected by drawing operations.
-  ]],
-  arguments = {
-    {
-      name = 'x',
-      type = 'number',
-      description = 'The x position of the upper left corner of the scissor.'
-    },
-    {
-      name = 'y',
-      type = 'number',
-      description = 'The y position of the upper left corner of the scissor.'
-    },
-    {
-      name = 'w',
-      type = 'number',
-      description = 'The width of the scissor in pixels.'
-    },
-    {
-      name = 'h',
-      type = 'number',
-      description = 'The height of the scissor in pixels.'
-    }
-  },
-  returns = {}
-}

+ 0 - 0
api/lovr/graphics/setPolygonWinding.lua → api/lovr/graphics/setWinding.lua


+ 15 - 0
api/lovr/graphics/transform.lua

@@ -9,6 +9,11 @@ return {
     transformation stack.
   ]],
   arguments = {
+    matrix = {
+      type = 'MatrixType',
+      default = [['model']],
+      description = 'The matrix to modify.'
+    },
     transform = {
       type = 'Transform',
       description = 'The Transform to apply to the coordinate system.'
@@ -74,6 +79,16 @@ return {
       description = 'Modify the coordinate system using a Transform object.',
       arguments = { 'transform' },
       returns = {}
+    },
+    {
+      description = 'Modify the model or view matrix.',
+      arguments = { 'matrix', 'x', 'y', 'z', 'sx', 'sy', 'sz', 'angle', 'ax', 'ay', 'az' },
+      returns = {}
+    },
+    {
+      description = 'Modify the model or view matrix using a Transform object.',
+      arguments = { 'matrix', 'transform' },
+      returns = {}
     }
   },
   related = {

+ 18 - 6
api/lovr/graphics/translate.lua

@@ -9,23 +9,35 @@ return {
     transformation stack.
   ]],
   arguments = {
-    {
-      name = 'x',
+    x = {
       type = 'number',
       description = 'The amount to translate on the x axis.'
     },
-    {
-      name = 'y',
+    y = {
       type = 'number',
       description = 'The amount to translate on the y axis.'
     },
-    {
-      name = 'z',
+    z = {
       type = 'number',
       description = 'The amount to translate on the z axis.'
+    },
+    matrix = {
+      type = 'MatrixType',
+      default = [['model']],
+      description = 'The matrix to modify.'
     }
   },
   returns = {},
+  variants = {
+    {
+      arguments = { 'x', 'y', 'z' },
+      returns = {}
+    },
+    {
+      arguments = { 'matrix', 'x', 'y', 'z' },
+      returns = {}
+    }
+  },
   notes = 'Order matters when scaling, translating, and rotating the coordinate system.',
   related = {
     'lovr.graphics.rotate',

+ 15 - 0
api/lovr/headset/Controller/getHand.lua

@@ -0,0 +1,15 @@
+return {
+  summary = 'Get the hand the Controller is in.',
+  description = 'Returns which hand the controller is in, if known.',
+  arguments = {},
+  returns = {
+    {
+      name = 'hand',
+      type = 'ControllerHand',
+      description = 'The hand the controller is in.'
+    }
+  },
+  related = {
+    'ControllerHand'
+  }
+}

+ 2 - 1
api/lovr/headset/Controller/isDown.lua

@@ -1,6 +1,6 @@
 return {
   summary = 'Get the state of a button on the Controller.',
-  description = 'Check if a button on the Controller is currently pressed.',
+  description = 'Returns the state of a button on the Controller.',
   arguments = {
     {
       name = 'button',
@@ -17,6 +17,7 @@ return {
   },
   related = {
     'ControllerButton',
+    'Controller:isTouched',
     'Controller:getAxis'
   }
 }

+ 26 - 0
api/lovr/headset/Controller/isTouched.lua

@@ -0,0 +1,26 @@
+return {
+  summary = 'Check whether a button on the Controller is touched.',
+  description = [[
+    Returns whether or not a given button on the Controller is currently touched.  This may not mean
+    the button is actually pressed since some controllers are touch-sensitive.
+  ]],
+  arguments = {
+    {
+      name = 'button',
+      type = 'ControllerButton',
+      description = 'The button to query.'
+    }
+  },
+  returns = {
+    {
+      name = 'touched',
+      type = 'boolean',
+      description = 'Whether the button is touched.'
+    }
+  },
+  related = {
+    'ControllerButton',
+    'Controller:isDown',
+    'Controller:getAxis'
+  }
+}

+ 4 - 0
api/lovr/headset/ControllerAxis.lua

@@ -1,6 +1,10 @@
 return {
   description = 'Axes on a Controller.',
   values = {
+    {
+      name = 'grip',
+      description = 'The grip.'
+    },
     {
       name = 'trigger',
       description = 'The trigger.'

+ 24 - 0
api/lovr/headset/ControllerButton.lua

@@ -1,6 +1,10 @@
 return {
   description = 'Buttons on a Controller.',
   values = {
+    {
+      name = 'unknown',
+      description = 'An unknown button.'
+    },
     {
       name = 'system',
       description = 'The system button.'
@@ -9,6 +13,10 @@ return {
       name = 'menu',
       description = 'The menu button.'
     },
+    {
+      name = 'trigger',
+      description = 'The trigger button.'
+    },
     {
       name = 'grip',
       description = 'The grip button.'
@@ -16,6 +24,22 @@ return {
     {
       name = 'touchpad',
       description = 'The button on the touchpad.'
+    },
+    {
+      name = 'a',
+      description = 'The A button present on Oculus Touch controllers.'
+    },
+    {
+      name = 'b',
+      description = 'The B button present on Oculus Touch controllers.'
+    },
+    {
+      name = 'x',
+      description = 'The X button present on Oculus Touch controllers.'
+    },
+    {
+      name = 'y',
+      description = 'The Y button present on Oculus Touch controllers.'
     }
   }
 }

+ 17 - 0
api/lovr/headset/ControllerHand.lua

@@ -0,0 +1,17 @@
+return {
+  description = 'Represents which hand a Controller is thought to be held in.',
+  values = {
+    {
+      name = 'left',
+      description = 'The left hand.'
+    },
+    {
+      name = 'right',
+      description = 'The right hand.'
+    },
+    {
+      name = 'unknown',
+      description = 'Can not be determined.'
+    }
+  }
+}

+ 19 - 0
api/lovr/headset/HeadsetOrigin.lua

@@ -0,0 +1,19 @@
+return {
+  summary = 'Different types of coordinate space origins.',
+  description = [[
+    Represents the different types of origins for coordinate spaces.  An origin of "floor" is common
+    for headsets that support roomscale tracking, and means that the origin is on the floor in the
+    center of the play area.  An origin of "head" is common for devices that do not support
+    positional tracking and means the origin is relative to the position of the head.
+  ]],
+  values = {
+    {
+      name = 'head',
+      description = 'The origin is near the head.'
+    },
+    {
+      name = 'floor',
+      description = 'The origin is on the floor.'
+    }
+  }
+}

+ 18 - 0
api/lovr/headset/HeadsetType.lua

@@ -0,0 +1,18 @@
+return {
+  summary = 'Types of VR headsets.',
+  description = 'Types of headsets LÖVR recognizes.',
+  values = {
+    {
+      name = 'vive',
+      description = 'HTC Vive.'
+    },
+    {
+      name = 'rift',
+      description = 'Oculus Rift.'
+    },
+    {
+      name = 'unknown',
+      description = 'An unknown headset.'
+    }
+  }
+}

+ 19 - 0
api/lovr/headset/getOriginType.lua

@@ -0,0 +1,19 @@
+return {
+  tag = 'headset',
+  summary = 'Get the type of tracking origin of the headset.',
+  description = [[
+    Returns the type of origin used for the tracking volume.  The different types of origins are
+    explained on the `HeadsetOrigin` page.
+  ]],
+  arguments = {},
+  returns = {
+    {
+      name = 'origin',
+      type = 'HeadsetOrigin',
+      description = 'The type of origin.'
+    }
+  },
+  related = {
+    'HeadsetOrigin'
+  }
+}

+ 24 - 0
api/lovr/math/RandomGenerator/getSeed.lua

@@ -0,0 +1,24 @@
+return {
+  summary = 'Get the seed value of the RandomGenerator.',
+  description = 'Returns the seed used to initialize the RandomGenerator.',
+  arguments = {},
+  returns = {
+    {
+      name = 'low',
+      type = 'number',
+      description = 'The lower 32 bits of the seed.'
+    },
+    {
+      name = 'high',
+      type = 'number',
+      description = 'The upper 32 bits of the seed.'
+    }
+  },
+  notes = [[
+    Since the seed is a 64 bit integer, each 32 bits of the seed are returned separately to avoid
+    precision issues.
+  ]],
+  related = {
+    'lovr.math.newRandomGenerator'
+  }
+}

+ 19 - 0
api/lovr/math/RandomGenerator/getState.lua

@@ -0,0 +1,19 @@
+return {
+  summary = 'Get the current state of the RandomGenerator.',
+  description = [[
+    Returns the current state of the RandomGenerator.  This can be used with
+    `RandomGenerator:setState` to reliably restore a previous state of the generator.
+  ]],
+  arguments = {},
+  returns = {
+    {
+      name = 'state',
+      type = 'string',
+      description = 'The serialized state.'
+    }
+  },
+  notes = [[
+    The seed represents the starting state of the RandomGenerator, whereas the state represents the
+    current state of the generator.
+  ]]
+}

+ 9 - 0
api/lovr/math/RandomGenerator/init.lua

@@ -0,0 +1,9 @@
+return {
+  summary = 'A pseudo-random number generator.',
+  description = [[
+    A RandomGenerator is a standalone object that can be used to independently generate
+    pseudo-random numbers. If you just need basic randomness, you can use `lovr.math.random` without
+    needing to create a random generator.
+  ]],
+  constructor = 'lovr.math.newRandomGenerator'
+}

+ 43 - 0
api/lovr/math/RandomGenerator/random.lua

@@ -0,0 +1,43 @@
+return {
+  summary = 'Get a random number.',
+  description = [[
+    Returns the next uniformly distributed pseudo-random number from the RandomGenerator's sequence.
+  ]],
+  arguments = {
+    low = {
+      type = 'number',
+      description = 'The minimum number to generate.'
+    },
+    high = {
+      type = 'number',
+      description = 'The maximum number to generate.'
+    }
+  },
+  returns = {
+    x = {
+      type = 'number',
+      description = 'A pseudo-random number.'
+    }
+  },
+  variants = {
+    {
+      description = 'Generate a pseudo-random floating point number in the range `[0,1)`',
+      arguments = {},
+      returns = { 'x' }
+    },
+    {
+      description = 'Generate a pseudo-random integer in the range `[1,high]`',
+      arguments = { 'high' },
+      returns = { 'x' }
+    },
+    {
+      description = 'Generate a pseudo-random integer in the range `[low,high]`',
+      arguments = { 'low', 'high' },
+      returns = { 'x' }
+    }
+  },
+  related = {
+    'lovr.math.random',
+    'RandomGenerator:randomNormal'
+  }
+}

+ 35 - 0
api/lovr/math/RandomGenerator/randomNormal.lua

@@ -0,0 +1,35 @@
+return {
+  summary = 'Get a random number from a normal distribution.',
+  description = [[
+    Returns a pseudo-random number from a normal distribution (a bell curve).  You can control the
+    center of the bell curve (the mean value) and the overall width (sigma, or standard deviation).
+  ]],
+  arguments = {
+    {
+      name = 'sigma',
+      type = 'number',
+      default = '1',
+      description = [[
+        The standard deviation of the distribution.  This can be thought of how "wide" the range of
+        numbers is or how much variability there is.
+      ]]
+    },
+    {
+      name = 'mu',
+      type = 'number',
+      default = '0',
+      description = 'The average value returned.'
+    }
+  },
+  returns = {
+    {
+      name = 'x',
+      type = 'number',
+      description = 'A normally distributed pseudo-random number.'
+    }
+  },
+  related = {
+    'lovr.math.randomNormal',
+    'RandomGenerator:random'
+  }
+}

+ 26 - 0
api/lovr/math/RandomGenerator/setSeed.lua

@@ -0,0 +1,26 @@
+return {
+  summary = 'Reinitialize the RandomGenerator with a new seed.',
+  description = [[
+    Seed the RandomGenerator with a new seed.  Each seed will cause the RandomGenerator to produce a
+    unique sequence of random numbers.
+  ]],
+  arguments = {
+    seed = {
+      type = 'number',
+      description = 'The random seed.'
+    },
+    low = {
+      type = 'number',
+      description = 'The lower 32 bits of the seed.'
+    },
+    high = {
+      type = 'number',
+      description = 'The upper 32 bits of the seed.'
+    }
+  },
+  returns = {},
+  notes = [[
+    For precise 64 bit seeds, you should specify the lower and upper 32 bits of the seed separately.
+    Otherwise, seeds larger than 2^53 will start to lose precision.
+  ]]
+}

+ 19 - 0
api/lovr/math/RandomGenerator/setState.lua

@@ -0,0 +1,19 @@
+return {
+  summary = 'Set the state of the RandomGenerator.',
+  description = [[
+    Sets the state of the RandomGenerator, as previously obtained using `RandomGenerator:getState`.
+    This can be used to reliably restore a previous state of the generator.
+  ]],
+  arguments = {
+    {
+      name = 'state',
+      type = 'string',
+      description = 'The serialized state.'
+    }
+  },
+  returns = {},
+  notes = [[
+    The seed represents the starting state of the RandomGenerator, whereas the state represents the
+    current state of the generator.
+  ]]
+}

+ 12 - 0
api/lovr/math/getRandomSeed.lua

@@ -0,0 +1,12 @@
+return {
+  summary = 'Get the random seed.',
+  description = 'Get the seed used to initialize the random generator.',
+  arguments = {},
+  returns = {
+    {
+      name = 'seed',
+      type = 'number',
+      description = 'The new seed.'
+    }
+  }
+}

+ 1 - 4
api/lovr/math/init.lua

@@ -1,8 +1,5 @@
 return {
   tag = 'modules',
   summary = 'Contains useful math helpers.',
-  description = [[
-    The `lovr.math` module provides math helpers commonly used for 3D applications.  Currently, only
-    `Transform` objects are exposed.
-  ]]
+  description = 'The `lovr.math` module provides math helpers commonly used for 3D applications.'
 }

+ 96 - 0
api/lovr/math/lookAt.lua

@@ -0,0 +1,96 @@
+return {
+  summary = 'Compute an angle/axis rotation from a vector.',
+  description = 'Returns the angle/axis orientation to use to get an object to look at a point.',
+  arguments = {
+    {
+      name = 'x',
+      type = 'number',
+      description = 'The x position of the object.'
+    },
+    {
+      name = 'y',
+      type = 'number',
+      description = 'The y position of the object.'
+    },
+    {
+      name = 'z',
+      type = 'number',
+      description = 'The z position of the object.'
+    },
+    {
+      name = 'tx',
+      type = 'number',
+      description = 'The x position of the target to look at.'
+    },
+    {
+      name = 'ty',
+      type = 'number',
+      description = 'The y position of the target to look at.'
+    },
+    {
+      name = 'tz',
+      type = 'number',
+      description = 'The z position of the target to look at.'
+    },
+    {
+      name = 'ux',
+      type = 'number',
+      default = '0',
+      description = 'The x component of the global up vector.'
+    },
+    {
+      name = 'uy',
+      type = 'number',
+      default = '1',
+      description = 'The y component of the global up vector.'
+    },
+    {
+      name = 'uz',
+      type = 'number',
+      default = '0',
+      description = 'The z component of the global up vector.'
+    }
+  },
+  returns = {
+    {
+      name = 'angle',
+      type = 'number',
+      description = 'The number of radians to rotate around the axis of rotation.'
+    },
+    {
+      name = 'ax',
+      type = 'number',
+      description = 'The x component of the axis of rotation.'
+    },
+    {
+      name = 'ay',
+      type = 'number',
+      description = 'The y component of the axis of rotation.'
+    },
+    {
+      name = 'az',
+      type = 'number',
+      description = 'The z component of the axis of rotation.'
+    }
+  },
+  notes = [[
+    With the default up vector, strange things will happen when trying to look in directions that
+    line up with the up vector, similar to how the cameras in a first person shooter work. Sometimes
+    this is alright, but other times you may want to keep track of the up vector of the camera as it
+    rotates and pass that in as the up vector to avoid issues.
+  ]],
+  example = {
+    description = 'Rotate a texture so it always faces the headset.',
+    code = [[
+      function lovr.load()
+        eye = lovr.graphics.newTexture('texture.png')
+      end
+
+      function lovr.draw()
+        local x, y, z = 0, 2, -2
+        local angle, ax, ay, az = lovr.math.lookAt(x, y, z, lovr.headset.getPosition())
+        lovr.graphics.plane(eye, x, y, z, 1, angle, ax, ay, az)
+      end
+    ]]
+  }
+}

+ 48 - 0
api/lovr/math/newRandomGenerator.lua

@@ -0,0 +1,48 @@
+return {
+  summary = 'Create a new RandomGenerator.',
+  description = [[
+    Creates a new `RandomGenerator`, which can be used to generate random numbers. If you just want
+    some random numbers, you can use `lovr.math.random`. Individual RandomGenerator objects are
+    useful if you need more control over the random sequence used or need a random generator
+    isolated from other instances.
+  ]],
+  arguments = {
+    seed = {
+      type = 'number',
+      description = 'The initial seed for the RandomGenerator.'
+    },
+    low = {
+      type = 'number',
+      description = 'The lower 32 bits of the seed.'
+    },
+    high = {
+      type = 'number',
+      description = 'The upper 32 bits of the seed.'
+    }
+  },
+  returns = {
+    randomGenerator = {
+      type = 'RandomGenerator',
+      description = 'The new RandomGenerator.'
+    }
+  },
+  variants = {
+    {
+      description = 'Create a RandomGenerator with a default seed.',
+      arguments = {},
+      returns = { 'randomGenerator' }
+    },
+    {
+      arguments = { 'seed' },
+      returns = { 'randomGenerator' }
+    },
+    {
+      description = [[
+        This variant allows creation of random generators with precise 64-bit seed values, since
+        Lua's number format loses precision with really big numbers.
+      ]],
+      arguments = { 'low', 'high' },
+      returns = { 'randomGenerator' }
+    }
+  }
+}

+ 46 - 0
api/lovr/math/random.lua

@@ -0,0 +1,46 @@
+return {
+  summary = 'Get a random number.',
+  description = [[
+    Returns a uniformly distributed pseudo-random number.  This function has improved randomness
+    over Lua's `math.random` and also guarantees that the sequence of random numbers will be the
+    same on all platforms (given the same seed).
+  ]],
+  arguments = {
+    low = {
+      type = 'number',
+      description = 'The minimum number to generate.'
+    },
+    high = {
+      type = 'number',
+      description = 'The maximum number to generate.'
+    }
+  },
+  returns = {
+    x = {
+      type = 'number',
+      description = 'A pseudo-random number.'
+    }
+  },
+  variants = {
+    {
+      description = 'Generate a pseudo-random floating point number in the range `[0,1)`',
+      arguments = {},
+      returns = { 'x' }
+    },
+    {
+      description = 'Generate a pseudo-random integer in the range `[1,high]`',
+      arguments = { 'high' },
+      returns = { 'x' }
+    },
+    {
+      description = 'Generate a pseudo-random integer in the range `[low,high]`',
+      arguments = { 'low', 'high' },
+      returns = { 'x' }
+    }
+  },
+  notes = 'You can set the random seed using `lovr.math.setRandomSeed`.',
+  related = {
+    'lovr.math.randomNormal',
+    'RandomGenerator'
+  }
+}

+ 35 - 0
api/lovr/math/randomNormal.lua

@@ -0,0 +1,35 @@
+return {
+  summary = 'Get a random number from a normal distribution.',
+  description = [[
+    Returns a pseudo-random number from a normal distribution (a bell curve).  You can control the
+    center of the bell curve (the mean value) and the overall width (sigma, or standard deviation).
+  ]],
+  arguments = {
+    {
+      name = 'sigma',
+      type = 'number',
+      default = '1',
+      description = [[
+        The standard deviation of the distribution.  This can be thought of how "wide" the range of
+        numbers is or how much variability there is.
+      ]]
+    },
+    {
+      name = 'mu',
+      type = 'number',
+      default = '0',
+      description = 'The average value returned.'
+    }
+  },
+  returns = {
+    {
+      name = 'x',
+      type = 'number',
+      description = 'A normally distributed pseudo-random number.'
+    }
+  },
+  related = {
+    'lovr.math.random',
+    'RandomGenerator'
+  }
+}

+ 16 - 0
api/lovr/math/setRandomSeed.lua

@@ -0,0 +1,16 @@
+return {
+  summary = 'Set the random seed.',
+  description = [[
+    Seed the random generator with a new seed.  Each seed will cause `lovr.math.random` and
+    `lovr.math.randomNormal` to produce a unique sequence of random numbers.  This is done once
+    automatically at startup by `lovr.run`.
+  ]],
+  arguments = {
+    {
+      name = 'seed',
+      type = 'number',
+      description = 'The new seed.'
+    }
+  },
+  returns = {}
+}

BIN
examples/Billboard/eye.png


+ 12 - 0
examples/Billboard/main.lua

@@ -0,0 +1,12 @@
+function lovr.load()
+  eye = lovr.graphics.newTexture('eye.png')
+end
+
+function lovr.draw()
+  local t = lovr.timer.getTime() * .5 + math.pi
+  local x = math.cos(t) * 2
+  local y = 2
+  local z = math.sin(t) * 2
+  local angle, ax, ay, az = lovr.math.lookAt(x, y, z, lovr.headset.getPosition())
+  lovr.graphics.plane(eye, x, y, z, 1, angle, ax, ay, az)
+end

+ 0 - 8
examples/Cube/main.lua

@@ -1,8 +0,0 @@
--- Draws a spinning cube
-
-function lovr.draw()
-  local x, y, z = 0, 0, -1
-  local size = .5
-  local angle = lovr.timer.getTime()
-  lovr.graphics.cube('line', x, y, z, size, angle)
-end

+ 3 - 0
examples/Lighting/conf.lua

@@ -0,0 +1,3 @@
+function lovr.conf(t)
+  t.window.msaa = 4
+end

+ 49 - 0
examples/Lighting/main.lua

@@ -0,0 +1,49 @@
+function lovr.load()
+  shader = lovr.graphics.newShader([[
+    out vec3 lightDirection;
+    out vec3 normalDirection;
+
+    vec3 lightPosition = vec3(0, 3, 3);
+
+    vec4 position(mat4 projection, mat4 transform, vec4 vertex) {
+      vec4 vVertex = transform * vec4(lovrPosition, 1.);
+      vec4 vLight = lovrView * vec4(lightPosition, 1.);
+
+      lightDirection = normalize(vec3(vLight - vVertex));
+      normalDirection = normalize(lovrNormalMatrix * lovrNormal);
+
+      return projection * transform * vertex;
+    }
+  ]], [[
+    in vec3 lightDirection;
+    in vec3 normalDirection;
+
+    vec3 cAmbient = vec3(.25);
+    vec3 cDiffuse = vec3(.75);
+    vec3 cSpecular = vec3(.35);
+
+    vec4 color(vec4 graphicsColor, sampler2D image, vec2 uv) {
+      float diffuse = max(dot(normalDirection, lightDirection), 0.);
+      float specular = 0.;
+
+      if (diffuse > 0.) {
+        vec3 r = reflect(lightDirection, normalDirection);
+        vec3 viewDirection = normalize(-vec3(gl_FragCoord));
+
+        float specularAngle = max(dot(r, viewDirection), 0.);
+        specular = pow(specularAngle, 5.);
+      }
+
+      vec3 cFinal = vec3(diffuse) * cDiffuse + vec3(specular) * cSpecular;
+      cFinal = clamp(cFinal, cAmbient, vec3(1.));
+      cFinal = pow(cFinal, vec3(.4545));
+      return vec4(cFinal, 1.) * graphicsColor * texture(image, uv);
+    }
+  ]])
+
+  lovr.graphics.setShader(shader)
+end
+
+function lovr.draw()
+  lovr.graphics.cube('fill', 0, 1.7, -1, .5, lovr.timer.getTime(), 0, 1, 0)
+end

+ 0 - 0
examples/360_Image/equirectangular.jpg → examples/Panorama/equirectangular.jpg


+ 0 - 0
examples/360_Image/main.lua → examples/Panorama/main.lua


+ 54 - 0
examples/Physics/main.lua

@@ -0,0 +1,54 @@
+shader = require 'shader'
+
+function lovr.load()
+  world = lovr.physics.newWorld()
+
+  -- Create the ground
+  world:newBoxCollider(0, 0, 0, 10, .01, 10):setKinematic(true)
+
+  -- Create boxes!
+  boxes = {}
+  for x = -1, 1, .25 do
+    for y = .125, 2, .25 do
+      local box = world:newBoxCollider(x, y, -2 - y / 10, .25)
+      table.insert(boxes, box)
+    end
+  end
+
+  controllerBoxes = {}
+end
+
+function lovr.update(dt)
+  for i, controller in ipairs(lovr.headset.getControllers()) do
+    if not controllerBoxes[i] then
+      controllerBoxes[i] = world:newBoxCollider(0, 0, 0, .25)
+      controllerBoxes[i]:setKinematic(true)
+    end
+    controllerBoxes[i]:setPosition(controller:getPosition())
+    controllerBoxes[i]:setOrientation(controller:getOrientation())
+  end
+
+  -- Update the physics simulation
+  world:update(dt)
+end
+
+-- A helper function for drawing boxes
+function drawBox(box)
+  local x, y, z = box:getPosition()
+  lovr.graphics.cube('fill', x, y, z, .25, box:getOrientation())
+end
+
+function lovr.draw()
+  lovr.graphics.setBackgroundColor(200, 200, 200)
+  lovr.graphics.setShader(shader)
+
+  lovr.graphics.setColor(255, 0, 0)
+  for i, box in ipairs(boxes) do
+    drawBox(box)
+  end
+
+  lovr.graphics.setColor(0, 0, 255)
+  for i, box in ipairs(controllerBoxes) do
+    drawBox(box)
+  end
+end

+ 39 - 0
examples/Physics/shader.lua

@@ -0,0 +1,39 @@
+return lovr.graphics.newShader([[
+out vec3 lightDirection;
+out vec3 normalDirection;
+
+vec3 lightPosition = vec3(0, 10, 3);
+
+vec4 position(mat4 projection, mat4 transform, vec4 vertex) {
+  vec4 vVertex = transform * vec4(lovrPosition, 1.);
+  vec4 vLight = lovrView * vec4(lightPosition, 1.);
+
+  lightDirection = normalize(vec3(vLight - vVertex));
+  normalDirection = normalize(lovrNormalMatrix * lovrNormal);
+
+  return projection * transform * vertex;
+}
+]], [[
+in vec3 lightDirection;
+in vec3 normalDirection;
+
+vec3 cAmbient = vec3(.25);
+vec3 cDiffuse = vec3(.75);
+vec3 cSpecular = vec3(.35);
+
+vec4 color(vec4 graphicsColor, sampler2D image, vec2 uv) {
+  float diffuse = max(dot(normalDirection, lightDirection), 0.);
+  float specular = 0.;
+
+  if (diffuse > 0.) {
+    vec3 r = reflect(lightDirection, normalDirection);
+    vec3 viewDirection = normalize(-vec3(gl_FragCoord));
+
+    float specularAngle = max(dot(r, viewDirection), 0.);
+    specular = pow(specularAngle, 5.);
+  }
+
+  vec3 cFinal = pow(clamp(vec3(diffuse) * cDiffuse + vec3(specular) * cSpecular, cAmbient, vec3(1.)), vec3(.4545));
+  return vec4(cFinal, 1.) * graphicsColor * texture(image, uv);
+}
+]])

+ 78 - 0
examples/Primitives/main.lua

@@ -0,0 +1,78 @@
+shader = require 'shader'
+
+local function drawLabel(str, x, y, z)
+  lovr.graphics.setColor(255, 255, 255)
+  lovr.graphics.print(str, x, y, z, .1)
+end
+
+function lovr.draw()
+  lovr.graphics.setBackgroundColor(30, 30, 30)
+  lovr.graphics.setShader(shader)
+
+  local hx, hy, hz = lovr.headset.getPosition()
+  local x, y, z
+
+  -- Point
+  x, y, z = -.6, 1.1, -1
+  lovr.graphics.setPointSize(5)
+  lovr.graphics.setColor(255, 255, 255)
+  lovr.graphics.points(x, y, z)
+
+  -- Line
+  x, y, z = 0, 1.1, -1
+  local points = {
+    x - .1, y, z,
+    x + .1, y, z
+  }
+  lovr.graphics.setColor(255, 255, 255)
+  lovr.graphics.line(points)
+
+  -- Triangle
+  local x, y, z = .6, 1.1, -1
+  local p1 = { x, y + .2, z }
+  local p2 = { x - .2, y - .2, z }
+  local p3 = { x + .2, y - .2, z }
+  lovr.graphics.setColor(92, 107, 192)
+  lovr.graphics.triangle('fill', p1[1], p1[2], p1[3], p2[1], p2[2], p2[3], p3[1], p3[2], p3[3])
+
+  -- Plane
+  local x, y, z = -.6, 1.7, -1.5
+  lovr.graphics.setColor(239, 83, 80)
+  lovr.graphics.plane('fill', x, y, z, .4, lovr.timer.getTime())
+
+  -- Cube
+  local x, y, z = 0, 1.7, -1.5
+  lovr.graphics.setColor(126, 87, 194)
+  lovr.graphics.cube('fill', x, y, z, .3, lovr.timer.getTime())
+
+  -- Box
+  local x, y, z = .6, 1.7, -1.5
+  lovr.graphics.setColor(255, 167, 45)
+  lovr.graphics.box('fill', x, y, z, .4, .2, .3, lovr.timer.getTime())
+
+  -- Cylinder
+  local x, y, z = -.6, 2.4, -2
+  lovr.graphics.setColor(102, 187, 106)
+  lovr.graphics.cylinder(x - .2, y, z, x + .2, y, z, .1, .1)
+
+  -- Cone
+  local x, y, z = 0, 2.4, -2
+  lovr.graphics.setColor(255, 241, 118)
+  lovr.graphics.cylinder(x, y + .2, z, x, y - .2, z, 0, .18)
+
+  -- Sphere
+  local x, y, z = .6, 2.4, -2
+  lovr.graphics.setColor(77, 208, 255)
+  lovr.graphics.sphere(x, y, z, .2)
+
+  lovr.graphics.setShader()
+  drawLabel('Point', -.6, 1.4, -1)
+  drawLabel('Line', 0, 1.4, -1)
+  drawLabel('Triangle', .6, 1.4, -1)
+  drawLabel('Plane', -.6, 2.0, -1.5)
+  drawLabel('Cube', 0, 2.0, -1.5)
+  drawLabel('Box', .6, 2.0, -1.5)
+  drawLabel('Cylinder', -.6, 2.7, -2)
+  drawLabel('Cone', 0, 2.7, -2)
+  drawLabel('Sphere', .6, 2.7, -2)
+end

+ 39 - 0
examples/Primitives/shader.lua

@@ -0,0 +1,39 @@
+return lovr.graphics.newShader([[
+out vec3 lightDirection;
+out vec3 normalDirection;
+
+vec3 lightPosition = vec3(0, 10, 3);
+
+vec4 position(mat4 projection, mat4 transform, vec4 vertex) {
+  vec4 vVertex = transform * vec4(lovrPosition, 1.);
+  vec4 vLight = lovrView * vec4(lightPosition, 1.);
+
+  lightDirection = normalize(vec3(vLight - vVertex));
+  normalDirection = normalize(lovrNormalMatrix * lovrNormal);
+
+  return projection * transform * vertex;
+}
+]], [[
+in vec3 lightDirection;
+in vec3 normalDirection;
+
+vec3 cAmbient = vec3(.25);
+vec3 cDiffuse = vec3(1);
+vec3 cSpecular = vec3(.35);
+
+vec4 color(vec4 graphicsColor, sampler2D image, vec2 uv) {
+  float diffuse = max(dot(normalDirection, lightDirection), 0.);
+  float specular = 0.;
+
+  if (diffuse > 0.) {
+    vec3 r = reflect(lightDirection, normalDirection);
+    vec3 viewDirection = normalize(-vec3(gl_FragCoord));
+
+    float specularAngle = max(dot(r, viewDirection), 0.);
+    specular = pow(specularAngle, 5.);
+  }
+
+  vec3 cFinal = pow(clamp(vec3(diffuse) * cDiffuse + vec3(specular) * cSpecular, cAmbient, vec3(1.)), vec3(.4545));
+  return vec4(cFinal, 1.) * graphicsColor * texture(image, uv);
+}
+]])

+ 5 - 2
examples/init.lua

@@ -1,4 +1,7 @@
 return {
-  'Cube',
-  '360_Image'
+  'Primitives',
+  'Panorama',
+  'Lighting',
+  'Physics',
+  'Billboard'
 }

+ 0 - 74
guides/3D_Models.md

@@ -1,74 +0,0 @@
-3D Models
-===
-
-In the previous guide we learned how to draw some simple shapes.  Now we're going to learn how to
-draw 3D models exported from tools like Blender, Maya, or 3DS Max.   LÖVR supports lots of different
-3D file formats, including `.obj`, `.fbx`, and `.dae`.  We can also apply textures to our models.
-Texture files can be `.png` or `.jpg`.  Once you have your model files, put them in the folder for
-your project.
-
-Create the Model
----
-
-To load a model into LÖVR, we can use the `lovr.graphics.newModel` function.  It takes a string with
-the name of the model file and returns an object representing the model.
-
-```
-model = lovr.graphics.newModel('assets/duck.dae')
-```
-
-Create the Texture
----
-
-Creating textures is really similar to creating models:
-
-```
-texture = lovr.graphics.newTexture('assets/duck.png')
-```
-
-We can apply a texture to a model using `Model:setTexture`.  The colon syntax in Lua is used when
-you're calling a function on an object.
-
-```
-model:setTexture(texture)
-```
-
-It is also possible to create and set the texture while creating the model, using the second
-parameter to `lovr.graphics.newModel`:
-
-```
-model = lovr.graphics.newModel('assets/duck.dae', 'assets/duck.png')
-```
-
-Rendering the Model
----
-
-To draw a model, call `:draw` on it.  It takes a position (x, y, z), a scale, and a rotation
-(angle/axis).
-
-```
-model:draw(x, y, z, size, angle, axisX, axisY, axisZ)
-```
-
-You may need to adjust the size/position of the model, depending on how it was exported.
-
-Putting it all together
----
-
-Here's the complete program for loading and rendering a 3D model:
-
-```
-function lovr.load()
-  model = lovr.graphics.newModel('assets/model.obj', 'assets/texture.png')
-end
-
-function lovr.draw()
-  model:draw(0, 0, 0, 1)
-end
-```
-
-The rotation parameters for `model:draw` were left out for simplicity.  The model and texture were
-put in a folder called `assets`.
-
-That's all there is to it!  Next we'll (finally!) talk about our first VR topic:
-<a data-key="Controllers">Controllers</a>.

+ 0 - 110
guides/Callbacks.md

@@ -1,110 +0,0 @@
-Callbacks
-===
-
-To start making things happen with LÖVR it's important to understand **callbacks**.  Callbacks are
-Lua functions you can write that LÖVR will call at certain points in time. The three most important
-callbacks are these ones:
-
-<table>
-  <tr>
-    <td>Callback</td>
-    <td>When is it called</td>
-  </tr>
-
-  <tr>
-    <td class="pre">lovr.load</td>
-    <td>Called once when LÖVR is started</td>
-  </tr>
-
-  <tr>
-    <td class="pre">lovr.update</td>
-    <td>Called over and over again really quickly</td>
-  </tr>
-
-  <tr>
-    <td class="pre">lovr.draw</td>
-    <td>Called over and over again after lovr.update</td>
-  </tr>
-</table>
-
-So the basic "flow" of a LÖVR program in terms of callbacks looks like this:
-
-1. `lovr.load`
-1. `lovr.update`
-1. `lovr.draw`
-1. `lovr.update`
-1. `lovr.draw`
-1. ...
-
-What in the world does this mean?  These callbacks give us a handy way to design our app.
-
-`lovr.load` is a great place to create a bunch of variables, load 3D models and sounds, and perform
-any other setup work.
-
-In `lovr.update` we put the "logic" of our application, so we might move objects around, simulate a
-physics system, or keep track of scores and timers.  Because lovr.update is called over and over
-again, we can update our simulation constantly to respond to user input or move objects smoothly
-over time to create animation.
-
-Finally, lovr.draw is a callback where "rendering" takes place.  In here we look at the stuff we
-did in lovr.update and use `lovr.graphics` functions to tell LÖVR what things should look like in
-the virtual world.  Usually we'll do things like draw 3D models or render visual effects.
-
-Here's a simple example to help explain things better:
-
-```
-function lovr.load()
-  cubeSize = 0
-end
-
-function lovr.update()
-  cubeSize = cubeSize + .0001
-end
-
-function lovr.draw()
-  lovr.graphics.cube('line', 0, 1, 0, size)
-end
-```
-
-This program will draw a cube in the middle of the play area, and make it get bigger and bigger over
-time.  In lovr.load we define a variable to keep track of the size of the cube.  In lovr.update we
-add a small amount to the variable (this is our "application logic").  In lovr.draw we use the
-variable to draw a cube with a certain size.  This is a simple application model that a large number
-of  games and experiences use.
-
-Understanding Delta Time
----
-
-There's one problem with our growing cube example though.  We have to keep in mind that some people
-have fast computers and some people have slow computers.  LÖVR calls lovr.update as fast as it can,
-and so people with a fast computer might have lovr.update get called a lot more often than people
-with slow computers.  The end result is that on a fast computer the cube will grow a lot faster than
-it would on a slow computer.  This isn't what we want because then it makes it difficult to fine
-tune the speed of the growth.
-
-Fortunately, it's easy to fix.  Instead of basing the speed on how often lovr.update is called, we
-can base the speed on real world time.  There is a single function parameter passed to lovr.update
-called `dt`, which stands for **delta time**.  It represents the amount of time that has passed
-since the last call to lovr.update.  Try changing the lovr.update function to this:
-
-```
-function lovr.update(dt)
-  cubeSize = cubeSize + dt * .5
-end
-```
-
-How is this different?  Instead of adding .001 to the cube size every time lovr.update is called,
-we're basing that number on actual time values.  A computer that's only able to call lovr.update 10
-times per second will have a `dt` value of (1 / 10) = .1, and so the cube size will increase by
-(.1 * .5) = .05 per update.  On the other hand, a fast computer that's calling lovr.update **100**
-times per second will have a `dt` value of (1 / 100) = .01, and so the cube size will increase by
-(.01 * .5) = .005 per update.  This means that the cube growth speed scales with the speed of the
-computer, and our cube will always grow by .5 units per second no matter what!  Keep this in mind
-when designing animations or physics and try to always base speed values on `dt`.
-
-Moving On
----
-
-That's about it for the callbacks guide.  If you're curious about what some of the other callbacks
-do, there is a "Callbacks" section on the left.  Otherwise, move on to the next tutorial:
-<a data-key="Simple_Shapes">Simple Shapes</a>.

+ 249 - 0
guides/Callbacks_and_Modules.md

@@ -0,0 +1,249 @@
+Callbacks and Modules
+===
+
+In the previous example we wrote some code in the `lovr.draw` function to draw text on the screen.
+This is an example of a **callback** because we wrote a function and LÖVR "called back" into that
+function later.  Defining a callback lets you specify how your project behaves when a specific event
+occurs.
+
+In the previous example we also used the `lovr.graphics.print` function to render text to the
+screen.  This is an example of using the `lovr.graphics` **module**.  LÖVR has several modules and
+each one contains functions related to a certain area of functionality.  For example, there's the
+`lovr.graphics` module for rendering graphics, the `lovr.audio` module for playing sounds, and the
+`lovr.headset` module for getting information about connected VR hardware.
+
+We can define **callbacks** and call functions from **modules** to make things with LÖVR.
+
+Callbacks
+---
+
+There are various callbacks that can be used for interesting things.  Three of the most used ones
+are `lovr.load`, `lovr.update`, and `lovr.draw`.  A simple project skeleton might look like this:
+
+```
+function lovr.load()
+  -- This is called once on load.
+  --
+  -- You can use it to load assets and set everything up.
+
+  print('loaded!')
+end
+
+function lovr.update(dt)
+  -- This is called continuously and is passed the "delta time" as dt, which
+  -- is the number of seconds elapsed since the last update.
+  --
+  -- You can use it to simulate physics or update game logic.
+
+  print('updating', dt)
+end
+
+function lovr.draw(eye)
+  -- This is called twice every frame (once for each eye).
+  --
+  -- You can use it to render the scene.
+
+  print('rendering the ' .. eye .. ' eye')
+end
+```
+
+By filling in the different callbacks you can start to define the behavior of an app.
+
+To see a list of all the callbacks and read more about their specifics, check out the "Callbacks"
+section on the sidebar to the left.
+
+Modules
+---
+
+You might be wondering what code to write in the different callbacks.  Inside callbacks you'll often
+call into different modules to get LÖVR to do useful things.
+
+A module is just a Lua table that exposes a set of functions you can call.  Each module is
+responsible for a specific area of functionality.  Some modules are relatively low level and, though
+useful, they often aren't used in smaller projects.  The most commonly used modules are:
+
+1. `lovr.graphics`
+2. `lovr.headset`
+3. `lovr.audio`
+4. `lovr.physics`
+
+Each module is described briefly below.
+
+lovr.graphics
+---
+
+The graphics module is the most exciting module, and is also the largest.  Most functions in
+`lovr.graphics` should be used in `lovr.draw`, since that's where rendering happens.
+
+`lovr.graphics` has a set of handy **graphics primitives** for rendering basic shapes and text.
+These can be used to quickly prototype a scene without needing to create or load assets.
+
+There are lots of different rendering-related objects that can be created using `lovr.graphics`,
+such as `Model`, `Texture`, `Font`, `Shader`, `Skybox`, and more.  Every function to create a new
+object is prefixed with `new`, so to create a 3D model object you can use `lovr.graphics.newModel`.
+
+> Note: Creating graphics objects uses memory and can slow things down if done every frame.  For
+> this reason, it's recommended to create objects only once in `lovr.load` before using them!
+
+Another important component of `lovr.graphics` is **graphics state**.  The graphics renderer has a
+number of state variables that can be changed, like the color of rendered objects, the font in use,
+or the coordinate system.  These functions usually have prefixes of `get` or `set`, so to change the
+active color you can use `lovr.graphics.setColor`.  It's important to keep in mind that this state
+is **global**, so changing the color will affect all subsequent drawing operations until it's
+changed again.
+
+Finally, we'll talk about the coordinate system.  LÖVR uses a 3D coordinate system with values
+specified in meters.  Negative z values are in front of the camera, positive y values are above the
+ground, and negative x values are to the left.  By default, the coordinate system maps to the VR
+play area, so the origin is on the ground in the middle of the play space.
+
+You've already seen `lovr.graphics.print`, but here's another example:
+
+```
+function lovr.load()
+  -- Load a model with a texture
+  model = lovr.graphics.newModel('monkey.obj', 'monkey.png')
+end
+
+function lovr.draw()
+  -- Use a dark grey background
+  lovr.graphics.setBackgroundColor(50, 50, 50)
+
+  -- Draw the model
+  lovr.graphics.setColor(255, 255, 255)
+  model:draw(-.5, 1, -3)
+
+  -- Draw a red cube using the "cube" primitive
+  lovr.graphics.setColor(255, 0, 0)
+  lovr.graphics.cube('fill', .5, 1, -3, .5, lovr.timer.getTime())
+end
+```
+
+lovr.headset
+---
+
+The headset module lets you interact with VR hardware.  You can get pose information for the HMD and
+controllers, and also query the input state of controllers to see if buttons are pressed.  You can
+also retrieve information about the configured play area so you know how much available space there
+is to place objects.
+
+Pose information consists of the position and orientation of a tracked device, which is useful
+because it lets you know where the device is and which way it's facing.  To get the position of the
+HMD, you can call `lovr.headset.getPosition` which returns 3 numbers corresponding to an xyz
+position in 3D space.  You can also call `lovr.headset.getOrientation` which returns four numbers
+representing a rotation in angle/axis format.
+
+The `lovr.headset.getControllers` function returns a list of all connected controllers.  You can
+query pose info for controllers using `Controller:getPosition` and `Controller:getOrientation`,
+similar to the HMD.  There are also `Controller:getAxis` and `Controller:isDown` functions for
+determining input state.
+
+Several callbacks are relevant to the headset module.  The `lovr.controlleradded` callback is run
+whenever a new controller is connected, and there is a similar `controllerremoved` callback.  Also,
+there is the `lovr.controllerpressed` callback for when a button on a controller is pressed, with
+a similar callback for `controllerreleased`.
+
+Here's an example that draws a sphere in the "opposite" position of the headset:
+
+```
+function lovr.draw()
+  local x, y, z = lovr.headset.getPosition()
+  lovr.graphics.sphere(-x, y, -z, .1)
+end
+```
+
+lovr.audio
+---
+
+Sound can be played with `lovr.audio`.  Audio is spatialized, so sounds can have positions and
+directions, which are used to make things sound realistic as the headset moves and rotates.
+
+Each instance of a sound is called a `Source`.  To create a sources, use `lovr.audio.newSource` and
+pass it an ogg file.  You can then call `play` on the source to play it.
+
+```
+function lovr.load()
+  ambience = lovr.audio.newSource('background.ogg')
+  ambience:setLooping(true)
+  ambience:play()
+end
+```
+
+See the `Source` page for more information.
+
+lovr.physics
+---
+
+Adding a physics simulation to a scene can make it feel more realistic and immersive.  The
+`lovr.physics` module can be used to set up a physics simulation.
+
+> Note: Physics engines can be tricky to set up.  There are lots of knobs to turn and it may take
+> some tweaking to get things working well.
+
+The first step to creating a simulation is to create a `World` using `lovr.physics.newWorld`.  After
+a world is created you can add `Collider`s to it, using functions like `World:newBoxCollider` or
+`World:newCylinderCollider`.  Each collider represents a single entity in the simulation and can have
+forces applied it.  The world should be updated in `lovr.update` using the `dt` value.
+
+Here's an example that makes a tower of boxes that you can knock down with controllers:
+
+```
+function lovr.load()
+  world = lovr.physics.newWorld()
+
+  -- Create the ground
+  world:newBoxCollider(0, 0, 0, 5, .01, 5):setKinematic(true)
+
+  -- Create boxes!
+  boxes = {}
+  for x = -1, 1, .25 do
+    for y = .125, 2, .25 do
+      local box = world:newBoxCollider(x, y, -1, .25)
+      table.insert(boxes, box)
+    end
+  end
+
+  -- Each controller is going to have a collider attached to it
+  controllerBoxes = {}
+end
+
+function lovr.update(dt)
+  -- Synchronize controllerBoxes with the active controllers
+  for i, controller in ipairs(lovr.headset.getControllers()) do
+    if not controllerBoxes[i] then
+      controllerBoxes[i] = world:newBoxCollider(0, 0, 0, .25)
+      controllerBoxes[i]:setKinematic(true)
+    end
+    controllerBoxes[i]:setPosition(controller:getPosition())
+    controllerBoxes[i]:setOrientation(controller:getOrientation())
+  end
+
+  -- Update the physics simulation
+  world:update(dt)
+end
+
+-- A helper function for drawing boxes
+function drawBox(box)
+  local x, y, z = box:getPosition()
+  lovr.graphics.cube('line', x, y, z, .25, box:getOrientation())
+end
+
+function lovr.draw()
+  lovr.graphics.setColor(255, 0, 0)
+  for i, box in ipairs(boxes) do
+    drawBox(box)
+  end
+
+  lovr.graphics.setColor(0, 0, 255)
+  for i, box in ipairs(controllerBoxes) do
+    drawBox(box)
+  end
+end
+```
+
+Next Steps
+---
+
+To explore a module or callback in more detail, see the reference page for the `lovr` global.
+
+There are also a number of <a data-key="Libraries">Libraries</a> you can use that may come in handy.

+ 0 - 141
guides/Controllers.md

@@ -1,141 +0,0 @@
-Controllers
-===
-
-This guide will teach you how to interact with tracked motion controllers.  Controllers are an
-important source of user input in VR experiences.  We'll learn how to draw models for controllers,
-figure out when a button on the controller is pressed, and even trigger haptic feedback!
-
-Discovering Controllers
----
-
-The VR functionality in LÖVR is all in the `lovr.headset` module.  To get the number of connected
-controllers, use `lovr.headset.getControllerCount`.
-
-```
-function lovr.load()
-  print(lovr.headset.getControllerCount())
-end
-```
-
-There are two callbacks related to Controllers: `lovr.controlleradded` and `lovr.controllerremoved`.
-Using them, you can determine when controllers are connected and disconnected:
-
-```
-function lovr.controlleradded(controller)
-  print('Now there are ' .. lovr.headset.getControllerCount() .. ' controllers.')
-end
-
-function lovr.controllerremoved(controller)
-  print('Now there are ' .. lovr.headset.getControllerCount() .. ' controllers.')
-end
-```
-
-Often, controllers need to be moved around a bit for them to be recognized, so make sure you add
-code in those callbacks to keep your list of controllers up to date.
-
-To get a list of currently connected controllers, use `lovr.headset.getControllers`.  We'll be using
-that more in a bit.
-
-Position and Orientation
----
-
-To retrieve the position of a controller, use `Controller:getPosition`:
-
-```
-controllers = lovr.headset.getControllers()
-for i, controller in ipairs(controllers) do
-  print(controller:getPosition())
-end
-```
-
-The `ipairs` function returns an iterator that can be used in a `for` loop.  This lets you run a
-chunk of code on each item in a list.  Here, we're printing out the position of each controller.
-
-Similarly, you can get the orientation of a controller (in angle-axis representation) using
-`Controller:getOrientation`.  We can combine these two functions to draw cubes at the position of
-the player's hands:
-
-```
-function lovr.draw()
-  controllers = lovr.headset.getControllers()
-  x, y, z = controller:getPosition()
-  angle, ax, ay, az = controller:getOrientation()
-  lovr.graphics.cube('line', x, y, z, .2, angle, ax, ay, az)
-end
-```
-
-Let's replace that cube with a realistic model of the controller.
-
-Controller Models
----
-
-We can create a new Model object for a controller with `Controller:newModel`.  The model will
-automatically have a texture applied.  Drawing the model with the position and orientation of the
-controller object greatly increases the feeling of presence of an application.  Here's an example
-that keeps track of the list of controllers and draws their models:
-
-```
-function refreshControllers()
-  controllers = lovr.headset.getControllers()
-  controllerModels = {}
-  for i, controller in ipairs(controllers) do
-    controllerModels[i] = controller:newModel()
-  end
-end
-
-function lovr.load()
-  refreshControllers()
-end
-
-function lovr.controlleradded()
-  refreshControllers()
-end
-
-function lovr.controllerremoved()
-  refreshControllers()
-end
-
-function lovr.draw()
-  for i, controller in ipairs(controllers) do
-    x, y, z = controller:getPosition()
-    angle, ax, ay, az = controller:getOrientation()
-    controllerModels[i]:draw(x, y, z, 1, angle, ax, ay, az)
-  end
-end
-```
-
-Controller Input
----
-
-To determine if a button on the controller is pressed, use `Controller:isDown`:
-
-```
-print(controller:isDown('menu'))
-```
-
-The first parameter is a string with the name of a button, which can be `system`, `menu`, `grip`,
-or `touchpad`.  The return value is a "boolean": either `true` or `false` depending on whether or
-not the button is pressed.
-
-You can also retrieve the input state of **axes**.  Axes are inputs that can take on a continuous
-value between 0 and 1.  Axis state can be retrieved using `Controller:getAxis`.  Available axes are
-`trigger`, `touchx`, and `touchy`.
-
-Haptics
----
-
-You can trigger haptic feedback on controllers to make them vibrate!  To do this, use
-`Controller:vibrate`.  The function takes in a number representing how long the controller should
-vibrate for, in seconds.  Currently, Vive controllers can only vibrate for durations less than .004
-seconds.  To create longer or stronger vibrations, call the function over a period of several
-frames.
-
-```
-function lovr.update(dt)
-  if controller:getAxis('trigger') == 1 then
-    controller:vibrate(.002)
-  end
-end
-```
-
-That's all for controllers.  The next guide is about <a data-key="Sound">Sound</a>.

+ 51 - 0
guides/Distribution.md

@@ -0,0 +1,51 @@
+Distribution
+===
+
+LÖVR projects can be exported to standalone executables or WebVR projects.  This guide will teach you
+how to export and distribute a project.
+
+Creating an Archive
+---
+
+The first step is to create an archive of your project, which is really just a zipped up version of
+its contents.  On Windows you can select all the files in a project (**not** the project folder),
+right click them, and choose "Send to" -> "Compressed (zip) folder".  On Unix systems, the `zip`
+utility can be used:
+
+```
+zip -9qr .
+```
+
+A zip archive can be run with LÖVR but isn't a standalone executable yet.
+
+Creating an Executable
+---
+
+Once you have a project archive, it can be appended to the LÖVR binary to create a standalone
+executable.  On Windows, this can be done using the command prompt:
+
+```
+copy /b lovr.exe+MyProject.zip MyProject.exe
+```
+
+On Unix systems, the `cat` utility can be used to concatenate the two files.
+
+> Once you have an executable, be sure to distribute it with all the `.dll` files that came with the
+original LÖVR download.
+
+WebVR
+---
+
+You can use the [WebVR Exporter](/share) to export to WebVR.  Just drag and drop a `.zip` file into
+your window to create a WebVR page.
+
+> Note that WebVR exports are removed after 30 days of inactivity!
+
+To create a custom WebVR build, see [this
+guide](https://github.com/bjornbytes/lovr/blob/master/COMPILING.md#webvr).
+
+Note that there are a few differences when running LÖVR in the browser:
+
+- `Controller:newModel` will always return `nil`.
+- `lovr.headset.getBoundsGeometry` is not supported and will return four zero vectors.
+- Oculus Touch controllers are not fully supported yet.

+ 0 - 37
guides/Game_Distribution.md

@@ -1,37 +0,0 @@
-Game Distribution
-===
-
-Let's pretend you made an awesome game with LÖVR and you want to show it to the world (or your mom).
-Here are instructions for packaging a game into a standalone `.exe` file that can be distributed.
-
-Step one
----
-
-The first step is to create a `.lovr` file from your project.  On Windows, create a `.zip` file from
-the files in your project by selecting them, right clicking, and choosing "Send to" -> "Compressed
-(zip) folder".
-
-Once you have a `.zip` file for your project, change the extension to `.lovr`.  You can test the
-`.lovr` file by dropping it onto `lovr.exe`.
-
-On unix systems, the `zip` utility can be used to create a `.lovr` archive for the project.
-
-Step two
----
-
-Once you have a `.lovr` file, it needs to be "appended" to `lovr.exe`.  To start, place your `.lovr`
-file in the same folder as `lovr.exe`.  Next, press Windows + R and type `cmd.exe` to open the
-command prompt.  From there, type `cd C:\Users\Cena\Desktop\lovr`, or wherever the LÖVR folder is.
-Finally, use the following command to create the executable:
-
-```
-copy /b lovr.exe+MyGame.lovr MyGame.exe
-```
-
-On unix systems, the `cat` utility can be used to concatenate the `lovr` file with `lovr.exe`.
-
-Step Three
----
-
-Awesome, now you have an exe for your game!  Be sure to distribute it with all the `.dll` files that
-were in the LÖVR folder.

+ 39 - 42
guides/Getting_Started.md

@@ -1,74 +1,71 @@
 Getting Started
 ===
 
-This guide will help you install LÖVR and teach you how to create a simple scene.  You'll need to
-have an HTC Vive or Oculus Rift handy, and you'll also need to have SteamVR installed on your
-computer.
+This guide will teach you how to install LÖVR, create a project, and run it. TL;DR version:
+
+<img src="../static/img/gettingStarted.png" width="400" height="515" class="flat">
 
 Installing LÖVR
 ---
 
-First, download LÖVR from the website:
+First, download LÖVR from the home page or click [here](http://bjornbyt.es/f/lovr.zip).  Extract the
+zip archive and open up the folder.  You should see the `lovr.exe` executable and a bunch of `.dll`
+files.
 
-![LOVR Home Page](../static/img/please.jpg)
+![Archive Contents](../static/img/dlls.png)
 
-Next, extract the zip file and open up the folder.  You should see a file called `lovr.exe` and a
-bunch of `.dll` files.
+Double click on `lovr.exe` to open LÖVR.  You should see a window with the LÖVR logo in it.  This is
+what's shown if you run LÖVR without specifying a project.
 
-![Archive Contents](../static/img/dlls.png)
+![The Default Project](../static/img/defaultProject.png)
 
-Double click on `lovr.exe` to open LÖVR.  If you see a window with a black screen, don't panic!
-That means LÖVR is working.  If you don't tell LÖVR what project you want to run, it will just open
-a blank window.  That's super boring though.  Let's create a project with a cube in it.
+> Note: If you're using a VR headset, you'll only see the logo if your headset is pointing in the
+> forward direction.
 
-To create a project, just create a new folder somewhere.  You can call the folder whatever you want.
-I'm going to call it `SuperEpicCube`.
+We're going to make a project so we see something more interesting than the logo.
 
-Writing Code
+Creating a Project
 ---
 
-Now we're going to have to write some code.  Code is really scary, so we're going to download a
-**text editor** to make it easier for us to write code.  There are lots of different kinds of text
-editors.  Some good ones are [Sublime Text](http://www.sublimetext.com), [Atom](http://atom.io), or
-[Zero Brane Studio](https://studio.zerobrane.com).  I'll use Sublime Text for this tutorial, but
-the approach should be similar no matter what text editor you use.
+A LÖVR project is just a folder.  The folder can have anything necessary for your app, like 3D
+models, sound files, or Lua code.  There isn't any required structure for the folder, so you can
+organize it however you want.
 
-Every LÖVR project needs a special file called `main.lua`.  Open a new file in your text editor and
-type out this Lua code.  If you don't know what Lua is or don't know what the code does or are
-beginning to have an out of body experience, that's fine.  It's not important to understand all the
-details of the code right now.  *However*, I recommend that you avoid just copying and pasting it!
-Typing out each line does this magical thing to your brain that helps you read, write, and
-understand the code better.  I promise.
+There is one special file that LÖVR looks for though, called `main.lua`.  If you put a `main.lua`
+file in your project folder, LÖVR will run the code in there when the project starts.
+
+Create a file called `main.lua` in a project folder and type the following Lua code in it:
 
 ```
 function lovr.draw()
-  lovr.graphics.cube('line', 0, 1, 0)
+  lovr.graphics.print('hello world', 0, 1.7, -3, .5)
 end
 ```
 
-Whew, that wasn't so bad.  Okay, save the file inside the `SuperEpicCube` folder and be sure to
-name it `main.lua`.  Now we're ready to run our project!
-
-> Note: If you're trying this out without a VR headset, use the coordinates `0, 0, -2` instead of
-> `0, 1, 0` so the cube shows up correctly!
+Don't worry if you're confused about the code, it's not important to understand it all right now.
+In short, we declared the `lovr.draw` callback and used `lovr.graphics.print` in there to render
+some text in the world.  We'll learn more about how this works in the next guide.
 
 Running a Project
 ---
 
-We're going to run our project with LÖVR and experience our cube in VR.  First, start SteamVR if it
-isn't started already:
+To run a LÖVR project, drop its folder onto `lovr.exe`.  You can also run `lovr.exe` from the
+command line and pass the path to the project as the first argument.
 
-![Steam VR](../static/img/steamvr.png)
+![Drag and Drop](../static/img/dragonDrop.png)
 
-Now, drag the `SuperEpicCube` folder on to the `lovr.exe` application we ran earlier:
+If you followed the example above, you should see the following in VR:
 
-![Drag and Drop](../static/img/dragonDrop.png)
+![Hello World](../static/img/helloWorld.png)
+
+That's all there is to it!
+
+Next Steps
+---
 
-Ok, put on your headset!  You should see a cube floating in the middle of the play space.  Walk
-around, introduce yourself to it, befriend it.
+The next guide will teach you how to make fancier projects using <a data-key="Callbacks_and_Modules">Callbacks and Modules</a>.
 
-![A Beautiful Cube](../static/img/cube.png)
+If you want to learn more about Lua, some good resources are:
 
-That's it for this guide.  I knew you could do it.
-If you want to make something even more cool, you'll need to learn a little bit more about Lua.
-Continue on to the <a data-key="How_to_Lua">How to Lua</a> guide to learn more about Lua!
+1. [Learn Lua in 15 Minutes](http://tylerneylon.com/a/learn-lua/)
+2. [Lua for Programmers](http://nova-fusion.com/2012/08/27/lua-for-programmers-part-1/)

+ 0 - 222
guides/How_to_Lua.md

@@ -1,222 +0,0 @@
-How to Lua
-===
-
-This is a very brief introduction to the Lua programming language and assumes you've never
-programmed before.
-
-If you're already familiar with Lua or you're feeling brave and just want to start making stuff,
-feel free to skip this guide.  If you know a different programming language or you want a refresher
-on Lua, check out these guides:
-
-1. [Learn Lua in 15 Minutes](http://tylerneylon.com/a/learn-lua/)
-1. [Lua for Programmers](http://nova-fusion.com/2012/08/27/lua-for-programmers-part-1/)
-
-You can use [repl.it](https://repl.it/languages/lua) to run Lua code on the web if you don't want to
-set up Lua on your computer.
-
-Printing
----
-
-You can use `print` to print out numbers and text.  It doesn't print things out using your printer,
-but will "print" text to the output so you can see it.  Try it:
-
-```
-print('hey')
-print(5)
-```
-
-Math
----
-
-Lua is really good at math.  You can use it like a basic calculator.  Try some of these examples:
-
-```
-print(5 + 5)
-print(3 - 2)
-print(2 * (7 - 8))
-print(1 + .5)
-print((2 ^ 5) - 16 / 3)
-```
-
-Variables
----
-
-Variables are like little mailboxes that can hold things inside of them.  Each one has a name.  To
-make a variable we type its name, type an equals sign, and then type the value, like this:
-
-```
-a = 3
-b = 1
-```
-
-So now the variable named `a` holds the value 3 inside of it.  If we print out a variable, we see
-the value it contains, not its name:
-
-```
-print(a)
-print(b)
-print(a + b)
-```
-
-The names of variable are _case sensitive_, so `a` and `A` are two different variables.  We change
-the value of a variable at any time, and even use other variables when we do it:
-
-```
-a = 3
-b = 1
-c = a + b
-b = 7
-print(a, b, c)
-```
-
-You can tell `print` to print multiple things using commas.  Pretty neato.  Variables can also hold
-strings, which is a fancy way of saying text.  To make a string, you just put some text inside
-quotes:
-
-```
-a = 'woah'
-print(a)
-```
-
-Lua also lets you assign multiple variables at once:
-
-```
-a, b = 1, 2
-print(a, b)
-```
-
-Functions
----
-
-Functions let you wrap up a chunk of code and give it a name.  This can be useful to keep things
-organized in a big file, or it can be useful to save typing if you want to do the same thing many
-times.  Let's write a function that adds one to a variable and then prints it:
-
-```
-function addOne()
-  a = a + 1
-  print(a)
-end
-```
-
-We typed `function` then typed the name of the function and put parentheses after it.  Then we
-wrote the code we want to run whenenver the function is run.  Finally, we typed `end` to tell Lua
-that we're done writing the function.
-
-If you run this code, you'll notice that nothing gets printed out!  This is because writing a
-function does not actually run it.  To run a function you type the name of the function followed
-by parentheses:
-
-```
-function addOne()
-  a = a + 1
-  print(a)
-end
-
-a = 0
-addOne()
-addOne()
-addOne()
-addOne()
-addOne()
-```
-
-There we go.  Now our function is getting "called" and the code inside it is getting run!
-
-We can also give a function inputs.  To do that we just add variable names into the function
-definition:
-
-```
-function sayHello(name)
-  print('hello', name)
-end
-
-sayHello('adele')
-sayHello('yall')
-```
-
-Because we put the `name` variable in parentheses, the code in the `sayHello` function can now use
-the `name` variable.  Note that `name` is a temporary variable that can only be used inside the
-function.
-
-A function can also output values using `return`.  This can be used to put the result of a function
-in a variable.  Here's an example:
-
-```
-function double(number)
-  return number * 2
-end
-
-print(double(10))
-```
-
-We don't have to put the result into a variable though, we can directly print it out.  In this
-example, the output of `double` is used as the input of `print`.
-
-Comments
----
-
-In Lua, you can write a _comment_ by using `--`.  If you put a comment, then the rest of the line
-will be ignored.  It's a useful tool that lets you write explanations for your code so you can
-remember what's going on:
-
-```
--- This variable contains my favorite color
-color = 'purple'
-```
-
-Nil
----
-
-The value `nil` is a value that represents the absence of a value.  If a variable doesn't have any
-value assigned to it, it has the value of `nil`.  Similarly, if a function doesn't return any
-values, then it returns `nil`.
-
-If, For, While
----
-
-Lastly, here are a few features of Lua called "control flow".  Normally lines of code are run in
-order, one after another, but control flow lets you change that.  For example, then `if` statement
-let you conditinally run lines of code:
-
-```
-if 5 > 3 then
-  print('ok cool five is greater than three')
-else
-  print('wait what')
-end
-```
-
-The `while` statement lets you run a segment of code over and over again until some condition is
-met:
-
-```
-i = 0
-while i < 5 do
-  i = i + 1
-  print(i)
-end
-```
-
-Finally, the `for` statement is similar to `while`, but is a little more concise if you want to run
-a piece of code a certain number of times:
-
-```
-for i = 1, 5 do
-  print(i)
-end
-```
-
-It automatically does the `i = i + 1` and the `i < 5` part from the previous example.
-
-More Resources
----
-
-This is really just the tip of the iceberg, but hopefully it helps you understand some of the other
-code in these guides.  For additional reference content, have a look at these:
-
-1. [Lua Wiki tutorial series](http://lua-users.org/wiki/LuaTutorial)
-1. The [Programming in Lua](http://lua.org/pil) book (first edition)
-1. [Lua Reference Manual](http://www.lua.org/manual/5.1/)
-
-Otherwise, continue on to the <a data-key="Callbacks">Callbacks</a> guide.

+ 0 - 46
guides/Introduction.md

@@ -1,46 +0,0 @@
-Welcome!
-===
-
-LÖVR is a framework for creating VR experiences with Lua.  It's great for small projects,
-prototyping, creative coding, game jams, and more.
-
-Why LÖVR?
----
-
-LÖVR aims to be simple to use.  You can start making VR prototypes with just a few lines of code,
-and there is no complicated editor to use, account to create, or compiling to do.
-
-There is no cost to use LÖVR and it's open source, so you can use it for pretty much any
-project without restrictions, and even modify the framework if you need to.
-
-LÖVR is really fast.  It's written in C and uses LuaJIT, an incredibly fast Lua implementation.
-
-Under the hood LÖVR uses OpenVR, so both the HTC Vive and the Oculus Rift are supported.  There is
-also a WebVR export tool that can be found [here](/share).
-
-Example
----
-
-What does LÖVR look like?  Here's a simple example that draws a cube at the position of each
-motion controller:
-
-```
-function lovr.load()
-  controllers = lovr.headset.getControllers()
-end
-
-function lovr.draw()
-  for i, controller in pairs(controllers) do
-    local x, y, z = controller:getPosition()
-    lovr.graphics.cube('line', x, y, z, .2, controller:getOrientation())
-  end
-end
-```
-
-Onward!
----
-
-If you want to learn more about creating experiences with LÖVR, check out the <a data-key="Getting_Started">Getting Started</a>
-guide.  You can also explore everything LÖVR can do
-using the sidebar on the left.  Or, if you're looking for a particular feature, you can type a
-keyword, like 'audio'.

+ 42 - 0
guides/Libraries.md

@@ -0,0 +1,42 @@
+Libraries
+===
+
+Libraries are external packages that you can import and use in your projects.  Some libraries extend
+the capabilities of LÖVR and others just make it easier to get stuff done.  Libraries are usually
+distributed as a single Lua file.  You can copy the Lua file into your project and `require` it to
+use the library:
+
+```
+-- library.lua is sitting next to our main.lua here
+local lib = require('library')
+
+function lovr.load()
+  lib.doStuff()
+end
+```
+
+Below is a catalog of useful libraries.  All of them can be used with LÖVR, although some of them
+only depend on Lua and aren't specific to LÖVR.
+
+List of Libraries
+---
+
+- [30log](https://github.com/Yonaba/30log) - A library for object oriented programming in Lua.
+- [cpml](https://github.com/excessive/cpml) - A 3D math library for Lua.
+- [flux](https://github.com/rxi/flux) - A tweening library for Lua.
+- [handy](https://github.com/bjornbytes/handy) - Helper utilities for managing controllers.
+- [knife](https://github.com/airstruck/knife) - A collection of useful micromodules for Lua.
+- [lovr-circle](https://github.com/bjornbytes/lovr-circle) - Create circle geometry.
+- [lovr-icosphere](https://github.com/bjornbytes/lovr-icosphere) - A library to create icospheres.
+- [lovr-grid](https://github.com/bjornbytes/lovr-grid) - A library for drawing grids.
+- [lovr-lighting](https://github.com/bjornbytes/lovr-lighting) - Simple lighting shaders to add to
+- [lovr-pointer](https://github.com/bjornbytes/lovr-pointer) - A general pointer system that can be
+  used to select objects or create teleporters.
+- [lovr-webvr-server](https://github.com/bjornbytes/lovr-webvr-server) - A local development server for
+  testing WebVR projects.
+- [lume](https://github.com/rxi/lume) - Lua utility functions geared towards game development.
+- [maf](https://github.com/bjornbytes/maf) - A 3D math library with vectors and quaternions.
+- [tiny-ecs](https://github.com/bakpakin/tiny-ecs) - An entity component system for Lua.
+- [tween.lua](https://github.com/kikito/tween.lua) - A tweening library for Lua.
+- [Vivid](https://github.com/WetDesertRock/vivid) - A Lua library for color manipulations and
+  conversions.

+ 0 - 115
guides/Simple_Shapes.md

@@ -1,115 +0,0 @@
-Simple Shapes
-===
-
-When you're quickly prototyping something or just getting started with LÖVR, it can be helpful to
-draw some simple shapes.  These are sometimes called "graphics primitives".  Note that all units
-for positions, sizes, etc. are in meters.
-
-Colors
----
-
-To change the color of a primitive, call `lovr.graphics.setColor(r, g, b)` before you draw the
-primitive.  The r, g, b parameters are the red, green, and blue components of the color, like you
-would find in Photoshop.  `(255, 255, 255)` is white, `(0, 0, 0)` is black, and `(0, 0, 128)` is
-darkish blue.  Note that the color will remain active until it's changed again.
-
-Points
----
-
-```
-lovr.graphics.points(x, y, z)
-```
-
-This draws a single point at an x, y, z position in 3D space.  If you try it out and draw a point at
-`(0, 0, 0)`, the point will be **really** hard to see because it's only 1 pixel big!  To change
-this, have a look at the `lovr.graphics.setPointSize` function.  You can also draw more than one
-point by passing in more point coordinates after the first.  Finally, you can also pass in a table:
-
-```
-local points = {
-  0, 0, 0,
-  1, 1, 1
-}
-
-lovr.graphics.points(points)
-```
-
-Lines
----
-
-```
-lovr.graphics.line(x1, y1, z1, x2, y2, z2, ...)
-```
-
-This function draws lines between points.  Here's how you would draw a square on the floor:
-
-```
-lovr.graphics.line(
-  -1, 0, -1,
-  -1, 0,  1,
-   1, 0,  1,
-   1, 0, -1
-)
-```
-
-Triangles
----
-
-```
-lovr.graphics.triangle(mode, x1, y1, z1, x2, y2, z2, x3, y3, z3)
-```
-
-This function draws a triangle from the specified 3 points.  `mode` can either be `line` for a
-wireframe triangle or `fill` for a solid triangle.
-
-Planes
----
-
-A plane is a flat rectangle.  They can be used for simple floors and walls.
-
-```
-lovr.graphics.plane(mode, x, y, z, size, nx, ny, nz)
-```
-
-This draws a plane `size` meters big centered at `(x, y, z)`.  To control the direction the plane
-is facing, you gotta specify a _normal_ vector.  The normal vector is a direction specified using
-3 numbers (x, y, and z).  The normal vector `(0, 1, 0)` is a vector that points straight up, because
-the x and z parts of the direction are zero and the y direction of the vector is positive 1, which
-is straight up.
-
-Cubes
----
-
-Finally, cubes, the pinnacle of primitives.
-
-```
-lovr.graphics.cube(mode, x, y, z, size, angle, ax, ay, az)
-```
-
-This function draws a cube.  You can draw it as a wireframe or as a filled cube using the `mode`
-parameter, similar to triangles and planes.  The `x`, `y`, and `z` parameters control the position.
-The `size` parameter controls how big it is.  Finally, the last four parameters control the cube's
-rotation.  The first number is the number of radians to rotate the cube around its axis of rotation,
-and the last three numbers define the x, y, and z components of the axis of rotation.  This is
-called "angle axis representation".  It's honestly pretty confusing, but LÖVR plans to add some
-utilities to make this easier in the future!
-
-Bonus!!
----
-
-Planes and cubes can have **textures** applied to them.  Here's how you would draw a plane on the
-ground with a ground texture applied to it:
-
-```
-function lovr.load()
-  texture = lovr.graphics.newTexture('ground.png')
-end
-
-function lovr.draw()
-  lovr.graphics.plane(texture, 0, 0, 0, 2, 0, 1, 0)
-end
-```
-
-For this to work, put an image named `ground.png` in the same folder that `main.lua` is in.
-
-Woohoo, let's take this a step further and start drawing <a data-key="3D_Models">3D Models</a>!

+ 0 - 73
guides/Sound.md

@@ -1,73 +0,0 @@
-Sound
-===
-
-Sound is a very important part of creating an immersive VR experience.  LÖVR makes it easy to load
-and play sound effects.
-
-Basics
----
-
-Sound functionality can be accessed via the `lovr.audio` module.  To play a sound, you'll need to
-create a `Source` object by calling `lovr.audio.newSource`.  Currently, only `.ogg` files are
-supported.
-
-```
-source = lovr.audio.newSource('airhorn.ogg')
-```
-
-Once the source is created, you can call a few functions on the source to control its playback:
-
-```
-source:play()   -- Play the source
-source:pause()  -- Pause the source
-source:resume() -- Resume a paused source
-source:rewind() -- Rewind a source, playing it from the beginning
-source:stop()   -- Stop a source
-```
-
-You can also get whether or not a source is in a particular state:
-
-```
-source:isPlaying()
-source:isPaused()
-source:isStopped()
-```
-
-You can set the volume and pitch of a source (even while it's playing):
-
-```
-source:setVolume(.5) -- Volume can be between 0 and 1
-source:setPitch(2)   -- The default pitch is 1
-```
-
-Finally, you can control whether or not a source loops, which can be useful for background music:
-
-```
-source:setLooping(loop) -- true or false
-```
-
-Spatial Audio
----
-
-LÖVR supports spatial audio, which means that sounds can be positioned in 3D space.  When
-positioned, they will be slightly distorted to make it sound as if they are coming from their
-location in the virtual world!  To position a source, use `Source:setPosition`:
-
-```
-source:setPosition(1.3, 2, -4.2)
-```
-
-Note that only mono sounds can be spatialized in this way.
-
-Muting Audio
----
-
-You can control all audio sources by using functions in `lovr.audio`.  For example,
-`lovr.audio.stop` will stop all audio, `lovr.audio.pause` will pause all audio, and
-`lovr.audio.resume` will resume all paused audio.
-
-You can also set the "master volume" for all sounds using `lovr.audio.setVolume`.  This could be
-used to easily allow for configurable volume settings in the experience, or all sounds could be
-muted by setting the master volume to zero.
-
-That's all you need to know to start adding sounds and music to your game!

+ 0 - 30
guides/WebVR.md

@@ -1,30 +0,0 @@
-WebVR
-===
-
-LÖVR is able to run in the browser using WebVR.  This guide explains some of the differences between
-running LÖVR on the desktop and running in the browser, and how to get up and running.
-
-First, you'll need to make sure your browser supports WebVR.  Visit [webvr.info](http://webvr.info)
-to find a browser that works with your headset.
-
-Next, you'll need a LÖVR project.  The <a data-key="Getting_Started">Getting Started</a> guide teaches
-you how to make a simple project with a spinning cube in it.
-
-Now we need to make a `.lovr` file from our project.  To do this, simply select all the files in
-your project and create a `.zip` file from them.  It is important to zip up all the files, _not_ the
-folder that contains them.  If you want, you can change the extension from `.zip` to `.lovr`.
-
-Finally, visit [lovr.org/share](/share) and drag and drop the `.lovr` file into your window.  Your
-project should be running in WebVR!
-
-Differences
----
-
-There are a few differences when running LÖVR in the browser:
-
-<ol>
-  <li>Audio is not spatialized.</li>
-  <li><code>Controller:newModel</code> will always return <code>nil</code>.</li>
-  <li><code>lovr.headset.getBoundsGeometry</code> will return a table with four zero vectors in it</li>
-  <li><code>lovr.getOS</code> will return "Web".</li>
-</ol>

+ 3 - 9
guides/init.lua

@@ -1,12 +1,6 @@
 return {
-  'Introduction',
   'Getting_Started',
-  'How_to_Lua',
-  'Callbacks',
-  'Simple_Shapes',
-  '3D_Models',
-  'Controllers',
-  'Sound',
-  'Game_Distribution',
-  'WebVR'
+  'Callbacks_and_Modules',
+  'Libraries',
+  'Distribution'
 }

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików