LogoObjToSchematic Wiki

Lua Scripting

How to write Lua scripts for use in OtS.

Scripting allows you to write custom Lua scripts to programatically manipulate voxel builds and block builds. OtS provides APIs for reading and writing voxels/blocks that can be executed to create a build or applied as a modifier on an existing build.

When scripts are applied as modifiers, you have access to the current state of the voxel/block structure via functions such as ots.voxels.get and ots.blocks.get. When a script is applied from scratch there will be no blocks unless you add them yourself.

Knowledge of basic Lua programming is required. If you need help getting started learning Lua, visit https://www.lua.org/start.html.

OtS includes a full Lua 5.4.7 interpreter which includes support for tables, functions, among other features. The standard libraries that are enabled include:

  • basic library,
  • math library,
  • string library,
  • table library,
  • utf8 library.

Setting up VSCode

Using VSCode is not required. This guide was written for VSCode 1.99.3.

Lua files should be written in a traditional text editor and then imported into OtS. Currently OtS does not have a built-in code editor. Below are steps on how to get VSCode setup to recognise the OtS API to provide useful Intellisense information when writing your scripts.

Download VSCode, if you haven't already.

Go to Extensions and download the 'Lua' extension by sumneko to provide a language server for Lua as VSCode does not include one out-the-box.

Download the provided workspace file which includes type definitions for the OtS library.

In VSCode, go to File > Open Workspace from File... and select the downloaded workspace file.

You're ready to start scripting! Create a new file in that folder with the .lua extension and start writing your script.

For example scripts, jump to the examples or continue reading for the API overview.

Voxel Scripts

Voxel scripts operate on voxels and the API is provided in the ots.voxels namespace.

API

The latest API version is v2. Older API versions will be marked as deprecated but will remain available for use.

Util type annotations

---@class Position
---@field x integer
---@field y integer
---@field z integer
 
---@class Colour
---@field r integer (0 <= r <= 255)
---@field g integer (0 <= g <= 255)
---@field b integer (0 <= b <= 255)
 
---@class Block
---@field xyz Position
---@field name string

ots.voxels.set - Place a voxel at a location with a colour

---@param position Position "The location to place the voxel at"
---@param colour Colour "The colour to set the voxel as"
---@return nil
function ots.voxels.set(position, colour) end

ots.voxels.get - Get the voxel at a location

---@param position Position "The location to query the voxel at"
---@return Colour|nil "The colour of the voxel if one exists at the provided location, nil otherwise"
function ots.voxels.get(position) end

ots.voxels.get_all - Get a list of all voxels

---@return Voxel[]
function ots.voxels.get_all() end

ots.voxels.remove - Remove a voxel at a location

---@param position Position "The location to remove the voxel at"
---@return nil
function ots.voxels.remove(position) end

ots.voxels.remove_all - Remove all voxels

---@return nil
function ots.voxels.remove_all() end

ots.voxels.for_each - Iterate over all voxels and call the provided function on each

---@param func fun(voxel: Voxel) "The function to execute per voxel"
---@return nil
function ots.voxels.for_each(func) end

Deprecated APIs

Examples

Basic operations

-- place red, green, and blue voxels
ots.voxels.set({ x = 0, y = 0, z = 0 }, { r = 255, g = 0,   b = 0 })
ots.voxels.set({ x = 1, y = 0, z = 0 }, { r = 0,   g = 255, b = 0 })
ots.voxels.set({ x = 2, y = 0, z = 0 }, { r = 0,   g = 0,   b = 255 })
 
-- get the green voxel
local green_voxel = ots.voxels.get({ x = 1, y = 0, z = 0 })
 
-- remove the green voxel
ots.voxels.remove({ x = 1, y = 0, z = 0 })
 
-- re-add the green voxel
ots.voxels.set(green_voxel.position, green_voxel.colour)
 
-- remove all the voxels
ots.voxels.remove_all()

Loops

-- create a staircase
for i = 1, 10 do
    ots.voxels.set({ x = i, y = i, z = 0 }, { r = 255, g = 255, b = 255 })
end
-- create a circle with radius 20 blocks
for x = -20, 20 do
    for z = -20, 20 do
        local dist = math.sqrt(x*x + z*z)
 
        if dist < 20 then
            ots.voxels.set({ x = x, y = 0, z = z }, { r = 255, g = 255, b = 255 })
        end
    end
end

Functions

-- create a sphere with radius 20 blocks
function place_sphere (cx, cy, cz, r)
    for x = -r, r do
        for y = -r, r do
            for z = -r, r do
                dist = math.sqrt(x*x + y*y + z*z)
 
                if dist < r then
                    ots.set_voxel({ x=x+cx, y=y+cy, z=z+cy }, { r=255, g=255, b=255 })
                end
            end
        end
    end
end
 
-- draw 3 spheres
place_sphere(0, 0, 0, 20)
place_sphere(20, 0, 0, 20)
place_sphere(40, 0, 0, 20)

Custom modifiers

