bjorn 7 years ago
parent
commit
35c413e41f

+ 191 - 0
api/init.lua

@@ -1729,6 +1729,23 @@ return {
             }
             }
           }
           }
         },
         },
+        {
+          name = "TextureProjection",
+          summary = "Different projection types for renderable textures.",
+          description = "When creating Textures to be used as render targets, they can be created in either \"2d\" or \"3d\" mode.  2D mode is good for 2D user interfaces or postprocessing, whereas 3d is useful for portals, weapon scopes, mirrors, and other situations where 3D content needs to be rendered.",
+          key = "TextureProjection",
+          module = "graphics",
+          values = {
+            {
+              name = "2d",
+              description = "Use an orthographic projection."
+            },
+            {
+              name = "3d",
+              description = "Use a perspective projection."
+            }
+          }
+        },
         {
         {
           name = "WrapMode",
           name = "WrapMode",
           summary = "How to wrap Textures.",
           summary = "How to wrap Textures.",
@@ -1851,6 +1868,64 @@ return {
                 }
                 }
               },
               },
               returns = {}
               returns = {}
+            },
+            {
+              arguments = {
+                {
+                  name = "texture",
+                  type = "Texture",
+                  description = "The Texture to apply to the cube faces."
+                },
+                {
+                  name = "x",
+                  type = "number",
+                  description = "The x coordinate of the center of the cube.",
+                  default = "0"
+                },
+                {
+                  name = "y",
+                  type = "number",
+                  description = "The y coordinate of the center of the cube.",
+                  default = "0"
+                },
+                {
+                  name = "z",
+                  type = "number",
+                  description = "The z coordinate of the center of the cube.",
+                  default = "0"
+                },
+                {
+                  name = "size",
+                  type = "number",
+                  description = "The size of the cube, in meters.",
+                  default = "1"
+                },
+                {
+                  name = "angle",
+                  type = "number",
+                  description = "The rotation of the cube around its rotation axis, in radians.",
+                  default = "0"
+                },
+                {
+                  name = "ax",
+                  type = "number",
+                  description = "The x coordinate of the cube's axis of rotation.",
+                  default = "0"
+                },
+                {
+                  name = "ay",
+                  type = "number",
+                  description = "The y coordinate of the cube's axis of rotation.",
+                  default = "1"
+                },
+                {
+                  name = "az",
+                  type = "number",
+                  description = "The z coordinate of the cube's axis of rotation.",
+                  default = "0"
+                }
+              },
+              returns = {}
             }
             }
           }
           }
         },
         },
@@ -2571,6 +2646,38 @@ return {
                   description = "The new Texture."
                   description = "The new Texture."
                 }
                 }
               }
               }
+            },
+            {
+              arguments = {
+                {
+                  name = "width",
+                  type = "number",
+                  description = "The width of the Texture, in pixels."
+                },
+                {
+                  name = "height",
+                  type = "number",
+                  description = "The height of the Texture, in pixels."
+                },
+                {
+                  name = "projection",
+                  type = "TextureProjection",
+                  description = "The type of projection to use when rendering to the Texture."
+                },
+                {
+                  name = "msaa",
+                  type = "number",
+                  description = "The number of samples to use for multisample antialiasing.",
+                  default = "0"
+                }
+              },
+              returns = {
+                {
+                  name = "texture",
+                  type = "Texture",
+                  description = "The new Texture."
+                }
+              }
             }
             }
           }
           }
         },
         },
@@ -2648,6 +2755,70 @@ return {
                 }
                 }
               },
               },
               returns = {}
               returns = {}
+            },
+            {
+              description = "Draw a textured plane.",
+              arguments = {
+                {
+                  name = "texture",
+                  type = "Texture",
+                  description = "The texture to apply to the plane."
+                },
+                {
+                  name = "x",
+                  type = "number",
+                  description = "The x coordinate of the center of the plane.",
+                  default = "0"
+                },
+                {
+                  name = "y",
+                  type = "number",
+                  description = "The y coordinate of the center of the plane.",
+                  default = "0"
+                },
+                {
+                  name = "z",
+                  type = "number",
+                  description = "The z coordinate of the center of the plane.",
+                  default = "0"
+                },
+                {
+                  name = "size",
+                  type = "number",
+                  description = "The size of the plane, in meters.",
+                  default = "1"
+                },
+                {
+                  name = "nx",
+                  type = "number",
+                  description = "The x coordinate of the normal vector of the plane.",
+                  default = "0"
+                },
+                {
+                  name = "ny",
+                  type = "number",
+                  description = "The y coordinate of the normal vector of the plane.",
+                  default = "1"
+                },
+                {
+                  name = "nz",
+                  type = "number",
+                  description = "The z coordinate of the normal vector of the plane.",
+                  default = "0"
+                }
+              },
+              returns = {}
+            },
+            {
+              description = "Draw a fullscreen textured plane.",
+              arguments = {
+                {
+                  name = "texture",
+                  type = "Texture",
+                  description = "The texture to apply to the plane."
+                }
+              },
+              returns = {}
             }
             }
           }
           }
         },
         },
