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

Example6: Using addElement() to build block type model

Btw. I am not so clear about these lines:

collision = STEP,        
ao = false

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, 25, 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 "myDirtModel1", using craft model
    newBlockTypeDirtWithCraftModel()
    
    -- Create a new block type "myDirtModel2", using addElement() to build model
    newBlockTypeDirtWithAddElement()
        
    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("myDirtModel2")   
    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 newBlockTypeDirtWithAddElement()
    local dirt = scene.voxels.blocks:new("myDirtModel2")    
    dirt.setTexture(ALL, "Blocks:Dirt")
    -- dirt.setColor(ALL, color(227, 89, 14))
    dirt.scripted = true

    -- custom model from state
    function dirt:buildModel(model)
        model:clear()
        model:addElement
        {
            lower = vec3(0,0,0),
            upper = vec3(255,80,255), 
            textures = "Blocks:Wood",   
            collision = STEP,        
            ao = false
        }
        
        model:addElement
        {
            lower = vec3(0,60,0),
            upper = vec3(156,156,190),  
            textures = "Blocks:Stone", 
            collision = STEP,   
            ao = false
        }  
        
        model:addElement
        {
            lower = vec3(0,0,0),
            upper = vec3(106,255,100),  
            textures = "Blocks:Dirt", 
            collision = STEP,   
            ao = false
        }  
    end
    
    return dirt
end

function newBlockTypeDirtWithCraftModel()
    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/pY7MW9dS8F8

@dave1707

15,000,000 voxel cubes before Codea crashed.
really?? i can only get around 16K 2d meshes on the screen at 15-30fps,

15 million is surprising

@binaryblues I don’t know about anyone else, but for me your examples are getting harder and harder to follow as they’re getting larger. It’s hard to figure out what code goes with what example is being drawn on the screen. Plus I’m seeing code that I can’t find anywhere in the document, for example, schedule() or code that doesn’t match the format of what’s in the document, set(COLOR,c). Maybe I’m just having trouble finding them in the documents.

@skar Here’s the code I was using drawing the cubes. I was able to do a cube(345) which gave me over a 15,000,000 cube count. I tried 350 and crashed. This code does cube(100) which is a cube 100x127x100. You can try cube() greater than 345 to see where you crash.

viewer.mode=FULLSCREEN

function setup()
    fill(255)
    assert(OrbitViewer, "Please include Cameras (not Camera) as a dependency")
    scene = craft.scene() 
    v=scene.camera:add(OrbitViewer,vec3(100,80,0), 400, 0, 10000)    
    v.rx=-10
    v.ry=-30
    
    scene.voxels.blocks:addAssetPack("Blocks")   
    grass = scene.voxels.blocks:new("Table")
    grass.setTexture(ALL, "Blocks:Table")  

    scene.voxels.visibleRadius=90
    scene.voxels:resize(vec3(60,1,60))          
    scene.voxels.coordinates = vec3(0,0,0) 
    
    count=0
    cube(100)       -- 345 works, larger sizes crashes codea
end

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

function draw()
    update(DeltaTime)
    scene:draw()
    text("Number of cubes drawn "..count,WIDTH/2,HEIGHT-50)
    text("memory "..collectgarbage("count"),WIDTH/2,HEIGHT-100)
    collectgarbage()
end

function cube(s)
    scene.voxels:fill("Table")  
    for x1=1,s do
        for y1=1,127 do
            for z1=1,s do
                count=count+1 
                scene.voxels:block((x1),(y1),(z1))
            end
        end
    end
end

@dave1707 I also think that the sample code is getting longer and longer, and I’m thinking about removing some of the stuff that was in the previous example, and keeping it in case I have to repeat it, it’ll make user feel more familiar.You’re right that the full usage of these functions can not be found in the documentation. I gleaned this information from two places:

That’s where I get confused. For example, in addElement () , I don’t know what these two parameters do?

  • collision = STEP
  • ao = false

Because in the example code, they only appear once, closing them doesn’t seem to make any particular difference, and I don’t know if there are any other arguments in this function that aren’t listed in the sample program. We have the same problem.It’s like doing a jigsaw puzzle together. Thanks for your test code.

A condensed version of Example 6, which retains only what is used each time:

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, 25, 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 "myDirtModel2", using addElement() to build model
    newBlockTypeDirtWithAddElement("myDirtModel2")
        
    -- Using new block type: myDirtModel2 as the filling unit
    scene.voxels:fill("myDirtModel2")
    -- A single block
    scene.voxels:block(vec3(30,20,35))
    -- Create a sphere and a line using "myDirtModel2" block type, 
    -- every unit in those two shapes is a "myDirtModel2" block
    scene.voxels:sphere(40,20,77,8)
    scene.voxels:line(10,20,0,10,50,30)
    
    -- craft.scene.main = scene
end


function newBlockTypeDirtWithAddElement(blockTypeName)
    scene.voxels.blocks:addAssetPack("Blocks")
    
    local dirt = scene.voxels.blocks:new(blockTypeName)    
    dirt.setTexture(ALL, "Blocks:Dirt")
    -- dirt.setColor(ALL, color(227, 89, 14))
    dirt.scripted = true

    -- custom model from state
    function dirt:buildModel(model)
        model:clear()
        model:addElement
        {
            lower = vec3(0,0,0),
            upper = vec3(255,80,255), 
            textures = "Blocks:Wood",   
            collision = STEP,        
            ao = false
        }
        
        model:addElement
        {
            lower = vec3(0,60,0),
            upper = vec3(156,156,190),  
            textures = "Blocks:Stone", 
            collision = STEP,   
            ao = false
        }  
        
        model:addElement
        {
            lower = vec3(0,0,0),
            upper = vec3(106,255,100),  
            textures = "Blocks:Dirt", 
            collision = STEP,   
            ao = false
        }  
    end
    
    return dirt
end


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

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

@binaryblues I also looked thru Block Library but it still didn’t explain things. As for your examples, there’s a lot there to learn from. Maybe you can separate each of the different voxels into their own projects so it’s easier to see what you did for each one. I also noticed the 2 parameters that didn’t seem to do anything when I commented them out. I was thinking that they were going to be use for something in future examples but they never got around to using them.

@binaryblues Thanks for the link above. There’s quite a lot there. I’ll have to look thru it tomorrow. I’ll look at your latest example above tomorrow also.

Example7: Simple block type again

To simplify the example, some simple unscripted block types are used several times in the follow-up, so the function that created the simple block types is overridden.

This time, I used the newly created simple block type “simpleGrass” to re fill the ground so it wasn’t dark but Green.

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, 25, 40), 8, 1, 400)
    
    -- set voxel terrain parameters: size, coord
    scene.voxels:resize(vec3(5,1,5))      
    scene.voxels.coordinates = vec3(0,0,0)    
    
    -- Create a new block type "simpleGrass"
    createSimpleBlockType("simpleGrass", "Blocks:Grass Top", color(201, 233, 80))
    -- Using default block type: SOLID as the filling unit
    scene.voxels:fill("simpleGrass")    
    -- 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 "simpleDirt1"
    createSimpleBlockType("simpleDirt1", "Blocks:Dirt", color(83, 80, 233))
        
    -- Using new block type: myDirtModel2 as the filling unit
    scene.voxels:fill("simpleDirt1")
    -- A single block
    scene.voxels:block(vec3(30,20,35))
    -- Create a sphere and a line using "simpleDirt1" block type, 
    -- every unit in those two shapes is a "simpleDirt1" block
    scene.voxels:sphere(40,20,77,8)
    scene.voxels:line(10,20,0,10,50,30)
    
    -- craft.scene.main = scene
end


function createSimpleBlockType(blockTypeName,texture,blockColor)
    -- Create user-defined block type
    scene.voxels.blocks:addAssetPack("Blocks")
    
    local dirt = scene.voxels.blocks:new(blockTypeName)
    dirt.setTexture(ALL, texture or "Blocks:Dirt")
    dirt.setColor(ALL, blockColor or color(239, 222, 5))
    dirt.tinted = false
end


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

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

@dave1707 The built-in sample Block Library is well written, with some comments in place, but more needs to be done to analyze the code and see what it does line by line. I’m glad these examples are helpful, and your suggestion is exactly what I’m considering. I’ll introduce only one block type for each example in the future to make it easier for beginners to learn. These are things we’ve seen all the sample code can not figure out, and you may end up having to turn to the developer. In fact, the most help comes from the documentation provided by the developers.

You are welcome. Don’t worry, it’s going to be a long term project, I’ll probably be around a lot in the near future, and we all share the goal of making voxels work.

To keep the sample code short and easy to read, but also to keep track of all the work I’ve done, I decided to publish it this way:

  • The posted code is the latest example, the newest, the simplest, the most essential parts can be copied, pasted and run independently.
  • The packaged ZIP project file contains all the content (the main tab is the pasted content).

Example 8: The procedural generation block type, it is composite block type.

We first generate two kinds of simple block types:

  • simpleGrass
  • simpleDirt1

and then use these two kinds of simple block types to generate tree block type.

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, 25, 40), 8, 1, 400)
    
    -- set voxel terrain parameters: size, coord
    scene.voxels:resize(vec3(5,1,5))      
    scene.voxels.coordinates = vec3(0,0,0)    
    
    -- Create 2 new block types: "simpleGrass", "simpleDirt1"
    createSimpleBlockType("simpleGrass", "Blocks:Grass Top", color(201, 233, 80))
    createSimpleBlockType("simpleDirt1", "Blocks:Dirt", color(83, 80, 233))
    
    -- Using block type: simpleGrass as the filling unit
    scene.voxels:fill("simpleGrass")    
    -- Create a box using "simpleGrass" block type, every unit in the box is a "simpleGrass" block
    scene.voxels:box(0,0,0, 16*8,10,16*8)
    
    -- Create a new block type "TreeGenerator", 
    -- This is a composite block type, based on two block type: "simpleGrass", "simpleDirt1"
    proceduralGenerateBlockType("TreeGenerator")
        
    -- Using new block type: TreeGenerator as the filling unit
    scene.voxels:fill("TreeGenerator")
    -- A single block
    scene.voxels:block(vec3(30,20,35))
    -- Create a sphere and a line using "TreeGenerator" block type, 
    -- every unit in those two shapes is a "TreeGenerator" block
    scene.voxels:line(10,20,0,10,30,40)
    scene.voxels:sphere(40,20,60,4)
    -- craft.scene.main = scene
end


function createSimpleBlockType(blockTypeName,texture,blockColor)
    -- Create user-defined block type
    scene.voxels.blocks:addAssetPack("Blocks")
    
    local dirt = scene.voxels.blocks:new(blockTypeName)
    dirt.setTexture(ALL, texture or "Blocks:Dirt")
    dirt.setColor(ALL, blockColor or color(239, 222, 5))
    dirt.tinted = false
end


function proceduralGenerateBlockType(blockTypeName)
    scene.voxels.blocks:addAssetPack("Blocks")
    local tree = scene.voxels.blocks:new(blockTypeName)
    tree.scripted = true
    -- tree.geometry = EMPTY -- Make generator block invisible
    tree.static.canDig = false
    tree.static.hasIcon = true
    tree.static.canPlace = true
    
    function tree:created()
        self:schedule(60)
    end
    
    function tree:blockUpdate(t)
        local x,y,z = self:xyz()
        
        -- Check is enough surrounding area has been loaded to generate
        if self.voxels:isRegionLoaded(x-3, y, z-3, x+3, y,z+3) then
            self:generate()
            -- If this fails then try again in a second
        else
            self:schedule(60)
        end
    end
    
    function tree:generate()
        local x,y,z = self:xyz()
        
        -- Base random seed on location of tree
        math.randomseed(x * y * z)
        local height = math.random(4,5)
        local size = math.floor(height/2)
        self.voxels:set(x,y,z,"empty")
        
        -- Trunk 
        -- Using block type "simpleDirt1" to fill trunk     
        self.voxels:set(x,y-1,z,"simpleDirt1")    
        self.voxels:fill("simpleDirt1")
        self.voxels:box(x,y,z,x,y+height,z)
        
        -- Leaves
        -- Using block type "simpleGrass" to fill Leaves     
        self.voxels:fill("simpleGrass")
        self.voxels:fillStyle(UNION)
        self.voxels:sphere(x,y+height,z,size)
    end
end


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

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

@binaryblues Heres an example I posted before. Try the different ss values (3, 9, 27, 81, 243). Don’t know why 243 doesn’t work right. Not sure if its my code or the way voxels work.

PS. I know how to draw Voxel blocks at x,y,z positions, but that’s about it. Your examples here are going to expand my Voxel knowledge.

viewer.mode=FULLSCREEN

function setup()
    ss=81       -- cube size, powers of 3 only (3, 9, 27, 81, 243)
    -- 243 doesnt draw right.
    
    fill(255)
    assert(OrbitViewer, "Please include Cameras as a dependency")
    scene = craft.scene() 
    v=scene.camera:add(OrbitViewer,vec3(ss,ss,ss), ss*3, 0, 6000)    
    v.rx=-10
    v.ry=-30
    
    scene.voxels.blocks:addAssetPack("Blocks")
    tab = scene.voxels.blocks:new("Table")
    tab.setTexture(ALL, "Blocks:Table") 
    scene.voxels:fill("Table")
    
    scene.voxels.visibleRadius=90
    scene.voxels:resize(vec3(60,1,60))          
    scene.voxels.coordinates = vec3(ss,ss,ss) 
    count,rec=0,0
    
    cube(0,0,0,ss)
end

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

function draw()
    update(DeltaTime)
    scene:draw()
    text("Cube size "..ss.."x"..ss.."x"..ss.." = "..(ss^3).." cubes",WIDTH/2,HEIGHT-25)
    text("Number of cubes drawn "..count,WIDTH/2,HEIGHT-50)
    text("Number of recursive calls "..rec,WIDTH/2,HEIGHT-75)
end

function cube(x,y,z,size)
    local s=size/3
    for x1=-s,s,s do
        for y1=-s,s,s do
            for z1=-s,s,s do
                if (y1~=0 or z1~=0) and (x1~=0 or z1~=0) and (x1~=0 or y1~=0) then
                    if size~=3 then
                        rec=rec+1
                        cube(x1+x,y1+y,z1+z,s)
                    else
                        count=count+1
                        scene.voxels:block((x1+x+ss),(y1+y+ss),(z1+z+ss))
                    end
                end
            end            
        end
    end
end   

@John @binaryblues - last two Voxel demos gave me an error when run, added dependency for cameras. @binaryblues -recommend you set up a template with cameras dependency included - a Craft template.

Zooming in, to check for the earlier issues with crashing , fired up errors on both,
Think this may be due to a different environment with the latest generation of Craft. Images attached.

@binaryblues, demos are getting more interesting and more involved. Thanks.

Edit: in the Craft environment can the camera traverse the central axis ? Seems a bit restricted.

@dave1707 I think I may have found the reason. Resize takes two parameters:

  • sizeInChunks: vec3(a,b,c)
  • chunkSize: vec3(d,e,f)

It indicates that the maximum range of the generated terrain is:

  • vec3(a * d,  b * e, c * f)
    

Your code is:

scene.voxels:resize(vec3(60,1,60))

There are two default settings, the b in sizeInChunks is only 1, and the chunkSize using the default value:

  • vec3(16,128,16),

So the maximum range of your terrain is vec3(60 * 16, 1 * 128, 60 * 16), if you want to expand the range of the y , you can only do that by setting parameter chunkSize, on My iPad, there are only so many settings that can be used to draw a quarter of the way to crash, and only a little bit bigger to run.

This default looks odd?It’s not very intuitive.

Here the maximum range is vec3(8 * 16, 1 * 128, 8 * 16)==vec3(128,128,128), You can get the 1st snapshot.

scene.voxels:resize(vec3(8,1,8), vec3(16,128,16))       

Here the maximum range is vec3(16 * 16, 1 * 256, 16 * 16)==vec3(256,256,256), You can get the 2nd snapshot.

scene.voxels:resize(vec3(16,1,16), vec3(16,256,16))          

That’s the limit without causing crash. Halfway done

scene.voxels:resize(vec3(32,1,16), vec3(16,512,16))      

https://youtu.be/dhbtDV09lf8

@Bri_G Thank you for your advice and for the time spent testing. I haven’t used the template very much, but I’ll try to include it in the next example program, in my environment, as long as there are a large number of complex block type (such as spheres with a larger radius) , crashes are common, causing me to have to reduce the size of the sphere, as soon as it’s finished, I click on the screen to rotate and zoom, and I lose touch, which means there’s nothing to do but click and rerun, this phenomenon can be repeated again and again, I wonder if you have encountered this problem?

@binaryblues - yes, occasionally. When you are in a voxel environment you tend to want to zoom around so camera mobility and zoom are important. Errors and crashes need to be resolved or at least explained.

@Bri_G Since many of the crash problems are due to a lack of lua memory, I was thinking that if I could run the collectgarbage() function in some places where complex blocks are heavily used, maybe the problem would improve. But sometimes it crashes at the first try, and there’s no good way to do that.

@binaryblues Thanks for the info. I was able to play around with the visibleRadius and resize values and managed to get the lower third of the cube to display. A radius of 8 is just enough to get the length and width of the cube to show. If I change the 218 for the height to a higher value, Codea crashes. But the 2 lines below doesn’t crash Codea and draws some of the cube.

I’m not sure why I can display a straight cube with a size of 345x345x345 that doesn’t crash, but this cube with a size of 243x243x243 does.

Guess I don’t understand voxels well enough yet.

    scene.voxels.visibleRadius=8
    scene.voxels:resize(vec3(16,1,16), vec3(24,218,24))          

@binaryblues - if there was some way of calculating scope, i.e. the visible extent of the scene on your pad, that could reduce processing power and memory allocation. Not sure how to approach that.