We’re at a fairly clean point right now. There are a couple of small topics in the code to consider, but we need to figure out a plan for what’s next.

I feel that the code we have is pretty good for what it does, with the exception of some side items. A look at the Main will show you what I mean:


-- Orc-1
-- RJ 20200203
-- upper case variables are global
-- 20200203: Orc Class
-- 20200207: Bird Class

function setup()
    Scene = craft.scene()
    setupSky(Scene)
    setupOrc(Scene)  
    local ship = setupWatercraft(Scene)
    setupBird(Scene, ship)
    setupCamera(Scene)
    parameter.boolean("OrcActive", true, orcCallback)
    parameter.boolean("ShipActive", true, shipCallback)
end

function orcCallback(aBoolean)
    Orc:activate(aBoolean)
end

function shipCallback(aBoolean)
    Ship:activate(aBoolean)
end

function setupBird(scene, shipEntity)
    scene:entity():add(Bird, shipEntity)
end

function setupWatercraft(scene)
    local entity = scene:entity()
    entity:add(Watercraft)
    return entity
end

function setupSky(scene)
    scene.sky.active = false
    createGround(-1.125, scene)
end

function setupOrc(scene)
    scene:entity():add(Orc)
end

function setupCamera(scene)
    Scene.camera.z = -4
    local cameraSettings = scene.camera:get(craft.camera)
    local fieldOfView = 60
    local ortho = false
    local orthoSize = 5
    cameraSettings.fieldOfView = fieldOfView
    cameraSettings.ortho = ortho
    cameraSettings.orthoSize = orthoSize
end

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

-- Called automatically by codea 
function draw()
    update(DeltaTime, Scene)
    Scene:draw()
end

-- Creates the ground using a box model and applies a simple textured material
function createGround(y, scene)
    local ground = scene:entity()
    ground.model = craft.model.cube(vec3(4,0.125,4))
    ground.material = craft.material("Materials:Specular")
    ground.material.map = readImage("Blocks:Dirt")
    ground.material.specular = color(0, 0, 0, 255)
    ground.material.offsetRepeat = vec4(0,0,5,5)
    ground.y = y
    return ground
end

function updateCamera(dt, scene)
    if CurrentTouch.state == MOVING then 
        CameraX = (CameraX or 0) - CurrentTouch.deltaX * 0.25
        CameraY = (CameraY or 0) - CurrentTouch.deltaY * 0.25
        scene.camera.eulerAngles = vec3(CameraY, CameraX, 0)
        scene.camera.position = -scene.camera.forward * 5
    end
end

Our working entities are pretty simple and solid, with what seems to me to be good encapsulation. The auxiliary items show us where I’m still ignorant.

Camera

The camera setup and camera motion are smushed into the Main, and by my standards they should be off in a separate tab and probably a separate object. I do recall that the “Cameras” app in Codea includes two camera setups, FirstPersonViewer and OrbitViewer. The camera we’re using is a slimmed-down version of the OrbitViewer, if I recall. FirstPersonViewer is used when you have a virtual player wandering in the 3D world: It shows the view out of their eyes. FirstPersonViewer, or something like it, is used in some of the Codea Craft examples, the ones that make a little world out of blocks.

Now for most purposes, I’d advise just importing Cameras and using OrbitViewer, which works just fine. That would certainly clean up our code.

However, if our possibly imaginary purpose is to write a useful introduction to Codea Craft, we need to understand how cameras work. To do that, I’d start with something simple, like what I have now, and adjust its parameters and extend it bit by bit. That would give me two things that I consider important:

  1. I’d gain a solid understanding of exactly what the camera does, with all its works and all its pomps.
  2. I’d come up with a good and consistent way of organizing camera code, since my possibly imaginary book wouldn’t just show procedural examples, but good examples.

Floor

The floor setup is pretty simple, and possibly one could even leave it in main. But what if we had a dungeon with many floors? Then we might want better encapsulation