@@ -4015,6 +4186,26 @@ return {
                 }
                 }
               }
               }
             },
             },
+            {
+              name = "renderTo",
+              summary = "Render to a Texture.",
+              description = "Renders to a Texture using a function.  The Texture must be created as a framebuffer by passing a width and height to `lovr.graphics.newTexture` instead of a filename.",
+              key = "Texture:renderTo",
+              module = "lovr.graphics",
+              notes = "Make sure you clear the contents of the Texture before rendering to it to clear any previous contents.",
+              variants = {
+                {
+                  arguments = {
+                    {
+                      name = "callback",
+                      type = "function",
+                      description = "A function that calls drawing commands to render to the Texture."
+                    }
+                  },
+                  returns = {}
+                }
+              }
+            },
             {
             {
               name = "setFilter",
               name = "setFilter",
               summary = "Set the FilterMode for the Texture.",
               summary = "Set the FilterMode for the Texture.",

+ 19 - 0
api/lovr/graphics/Texture/renderTo.lua

@@ -0,0 +1,19 @@
+return {
+  summary = 'Render to a Texture.',
+  description = [[
+    Renders to a Texture using a function.  The Texture must be created as a framebuffer by passing
+    a width and height to `lovr.graphics.newTexture` instead of a filename.
+  ]],
+  arguments = {
+    {
+      name = 'callback',
+      type = 'function',
+      description = 'A function that calls drawing commands to render to the Texture.'
+    }
+  },
+  returns = {},
+  notes = [[
+    Make sure you clear the contents of the Texture before rendering to it to clear any previous
+    contents.
+  ]]
+}

+ 18 - 0
api/lovr/graphics/TextureProjection.lua

@@ -0,0 +1,18 @@
+return {
+  summary = 'Different projection types for renderable textures.',
+  description = [[
+    When creating Textures to be used as render targets, they can be created in either "2d" or "3d"
+    mode.  2D mode is good for 2D user interfaces or postprocessing, whereas 3d is useful for
+    portals, weapon scopes, mirrors, and other situations where 3D content needs to be rendered.
+  ]],
+  values = {
+    {
+      name = '2d',
+      description = 'Use an orthographic projection.'
+    },
+    {
+      name = '3d',
+      description = 'Use a perspective projection.'
+    }
+  }
+}

+ 24 - 19
api/lovr/graphics/cube.lua

@@ -3,59 +3,64 @@ return {
   summary = 'Draw a cube.',
   summary = 'Draw a cube.',
   description = 'Draws a cube.',
   description = 'Draws a cube.',
   arguments = {
   arguments = {
-    {
-      name = 'mode',
+    texture = {
+      type = 'Texture',
+      description = 'The Texture to apply to the cube faces.'
+    },
+    mode = {
       type = 'DrawMode',
       type = 'DrawMode',
       description = 'How to draw the cube.'
       description = 'How to draw the cube.'
     },
     },
-    {
-      name = 'x',
+    x = {
       type = 'number',
       type = 'number',
       default = '0',
       default = '0',
       description = 'The x coordinate of the center of the cube.'
       description = 'The x coordinate of the center of the cube.'
     },
     },
-    {
-      name = 'y',
+    y = {
       type = 'number',
       type = 'number',
       default = '0',
       default = '0',
       description = 'The y coordinate of the center of the cube.'
       description = 'The y coordinate of the center of the cube.'
     },
     },
-    {
-      name = 'z',
+    z = {
       type = 'number',
       type = 'number',
       default = '0',
       default = '0',
       description = 'The z coordinate of the center of the cube.'
       description = 'The z coordinate of the center of the cube.'
     },
     },
-    {
-      name = 'size',
+    size = {
       type = 'number',
       type = 'number',
       default = '1',
       default = '1',
       description = 'The size of the cube, in meters.'
       description = 'The size of the cube, in meters.'
     },
     },
-    {
-      name = 'angle',
+    angle = {
       type = 'number',
       type = 'number',
       default = '0',
       default = '0',
       description = 'The rotation of the cube around its rotation axis, in radians.'
       description = 'The rotation of the cube around its rotation axis, in radians.'
     },
     },
-    {
-      name = 'ax',
+    ax = {
       type = 'number',
       type = 'number',
       default = '0',
       default = '0',
       description = 'The x coordinate of the cube\'s axis of rotation.'
       description = 'The x coordinate of the cube\'s axis of rotation.'
     },
     },
-    {
-      name = 'ay',
+    ay = {
       type = 'number',
       type = 'number',
       default = '1',
       default = '1',
       description = 'The y coordinate of the cube\'s axis of rotation.'
       description = 'The y coordinate of the cube\'s axis of rotation.'
     },
     },
-    {
-      name = 'az',
+    az = {
       type = 'number',
       type = 'number',
       default = '0',
       default = '0',
       description = 'The z coordinate of the cube\'s axis of rotation.'
       description = 'The z coordinate of the cube\'s axis of rotation.'
     }
     }
   },
   },
-  returns = {}
+  returns = {},
+  variants = {
+    {
+      arguments = { 'mode', 'x', 'y', 'z', 'size', 'angle', 'ax', 'ay', 'az' },
+      returns = {}
+    },
+    {
+      arguments = { 'texture', 'x', 'y', 'z', 'size', 'angle', 'ax', 'ay', 'az' },
+      returns = {}
+    }
+  }
 }
 }

+ 29 - 3
api/lovr/graphics/newTexture.lua

@@ -3,17 +3,43 @@ return {
   summary = 'Create a new Texture.',
   summary = 'Create a new Texture.',
   description = 'Creates a new Texture from an image file.',
   description = 'Creates a new Texture from an image file.',
   arguments = {
   arguments = {
-    {
-      name = 'filename',
+    filename = {
       type = 'string',
       type = 'string',
       description = 'The filename of the image to load.'
       description = 'The filename of the image to load.'
+    },
+    width = {
+      type = 'number',
+      description = 'The width of the Texture, in pixels.'
+    },
+    height = {
+      type = 'number',
+      description = 'The height of the Texture, in pixels.'
+    },
+    projection = {
+      type = 'TextureProjection',
+      description = 'The type of projection to use when rendering to the Texture.'
+    },
+    msaa = {
+      type = 'number',
+      default = '0',
+      description = 'The number of samples to use for multisample antialiasing.'
     }
     }
   },
   },
   returns = {
   returns = {
-    {
+    texture = {
       name = 'texture',
       name = 'texture',
       type = 'Texture',
       type = 'Texture',
       description = 'The new Texture.'
       description = 'The new Texture.'
     }
     }
+  },
+  variants = {
+    {
+      arguments = { 'filename' },
+      returns = { 'texture' }
+    },
+    {
+      arguments = { 'width', 'height', 'projection', 'msaa' },
+      returns = { 'texture' }
+    }
   }
   }
 }
 }

