I found craft.voxels to be an excellent system and tried to organize its hierarchy

I recently spent some time studying craft.voxels and found that it was well designed, but it didn’t seem to have many users working on formal projects, and I wanted to use it to develop an educational app, but in the process of learning to use it encountered many difficulties, but also some gains. I’d like to share it here so that more users are interested in craft.voxels and we can talk about all the voxels issues together.

For everyone who wants to get to know craft.voxels better, here’s a document I’ve compiled of all the voxels’available functions. This is the most comprehensive information I have been able to gather so far. If you know something I missed, please add it. Thank you.

The hierarchy of Voxels system

The `Voxels system has three levels:

  • voxel?craft.voxels
  • block?craft.blocks
  • volume?craft.volume

voxels is the most basic unit and provides many basic properties and methods?

  • read-only property:
    • blocks
    • visibleChunks
    • generatingChunks
    • meshingChunks
  • read-write property:
    • coordinates
    • visibleRadius
  • method:
    • resize()
    • set()
    • get()
    • fill()
    • fillstyle
    • find()
    • block()
    • sphere()
    • box()
    • line()
    • updateBlock()
    • raycast()
    • generate()
    • isRegionLoaded()
    • iterateBounds()
    • enableStorage()
    • disableStorage()
    • deleteStorage()

Based on craft.voxels ?craft.blocks?made up of multiple voxels, there are two types?

  • The default block type built into the system:
    • SOLID
    • EMPTY
  • User-defined type of box: freely named

For user-defined block type? Have the following properties and methods?

  • read-only property:
    • id
    • name
    • state
    • model
  • read-write property:
    • geometry
    • renderPass
    • tinted
    • dynamic
    • scripted
  • position property:
    • x
    • y
    • z
  • method:
    • setTexture()
    • setColor()
    • craft.block.directionToFace()
    • craft.block.faceToDirection()

Because block type supports scripting, there are methods that can only be used for scripting?

  • methods for blockType internal scripts:
    • blockType:created()
    • blockType:destroyed()
    • blockType:buildModel()
    • blockType:placed()
    • blockType:interact()
    • blockType:blockUpdate()
    • blockType:update()
    • set()
    • get()
    • schedule()
    • xyz()
  • block.state provides the method:
    • blockType.state.addFlag()
    • blockType.state.addRange()
    • blockType.state.addList()
  • block.model provides the method:
    • clear()
    • rotateY()
    • rotateX()
    • addElement() There are special parameters required(I am not so clear if there are other parameters?)
      - lower = vec3(128,128,128)
      - upper = vec3(255,255,255)
      - collision = STEP
      - textures = “Blocks:Grass”
      - ao = false

craft.volume is the higher level of abstraction provided by the craft.voxels` system, which is designed as a component that can be attached to an entity (providing three forms of syntax for attaching) . It provides the following properties and methods:

  • property:
    • model
  • method:
    • size()
    • get ()
    • set ()
    • raycast ()
    • clear ()
    • resize ()
    • load ()
    • save ()
    • loadSnapshot ()
    • saveSnapshot ()
    • blockID ()
    • setWithNoise ()
    • updateBlock ()

There is also a constructor?VolumeSnapshot()

An global method: bounds(min, max)

  • read-only property:
    - min: vec3
    - max: vec3
    - valid: Boolean
    - center: vec3
    - offset: vec3
    - size:
  • method:
    - intersects(),
    - encapsulate(),
    - translate(),
    - set()

Some of the above content appears in the built-in help documentation, and some does not appear in the built-in help documentation, but appears in the sample code

I will try to write some of the simplest sample code, to let a novice know where to start, step by step, I will try to reduce the complexity of the sample code

Example1 The simplest voxel routines keep only those things that can’t be deleted.

The code is below:

-- ExEzVoxel
viewer.mode = FULLSCREEN

function setup()
    scene = craft.scene()

    player = scene.camera:add(OrbitViewer, vec3( 40, 20, 40), 8, 1, 400)
    
    -- ?? voxel ????:???????
    scene.voxels:resize(vec3(5,1,5))      
    scene.voxels.coordinates = vec3(0,0,0)    
        
    -- ?? craft.voxels ??????? block type:SOLID
    scene.voxels:fill("SOLID")
    
    -- ?????? SOLID ????????????????
    scene.voxels:box(0,0,0, 16*8,10,16*8)
    scene.voxels:sphere(40,50,90,16*2)
    scene.voxels:line(10,20,0,10,50,30)
    scene.voxels:block(vec3(30,20,35))
    
    craft.scene.main = scene
end

https://youtu.be/NYM5vJvSuN8

Example 2 Using user-defined block type

the code:

-- ExEzVoxel

viewer.mode = FULLSCREEN

function setup()
    scene = craft.scene()

    player = scene.camera:add(OrbitViewer, vec3( 40, 20, 40), 8, 1, 400)
    
    -- ?? voxel ????:???????
    scene.voxels:resize(vec3(5,1,5))      
    scene.voxels.coordinates = vec3(0,0,0)    
        
    -- Using default block type: SOLID 
    scene.voxels:fill("SOLID")
    
    -- create a box using "Solid" block type
    scene.voxels:box(0,0,0, 16*8,10,16*8)
    
    -- Create user-defined block type
    scene.voxels.blocks:addAssetPack("Blocks")
    local dirt = scene.voxels.blocks:new("myDirt")
    dirt.setTexture(ALL, "Blocks:Dirt")
    dirt.setColor(ALL, color(239, 222, 5))
    
    scene.voxels:fill("myDirt")
    scene.voxels:sphere(40,50,90,16*2)
    scene.voxels:line(10,20,0,10,50,30)
    scene.voxels:block(vec3(30,20,35))
    
    craft.scene.main = scene
end

https://youtu.be/NRATwYXFuY4

Example 3 Using the most simplest internal script

The code:

viewer.mode = FULLSCREEN

function setup()
    scene = craft.scene()

    player = scene.camera:add(OrbitViewer, vec3( 40, 20, 40), 8, 1, 400)
    
    -- ?? voxel ????:???????
    scene.voxels:resize(vec3(5,1,5))      
    scene.voxels.coordinates = vec3(0,0,0)    
        
    -- Using default block type: SOLID 
    scene.voxels:fill("SOLID")
    
    -- create a box using "Solid" block type
    scene.voxels:box(0,0,0, 16*8,10,16*8)
    
    -- Create user-defined block type
    scene.voxels.blocks:addAssetPack("Blocks")
    local dirt = scene.voxels.blocks:new("myDirt")
    dirt.setTexture(ALL, "Blocks:Dirt")
    dirt.setColor(ALL, color(239, 222, 5))
    dirt.tinted = true
    dirt.scripted = true
    
    function dirt:created()
        -- Randomise colour based on location
        local x,y,z = self:xyz()
        math.randomseed(x * y * z)
        local c =color(math.random(128,255), math.random(128,255), math.random(128,255))
        self.voxels:set(x,y,z,"color", c)
    end
    
    scene.voxels:fill("myDirt")
    scene.voxels:sphere(40,50,90,16*2)
    scene.voxels:line(10,20,0,10,50,30)
    scene.voxels:block(vec3(30,20,35))
    
    craft.scene.main = scene
end

https://youtu.be/zYFlhw88ZPA

@binaryblues Very interesting and helpful.

my main focus would be how many voxels can you get on the screen at once and how complex of a scene can you render with them? the current usage as a minecraft style world is great but limited, i’ve seen some cool destruction demos using voxels in unity, how close can we get to that? https://twitter.com/tuxedolabs/status/1159081158145204225

@dave1707 Glad to hear that. I’m emulating your short, pithy way of demonstrating code, I find this style of coding particularly helpful for beginners.

@skar Depending on how complex a custom block type is, the more complex a single block is, the more resources it consumes. As far as my demo is concerned, you can test the approximate answer to the first question (using the simplest Solid block type) by modifying the parameters of this code:

scene.voxels:resize(vec3(5,1,5))
scene.voxels:box(0,0,0, 16*8,10,16*8)

try to change it:

scene.voxels:resize(vec3(5,5,5))
scene.voxels:box(0,0,0, 16*20,16*2,16*20)

As for the second question, i don’t know how to clearly define the complexity of the scene, but I can only make it subjective based on the actual rendering speed in programming. Finally, I think it can be completely duplicated, there is no inherent limitation. I might add that static scenes shouldn’t be too much of a problem, but the smoke and shadows can be a bit difficult because I’m not quite sure how to use shaders in voxels yet, i’m still trying to familiarize myself with the voxels system, so maybe when I get to know the voxels system a little better, I’ll have a solution.

@skar I was able to draw over 15,000,000 voxel cubes before Codea crashed.

@binaryblues I think very simple examples are the best way for beginners. Just a few things in simple terms that are easy to figure out. Large, complicated examples are great to show the power of what can be done, but it’s very difficult to understand and figure out what’s happening or how to do something.

@binaryblues - trying to zoom your second and third Voxel demos with finger and thumb, with some rotation, and it crashed Codea. Reported to dev team via crash report - not sure if this is a feature of Craft or how you have set up your demos. I have been playing with your demos but the crashes occurred both with your demos and my own fiddling with your code.

