Created
December 22, 2017 23:46
-
-
Save geekhunger/86f379e6ffe8a2a99c3a0e74fb20c83c to your computer and use it in GitHub Desktop.
Codea CraftAR example. Place window frames which display your current camera feed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--# 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