+ 29 - 16
api/lovr/graphics/plane.lua

@@ -3,53 +3,66 @@ return {
   summary = 'Draw a plane.',
   summary = 'Draw a plane.',
   description = 'Draws a plane with a given position, size, and orientation.',
   description = 'Draws a plane with a given position, size, and orientation.',
   arguments = {
   arguments = {
-    {
-      name = 'mode',
+    texture = {
+      type = 'Texture',
+      description = 'The texture to apply to the plane.'
+    },
+    mode = {
       type = 'DrawMode',
       type = 'DrawMode',
       description = 'How to draw the plane.'
       description = 'How to draw the plane.'
     },
     },
-    {
-      name = 'x',
+    x = {
       type = 'number',
       type = 'number',
       default = '0',
       default = '0',
       description = 'The x coordinate of the center of the plane.'
       description = 'The x coordinate of the center of the plane.'
     },
     },
-    {
+    y = {
       name = 'y',
       name = 'y',
       type = 'number',
       type = 'number',
       default = '0',
       default = '0',
       description = 'The y coordinate of the center of the plane.'
       description = 'The y coordinate of the center of the plane.'
     },
     },
-    {
-      name = 'z',
+    z = {
       type = 'number',
       type = 'number',
       default = '0',
       default = '0',
       description = 'The z coordinate of the center of the plane.'
       description = 'The z coordinate of the center of the plane.'
     },
     },
-    {
-      name = 'size',
+    size = {
       type = 'number',
       type = 'number',
       default = '1',
       default = '1',
       description = 'The size of the plane, in meters.'
       description = 'The size of the plane, in meters.'
     },
     },
-    {
-      name = 'nx',
+    nx = {
       type = 'number',
       type = 'number',
       default = '0',
       default = '0',
       description = 'The x coordinate of the normal vector of the plane.'
       description = 'The x coordinate of the normal vector of the plane.'
     },
     },
-    {
-      name = 'ny',
+    ny = {
       type = 'number',
       type = 'number',
       default = '1',
       default = '1',
       description = 'The y coordinate of the normal vector of the plane.'
       description = 'The y coordinate of the normal vector of the plane.'
     },
     },
-    {
-      name = 'nz',
+    nz = {
       type = 'number',
       type = 'number',
       default = '0',
       default = '0',
       description = 'The z coordinate of the normal vector of the plane.'
       description = 'The z coordinate of the normal vector of the plane.'
     }
     }
   },
   },
-  returns = {}
+  returns = {},
+  variants = {
+    {
+      arguments = { 'mode', 'x', 'y', 'z', 'size', 'nx', 'ny', 'nz' },
+      returns = {}
+    },
+    {
+      description = 'Draw a textured plane.',
+      arguments = { 'texture', 'x', 'y', 'z', 'size', 'nx', 'ny', 'nz' },
+      returns = {}
+    },
+    {
+      description = 'Draw a fullscreen textured plane.',
+      arguments = { 'texture' },
+      returns = {}
+    }
+  }
 }
 }