But, your demos are very good, interesting, especially with your approach to building up a scene with minimal code. I don’t recognise the structures you build in them - any chance you can give a more detailed description of the third demo I’d like to be able to build code like that myself but I.m still low on the learning curve with Craft/Voxels.

Thanks for the demos.

@Bri_G @binaryblues Tried the 2nd and 3rd examples with no problems. Was able to zoom and rotate.

@dave1707 - it didn’t crash straight away, I rotated the sphere front to be facing the camera the tried to zoom in. In the process the sphere tended to rotate slightly from left to right and the crash occured then

@dave1707 I completely agree with you, and I always try out all the sample code you posted on the forums. Simple examples show basic functionality, and more complex examples provide an advanced template for beginners, so it’s important to find a balance between the two to help document writers, and Thanks for your tests.

@Bri_G Thanks to your testing, On my iPad it didn’t crash. When I ran the other projects, there will be a one-click run on the crash situation. I’ve omitted a lot of settings in these sample programs, and for the sake of reducing complexity, I’ll add new content bit by bit to make it easier for new users to understand. Also, I added some comments to the latest example, so if you’re not sure what line of code you want to use, mention it, and I’ll explain it.

Example4: Using blockUpdate() to show some blink block.
An interesting thing, if I draw a whole sphere, it can not blink, but if I draw half sphere, it can blink, the same size.

In this example I made some changes?

  • I’ve saved a few comment lines for the experiment, and you can try turning them on and off to see what happens.
  • To make it easier to write an internal script for the block, the code that creates the block type is extracted and written as a separate function.
  • Return to normal Codea Craft code structure: setup(), update(dt), draw()

The code:

viewer.mode = FULLSCREEN

function setup()
    scene = craft.scene()

    player = scene.camera:add(OrbitViewer, vec3( 40, 20, 40), 8, 1, 400)
    
    -- set voxel terrain parameters: size, coord
    scene.voxels:resize(vec3(5,1,5))      
    scene.voxels.coordinates = vec3(0,0,0)    
        
    -- Using default block type: SOLID as the filling unit
    scene.voxels:fill("solid")
    
    -- Create a box using "Solid" block type, every unit in the box is a "solid" block
    scene.voxels:box(0,0,0, 16*8,10,16*8)
    
    -- Create a new block type "myDirt"    
    newBlockTypeDirt()
        
    scene.voxels:fill("myDirt")
    -- A sphere larger than half can not blink, so interesting
    scene.voxels:sphere(40,20,77,8)
    -- Half sphere can blink
    scene.voxels:sphere(10,20,0,8)
    scene.voxels:line(10,20,0,10,50,30)
    scene.voxels:block(vec3(30,20,35))
    
    -- craft.scene.main = scene
end

function newBlockTypeDirt()
    -- Create user-defined block type
    scene.voxels.blocks:addAssetPack("Blocks")
    local dirt = scene.voxels.blocks:new("myDirt")
    dirt.setTexture(ALL, "Blocks:Dirt")
    dirt.setColor(ALL, color(239, 222, 5))
    dirt.tinted = true
    dirt.scripted = true
    dirt.state:addFlag("on", false)
    
    -- The internal script
    function dirt:created()
        self:schedule(60)
        self:set(COLOR, color(108, 233, 80))
    end
    
    function dirt:blockUpdate()
        -- Randomise colour based on location
        -- local x,y,z = self:xyz()
        -- math.randomseed(x * y * z)
        local c =color(math.random(128,255), math.random(128,255), math.random(128,255))
        -- self.voxels:set(x,y,z,"color", c)
        self:set(COLOR, c)
        self:schedule(10)
    end
       
    return dirt
end

function update(dt)
    scene:update(dt)
end

function draw()
    update(DeltaTime)    
    scene:draw()
end

https://youtu.be/V-rIO80-Vm4

@binaryblues - hmmmm, couldn’t crash this. Wonder if it’s down to the following line in the other examples.

craft.scene.main = scene

Thanks for this, the comments are very helpful.

Example5: Using the craft standard model–craft.model() to custom the model of block type.

Block type has three modeling methods:

  • one is to use the craft model,
  • the other is to use the block’method addElement() to customize the block type model.
  • the 3rd one is procedural generation

The first involves loading the physical system manually, using these lines of code:

self.top:add(craft.rigidbody, STATIC)
self.top:add(craft.shape.box, vec3(0.8,0.8,0.8), offset or vec3(0.4,0.4,0.4))

The second can use the craft.voxels system has been set up, do not need to manually load. With craft.model, you can provide an unrestricted look and feel for blocks, you can use a variety of textures and textures, and you can use custom shaders for maximum freedom, but all of this requires you to write your own processing code.

Here I demonstrate the use of craft.model to model blocks:

The code:

viewer.mode = FULLSCREEN

function setup()
    scene = craft.scene()
    scene.sky.material.sky = color(80, 203, 233)
    scene.sky.material.horizon = color(80, 108, 233)

    player = scene.camera:add(OrbitViewer, vec3( 40, 20, 40), 8, 1, 400)
    
    -- set voxel terrain parameters: size, coord
    scene.voxels:resize(vec3(5,1,5))      
    scene.voxels.coordinates = vec3(0,0,0)    
        
    -- Using default block type: SOLID as the filling unit
    scene.voxels:fill("solid")
    
    -- Create a box using "Solid" block type, every unit in the box is a "solid" block
    scene.voxels:box(0,0,0, 16*8,10,16*8)
    
    -- Create a new block type "myDirt"    
    newBlockTypeDirt()
    
    -- Create a new block type "myDirt", using craft model
    newBlockTypeDirtMWithCraftModel()
        
    scene.voxels:fill("myDirt")
    -- A sphere larger than half can not blink, so interesting
    scene.voxels:sphere(40,20,77,8)
    -- Half sphere can blink
    scene.voxels:sphere(10,20,0,8)
    scene.voxels:line(10,20,0,10,50,30)
    
    -- Building model
    scene.voxels:fill("myDirtModel1")   
    scene.voxels:block(vec3(30,20,35))
    scene.voxels:line(40,20,30,10,50,30)
    scene.voxels:sphere(70,20,77,2)
    
    -- craft.scene.main = scene
end


function newBlockTypeDirtMWithCraftModel()
    local dirt = scene.voxels.blocks:new("myDirtModel1")
    dirt.dynamic = true
    dirt.scripted = true
    dirt.geometry = TRANSPARENT
    
    -- The internal script
    function dirt:created()
        e = self.entity
        self.base = scene:entity()
        self.base.parent = e
        self.base.position = vec3(0.5, 0.3, 0.5)
        local r = self.base:add(craft.renderer, craft.model.cube(vec3(2.8,0.6,2.8)))
        r.material = craft.material(asset.builtin.Materials.Specular)
        r.material.diffuse = color(133, 79, 30, 255)
        r.material.map = readImage(asset.builtin.Blocks.Brick_Grey)
        
        self.top = scene:entity()
        self.top.parent = e
        self.top.position = vec3(0.1, 0.6, 0.1)
        local r2 = self.top:add(craft.renderer, craft.model.cube(vec3(2.8,0.2,2.8), vec3(1.4,0.1,1.4)))
        -- local r2 = self.top:add(craft.renderer, craft.model(asset.builtin.Primitives.Monkey))
        r2.material = craft.material(asset.builtin.Materials.Specular)
        r2.material.diffuse = color(66, 47, 30, 255)
        r2.material.map = readImage(asset.n0)
        r2.material.normalMap = readImage(asset.n1)
        self.angle = 0
    end
    
    -- Because of using the craft standard model(craft.model.cube), here’s update()
    function dirt:update()
        self.top.rotation = quat.eulerAngles(0,  self.angle+ElapsedTime*100, 0)
    end
        
    return dirt
end

function newBlockTypeDirt()
    -- Create user-defined block type
    scene.voxels.blocks:addAssetPack("Blocks")
    local dirt = scene.voxels.blocks:new("myDirt")
    dirt.setTexture(ALL, "Blocks:Dirt")
    dirt.setColor(ALL, color(239, 222, 5))
    dirt.tinted = true
    dirt.scripted = true
    dirt.state:addFlag("on", false)
    
    -- The internal script
    function dirt:created()
        self:schedule(60)
        self:set(COLOR, color(108, 233, 80))
    end
    
    function dirt:blockUpdate()
        -- Randomise colour based on location
        -- local x,y,z = self:xyz()
        -- math.randomseed(x * y * z)
        local c =color(math.random(128,255), math.random(128,255), math.random(128,255))
        -- self.voxels:set(x,y,z,"color", c)
        self:set(COLOR, c)
        self:schedule(10)
    end
       
    return dirt
end

function update(dt)
    scene:update(dt)
end

function draw()
    update(DeltaTime)    
    scene:draw()
end

https://youtu.be/GVzNfEIydWk

Here using a loaded model, you can use any obj model:

https://youtu.be/dN0K1PIBpu0

@Bri_G A good news! Nice to hear that.

can you use any model as the base voxel? it doesn’t have to be a box right?

@skar So far it looks like you can use any model as your own basic block type unit, and the craft.voxels system provides this capability.The only thing you need to think about is performance