Skip to content

Instantly share code, notes, and snippets.

@geekhunger
Created December 22, 2017 23:46
Show Gist options
  • Save geekhunger/86f379e6ffe8a2a99c3a0e74fb20c83c to your computer and use it in GitHub Desktop.
Save geekhunger/86f379e6ffe8a2a99c3a0e74fb20c83c to your computer and use it in GitHub Desktop.
Codea CraftAR example. Place window frames which display your current camera feed.
--# Main
supportedOrientations(LANDSCAPE_ANY)
displayMode(OVERLAY)
function setup()
    scene = craft.scene()
    --scene.sun:get(craft.light).intensity = 0.7
    
    
    if craft.ar.isSupported then
        
        scene.ar:run()
        
        
        trackingState = {
            [AR_NOT_AVAILABLE] = {message = "Not Available", color = color(255, 0, 58, 255)},
            [AR_LIMITED] = {message = "Limited", color = color(255, 177, 0, 255)},
            [AR_NORMAL] = {message = "Normal", color = color(101, 198, 58, 255)}
        }
        
        do
            cross = image(8, 8)
            setContext(cross)
            pushStyle()
            fill(120, 200, 255)
            noStroke()
            rectMode(CENTER)
            rect(cross.width/2, cross.height/2, 2, cross.height)
            rect(cross.width/2, cross.height/2, cross.width, 2)
            popStyle()
            setContext()
        end
        
        planes = {}
        local grid = readImage("Project:GridWhite")
        
        
        parameter.boolean("PlaneDetection", true, function(flag)
            scene.ar.planeDetection = flag
        end)
        
        
        parameter.boolean("DrawPlanes", true, function(flag)
            local cam = scene.camera:get(craft.camera)
            if flag then
                cam.mask = ~0
            else
                cam.mask = 1
            end
        end)
        
        
        parameter.boolean("DrawPointCloud", true)
        
        
        function scene.ar.didAddAnchors(anchors)
            for k, v in pairs(anchors) do
                local p = scene:entity():add(Plane, v, grid)
                planes[v.identifier] = p
            end
        end
        
        
        function scene.ar.didUpdateAnchors(anchors)
            for k, v in pairs(anchors) do
                local p = planes[v.identifier]
                p:updateWithAnchor(v)
            end
        end
        
        
        function scene.ar.didRemoveAnchors(anchors)
            for k, v in pairs(anchors) do
                local p = planes[v.identifier]
                p.entity:destroy()
                planes[v.identifier] = nil
            end
        end
        
    end
end
function draw()
    scene:update(DeltaTime)
    scene:draw()
    
    
    local statusMessage = "AR Not Supported"
    local statusColor = color(127, 127, 127, 255)
    local cam = scene.camera:get(craft.camera)
    
    
    if craft.ar.isSupported then
        statusMessage = trackingState[scene.ar.trackingState].message
        statusColor = trackingState[scene.ar.trackingState].color
        
        
        -- show feature point cloud
        
        if DrawPointCloud then
            for k, v in pairs(scene.ar.points) do
                local p = cam:worldToScreen(v)
                sprite(cross, p.x, p.y)
            end
        end
        
        
        -- show world origin
        
        local pnts = {
            vec3(-0, 0, 0), -- x axis
            vec3( 1, 0, 0),
            vec3(0, -0, 0), -- y axis
            vec3(0,  1, 0),
            vec3(0, 0, -0), -- z axis
            vec3(0, 0,  1)
        }
        
        local clrs = {
            color(255, 70, 0, 255),
            color(147, 255, 0, 255),
            color(180, 50, 239, 255)
        }
        
        local j = 0
        for i = 1, #pnts, 2 do
            local p1 = cam:worldToScreen(pnts[i])
            local p2 = cam:worldToScreen(pnts[i+1])
            j = j + 1
            pushStyle()
            stroke(clrs[j])
            strokeWidth(3)
            line(p1.x, p1.y, p2.x, p2.y)
            popStyle()
        end
    end
    
    
    -- show AR status indicator
    
    fill(255)
    text(statusMessage, WIDTH/2, HEIGHT - 50)
    fill(statusColor)
    rect(WIDTH - 32, HEIGHT - 32, 24, 24)
    
    
    -- show device position and rotation debug info
    
    textAlign(RIGHT)
    text(string.format("@cam_pos: %s\n@cam_dir: %s", cam:screenToRay(vec2())), WIDTH/2, 30)
    