+ 68 - 0
guides/3D_Models.md

@@ -0,0 +1,68 @@
+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('duck.dae')
+```
+
+Create the Texture
+---
+
+Creating textures is really similar to creating models:
+
+```
+texture = lovr.graphics.newTexture('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)
+```
+
+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('model.obj')
+  model:setTexture(lovr.graphics.newTexture('texture.png'))
+end
+
+function lovr.draw()
+  model:draw(0, 0, 0, 1)
+end
+```
+
+I combined the calls to `lovr.graphics.newTexture` and `model:setTexture`, and I left out the
+rotation parameters from `model:draw` for simplicity.
+
+That's all there is to it!  Next we'll (finally!) talk about our first VR topic:
+<a data-key="Controllers">Controllers</a>.

+ 110 - 0
guides/Callbacks.md

@@ -0,0 +1,110 @@
+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>.

+ 141 - 0
guides/Controllers.md

@@ -0,0 +1,141 @@
+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>.

+ 0 - 57
guides/Drawing_shapes.md

@@ -1,57 +0,0 @@
-Drawing Shapes
-===
-
-Drawing simple shapes is a fun way to learn more about how LÖVR works.
-
-You can draw a cube using the `lovr.graphics.cube` function:
-
-    function lovr.draw()
-      lovr.graphics.cube('line')
-    end
-
-This will draw a wireframe cube, 1 meter in size, at the position `(0, 0, 0)`.  If you tried this
-in VR, you would notice that the cube goes through the floor.  We can move it up a few feet in the
-air by changing its y coordinate:
-
-    function lovr.draw()
-      lovr.graphics.cube('line', 0, 1, 0)
-    end
-
-The three numbers there are the x, y, and z coordinate of the cube.  The units of these coordinates
-are in meters, so drawing the cube at `(0, 1, 0)` raises it 1 meter off the ground.
-
-We can also change the size of the cube.  The fifth argument to `lovr.graphics.cube` is the size of
-the cube
-
-    function lovr.draw()
-      lovr.graphics.cube('line', 0, 1, 0, .2)
-    end
-
-Now our cube is really small!  Let's make the cube spin:
-
-    function lovr.draw()
-      lovr.graphics.cube('line', 0, 1, 0, .2, lovr.timer.getTime())
-    end
-
-The sixth argument is a rotation for the cube, in radians.  If we pass the current time as our
-rotation using `lovr.timer.getTime`, we get a different rotation value every frame.  The end result
-is an animated spinning cube.  Finally, let's make our cube red:
-
-    function lovr.draw()
-      lovr.graphics.setColor(255, 0, 0)
-      lovr.graphics.cube('line', 0, 1, 0, .2, lovr.timer.getTime())
-    end
-
-`lovr.graphics.setColor` sets the color of drawn objects.  It takes in 3 numbers, each from 0 to
-255. One is for the red, one is for the green, and one is for the blue.  We are maxing out the red
-color and turning off the other ones, which results in a red color.
-
-There are functions for drawing shapes other than cubes too.  We can draw a floor using
-`lovr.graphics.plane`:
-
-    function lovr.draw()
-      lovr.graphics.plane('fill', 0, 0, 0)
-    end
-
-We can also draw a triangle from 3 points using `lovr.graphics.triangle`, or draw a line using
-`lovr.graphics.line`.  Finally, we can draw single pixels using `lovr.graphics.points`.

+ 37 - 0
guides/Game_Distribution.md

@@ -0,0 +1,37 @@
+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.

+ 74 - 0
guides/Getting_Started.md

@@ -0,0 +1,74 @@
+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.
+
+Installing LÖVR
+---
+
+First, download LÖVR from the website:
+
+![LOVR Home Page](../static/img/please.jpg)
+
+Next, extract the zip file and open up the folder.  You should see a file called `lovr.exe` and a
+bunch of `.dll` files.
+
+![Archive Contents](../static/img/dlls.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.
+
+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`.
+
+Writing Code
+---
+
+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.
+
+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.
+
+```
+function lovr.draw()
+  lovr.graphics.cube('line', 0, 1, 0)
+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!
+
+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:
+
+![Steam VR](../static/img/steamvr.png)
+
+Now, drag the `SuperEpicCube` folder on to the `lovr.exe` application we ran earlier:
+
+![Drag and Drop](../static/img/dragonDrop.png)
+
+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.
+
+![A Beautiful Cube](../static/img/cube.png)
+
+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!

+ 222 - 0
guides/How_to_Lua.md

@@ -0,0 +1,222 @@
+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.

+ 23 - 16
guides/Introduction.md

@@ -7,33 +7,40 @@ prototyping, creative coding, game jams, and more.
 Why LÖVR?
 Why LÖVR?
 ---
 ---
 
 
-LÖVR aims to be simple to use.  You can get up and running with just a few lines of Lua,
-and there is no bloated editor to use, account to create, or compiling to do.
+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
 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.
 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, a crazy-fast just in time compiler for Lua
-code.
+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.  Support
 Under the hood LÖVR uses OpenVR, so both the HTC Vive and the Oculus Rift are supported.  Support
 for other headsets is also in the works.
 for other headsets is also in the works.
 
 
-Hello World
+Example
 ---
 ---
 
 
-First, download LÖVR from the home page, or click [here](http://bjornbyt.es/f/lovr.zip).
+What does LÖVR look like?  Here's a simple example that draws a cube at the position of each
+motion controller:
 
 
-You'll need a directory to hold the code for your game.  For this example, we'll create a folder
-called `myGame`.  In the `myGame` folder we'll create a file called `main.lua`, which is the "entry
-point" for any LÖVR project.  We'll put some code in `main.lua` that draws a cube:
+```
+function lovr.load()
+  controllers = lovr.headset.getControllers()
+end
 
 
-    function lovr.draw()
-      lovr.graphics.cube('line', 0, 1, 0, .5)
-    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
+```
 
 
-The numbers `(0, 1, 0)` are the x, y, z, position of the cube in meters.  So the cube will be drawn
-one meter off the ground.  The number `.5` is the size of the cube, in meters.
+Onward!
+---
 
 
-Start SteamVR, then drag and drop the `myGame` folder onto `lovr.exe`.  Put on your headset and you
-should see a white cube at the center of your play area!
+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'.

+ 115 - 0
guides/Simple_Shapes.md

@@ -0,0 +1,115 @@
+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>!

+ 73 - 0
guides/Sound.md

@@ -0,0 +1,73 @@
+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!

+ 8 - 1
guides/init.lua

@@ -1,4 +1,11 @@
 return {
 return {
   'Introduction',
   'Introduction',
-  'Drawing_Shapes'
+  'Getting_Started',
+  'How_to_Lua',
+  'Callbacks',
+  'Simple_Shapes',
+  '3D_Models',
+  'Controllers',
+  'Sound',
+  'Game_Distribution'
 }
 }