### Example HTTP Request Source: https://lovr.org/docs/v0.18.0/http A complete example demonstrating how to require the module, perform a GET request, and iterate over the returned headers. ```lua local http = require 'http' local status, data, headers = http.request('https://zombo.com') print('welcome') print(status) print(data) print('headers:') for k, v in pairs(headers) do print('\t' .. k, v) end ``` -------------------------------- ### LOVR Initialization and Setup Source: https://lovr.org/docs/v0.18.0/Animation/2_Bone_IK Sets up initial bone lengths, root, target, and control points for the IK solver, and defines a point size for interaction. This function is called once when the LOVR application starts. ```lua function lovr.load() boneLengths = { .3, .3 } root = lovr.math.newVec3(-.2, 1.5, -.5) target = lovr.math.newVec3(.2, 1.5, -.7) control = lovr.math.newVec3(0, 1.8, -.6) pointSize = .04 drags = {} end ``` -------------------------------- ### Start a thread with arguments Source: https://lovr.org/docs/v0.18.0/Thread%3Astart Example of creating a new thread and passing the LÖVR version as an argument. ```lua function lovr.load() lovr.thread.newThread([[ print(...) ]]):start(lovr.getVersion()) end ``` -------------------------------- ### Buffer data formatting examples Source: https://lovr.org/docs/Buffer%3AgetData Examples demonstrating how different buffer formats are mapped to Lua tables. ```lua buffer = lovr.graphics.newBuffer('int', { 7 }) buffer:getData() --> returns { 7 } buffer = lovr.graphics.newBuffer('vec3', { 7, 8, 9 }) buffer:getData() --> returns {{ 7, 8, 9 }} buffer = lovr.graphics.newBuffer('int', { 1, 2, 3 }) buffer:getData() --> returns { 1, 2, 3 } buffer = lovr.graphics.newBuffer({ 'vec2', 'vec2' }, { vec2(1,2), vec2(3,4), vec2(5,6), vec2(7,8) }) buffer:getData() --> returns { { 1, 2, 3, 4 }, { 5, 6, 7, 8 } } buffer = lovr.graphics.newBuffer({ { 'a', 'float' }, { 'b', 'float' } }, { a = 1, b = 2 }) buffer:getData() --> returns { { 1, 2 } } buffer = lovr.graphics.newBuffer({ { 'x', 'int', 3 } }, { x = { 1, 2, 3 } }) buffer:getData() --> returns { { x = { 1, 2, 3 } } } buffer = lovr.graphics.newBuffer({ { 'lights', { { 'pos', 'vec3' }, { 'size', 'float' }, }, 10} }, data) buffer:getData() --> returns { { lights = { { pos = ..., size = ... }, ... } } } ``` -------------------------------- ### Thread:start syntax Source: https://lovr.org/docs/v0.18.0/Thread%3Astart The basic syntax for starting a thread with optional arguments. ```lua Thread:start(...arguments) ``` -------------------------------- ### Install Linux Dependencies Source: https://lovr.org/docs/v0.18.0/Compiling Install required build tools and libraries on Debian/Ubuntu and Fedora systems. ```bash $ sudo apt install make cmake xorg-dev libcurl4-openssl-dev libxcb-glx0-dev libx11-xcb-dev python3-minimal ``` ```bash $ sudo dnf install cmake clang libX11-devel libXrandr-devel libXinerama-devel libXcursor-devel libXi-devel libcurl-devel ``` -------------------------------- ### Setup Physics World and Create Objects Source: https://lovr.org/docs/Callbacks_and_Modules Initializes a physics world, creates a ground plane, and stacks boxes. This setup is for a physics-based scene where objects can interact. ```lua 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 ``` -------------------------------- ### Thread:start Source: https://lovr.org/docs/v0.18.0/Thread%3Astart Starts the execution of a LÖVR thread with optional arguments. ```APIDOC ## Thread:start ### Description Starts the Thread. The arguments can be nil, booleans, numbers, strings, or LÖVR objects. ### Method Method call on Thread object ### Parameters #### Arguments - **...arguments** (*) - Optional - Up to 4 arguments to pass to the Thread's function. ### Request Example ```lua function lovr.load() lovr.thread.newThread([[ print(...) ]]):start(lovr.getVersion()) end ``` ### Response #### Success Response - **Returns** (Nothing) - This method does not return a value. ``` -------------------------------- ### Install libcurl on Debian Source: https://lovr.org/docs/v0.18.0/http System command to install the required curl library dependency on Linux. ```bash sudo apt install libcurl4 ``` -------------------------------- ### Example of Pass:barrier Usage Source: https://lovr.org/docs/Pass%3Abarrier This example demonstrates how to use Pass:barrier() to synchronize multiple compute operations. The third compute call will wait for the first two to finish. ```lua pass = lovr.graphics.newPass() pass:setShader(computeShader) pass:compute(x, y, z) pass:compute(x, y, z) pass:barrier() pass:compute(x, y, z) --> waits for the previous 2 :computes to complete ``` -------------------------------- ### Install LÖVR APK using ADB Source: https://lovr.org/docs/v0.18.0/Compiling Install the generated lovr.apk onto a connected Android device using the Android Debug Bridge (ADB). Ensure a device is listed by 'adb devices' before running the install command. ```bash adb devices ``` ```bash adb install -r lovr.apk ``` -------------------------------- ### Create and Start a Thread in LOVR Source: https://lovr.org/docs/v0.18.0/Intro/Thread This snippet demonstrates how to define thread code, create a channel for communication, instantiate a new thread, and start its execution within the LOVR engine. Ensure the thread code is correctly formatted as a string literal. ```lua function lovr.load() -- This holds the thread code -- This must be wrapped with [[]] or '' to allow the engine to run it as Lua threadCode = [[ local lovr = { thread = require 'lovr.thread' } local channel = lovr.thread.getChannel('test') local x = 0 while true do x = x + 1 channel:push(x) end ]] -- Create a new test channel channel = lovr.thread.getChannel('test') -- Create a new thread called 'thread' using the code above thread = lovr.thread.newThread(threadCode) -- Start the thread thread:start() end function lovr.update(dt) -- Read and delete the message message = channel:pop() end function lovr.draw(pass) -- Display the message on screen/headset pass:text(tostring(message), 0, 1.7, -5) end ``` -------------------------------- ### Get Sound sample format Source: https://lovr.org/docs/v0.18.0/Sound%3AgetFormat Returns the SampleFormat of the sound data. ```lua format = Sound:getFormat() ``` -------------------------------- ### Mesh Vertex Update Example Source: https://lovr.org/docs/v0.18.0/Mesh%3AsetVertices This example demonstrates how vertex data is updated. Note that only the final state of the vertices before the `pass:draw` call is used. Multiple calls to `setVertices` before a draw call will result in the last set of vertices being drawn. ```lua function lovr.draw(pass) -- Due to the second :setVertices call below, the Mesh -- contains a sphere when this pass is submitted! So -- this code will actually draw 2 spheres! mesh:setVertices(cube) pass:draw(mesh, x1, y1, z1) mesh:setVertices(sphere) pass:draw(mesh, x2, y2, z2) end ``` -------------------------------- ### ENet Echo Client Example Source: https://lovr.org/docs/v0.18.0/enet A client that connects to an ENet server, sends a 'hello world' message, and waits for a response. Requires the ENet plugin to be set up. ```lua -- 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 ``` -------------------------------- ### Get Mesh Draw Range Source: https://lovr.org/docs/v0.18.0/Mesh%3AgetDrawRange Retrieves the start index, count, and offset of the vertices currently set to be drawn. ```lua start, count, offset = Mesh:getDrawRange() Mesh:getDrawRange() ``` -------------------------------- ### Initialize Buffer with Shader Format Source: https://lovr.org/docs/v0.18.0/Shader%3AgetBufferFormat This example shows how to use the results of Shader:getBufferFormat to create a new buffer that matches the shader's buffer definition. You can also provide initial data. ```lua shader = lovr.graphics.newShader([[ uniform Transforms { mat4 transforms[32]; }; vec4 lovrmain() { return Projection * View * transforms[InstanceIndex] * VertexPosition; } ]], 'unlit') local format, length = shader:getBufferFormat('Transforms') print(length) --> 32 print(format[1].name) --> 'transforms' print(format[1].type) --> 'mat4' -- Can pass the 2 results directly to newBuffer: transforms = lovr.graphics.newBuffer(shader:getBufferFormat('Transforms')) -- Or override the length with some initial data: transforms = lovr.graphics.newBuffer(shader:getBufferFormat('Transforms'), objects) ``` -------------------------------- ### World:setCallbacks Source: https://lovr.org/docs/World%3AsetCallbacks Assigns collision callbacks to the world. These callbacks are used to filter collisions or get notifications when colliders start or stop touching. Callbacks are called during World:update. ```APIDOC ## World:setCallbacks ### Description Assigns collision callbacks to the world. These callbacks are used to filter collisions or get notifications when colliders start or stop touching. Callbacks are called during `World:update`. ### Filter Filters collisions. Receives two colliders and returns a boolean indicating if they should collide. Note that it is much faster to use tags and `World:enableCollisionBetween` to control collision. This should only be used when the logic for filtering the collision is highly dynamic. ### Enter Called when two colliders begin touching. Receives two colliders and a `Contact` object with more information about the collision. The `contact` callback will also be called for this collision. ### Exit Called when two colliders stop touching. Receives two colliders. ### Contact Called continuously while two colliders are touching. Receives two colliders and a `Contact` object with more information about the collision. The contact can also be disabled to disable the collision response, and its friction/resitution/velocity can be changed. There can be multiple active contact areas (called "manifolds") between a pair of colliders; this callback will be called for each one. ### Method `World:setCallbacks(callbacks)` ### Parameters #### Arguments - **callbacks** (table) - The World collision callbacks. - **.filter** (function) - The function to use to filter collisions. - **.enter** (function) - The function to call when 2 colliders start touching. - **.exit** (function) - The function to call when 2 colliders stop touching. - **.contact** (function) - The function to call every frame while 2 colliders are in contact. ### Returns Nothing ### Notes The `Thread` that last set these callbacks must also be the thread to call `World:update`. Note that when a collider goes to sleep, its active contacts will be removed and the `exit` callback will be called. ### Example ```lua world:setCallbacks({ filter = function(a, b) return true end, enter = function(a, b, contact) -- play sounds, spawn particles, etc. -- the collision has not been resolved yet, so the velocity of a and b -- is the velocity before the collision, and can be used to estimate the -- collision force end, exit = function(a, b) -- a and b have stopped touching! end, contact = function(a, b, contact) -- a and b are touching this frame -- use sparingly, as this may be called many times per frame -- use Contact:setFriction and Contact:setResitution to update -- the contact behavior, or Contact:setSurfaceVelocity, for a -- conveyor belt effect, or Contact:setEnabled to disable the -- collision completely. end }) ``` ### See also * `World:update` * `Contact` * `World:getCallbacks` * `World` ``` -------------------------------- ### Initialize Submodules Source: https://lovr.org/docs/v0.18.0/Compiling Ensure all required dependencies are cloned into the repository. ```bash git submodule update --init --recursive ``` -------------------------------- ### Get Signed 32-bit Integers from Blob Source: https://lovr.org/docs/v0.18.0/Blob%3AgetI32 Use this function to read a specified number of signed 32-bit integers from a Blob, starting at a given byte offset. Ensure the offset is non-negative. The integers returned will be within the range of -2147483648 to 2147483647. ```lua ... = Blob:getI32(offset, count) ``` -------------------------------- ### Initialize FPS Camera and Mouse Input Source: https://lovr.org/docs/v0.18.0/Flatscreen/FPS_Controls Sets up the mouse module for relative mode and initializes camera properties like position, speed, and rotation. Requires the 'lovr-mouse' module. ```lua lovr.mouse = require 'lovr-mouse' function lovr.load() lovr.mouse.setRelativeMode(true) camera = { transform = lovr.math.newMat4(), position = lovr.math.newVec3(), movespeed = 10, pitch = 0, yaw = 0 } end ``` -------------------------------- ### Set up a Sensor Collider and Collision Callbacks Source: https://lovr.org/docs/v0.18.0/Collider%3AisSensor This example demonstrates creating a sensor collider and defining a callback function to handle collision entry events. The callback checks for interactions between the sensor and the player object. ```lua danger = world:newBoxCollider(x, y, z, width, height, depth) danger:setKinematic(true) danger:setSensor(true) world:setCallbacks({ enter = function(a, b) if (a == danger and b == player) or (a == player and b == danger) then damagePlayer() end end }) ``` -------------------------------- ### Initialize Graphics Pipeline Source: https://lovr.org/docs/v0.18.0/Lighting/Shadows Sets up the graphics pipeline by initializing shaders, background color, and creating necessary textures and passes for rendering and shadow mapping. Handles differences between VR and windowed modes. ```lua function lovr.load(args) shader = lighting_shader() lovr.graphics.setBackgroundColor(0x4782B3) local shadow_map_format = debug_render_from_light and 'rgba8' or 'd32f' shadow_map_texture = lovr.graphics.newTexture(shadow_map_size, shadow_map_size, { format = shadow_map_format, linear = false, mipmaps = false }) shadow_map_sampler = lovr.graphics.newSampler({ wrap = 'clamp' }) if lovr.headset then local width, height = lovr.headset.getDisplayDimensions() local layers = lovr.headset.getViewCount() render_texture = lovr.graphics.newTexture(width, height, layers, { mipmaps = false }) else local width, height = lovr.system.getWindowDimensions() render_texture = lovr.graphics.newTexture(width, height, 1, { mipmaps = false }) end if debug_render_from_light then shadow_map_pass = lovr.graphics.newPass({ shadow_map_texture, samples = 1 }) else shadow_map_pass = lovr.graphics.newPass({ depth = shadow_map_texture, samples = 1 }) shadow_map_pass:setClear({ depth = light_orthographic and 1 or 0 }) end lighting_pass = lovr.graphics.newPass(render_texture) end ``` -------------------------------- ### Generate a sine wave Source: https://lovr.org/docs/Sound%3AsetFrames Example demonstrating how to populate a Sound object with generated sine wave data and play it as an audio source. ```lua function lovr.load() local length = 1 local rate = 48000 local frames = length * rate local frequency = 440 local volume = 1.0 sound = lovr.data.newSound(frames, 'f32', 'stereo', rate) local data = {} for i = 1, frames do local amplitude = math.sin((i - 1) * frequency / rate * (2 * math.pi)) * volume data[2 * i - 1] = amplitude data[2 * i - 0] = amplitude end sound:setFrames(data) source = lovr.audio.newSource(sound) source:setLooping(true) source:play() end ``` -------------------------------- ### Get Collider Shape Source: https://lovr.org/docs/v0.18.0/Collider%3AgetShape Use this to get the primary shape of a collider. Returns nil if no shapes are attached. ```lua shape = Collider:getShape() ``` -------------------------------- ### Measure GPU Time for Rendering a Cube Source: https://lovr.org/docs/v0.18.0/Pass%3AgetStats This example demonstrates how to enable timing, render a cube, and then print the GPU time taken to render it using the statistics obtained from `pass:getStats()`. The `gpuTime` is in seconds and is converted to microseconds for display. ```lua lovr.graphics.setTimingEnabled(true) function lovr.draw(pass) pass:cube(0, 1.7, -1, .5, lovr.timer.getTime() * .2, 0, 1, 0) local stats = pass:getStats() print(('Rendering a cube takes %f microseconds'):format(stats.gpuTime * 1e6)) end ``` -------------------------------- ### Get Animation Smooth Mode by Name Source: https://lovr.org/docs/v0.18.0/ModelData%3AgetAnimationSmoothMode Use this to get the smooth mode of a channel in an animation when you have the animation's name. ```lua smooth = ModelData:getAnimationSmoothMode(name, channel) ``` -------------------------------- ### Get Animation Smooth Mode by Index Source: https://lovr.org/docs/v0.18.0/ModelData%3AgetAnimationSmoothMode Use this to get the smooth mode of a channel in an animation when you have the animation's index. ```lua smooth = ModelData:getAnimationSmoothMode(index, channel) ``` -------------------------------- ### Initialize Buffer with Alias Source: https://lovr.org/docs/v0.18.0/DataType Example of using a convenience alias when creating a new buffer. ```lua lovr.graphics.newBuffer(4, 'floats') ``` -------------------------------- ### Initialize Shadow Shader and Assets Source: https://lovr.org/docs/v0.18.0/Lighting/Circular_Shadows Sets up the 'unlit' vertex shader and a custom fragment shader for circular shadows. It also generates a procedural grass texture and initializes a list of boxes with random properties. ```lua function lovr.load() -- shadow shader shadowVertex = 'unlit' shadowFragment = [[ Constants { vec4 left; vec4 right; vec4 head; }; vec4 lovrmain() { vec4 shadowColor; if ( (length( left.xz - PositionWorld.xz) < left.w && left.y > PositionWorld.y) || (length(right.xz - PositionWorld.xz) < right.w && right.y > PositionWorld.y) || (length( head.xz - PositionWorld.xz) < head.w && head.y > PositionWorld.y) ) { shadowColor = vec4(.25, .625, 1, 1); } else { shadowColor = vec4(1, 1, 1, 1); } return shadowColor * Color * getPixel(ColorTexture, UV); } ]] shadowShader = lovr.graphics.newShader(shadowVertex, shadowFragment) -- grass texture grassTextureWidth = 256 grassImage = lovr.data.newImage(grassTextureWidth, grassTextureWidth) for x = 0, grassTextureWidth - 1 do for y = 0, grassTextureWidth - 1 do grassImage:setPixel(x, y, lovr.math.noise(x + .5, y + .5 + 0) / 2, lovr.math.noise(x + .5, y + .5 + 16) / 2 + .5, lovr.math.noise(x + .5, y + .5 + 32) / 2 ) end end grassTexture = lovr.graphics.newTexture(grassImage) -- boxes boxes = {} for i = 1, 5 do table.insert(boxes, { x = lovr.math.random() / 2, y = lovr.math.random() / 2 + .5, z = lovr.math.random() / 2, width = lovr.math.random() / 3, height = lovr.math.random() / 3, depth = lovr.math.random() / 3, r = lovr.math.random(), g = lovr.math.random(), b = lovr.math.random() }) end -- sky lovr.graphics.setBackgroundColor(0,.5,1) end ``` -------------------------------- ### Get Collider Orientation Source: https://lovr.org/docs/v0.18.0/Collider%3AgetOrientation Use this to get the current orientation of a collider. If World:interpolate has been called, this returns an interpolated orientation. ```lua angle, ax, ay, az = Collider:getOrientation() ``` -------------------------------- ### Get Bounding Box of Glyph or All Glyphs Source: https://lovr.org/docs/v0.18.0/Rasterizer%3AgetBoundingBox Call this function with a character, codepoint, or no arguments to get the bounding box. The coordinates are in pixels, with 'y up'. ```lua x1, y1, x2, y2 = Rasterizer:getBoundingBox(character) ``` ```lua x1, y1, x2, y2 = Rasterizer:getBoundingBox(codepoint) ``` ```lua x1, y1, x2, y2 = Rasterizer:getBoundingBox() ``` -------------------------------- ### Setup Physics World and Colliders Source: https://lovr.org/docs/v0.18.0/Callbacks_and_Modules Initializes a physics world, creates a ground plane, and stacks boxes. Also prepares colliders for controllers. The physics world is updated in lovr.update. ```lua 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, hand in ipairs(lovr.headset.getHands()) do if not controllerBoxes[i] then controllerBoxes[i] = world:newBoxCollider(0, 0, 0, .25) controllerBoxes[i]:setKinematic(true) end controllerBoxes[i]:setPosition(lovr.headset.getPosition(hand)) controllerBoxes[i]:setOrientation(lovr.headset.getOrientation(hand)) end -- Update the physics simulation world:update(dt) end -- A helper function for drawing boxes function drawBox(pass, box) local x, y, z = box:getPosition() pass:cube(x, y, z, .25, quat(box:getOrientation()), 'line') end function lovr.draw(pass) pass:setColor(1.0, 0, 0) for i, box in ipairs(boxes) do drawBox(pass, box) end pass:setColor(0, 0, 1.0) for i, box in ipairs(controllerBoxes) do drawBox(pass, box) end end ``` -------------------------------- ### Basic LÖVR Callbacks: Load, Update, Draw Source: https://lovr.org/docs/Callbacks_and_Modules Defines the essential `lovr.load`, `lovr.update`, and `lovr.draw` callbacks. Use `lovr.load` for one-time setup, `lovr.update` for continuous logic, and `lovr.draw` for rendering. ```lua 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(pass) -- This is called once every frame. -- -- You can call functions on the pass to render graphics. print('rendering') end ``` -------------------------------- ### Get Glyph Bearing by Codepoint Source: https://lovr.org/docs/v0.18.0/Rasterizer%3AgetBearing Use this to get the bearing of a glyph when you have its codepoint. The bearing is the horizontal distance from the cursor to the edge of the glyph. ```lua bearing = Rasterizer:getBearing(codepoint) ``` -------------------------------- ### Buffer:setData examples Source: https://lovr.org/docs/Buffer%3AsetData Demonstrates various ways to use Buffer:setData with different data types and structures, including integers, vectors, structs, and nested formats. ```lua function lovr.load() buffer = lovr.graphics.newBuffer('int', 3) buffer:setData({ 1, 2, 3 }) buffer = lovr.graphics.newBuffer('vec3', 2) buffer:setData({ 1,2,3, 4,5,6 }) buffer:setData({ { 1, 2, 3 }, { 4, 5, 6 } }) buffer:setData({ vec3(1, 2, 3), vec3(4, 5, 6) }) -- When the Buffer's length is 1, wrapping in table is optional: buffer = lovr.graphics.newBuffer('vec3') buffer:setData(1, 2, 3) buffer:setData(vec3(1, 2, 3)) -- Same for key-value structs buffer = lovr.graphics.newBuffer({ { 'x', 'float' }, { 'y', 'float' } }) buffer:setData({ x = 1, y = 2 }) -- Key/value formats buffer = lovr.graphics.newBuffer({ { 'x', 'float' }, { 'y', 'float' } }, 3) buffer:setData({ { x = 1, y = 2 }, { x = 3, y = 4 }, { x = 5, y = 6 } }) buffer:setData({ 1, 2, 3, 4, 5, 6 }) buffer:setData({ { 1, 2 }, { 3, 4 }, { 5, 6 } }) -- Nested formats buffer = lovr.graphics.newBuffer({ { 'a', { {'b', { 'c', 'float' }} }} }) buffer:setData({ a = { b = { c = 42 } } }) -- Inner arrays buffer = lovr.graphics.newBuffer({ { 'positions', 'vec3', 10 }, { 'sizes', 'float', 10 }, layout = 'std140' }) local data = { positions = {}, sizes = {} } for i = 1, buffer:getLength() do data.positions[i] = vec3(i, i, i) data.sizes[i] = i end buffer:setData(data) end ``` -------------------------------- ### Get Node Name by Index Source: https://lovr.org/docs/v0.18.0/ModelData%3AgetNodeName Use this function to get the name of a node using its index. Returns nil if the node does not have a name. ```lua name = ModelData:getNodeName(index) ``` -------------------------------- ### Get Collider for a Shape Source: https://lovr.org/docs/v0.18.0/Shape%3AgetCollider Call this function on a Shape object to get the Collider it is currently attached to. Returns nil if the Shape is not attached to any Collider. ```lua collider = Shape:getCollider() ``` -------------------------------- ### Using Shader:getBufferFormat with lovr.graphics.newBuffer Source: https://lovr.org/docs/Shader%3AgetBufferFormat This example shows how to use the results of Shader:getBufferFormat directly with lovr.graphics.newBuffer to create a buffer that matches the shader's requirements. You can also optionally provide initial data. ```lua shader = lovr.graphics.newShader([[ uniform Transforms { mat4 transforms[32]; }; vec4 lovrmain() { return Projection * View * transforms[InstanceIndex] * VertexPosition; } ]], 'unlit') local format, length = shader:getBufferFormat('Transforms') print(length) print(format[1].name) print(format[1].type) -- Can pass the 2 results directly to newBuffer: transforms = lovr.graphics.newBuffer(shader:getBufferFormat('Transforms')) -- Or override the length with some initial data: transforms = lovr.graphics.newBuffer(shader:getBufferFormat('Transforms'), objects) ``` -------------------------------- ### Read and Print File Bytes Source: https://lovr.org/docs/v0.18.0/Blob%3AgetString This example demonstrates how to read the entire content of a file into a Blob, then convert it to a string and print each byte individually. Ensure the 'main.lua' file exists in the appropriate directory. ```lua blob = lovr.filesystem.newBlob('main.lua') str = blob:getString() for i = 1, #str do print(string.byte(str, i)) end ``` -------------------------------- ### Get Glyph Bearing by Character Source: https://lovr.org/docs/v0.18.0/Rasterizer%3AgetBearing Use this to get the bearing of a glyph when you have its character representation. The bearing is the horizontal distance from the cursor to the edge of the glyph. ```lua bearing = Rasterizer:getBearing(character) ``` -------------------------------- ### Get Blend Shape Name by Index Source: https://lovr.org/docs/v0.18.0/ModelData%3AgetBlendShapeName Use this function to get the name of a blend shape. An error is thrown if the provided index is out of bounds. ```lua name = ModelData:getBlendShapeName(index) ``` -------------------------------- ### Create Shader from File or Default Source: https://lovr.org/docs/v0.18.0/Shaders Load shader code from a file or use a built-in default shader. ```lua shader = lovr.graphics.newShader('vertex.glsl', 'unlit') ``` -------------------------------- ### Get Joints Attached to a Collider Source: https://lovr.org/docs/v0.18.0/Collider%3AgetJoints Call this function on a Collider object to get a table containing all Joint objects connected to it. No arguments are required. ```lua joints = Collider:getJoints() ``` -------------------------------- ### ENet Echo Server Example Source: https://lovr.org/docs/v0.18.0/enet A server that listens for ENet connections on localhost:6789, receives messages, and echoes them back to the sender. Requires the ENet plugin to be set up. ```lua -- 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 ``` -------------------------------- ### Get Readback Data as Blob Source: https://lovr.org/docs/Readback%3AgetBlob Use this function to get the Readback's data as a Blob. Returns nil if the Readback is processing a Texture. ```lua blob = Readback:getBlob() ``` -------------------------------- ### Get HingeJoint Angle Source: https://lovr.org/docs/v0.18.0/HingeJoint%3AgetAngle Call this function to get the current angle of the HingeJoint. The angle is measured in radians relative to the joint's rest position. ```lua angle = HingeJoint:getAngle() ``` -------------------------------- ### Create Project Archive (Unix) Source: https://lovr.org/docs/v0.18.0/Distribution Use the `zip` utility to create a compressed archive of your LÖVR project files. Ensure you are in the project directory and zip all contents, not the folder itself. The `.lovr` extension is conventional. ```bash cd /path/to/project zip -9qr project.lovr . ``` -------------------------------- ### Get Vertices for Plain String Source: https://lovr.org/docs/v0.18.0/Font%3AgetVertices Use this to get vertices and material for a simple string. The wrap, halign, and valign arguments control text layout. ```lua vertices, material = Font:getVertices(string, wrap, halign, valign) ``` -------------------------------- ### Initialize Teleportation Motion Parameters Source: https://lovr.org/docs/v0.18.0/Locomotion/Teleportation_Flat Sets up initial parameters for VR motion, including thumbstick deadzones, snap turn angles, and teleportation distances. These values control the responsiveness and behavior of the VR controls. ```lua local motion = { pose = lovr.math.newMat4(), -- Transformation in VR initialized to origin (0,0,0) looking down -Z thumbstickDeadzone = 0.4, -- Smaller thumbstick displacements are ignored (too much noise) -- Snap motion parameters snapTurnAngle = 2 * math.pi / 12, thumbstickCooldownTime = 0.3, thumbstickCooldown = 0, -- Teleport motion parameters teleportDistance = 12, blinkTime = 0.5, blinkStopwatch = math.huge, teleportValid = false, targetPosition = lovr.math.newVec3(), teleportCurve = lovr.math.newCurve(3), } lovr.graphics.setBackgroundColor(0.1, 0.1, 0.1) ``` -------------------------------- ### Initialize collider in lovr.load Source: https://lovr.org/docs/v0.18.0/World%3AnewCollider Demonstrates creating a world, adding a collider, and attaching a sphere shape during the load phase. ```lua function lovr.load() world = lovr.physics.newWorld() collider = world:newCollider(0, 0, 0) shape = lovr.physics.newSphereShape(.5) collider:addShape(shape) end ``` -------------------------------- ### Get Pass Statistics Source: https://lovr.org/docs/v0.18.0/Pass%3AgetStats Retrieves a table containing various statistics for the Pass. Ensure timing is enabled to get accurate GPU and CPU submit times. ```lua stats = Pass:getStats() ``` -------------------------------- ### Initialize Newton's Cradle Simulation Source: https://lovr.org/docs/v0.18.0/Physics/Newtons_Cradle Sets up the physics world, creates the static frame, and initializes the balls with distance joints. The last ball is displaced to start the motion. Requires `lovr.physics` and `lovr.math` modules. ```lua -- Newton's cradle: array of balls suspended from two strings, demonstrating conservation of energy / momentum -- Strings are modeled with distance joints, which means they behave more like rods. local world local frame local framePose local balls = {} local count = 10 local radius = 1 / count / 2 -- small air gap between balls results in collisions in separate frames, to carry impulse through to last ball -- without this gap the physics engine would need to calculate transfer of impulses between contacts local gap = 0.01 function lovr.load() world = lovr.physics.newWorld({ restitutionThreshold = .05 }) -- a static geometry from which balls are suspended local size = vec3(1.2, 0.1, 0.3) frame = world:newBoxCollider(vec3(0, 2, -2), size) frame:setKinematic(true) framePose = lovr.math.newMat4(frame:getPose()):scale(size) -- create balls along the length of frame and attach them with two distance joints to frame for x = -0.5, 0.5, 1 / count do local ball = world:newSphereCollider(vec3(x, 1, -2), radius - gap) ball:setRestitution(1.0) table.insert(balls, ball) lovr.physics.newDistanceJoint(frame, ball, vec3(x, 2, -2 + 0.25), vec3(x, 1, -2)) lovr.physics.newDistanceJoint(frame, ball, vec3(x, 2, -2 - 0.25), vec3(x, 1, -2)) end -- displace the last ball to set the Newton's cradle in motion local lastBall = balls[#balls] lastBall:applyLinearImpulse(.6, 0, 0) lovr.graphics.setBackgroundColor(0.1, 0.1, 0.1) end ``` -------------------------------- ### Get Model Center Coordinates Source: https://lovr.org/docs/v0.18.0/Model%3AgetCenter Call this function to get the center of the Model's axis-aligned bounding box. The coordinates are relative to the Model's origin. ```lua x, y, z = Model:getCenter() ``` -------------------------------- ### Get Parent Node by Name Source: https://lovr.org/docs/v0.18.0/Model%3AgetNodeParent Use this function to get the parent index when you know the child node's name. Ensure the name is a valid string. ```lua parent = Model:getNodeParent(name) ``` -------------------------------- ### Implement Shadow Mapping in LÖVR Source: https://lovr.org/docs/v0.18.0/Lighting/Shadows This example provides a complete implementation of shadow mapping, including scene rendering, custom lighting shaders, and shadow map pass configuration. ```lua --[[ Example of basic shadow mapping in LÖVR Ben Porter, 2022 ]] local z = -2 local light_pos = lovr.math.newVec3(3.0, 4.0, z) local light_orthographic = false -- Use orthographic light local shadow_map_size = 2048 local debug_render_from_light = false -- Enable to render scene from light local debug_show_shadow_map = false -- Enable to view shadow map in overlap local shader, render_texture local shadow_map_texture, shadow_map_sampler local light_space_matrix local shadow_map_pass, lighting_pass local function render_scene(pass) { local t = lovr.timer.getTime() pass:push() pass:setColor(0xCBC1AD) pass:circle(0, 0, z, 5, -math.pi / 2, 1, 0, 0, 'fill', 0, 2 * math.pi, 256) local count = 2 local min, max = -(count - 1) / 2, (count - 1) / 2 pass:setColor(0xEEEEEE) pass:translate(0, -min + .55, z) for i = min, max do for j = min, max do for k = min, max do pass:push() pass:translate(i, j, k) pass:rotate(t + i * 0.3 + j * 0.3, 1, 1, 1) pass:scale(0.45) if i + j % 2 == 1 then pass:box() else pass:scale(0.7) pass:sphere() end pass:pop() end end end pass:pop() } local function lighting_shader() { local vs = [[ vec4 lovrmain() { return Projection * View * Transform * VertexPosition; } ]] local fs = [[ uniform vec3 lightPos; uniform mat4 lightSpaceMatrix; uniform bool lightOrthographic; uniform texture2D shadowMapTexture; vec4 diffuseLighting(vec3 lightDir, vec3 normal, float shadow) { float diff = max(dot(normal, lightDir), 0.0); vec4 diffuse = diff * vec4(1.0, 1.0, 0.8, 1.0); vec4 baseColor = Color * getPixel(ColorTexture, UV); vec4 ambience = vec4(0.05, 0.05, 0.1, 1.0); return baseColor * (ambience + (1 - shadow) * diffuse); } // Falloff shadow near edge of light bounds/frustum float shadowFalloff(vec2 uv) { const float margin = 0.05; uv = clamp(uv, vec2(0,0), vec2(1,1)); float dx = 1; if (uv.x < margin) dx = uv.x / margin; else if (uv.x > 1 - margin) dx = ( 1 - uv.x ) / margin; float dy = 1; if (uv.y < margin) dy = uv.y / margin; else if (uv.y > 1 - margin) dy = ( 1 - uv.y ) / margin; return dx * dy; } vec4 lovrmain() { vec3 lightDir = normalize(lightPos - PositionWorld); vec3 normal = normalize(Normal); vec4 positionLightSpace = lightSpaceMatrix * vec4(PositionWorld, 1); vec3 positionLightSpaceProj = 0.5 * (positionLightSpace.xyz / positionLightSpace.w) + 0.5; vec4 shadowMap = getPixel(shadowMapTexture, positionLightSpaceProj.xy); float closestDepth = shadowMap.r * 0.5 + 0.5; float currentDepth = positionLightSpaceProj.z; float bias = max(0.05 * (1.0 - dot(normal, lightDir)), 0.005); float falloff = shadowFalloff(positionLightSpaceProj.xy); float shadow; if (lightOrthographic) { shadow = ((currentDepth - bias) >= closestDepth) ? 1.0 : 0.0; } else { shadow = ((currentDepth + bias) <= closestDepth) ? 1.0 : 0.0; } return diffuseLighting(lightDir, normal, falloff * shadow); } ]] return lovr.graphics.newShader(vs, fs, {}) } local function render_shadow_map(draw) { local near_plane = 2 local projection if light_orthographic then local radius = 3 local far_plane = 15 projection = mat4():orthographic(-radius, radius, -radius, radius, near_plane, far_plane) else projection = mat4():perspective(math.pi / 3, 1, near_plane) end local view = mat4():lookAt(light_pos, vec3(0, 1, z)) light_space_matrix = mat4(projection):mul(view) shadow_map_pass:reset() shadow_map_pass:setProjection(1, projection) shadow_map_pass:setViewPose(1, view, true) if light_orthographic then -- Note for ortho projection with a far plane the depth coord is reversed shadow_map_pass:setDepthTest('lequal') end if debug_render_from_light then shadow_map_pass:setShader(shader) shadow_map_pass:send('lightPos', light_pos) end draw(shadow_map_pass) } local function render_lighting_pass(draw) { lighting_pass:reset() if lovr.headset then for i = 1, lovr.headset.getViewCount() do lighting_pass:setViewPose(i, lovr.headset.getViewPose(i)) lighting_pass:setProjection(i, lovr.headset.getViewAngles(i)) end else local t = lovr.timer.getTime() lighting_pass:setViewPose(1, 0, 3 - math.sin(t * 0.1), 4, -math.pi / 8, 1, 0, 0) end lighting_pass:setShader(shader) lighting_pass:setSampler(shadow_map_sampler) lighting_pass:send('shadowMapTexture', shadow_map_texture) lighting_pass:send('lightPos', light_pos) lighting_pass:send('lightSpaceMatrix', light_space_matrix) lighting_pass:send('lightOrthographic', light_orthographic) draw(lighting_pass) lighting_pass:setShader() lighting_pass:setColor(1, 1, 1, 1) ``` -------------------------------- ### Get Parent Node by Index Source: https://lovr.org/docs/v0.18.0/Model%3AgetNodeParent Use this function to get the parent index when you know the child node's index. Ensure the index is a valid number. ```lua parent = Model:getNodeParent(index) ``` -------------------------------- ### Get Curve Tangent Direction Source: https://lovr.org/docs/v0.18.0/Curve%3AgetTangent Use this function to get the direction vector of the Curve at a specific point t (0 to 1). The returned vector will have a length of one. ```lua x, y, z = Curve:getTangent(t) ``` -------------------------------- ### Sending shader data in LÖVR Source: https://lovr.org/docs/Pass%3Asend Example demonstrating how to initialize shader resources and send them to the shader during the draw pass. ```lua function lovr.load() shader = lovr.graphics.newShader([[ uniform sampler mySampler; uniform Colors { vec4 colors[256]; }; uniform texture2D rocks; uniform uint constant; vec4 lovrmain() { return DefaultPosition; } ]], 'unlit') clampler = lovr.graphics.newSampler({ wrap = 'clamp' }) colorBuffer = lovr.graphics.newBuffer(256, 'vec4') rockTexture = lovr.graphics.newTexture('rocks.jpg') end function lovr.draw(pass) pass:setShader(shader) pass:send('mySampler', clampler) pass:send('Colors', colorBuffer) pass:send('rocks', rockTexture) pass:send('constant', 42) -- Draw end ``` -------------------------------- ### LOVR Initialization and Terrain Setup Source: https://lovr.org/docs/v0.18.0/Environment/Terrain_-_Procedural This Lua code initializes the LOVR environment, sets the background color, compiles the shaders, generates the terrain mesh with noise-based height, and sets up the mesh. ```lua function lovr.load() skyColor = {0.208, 0.208, 0.275} lovr.graphics.setBackgroundColor(skyColor) shader = lovr.graphics.newShader(unpack(shaderCode)) local vertices, indices = grid(100) local offset = lovr.math.noise(0, 0) -- ensure zero height at origin for vi = 1, #vertices do local x,y,z = unpack(vertices[vi]) z = (lovr.math.noise(x * 10, y * 10) - offset) / 20 vertices[vi][1], vertices[vi][2], vertices[vi][3] = x, y, z end mesh = lovr.graphics.newMesh({{ 'VertexPosition', 'vec3' }}, vertices) mesh:setIndices(indices) end ``` -------------------------------- ### Get Collider Bounding Box Source: https://lovr.org/docs/v0.18.0/Collider%3AgetAABB Returns the world-space axis-aligned bounding box of the Collider, computed from attached shapes. Use this to get the spatial extent of a collider for queries. ```lua minx, maxx, miny, maxy, minz, maxz = Collider:getAABB() ```