So, as with Camera, we might do some study and refactoring.

Terrain

Relatedly, there are some fairly wild “terrain” examples in Codea, that show how to build up terrain like this:

The Terrain code example generates a random map like the one above, including rivers, hills, trees, and even underground caves containing minerals. It includes an OrbitViewer, used to create those pictures, and can spawn a “player” where you drive a FirstPersonViewer around the terrain. You can even create and destroy blocks.

I could imagine just documenting the Terrain example, or deriving one of our own. If we were building some kind of terrain-based game, we might either start from the existing example, or roll our own. Either way could be right, depending on our objectives and how well the existing code fit our needs.

For now, I’m going to keep exploring examples, and writing about them, sometimes using them to build up my Orc program or some new one.

Voxels

It’s Wednesday. I’ve taken a day off writing for study and some appointments.

My next little exploration is into the “voxel” capability built into Craft. A voxel is like a pixel, only 3d. “Vo”lume x “Element”, I guess. A voxel is a cubical thing that can have a texture (picture) painted on it. They can apparently be different sizes – I’m somewhat guessing here, but the terrain example I showed has trees and clouds made of different sized cubes. Apparently the idea is that voxels are “streaming”, which seems to mean that the whole terrain, or big masses of it, is pre-calculated, and then viewed from whatever camera position. I think they’re optimized for rapid display of mass quantities of voxels, rather than the one-by-one display we see with entities.

Some of the above may be right. Be that as it may, the voxel code is part of craft and there are a few really complicated examples in the Codea Craft examples.

Most of the examples are by John Millard (@twolivesleft), and they appear to me to be very nice code. I’m not an easy grader, so that’s saying a lot. If you drill into the examples, you see some decent ways of doing things, as well as some code elements you can adopt and use directly.

There’s always a tradeoff to be made between using a library or other chunk of code, or writing one’s own. Often, using the available code gets us going sooner … and often, we slow down later because it isn’t quite what we need, and we don’t have the same understanding we’d have had we written it all ourselves. When it’s not very well written, that swings the scales toward rolling our own. When it’s well-written, as here, the odds favor using all or part of what’s provided.

As an example, one of the examples includes a terrain builder with caves and mineral deposits, and a rudimentary first-person viewer allowing you to wander around in the world. There’s an inventory of blocks you can use to build things, and a TNT block you can use to destroy things. It’s not actually a game as it stands, but as a foundation for a game, it might be a good start.

Now my purpose is to build a detailed understanding, not just of how to use those examples, but how to write things from scratch that might wind up like them. And there is a sub-purpose, perhaps not to be fulfilled, of being able to write a Codea Craft manual that better expresses how it works and how to write code using it. I might do that, I might now, but the desire to have that level of understanding is definitely here.

So, right now, I’ve been exploring the voxel-related examples and the block library that John has provided. And I learned an interesting thing:

The Fence

In exploring the specialized blocks in John Millard’s “Block Library”, I came across the “Fence”. When you set down a fence block, it looks like a post. If you set another bit down two blocks away, it looks like another post:

But if you set a post within one block of another, the posts grow half-rails that touch and make a full rail. Here’s what happened when I put a fence post between my other two:

Voila! Nice. So I wanted to see how that worked. Here’s the complete code for the Fence. There’s supporting code elsewhere for driving things and so on. We may need to explore that. For now, the Fence:

function fence(name, texture)
    local fence = scene.voxels.blocks:new(name)
    fence.setTexture(ALL, texture)
    fence.scripted = true
    fence.geometry = TRANSPARENT
    
    fence.state:addFlag("n", false)
    fence.state:addFlag("e", false)   
    fence.state:addFlag("s", false)   
    fence.state:addFlag("w", false) 
    
    function fence:triggerUpdate()
        self:schedule(0)
        local x,y,z = self:xyz()
        self.voxels:updateBlock(x,y,z-1,0)
        self.voxels:updateBlock(x,y,z+1,0)
        self.voxels:updateBlock(x-1,y,z,0)
        self.voxels:updateBlock(x+1,y,z,0)        
    end
    
    function fence:created()
        self:triggerUpdate()
    end
    
    function fence:destroyed()
        self:triggerUpdate()
    end
    
    function fence:blockUpdate()  
        local x,y,z = self:xyz()
        self:set("n", self.voxels:get(x,y,z-1,BLOCK_ID) ~= 0)
        self:set("s", self.voxels:get(x,y,z+1,BLOCK_ID) ~= 0)
        self:set("w", self.voxels:get(x+1,y,z,BLOCK_ID) ~= 0)
        self:set("e", self.voxels:get(x-1,y,z,BLOCK_ID) ~= 0)
    end
    
    function fence:addSlats(model, var, x1, x2, z1, z2)
        if self:get(var) then
            model:addElement
            {
                lower = vec3(x1,180,z1),
                upper = vec3(x2,230,z2)
            }
            
            model:addElement
            {
                lower = vec3(x1,180-90,z1),
                upper = vec3(x2,230-90,z2)
            }
        end        
    end
    
    function fence:buildModel(model)
        model:clear()
        
        model:addElement
        {
            lower = vec3(98,0,98),
            upper = vec3(158,255,158)
        }
        
        --local n = self.get("n")
        self:addSlats(model, "n", 114, 142, 0, 98)
        self:addSlats(model, "s", 114, 142, 158, 255)        
        self:addSlats(model, "e", 0, 98, 114, 142)
        self:addSlats(model, "w", 158, 255, 114, 142)        
    end
        
    return fence
end

First thing we notice is that we have a function fence that includes other functions. This is a Lua-style way of building what amounts to an object class. In Lua, if you call a function by saying to some variable

foo.func(x,y,z)

The table for foo is interrogated to find a function named func and that function is called with the parameters x, y, and z. (Everything in Lua has a table of functions associated with it, even an integer.)

If, however you say

foo:func(x,y,z)

The same function will be called but the parameters will be foo, x, y, and z. This is the conventional way you call an object method, and when you declare the function in Lua, you declare it as

fooClass:func(x,y,z,)

which tells the compiler to define and set up to access self. So we see in John’s code that he’s doing that and intends these functions to be applied to a specific instance of fence.

So we can read these functions as methods on the fence element. At this moment, I don’t know exactly how and when things get called but based on years of experience and a general sense of things, plus rather decent names on the part of John and Simon (Codea’s main author), we can get a sense of what goes on.

When the main fence is called, that’s probably dropping a new post into the world. A few values and flags get set up, notably whatever

local fence = scene.voxels.blocks:new(name)

does. I think that’s allocating a new block in the space. I’ve read somewhere that the new function can allocate a chunk of blocks but here it seems clear we just want the one. We’ll try to remember to learn more about that.

And we set n s e w flags, which surely are telling us whether there’s a post to our north, south, east, or west. All the included functions go into our table – guessing here – and we return the chunk we just allocated.

It seems likely that the functions created and destroyed are called when the fence is created or destroyed. We note that creation just calls triggerUpdate, which looks like this:


   
    function fence:triggerUpdate()
        self:schedule(0)
        local x,y,z = self:xyz()
        self.voxels:updateBlock(x,y,z-1,0)
        self.voxels:updateBlock(x,y,z+1,0)
        self.voxels:updateBlock(x-1,y,z,0)
        self.voxels:updateBlock(x+1,y,z,0)        
    end

I looked up the call to schedule, and it has to do with telling Codea that this cell needs attention in the current time slice. I’ve lost the reference to that but that’s what I recall.

“Obviously” self:xyz returns our position exploded into x, y, and z … and the remaining four lines tell the voxels collection, somehow to tell our neighbors to update. That’ll give any neighboring posts the chance to draw their half of the rails.

Somewhere along the line, we’re going to call this:


    function fence:blockUpdate()  
        local x,y,z = self:xyz()
        self:set("n", self.voxels:get(x,y,z-1,BLOCK_ID) ~= 0)
        self:set("s", self.voxels:get(x,y,z+1,BLOCK_ID) ~= 0)
        self:set("w", self.voxels:get(x+1,y,z,BLOCK_ID) ~= 0)
        self:set("e", self.voxels:get(x-1,y,z,BLOCK_ID) ~= 0)
    end

That code appears to set the flags n, s, w, e to true if there’s a block present as our neighbor and false otherwise. I don’t think it is checking here to see if it’s a fence. That’s interesting … a bit of experimentation shows me that if you set a fence post down next to anything, it’ll extend its half of the rails. That presumably happens in this code:

    function fence:buildModel(model)
        model:clear()
        
        model:addElement
        {
            lower = vec3(98,0,98),
            upper = vec3(158,255,158)
        }
        
        --local n = self.get("n")
        self:addSlats(model, "n", 114, 142, 0, 98)
        self:addSlats(model, "s", 114, 142, 158, 255)        
        self:addSlats(model, "e", 0, 98, 114, 142)
        self:addSlats(model, "w", 158, 255, 114, 142)        
    end

Here’s where the going gets weird. Let’s first include the addSlats function:

    function fence:addSlats(model, var, x1, x2, z1, z2)
        if self:get(var) then
            model:addElement
            {
                lower = vec3(x1,180,z1),
                upper = vec3(x2,230,z2)
            }
            
            model:addElement
            {
                lower = vec3(x1,180-90,z1),
                upper = vec3(x2,230-90,z2)
            }
        end        
    end

Since we know what a fence post looks like, i.e. a post with some half-slats sticking out the sides, we can conclude that this code must in fact draw a post and then one or more pairs of slats. So that addElement stuff with lower and upper must be doing the job.

But what the heck are those numbers? The 180 and 90 make me think degrees. The 255 makes me think color. And this addElement thing turns out not to be documented. So I asked on the Codea forum and got this answer:

That code is adding an element to a block model. In craft each voxel can have a type and each type can use a model.

Models are made up of elements, each element is a box mesh. lower and upper represent the coordinates of the bounding box of an element. These vectors contain integer values ranging from 0 to 255 in the x, y and z axes. Changing these will allow you to make various shapes. This is how you get stuff like fences, signs, pressure plates, half-height bricks, etc.

You can also have multiple elements in a single model. You can also have a tint colour, textures and ambient occlusion. AO doesn’t really work properly for anything other than full and empty blocks.

Textures generally need to be square and all the same size to work properly since Craft’s voxel system uses atlases internally for rendering.

So there you go. 0 to 255 refer to the fraction of the way from bottom to top, left to right, front to back of the containing cube. Like percentages but range 0-255. Somebody’s storing a byte someplace. OK, now we know. Presumably if I were to draw a picture, we’d see that these values are in sensible places for drawing rectangular posts and slats. We could fiddle them to see what happens.

If I do this …

    function fence:addSlats(model, var, x1, x2, z1, z2)
        if self:get(var) then
            model:addElement
            {
                lower = vec3(x1,180,z1),
                upper = vec3(x2,190,z2) --230
            }
            
            model:addElement
            {
                lower = vec3(x1,180-90,z1),
                upper = vec3(x2,230-90,z2)
            }
        end        
    end

I get a thin slat:

I’ll have to draw some pictures to get a real sense of those values, I’m not used to working in 256ths, and the builder of this object doesn’t seem to be centering things around any convenient powers of two anyway.

I’ve “learned” enough for today. See you tomorrow!

Thursday

Here we are in tomorrow. I was thinking on the way to the coffee shop that I’d like to better understand how the update process goes, particularly in the light of that schedule call. As I mentioned, I vaguely recall that that triggers an update at some possibly later time.

To me, this kind of random digging is typical when we try to understand some new framework. If I were focused on building something, the digging would be different. Very likely, I’d be digging while frustrated. I’d be focused on something that was in my way.

When I can, I like to explore in a more relaxed mode, as we’re doing here, jumping around, getting a sense of how things work, and occasionally trying experiments to firm up my understanding. So here we go … looking at the main program.

Well. That told me nothing. The main just calls scene:update(dt) as usual. I dug through the documentation a bit, and found craft.block, which represents a “specific voxel block variant”. Those would be the fence posts, TNT boxes, all the various blocks that the Block Library example creates. This quote is helpful:

A block type’s behaviour can be customised by enabling scriping and adding special methods to it:

blockType:created() - called when the block is created blockType:destroyed() - called when the block is destroyed (removed) blockType:buildModel(model) - called when the block is about to be modeled

Saturday

OK, picking and choosing things from the examples to understand and explain seems to be unlikely to work: there is too much. So I’ve decided to build a little app or game. I’ll still be borrowing code from the examples, because that’s a good way to learn, and I”ll explain what I learn as I learn it and make it my own.

I think that’ll be better. We’ll see.

So here’s what I think I’ll be aiming for:

We’ll have something a bit like Codea’s “Voxel Terrain” example. We’ll be aiming at generating an interesting terrain, and we’ll have some kind of first person “Player” to wander around. The Player will have an inventory of various blocks that they can use to build things.

We’ll evolve our “game” as we go, as interesting things come to mind. And we’ll start very simply. I’ll try to be sure to quote my sources.

Initial game

I have in mind a simple flat terrain, basically a floor, and the ability to place, destroy and operate scripted blocks. We’ll need some kind of inventory and a GUI, perhaps. We’ll figure that out.

First step will be a floor, and second, I think, will be to put down a block where you touch. I’m going to try to defer having a first-person player for a while: we’ll just work from the screen.

Floor

Conveniently, there’s floor code in the “Block Library” example, and I’ll start by adopting that, in a new project I’ll call Game-1.

And I’ll connect that to Working Copy right away.

We init a new repo in Working Copy, name it Game-1, then long-press the repo line and select synched project, which is the icon that looks like Codea’s. Now we can version and commit and I”ll commit the initial project before I even clean it up.

Then I remove all the silly comments, leaving this bare-bones project:


-- Game-1
-- 20200222 initial plan, make a floor

function setup()
    scene = craft.scene()
    local e = scene:entity()
end

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

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

I commit again, named “bare bones”. Trying to get in the habit.

Then I copy the setup from the Block Library project and cut it down to size. My first try is this:


function setup()
    scene = craft.scene()

    -- Setup camera and lighting
    scene.sun.rotation = quat.eulerAngles(25, 125, 0)

    -- Set the scenes ambient lighting
    scene.ambientColor = color(127, 127, 127, 255)   
    
    -- Setup voxel terrain
    scene.voxels:resize(vec3(5,1,5))      
    scene.voxels.coordinates = vec3(0,0,0)    
    -- Create ground put of grass
    scene.voxels:fill("Bedrock")
    scene.voxels:box(0,10,0,16*5,10,16*5)
    scene.voxels:fill("Dirt")
    scene.voxels:box(0,0,0,16*5,9,16*5)
end

That fails with a message about invalid block type at line 17, “fill”. That’ll be Bedrock. I left out this line:

    -- Set the scenes ambient lighting
    scene.ambientColor = color(127, 127, 127, 255)   
    
    allBlocks = blocks()
    
    -- Setup voxel terrain

I left that out, hoping I could do without it, because the blocks() function is a fairly complex one. And it brings in most of the many tabs of the Block Library project.

I had hoped not to have to replan quite so soon. Let’s see what we can do. Looking at the Basic Blocks example I decide to steal ideas one by one, consistent with the building up plan. I will follow John MIllard’s lead here, by making new tabs for the block building, as I go along. For now, just one.

