LogoObjToSchematic Wiki

Lua Scripting

How to write scripts to edit/create voxels or blocks.

Scripting allows you to write custom Lua scripts to manipulate voxels or blocks.

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

Lua scripts can be applied in a few places. They can be applied as custom voxel/block modifiers or if you are using the Project workflow then they can also be used to create a new Voxels or Blocks objects.

The editor provides 2 different APIs. One for reading/writing voxels and another for reading/writing blocks. Scripts using the voxels API can only be applied to Voxels objects. Scripts using the blocks API can only be applied to Blocks objects.

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.

API

Voxel Scripts

---@class Position
---@field x integer
---@field y integer
---@field z integer
 
-- Values of R,G,B are between 0 and 255, inclusive
---@class Colour
---@field r integer
---@field g integer
---@field b integer
 
---@class Voxel
---@field pos Position
---@field col 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
 
---@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
 
---@return Voxel[]
function ots.voxels.get_all() end
 
---@param position Position "The location to remove the voxel at"
---@return nil
function ots.voxels.remove(position) end
 
---@return nil
function ots.voxels.remove_all() end
 
---@param func fun(voxel: Voxel) "The function to execute per voxel"
---@return nil
function ots.voxels.for_each(func) end

Block Scripts

---@class Position
---@field x integer
---@field y integer
---@field z integer
 
---@class Block
---@field pos Position
---@field block string
 
---@param position Position "The location to place the block at"
---@param block string "The block name to set the block as"
---@return boolean "true if the block was placed successfully, false if the block name is not in the atlas"
function ots.blocks.set(position, block) end
 
---@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
 
---@return Block[]
function ots.blocks.get_all() end
 
---@param position Position "The location to remove the block at"
---@return boolean "true if a block existed at the position and was removed, false otherwise"
function ots.blocks.remove(position) end
 
---@return nil
function ots.blocks.remove_all() end
 
---@param func fun(block: Block) "The function to execute per block"
---@return nil
function ots.blocks.for_each(func) end

Examples

Voxel Scripts

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 colour
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({ x = 1, y = 0, z = 0 }, green_voxel)
 
-- 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.voxels.set({ x=x+cx, y=y+cy, z=z+cz }, { 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.col.r < 64 and voxel.col.g < 64 and voxel.col.b < 64) then
        ots.voxels.remove(voxel.pos)
    end
end)

Block Scripts

Basic operations

-- place red, green, and blue voxels
ots.blocks.set({ x = 0, y = 0, z = 0 }, "minecraft:dirt")
ots.blocks.set({ x = 1, y = 0, z = 0 }, "minecraft:stone")
 
-- get the stone block
local stone_block = ots.blocks.get({ x = 1, y = 0, z = 0 })
 
-- remove the dirt block
ots.blocks.remove({ x = 0, y = 0, z = 0 })
 
-- remove all the blocks
ots.blocks.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.blocks.set({ x=math.floor(x), y=math.floor(y), z=math.floor(z) }, "minecraft:red_wool")
            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.block == "minecraft:stone") then
        ots.blocks.remove(block.pos)
    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