-- When applied to a voxel structure as a modifier will remove all dark blocks
ots.voxels.for_each(function (voxel)
    if (voxel.colour.r < 64 and voxel.colour.g < 64 and voxel.colour.b < 64) then
        ots.voxels.remove(voxel.position)
    end
end)

Block Scripts

Block scripts operate on blocks and the API is provided in the ots.blocks namespace.

API

The latest API version is v2. Older API versions will be marked as deprecated but will remain available for use.

Util type annotations

---@class Position
---@field x integer
---@field y integer
---@field z integer
 
---@class Colour
---@field r integer (0 <= r <= 255)
---@field g integer (0 <= g <= 255)
---@field b integer (0 <= b <= 255)
 
---@class Block
---@field xyz Position
---@field name string

ots.blocks.set - Place a block at a location with a colour

---@param position Position "The location to place the block at"
---@param block string "The block name to set the block as"
---@return nil
function ots.blocks.set(position, block) end

ots.blocks.get - Get the block at a location

---@param position Position "The location to query the block at"
---@return string|nil "The name of the block if one exists at the provided location, nil otherwise"
function ots.blocks.get(position) end

ots.blocks.get_all - Get a list of all blocks

---@return Block[]
function ots.blocks.get_all() end

ots.blocks.remove - Remove a block at a location

---@param position Position "The location to remove the block at"
---@return nil
function ots.blocks.remove(position) end

ots.blocks.remove_all - Remove all blocks

---@return nil
function ots.blocks.remove_all() end

ots.blocks.for_each - Iterate over all blocks and call the provided function on each

---@param func fun(block: Block) "The function to execute per block"
---@return nil
function ots.blocks.for_each(func) end

Deprecated APIs

Examples

Basic operations

-- place red, green, and blue voxels
ots.voxels.set({ x = 0, y = 0, z = 0 }, { r = 255, g = 0,   b = 0 })
ots.voxels.set({ x = 1, y = 0, z = 0 }, { r = 0,   g = 255, b = 0 })
ots.voxels.set({ x = 2, y = 0, z = 0 }, { r = 0,   g = 0,   b = 255 })
 
-- get the green voxel
local green_voxel = ots.voxels.get({ x = 1, y = 0, z = 0 })
 
-- remove the green voxel
ots.voxels.remove({ x = 1, y = 0, z = 0 })
 
-- re-add the green voxel
ots.voxels.set(green_voxel.position, green_voxel.colour)
 
-- remove all the voxels
ots.voxels.remove_all()

Loops

-- create a stone staircase
for i = 1, 10 do
    ots.blocks.set({ x = i, y = i, z = 0 }, "minecraft:stone")
end
-- create a dirt floor with radius 20 blocks
for x = -20, 20 do
    for z = -20, 20 do
        ots.blocks.set({ x = x, y = 0, z = z }, "minecraft:dirt")
    end
end

Functions

-- create a red heart
for x = -30, 30 do
    for y = -30, 30 do
        for z = -30, 30 do
            xf, yf, zf = x / 20.0, y / 20.0, z / 20.0
            f = (xf^2 + (9/4)*yf^2 + zf^2 - 1)^3 - xf^2 * zf^3 - (9/80)*yf^2 * zf^3
            if f <= 0 then
                ots.set_voxel({ x=math.floor(x), y=math.floor(y), z=math.floor(z) }, { r=255, g=0, b=0 })
            end
        end
    end
end

Custom modifiers

-- When applied to a voxel structure as a modifier will remove all stone blocks
ots.blocks.for_each(function (block)
    if (block.name == "minecraft:stone") then
        ots.blocks.remove(block.position)
    end
end)

Troubleshooting

Check your parenthesis are correct. For example, functions such as ots.voxels.set and ots.blocks.set expect 2 arguments that are tables. Check the type annotations and examples for help.

Positions should be integer values not floats Functions that require positions require the values are whole numbers (integers) instead of numbers with decimal places (floats). OtS will not automatically round the values for you. Your scripts will fail if you do not convert your floats to integers with functions like math.floor.

For example:

 
-- THIS WILL FAIL!
position_a = { x = 3.1, y = 4.1, z = 5.9 }
ots.blocks.set(position_a, "minecraft:stone")
 
-- This will work
position_b = { x = 3, y = 4, z = 5 }
ots.blocks.set(position_b, "minecraft:stone")
 
-- This will work
position_c = { x = math.floor(3.1), y = math.floor(4.1), z = math.floor(5.9) }
ots.blocks.set(position_c, "minecraft:stone")
 
-- This will work
position_d = { x = 3.1, y = 4.1, z = 5.9 }
ots.blocks.set({ x = math.floor(position_d.x), y = math.floor(position_d.y), z = math.floor(position_d.z) }, "minecraft:stone")
 
-- You can write your own function to do this
function floor_position(position)
    position.x = math.floor(position.x)
    position.y = math.floor(position.y)
    position.z = math.floor(position.z)
 
    return position
end
 
-- This will work
position_d = { x = 3.1, y = 4.1, z = 5.9 }
ots.blocks.set(floor_position(position_d), "minecraft:stone")

On this page