end
function touched(touch)
    if craft.ar.isSupported and touch.state == ENDED then
        
        local cam = scene.camera:get(craft.camera)
        local cam_position, cam_direction = cam:screenToRay(vec2(WIDTH/2, HEIGHT/2))
        
        
        -- try 1:
        --[[
        --local inverse_cam_rotation = quat.lookRotation(-cam_direction, vec3(0, 1, 0)):angles()
        --local model_direction = quat.eulerAngles(0, inverse_cam_rotation.y, 0)
        --]]
        
        -- try 2:
        --[[
        --local inverse_cam_rotation = quat.lookRotation(cam_direction, vec3(0, 1, 0)):angles() + vec3(0, 180, 0)
        --local model_direction = quat.eulerAngles(0, inverse_cam_rotation.y, 0)
        --]]
        
        -- try 3 (basically like try 1 but also rotating on x):
        local model_direction = quat.lookRotation(-cam_direction, vec3(0, 1, 0))
        
        
        local model_size = vec3(WIDTH, HEIGHT, .01) * .00025
        local e = scene:entity()
        --e:add(Cube, cam_position, model_size, model_direction) -- try with thin cubes
        e:add(Snapshot, cam_position, model_size, model_direction) -- TODO: turn on double sided rendering
        
        
        
        -- Fallowing code is not completely working yet...
        -- AR seems to block CAMERA access so I can not use setContext to draw a snapshot of its current view
        
        e.material.map = CAMERA -- for now just grab the live view
        
        --[[
        pushMatrix()
        pushStyle()
        ortho() --?
        
        local img = image(model_size.x, model_size.y)
        setContext(img)
        spriteMode(CORNER)
        sprite(CAMERA)
        setContext()
        e.material.map = img
        
        popStyle()
        popMatrix()
        --]]
        
    end
end
--# Planes
Plane = class()
function Plane:init(entity, anchor, map)
    self.entity = entity
    self.entity.model = craft.model.plane(vec2(1, 1))
    self.entity:get(craft.renderer).mask = 1<<2
    
    local mat = craft.material("Materials:Basic")
    mat.map = map
    mat.blendMode = NORMAL
    mat.diffuse = color(120, 200, 255)
    self.entity.material = mat
    
    self:updateWithAnchor(anchor)
end
function Plane:updateWithAnchor(anchor)
    self.entity.position = anchor.position
    self.entity.scale = anchor.extent + vec3(0, 1, 0)
    self.entity.rotation = anchor.rotation
    self.entity.material.offsetRepeat = vec4(0, 0, anchor.extent.x, anchor.extent.z)   
end
--# Cubes
Cube = class()
function Cube:init(entity, position, size, dir)
    self.entity = entity
    self.entity.model = craft.model.cube(size)
    self.entity.material = craft.material("Materials:Standard")
    self.entity.position = position
    self.entity.rotation = dir
    --self.entity:add(craft.shape.box, size)
    --self.entity:add(craft.rigidbody, STATIC, 1)
end
--# Snapshots
Snapshot = class()
function Snapshot:init(entity, position, size, dir)
    local w = size.x/2
    local h = size.y/2
    local c = color(255, 255, 255, 255) -- color of each vertex
    local n = vec3(0, 0, 1) -- lookat direction of each vertex
    local model = craft.model()
    
    -- It seems like glFrontFace() is set to GL_CW winding order inside of Codea(?)
    -- because when listing vertex-indices at counter-clockwise order the resulting faces get culled by OpenGL
    -- and also textures are flipped on y
    -- ...or I'm doing something wrong...
    
    model.positions = { -- CCW winding order
        vec3(-w, -h, 0),
        vec3(w, -h, 0),
        vec3(w, h, 0),
        vec3(-w, h, 0)
    }
    
    -- default uv space (0,0) bottom left
    --[[
    model.uvs = {
        vec2(0, 0),
        vec2(1, 0),
        vec2(1, 1),
        vec2(0, 1)
    }
    --]]
    
    -- upside-down uv space (0,0) top left
    model.uvs = {
        vec2(0, 1),
        vec2(1, 1),
        vec2(1, 0),
        vec2(0, 0)
    }
    
    --model.indices = {1, 2, 3, 3, 4, 1} -- CCW
    model.indices = {1, 4, 3, 3, 2, 1} -- CW!
    model.normals = {n, n, n, n}
    model.colors = {c, c, c, c}
    self.entity = entity
    self.entity.model = model
    self.entity.material = craft.material("Materials:Standard")
    self.entity.position = position
    self.entity.rotation = dir
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment