Codea Craft
One of the things I like to find out is how much trouble I can get into by working incrementally, when some new requirement comes out of the blue. Today’s new requirement is to display my Braitenberg Vehicles in 3D in Codea. Codea has a capability called “Craft” that can do that. (We could also roll our own but it makes sense to at least look into the Craft framework.) So for a few articles, let’s do that.
Codea is lovely, but its documentation, while comprehensive, leaves a lot to be desired, at least for me. I would like to read something other than what amounts to an alphabetized reference manual. I’d like to have a description that builds up capability and understanding over time, with real running examples as we go. It seems likely that I’m going to have to create my own. There are running examples, and they are quite interesting, but they all seem to use somewhat different approaches and components, so it’s difficult to get a coherent view. Difficult for me, anyway.
I did a little frustrating play over the weekend, so my mind isn’t entirely empty right now, but I’m going to start here from scratch. There are a lot of possible sources on the web, mostly with examples that no longer work, as Craft seems to have evolved. I’ll point to the resources as I use them.
I’ll start with the Learn Craft example. I duplicate the example, so that I can edit it, and I hook Working Copy up to it so that I can revert when I mess up. I’ve also got the Craft reference page open. Now let’s look around.
Codea usually provides nothing much but a “draw” method that’s called 60x a second. The Craft framework adds a “scene”, which is a class (I think) that contains all the items in the scene. The main tab in our example here looks like this:
— Craft Scenes
function setup()
— Create a new craft scene
scene = craft.scene()
scene.sky.active = false
end
function cleanup()
if viewer then
touches.removeHandler(viewer)
viewer = nil
end
scene = nil
collectgarbage()
end
function update(dt)
— Update the scene (physics, transforms etc)
scene:update(dt)
end
— Called automatically by codea
function draw()
update(DeltaTime)
— Draw the scene
scene:draw()
— 2D drawing goes here
drawStepName()
end
function PrintExplanation()
output.clear()
print("Where is everything? This is a blank scene, the starting point for a 3D project.")
print("To setup an initial blank scene:")
print("Create an instance of craft.scene in setup()")
print("The scene then needs to be updated and drawn each frame to work.")
print("Theres no need to clear the background as scene will do it for you.")
print("2D drawing can be done after scene:draw() as normal.")
end
We see the same basic structure, but the draw
call first calls update, passing it DeltaTime, and then tells the scene to draw. In our example file, it also draws the step name. We’ll take a look at what that is in due time. I’ll run the thing and see what it does.
As you see above, the first step just shows a blank screen. There’s a slider to show the step number, and a run and next button. Run just runs the current step, Next runs the next step. Here goes a Next:
Step 2 shows this little robot guy, and there’s a slider to rotate the view:
Let’s look at the code that does that. It says “Entities” at the top, and there’s an Entities tab. Here’s its contents:
— Craft Entities
function setup()
— Create a new craft scene
scene = craft.scene()
scene.sky.active = false
createGround(-1.125)
— Create a new entity
myEntity = scene:entity()
— Set its model for drawing
myEntity.model = craft.model("Blocky Characters:Robot")
— Adjust position and scale
myEntity.y = -1
myEntity.z = 0
myEntity.scale = vec3(1,1,1) / 8
— Move camera back a little
scene.camera.z = -4
parameter.number("Rotate", 0, 360, 180)
end
— Creates the ground using a box model and applies a simple textured material
function createGround(y)
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 update(dt)
— Rotate the entity
myEntity.eulerAngles = vec3(0, Rotate, 0)
— Update the scene (physics, transforms etc)
scene:update(dt)
end
— Called automatically by codea
function draw()
update(DeltaTime)
— Draw the scene
scene:draw()
— 2D drawing goes here
drawStepName()
end
function PrintExplanation()
output.clear()
print("Entities are flexible objects used for displaying 3D models, simulating physics and more")
print("To create an entity use - myEntiy = scene:entity()")
print("To attach a 3D model, use myEntity.model = craft.model(...)")
print("Use the x, y, z and position properties to move entities around")
print("Use the eulerAngles and rotation properties to rotate an entity")
end
Now there are a number of things to note here, and one of them is that this tab has all the setup material in it all over again. And it’s not clear to me right off where the Run and Next buttons are defined, or what they do. So I’ll divert a moment to look for that: it might be important. Ah! There’s a tab called “Main”, containing this:
——————————————
— Learn Craft
— Written by John Millard
— Special thanks to Ignatz for the MultiStep project template
——————————————
— Description:
——————————————
— MultiStep
function setup()
steps = listProjectTabs()
if steps[1]=="Notes" then table.remove(steps,1) end —remove first tab if named Notes
table.remove(steps) —remove the last tab
startStep()
global = "select a step"
end
function showList()
output.clear()
for i=1,#steps do print(i,steps[i]) end
end
function drawStepName()
fill(255, 255, 255, 255)
font("Inconsolata")
fontSize(40)
textAlign(LEFT)
textMode(CORNER)
local name = string.gsub(steps[lastStep], "([A-Z])", " %1")
text(name, 10, HEIGHT - 60)
end
function startStep()
if cleanup then cleanup() end
lastStep=Step or readLocalData("lastStep") or 1
lastStep=math.min(lastStep,#steps)
saveLocalData("lastStep",lastStep)
parameter.clear()
parameter.integer("Step", 1, #steps, lastStep)
parameter.watch("steps[Step]")
parameter.action("Run", startStep)
parameter.action("Next", function()
Step = math.min(Step + 1, #steps)
startStep()
end)
loadstring(readProjectTab(steps[Step]))()
if PrintExplanation then PrintExplanation() end
setup()
end
This is just a collection of functions, and the main purpose is to set up the sidebar so that it can run each of the tabs in the project (except for the last one, in our case “Main”, and a tab called “Notes” if you have one, which we do not). We’ll look more deeply at this later, but my initial take is that what happens is that each tab is run separately under control of this tab. Notice down at the bottom, loadstring(readProjectTab(steps[Step]))()
. That will read in and compile the named tab, which means that the setup and such for that tab will replace what was there before. So each of these tabs can run separately. Probably. Anyway we’ll assume that they’re independent until we learn otherwise.
So, back to the Entities code, here’s a good part:
function setup()
— Create a new craft scene
scene = craft.scene()
scene.sky.active = false
createGround(-1.125)
— Create a new entity
myEntity = scene:entity()
— Set its model for drawing
myEntity.model = craft.model("Blocky Characters:Robot")
— Adjust position and scale
myEntity.y = -1
myEntity.z = 0
myEntity.scale = vec3(1,1,1) / 8
— Move camera back a little
scene.camera.z = -4
parameter.number("Rotate", 0, 360, 180)
end
We can begin to see some flow. It appears that we need a scene, and that it has something about sky. We should try turning that to true to see what happens. Then it creates ground. We’ll look at that in a moment but our pictures make me assume that it’s creating the floor the robot stands on.
Then we create an entity by calling scene:entity. We set some values on it, but we don’t register it again with scene. I think I’ve seen things being added to scene elsewhere: that’s why I bring it up. Apparently when you call scene:entity()
the entity is registered. Makes sense.
We give the entity a model, in this case the robot. There are a lot of provided models and textures and shaders in Codea, and at this writing I don’t know where they came from or how they were created. I’ve read something that makes me think you can actually load 3D files like .blend
files into Codea but I don’t know how. Anyway all that happens here next is that we set his coordinates and scale. I happen to know that x is across the screen, y is up and down the screen, and z is into and out of the screen. We’ll fiddle those values a bit to see what happens to our picture.
There’s a reference to the scene.camera
, and moving it back, so presumably -z is outward. We can play with that. I’ve seen other examples that create their own camera, so there’s yet another topic we need to learn about.
Finally it sets up a parameter, the rotation slider I mentioned.
Let’s look at createGround
a bit:
— Creates the ground using a box model and applies a simple textured material
function createGround(y)
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
So at a guess. New entity, to be the ground. It’s a cube (actually rectangular parallelepiped?) and apparently it’s 4 wide, 0.125 thick, and 4 front to back. And then some interesting new concepts. I’ve read a bit about these.
Some models, perhaps all, are pure geometry, made of triangles. They can have associated at least one material, maybe more. A material seems to have some rudimentary pattern and reflective characteristics. It can also have a “map” which seems to be an image you can read in. Then specular, which surely has to do with reflection, and I’m guessing offsetRepeat has to do with how the map is applied. We’ll play with these in a bit to build up our intuition and maybe even knowledge of what this all does.
I’m interested in one more thing, though: what does that rotation parameter do?
function update(dt)
— Rotate the entity
myEntity.eulerAngles = vec3(0, Rotate, 0)
— Update the scene (physics, transforms etc)
scene:update(dt)
end
So we see that Rotate rotates the robot (not the camera, as I was wondering about above) around his y axis (vertical). This gives us a bit of understanding of what happens in update. Presumably we could rig him to move or spin with code by changing what happens in update. We will probably try that.
First, though, let’s fiddle some parameters.
In that documents page I linked to, I find a reference to offsetRepeat
, saying it changes offset and repeat (tiling). Looks like the first two parameters are offset and the second two, tiling. I’ll try this:
ground.material.offsetRepeat = vec4(0,0,1,1)
I expect that to make the ground pattern larger with only one repetition, and it does:
So if a cube has a material and has a pattern in its map, we can adjust how the pattern repeats. Not a big insight but I’ll take what I can get. I played with the specular setting, putting different colors in, and nothing seemed to happen. That remains a mystery for now.
Let’s move on to some of the robot’s settings. I expect we could change the model, and just to prove it, I’ll click that bit and see what else I can find. With this setting:
— Set its model for drawing
myEntity.model = craft.model("Primitives:Monkey")
— Adjust position and scale
myEntity.y = 0
myEntity.z = 0
myEntity.scale = vec3(1,1,1)
We get this:
Note that I had to scale it up and raise its y. Makes me wonder why the robot needed y to be -1, but apparently it did.
My monkey is white. I wonder if I can give him a texture, like with the floor.
myEntity.material = craft.material("Materials:Specular")
myEntity.material.map = readImage("Blocks:Gravel Stone")
I just picked a random texture out of the Blocks package. There are a lot in there. Look how shiny he is. Let’s try a different material, “Materials:Standard”. That gives us this:
Much less shiny but there are still some highlights. So that’s interesting. Just for fun, I selected a project asset that I had made for the roulette project, and the monkey looks like this:
Further fiddling with the repeats and specular are inconclusive: He just turns black. Let me revert to the original and sum up what we’ve learned so far:
Using Craft, we create a scene. We use that scene to create entities. Entities have a shape, like a robot or a monkey. It appears that some entities, like the robot, have their coloring (map) built in, and others like the floor and monkey, are plain until colored using map.
The entity settings appear to interact in ways that I don’t yet understand, but it seems to be shaping up. I think I’ll try to add another item to this picture before moving on to the next tab. Adding this:
ronEntity = scene:entity()
ronEntity.model = craft.model("Nature:naturePack_061")
ronEntity.x = -2
ronEntity.y = -1
ronEntity.z = 2
ronEntity.scale = vec3(1,1,1) / 2
We now get this:
A small tree has sprouted behind the robot. Notice that I put it at z = 2, and x = -2. So negative x is to the robot’s left as he sees it, and to our right. Remember that our camera is a a negative z value. Z is increasing inward, and since the floor is 4x4, 2 is kind of the back edge. We’ll need to learn more about this as we go on.
I think I’ll commit this version, since it seems to embody some real learning.
I’ve done other play with Craft, as I mentioned, so I know that I don’t fully understand what different things can go into the model, or how one like the robot can be created. Perhaps you build and texture them in some 3D modeling system: I just don’t know. We also say that ground.model.cube
construction, so there are some number of models that model understands. I believe that another one is icosphere
, based on my earlier reading and play. (I happen to know from Blender that there are at least two ways to make a sphere out of triangles, and icosphere is one of them. Probably we won’t care about that. But we will have to learn what’s in there and how to use and texture things like that.
And if my bugs are going to live in 3D, we may have to learn how to make cute little bug models, though they look like rectangles at the moment.
Anyway, enough for a morning’s learning and writing. See you tomorrow!