This is not going well. So far, I’ve got this for the blocks() function, borrowing from the Block Library. But nothing displays at all.

-- blocks
-- RJ 20200222

function blocks()
    -- create one block type, Dirt. Stealing from Block Library project.
    scene.voxels.blocks:addAssetPack("Blocks")
    local dirt = scene.voxels.blocks:new("Dirt")
    dirt.setTexture(ALL, "Blocks:Dirt")
    local allBlocks = scene.voxels.blocks:all()
    return allBlocks
end

There’s some static rigmarole in the Block Library’s version, and I’ll show it here:

-- Loads all blocks
function blocks()
    basicBlocks()
    signBlock()
    piston()
    stairsBlock("Wooden Stairs", "Blocks:Wood")    
    stairsBlock("Stone Stairs", "Blocks:Stone")
    fence("Wooden Fence", "Blocks:Wood") 
    fence("Stone Fence", "Blocks:Stone")       
    chest(40)
    soundb()
    treeGenerator()
    tnt()
        
    -- Get a list of all block types
    local allBlocks = scene.voxels.blocks:all()
    
    -- Generate preview icons for all blocks
    for k,v in pairs(allBlocks) do
        if v.hasIcon == true then
            v.static.icon = generateBlockPreview(v)
        end
    end
    
    return allBlocks
end

And in Basic, it has:


-- A set of basic blocks (mainly cubes)
function basicBlocks()
    
    -- Assets must be added to the voxel system for them to be available
    scene.voxels.blocks:addAssetPack("Blocks")
    
    -- Add some helper functions to the block class
    local directions =
    {
        [NORTH] = vec3(0,0,-1),
        [EAST] = vec3(1,0,0),
        [SOUTH] = vec3(0,0,1),
        [WEST] = vec3(-1,0,0),
        [UP] = vec3(0,1,0),
        [DOWN] = vec3(0,-1,0)
    }
    
    function craft.block.static.faceToDirection(face)
        return directions[face]
    end   
    
    function craft.block.static.directionToFace(dir)
        dir = vec3(dir.x, dir.y/2, dir.z):normalize()
        local minFace = nil
        local minDot = nil
        for k,v in pairs(directions) do
            local dot = dir:dot(v)
            if minFace == nil or dot < minDot then
                minFace = k
                minDot = dot
            end
        end
        return minFace
    end
    
    -- By default all blocks can be dug, have icons and can be placed by the player
    -- Individual block types can override these defaults
    craft.block.static.canDig = true
    craft.block.static.hasIcon = true
    craft.block.static.canPlace = true   
    
    -- Empty block cannot be placed and has no icon
    scene.voxels.blocks.Empty.static.hasIcon = false
    scene.voxels.blocks.Empty.static.canPlace = false        
    
    
    local grass = scene.voxels.blocks:new("Grass")
    grass.setTexture(ALL, "Blocks:Dirt Grass")
    grass.setTexture(DOWN, "Blocks:Dirt")
    grass.setTexture(UP, "Blocks:Grass Top")
    
    local dirt = scene.voxels.blocks:new("Dirt")
    dirt.setTexture(ALL, "Blocks:Dirt")
    
    local sand = scene.voxels.blocks:new("Sand")
    sand.setTexture(ALL, "Blocks:Sand") 
  
    local stone = scene.voxels.blocks:new("Stone")
    stone.setTexture(ALL, "Blocks:Stone")
   
    local bedrock = scene.voxels.blocks:new("Bedrock")
    bedrock.setTexture(ALL, "Blocks:Greystone")
    bedrock.static.canDig = false
    bedrock.static.canPlace = false
    --bedrock.tinted = true
    bedrock.setColor(ALL, color(128, 128, 128, 255))
    
    local water = scene.voxels.blocks:new("Water")
    water.setTexture(ALL, "Blocks:Water")
    water.setColor(ALL, color(100,100,200,170))
    -- Translucent geometry prevents blocks from rendering internal faces between each other
    water.geometry = TRANSLUCENT  
    -- Translucent renderPass is for semi-transparent blocks (i.e alpha less than 255 and greater than 0)
    water.renderPass = TRANSLUCENT
        
    local glass = scene.voxels.blocks:new("Glass")
    glass.setTexture(ALL, "Blocks:Glass")
    glass.geometry = TRANSLUCENT
    glass.renderPass = TRANSLUCENT 
    
    local glassFrame = scene.voxels.blocks:new("Glass Frame")
    glassFrame.setTexture(ALL, "Blocks:Glass Frame")
    glassFrame.geometry = TRANSLUCENT
    glassFrame.renderPass = TRANSLUCENT  
        
    local brickRed = scene.voxels.blocks:new("Red Brick")
    brickRed.setTexture(ALL, "Blocks:Brick Red")
    
    local brick = scene.voxels.blocks:new("Brick")
    brick.setTexture(ALL, "Blocks:Brick Grey")   
    
    local coalOre = scene.voxels.blocks:new("Coal Ore")
    coalOre.setTexture(ALL, "Blocks:Stone Coal")      
    
    local goldOre = scene.voxels.blocks:new("Gold Ore")
    goldOre.setTexture(ALL, "Blocks:Stone Gold")  

    local diamondOre = scene.voxels.blocks:new("Diamond Ore")
    diamondOre.setTexture(ALL, "Blocks:Stone Diamond")   

    local redstoneOre = scene.voxels.blocks:new("Redstone Ore")
    redstoneOre.setTexture(ALL, "Blocks:Redstone")        
       
    local planks = scene.voxels.blocks:new("Planks")
    planks.setTexture(ALL, "Blocks:Wood")
      
    local wood = scene.voxels.blocks:new("Wood")
    wood.setTexture(ALL, "Blocks:Trunk Side")
    wood.setTexture(DOWN, "Blocks:Trunk Top")
    wood.setTexture(UP, "Blocks:Trunk Top")
    
    local leaves = scene.voxels.blocks:new("Leaves")
    leaves.setTexture(ALL, "Blocks:Leaves Transparent")
    --leaves.geometry = TRANSPARENT
    --leaves.renderPass = CUTOUT
    leaves.scripted = true     
    
    local craftingTable = scene.voxels.blocks:new("Crafting Table")
    craftingTable.setTexture(ALL, "Blocks:Table")
end

Most of that activity just creates local blocks, asking for new ones from scene.voxels.blocks:new and assigning some values. Possibly some of those static values matter. I’ve copied what I thought would be enough for dirt.

I’ll copy in that atatic stuff but I am not optimistic. And one possibility is just that the camera isn’t set right … I’ll check that too.

Wow. I checked that first, by adding a dependency on “VoxelPlayer” and adding one line to setup:

function setup()
    scene = craft.scene()

    -- Setup camera and lighting
    scene.sun.rotation = quat.eulerAngles(25, 125, 0)

    -- Set the scenes ambient lighting
    scene.ambientColor = color(127, 127, 127, 255)   
    
    allBlocks = blocks()
    
    -- Setup voxel terrain
    scene.voxels:resize(vec3(5,1,5))      
    scene.voxels.coordinates = vec3(0,0,0)    
    -- Create ground put of grass
    --scene.voxels:fill("Bedrock")
    --scene.voxels:box(0,10,0,16*5,10,16*5)
    scene.voxels:fill("Dirt")
    scene.voxels:box(0,0,0,16*5,9,16*5)
    player = scene:entity():add(BasicPlayer,    scene.camera:get(craft.camera), 40, 20, 40)
end

Now the terrain displays so presumably the problem was that I couldn’t see it. However, I’ve imported a mass of code that I don’t understand (yet).

I’ll leave you here, and in the next article address some concerns with how things are going.