Codea for Cardboard style VR?

I’d like to use Codea for some virtual reality prototypes I’m building on the iphone for Google Cardboard style VR. I need to setup 2 viewports with cameras for each eye. And also link the gyro to the camera orientation.

Any idea on where I would start to get 2 viewports setup? I’m assuming in the Xcode project. I’ve been poking around the xcode project however and can’t seem to find where the viewport is setup (I’m an xcode beginner).

Any pointers would be appreciated!

You don’t need to have two separate OpenGL viewports, you can do this purely within Codea using setContext. I made a test project for something like this a while ago, I’ll see if I can clean it up and post it.

Edit: Here’s an example


--# Main
spriteBatching(false)

-- Use this function to perform your initial setup
function setup()
    displayMode(STANDARD)
 
    availableTests = { Test2(), Test1() }
    currentTest = availableTests[1]

    parameter.integer("SceneSelect",1,#availableTests,1)
    
    parameter.number("Size",50,500,150)
    parameter.number("ImageSize", 0.25, 1, 1)
    parameter.number("Dist", 300, 1000, 500)
    parameter.number("CamHeight", 0, 1000, 0)
    parameter.number("Angle",-360, 360, 0)
    parameter.number("FieldOfView", 10, 140, 45)
    
    
    the3DViewMatrix = viewMatrix()
    parameter.watch("the3DViewMatrix")
    
    -- These watches are evaluated after draw() is finished
    -- Thus the contents are not as interesting
    parameter.watch("viewMatrix()")
    parameter.watch("modelMatrix()")
    parameter.watch("projectionMatrix()")
    
    leftEye = image(WIDTH / 2, HEIGHT) -- The left eye's image to draw into
    rightEye = image(WIDTH / 2, HEIGHT) -- The right eye's image to draw into
    
    rotation = 0 -- How far the device has turned horizontally
end

-- This function gets called once every frame
function draw()
    rotation = rotation - RotationRate.y / math.abs(Gravity.y)
    
    background(40, 40, 50)
    
    local eyeDist = 0.1 -- Distance in 3D space between eyes
    local lookOffsetXZ = vec2(0, 1):rotate(math.rad(rotation)) -- The X/Z offset from the camera's position to look at (X and Y values are X and Z respectively)
    local lookOffsetY = vec2(1, 0):rotate(Gravity.z * math.pi) -- The X/Z multiplier and Y offset from the camera's position to look at (X value is the X/Z multiplier and the Y value is the Y offset)
    local upMult = vec2(0, 1):rotate(Gravity.x * math.pi - math.pi) -- The up vector's X/Z multiplier and Y value (X value is the X/Z multiplier and the Y value is the Y offset)
    local up = vec3( -- The up vector (pretty complicated, handles tilting)
        (lookOffsetXZ:rotate(math.pi / 2) * (upMult.x)).x,
        1 - upMult.y,
        (lookOffsetXZ:rotate(math.pi / 2) * (upMult.x)).y
    ):normalize()
    
    setContext(leftEye, true)
    
    -- Set the currentTest to the selected scene
    currentTest = availableTests[SceneSelect]
    
    -- First arg is FOV, second is aspect
    perspective(FieldOfView, (WIDTH/2)/HEIGHT)
    
    camera(
        eyeDist,
        CamHeight,
        -Dist,
        eyeDist + lookOffsetXZ.x * lookOffsetY.x,
        CamHeight + lookOffsetY.y,
        -Dist + lookOffsetXZ.y * lookOffsetY.x, 
        up.x,
        up.y,
        up.z
    )
    
    -- This sets a dark background color 
    background(40, 40, 50)

    -- Do your drawing here
    currentTest:draw()
    
    setContext(rightEye, true)
    
    -- Set the currentTest to the selected scene
    currentTest = availableTests[SceneSelect]
    
    -- First arg is FOV, second is aspect
    perspective(FieldOfView, (WIDTH/2)/HEIGHT)
    
    camera(
        -eyeDist,
        CamHeight,
        -Dist,
        -eyeDist + lookOffsetXZ.x * lookOffsetY.x,
        CamHeight + lookOffsetY.y,
        -Dist + lookOffsetXZ.y * lookOffsetY.x, 
        up.x,
        up.y,
        up.z
    )
    
    -- This sets a dark background color 
    background(40, 40, 50)

    -- Do your drawing here
    currentTest:draw()
    setContext()
    
    -- Restore orthographic projection
    ortho()
    
    -- Restore the view matrix to the identity
    viewMatrix(matrix())
    
    resetMatrix()
    
    -- Draw a label at the top of the screen
    fill(255)
    font("MyriadPro-Bold")
    fontSize(30)
    
    text(currentTest:name(), WIDTH/2, HEIGHT - 30)
    sprite(leftEye, WIDTH / 4 * ImageSize + WIDTH / 2 * (1 - ImageSize), HEIGHT / 2, WIDTH / 2 * ImageSize, HEIGHT * ImageSize)
    sprite(rightEye, 3 * (WIDTH / 4) * ImageSize + WIDTH / 2 * (1 - ImageSize), HEIGHT / 2, WIDTH / 2 * ImageSize, HEIGHT * ImageSize)
end


--# Test1
Test1 = class()

function Test1:name()
    return "Codea Primitives in 3D"
end

function Test1:init()
    -- you can accept and set parameters here
end

function Test1:draw()    
    -- Preserve existing transform and style
    pushMatrix()
    pushStyle()
    
    -- This sets the line thickness
    strokeWidth(5)
 
    smooth()
    rectMode(CENTER)
 
    -- Make a floor
    translate(0,-Size/2,0)
    rotate(Angle,0,1,0)
    rotate(90,1,0,0)
    sprite("SpaceCute:Background", 0, 0, 300, 300)
 
    -- Rotate and translate the square
    resetMatrix()
    rotate(Angle,0,1,0)
    translate(0, 0, -100)
 
    fill(44, 97, 161, 255)
    rect(0, 0, Size, Size)
 
    resetMatrix()
    rotate(Angle,0,1,0)
    fill(191, 26, 26, 255)
    ellipse(0, 0, Size*0.8)
    
    -- Restore transform and style
    popStyle()
    popMatrix()
end


--# Test2
Test2 = class()

function Test2:name()
    return "3D Blocks"
end

function Test2:init()
    -- all the unique vertices that make up a cube
    local vertices = {
      vec3(-0.5, -0.5,  0.5), -- Left  bottom front
      vec3( 0.5, -0.5,  0.5), -- Right bottom front
      vec3( 0.5,  0.5,  0.5), -- Right top    front
      vec3(-0.5,  0.5,  0.5), -- Left  top    front
      vec3(-0.5, -0.5, -0.5), -- Left  bottom back
      vec3( 0.5, -0.5, -0.5), -- Right bottom back
      vec3( 0.5,  0.5, -0.5), -- Right top    back
      vec3(-0.5,  0.5, -0.5), -- Left  top    back
    }


    -- now construct a cube out of the vertices above
    local cubeverts = {
      -- Front
      vertices[1], vertices[2], vertices[3],
      vertices[1], vertices[3], vertices[4],
      -- Right
      vertices[2], vertices[6], vertices[7],
      vertices[2], vertices[7], vertices[3],
      -- Back
      vertices[6], vertices[5], vertices[8],
      vertices[6], vertices[8], vertices[7],
      -- Left
      vertices[5], vertices[1], vertices[4],
      vertices[5], vertices[4], vertices[8],
      -- Top
      vertices[4], vertices[3], vertices[7],
      vertices[4], vertices[7], vertices[8],
      -- Bottom
      vertices[5], vertices[6], vertices[2],
      vertices[5], vertices[2], vertices[1],
    }

    -- all the unique texture positions needed
    local texvertices = { vec2(0.03,0.24),
                          vec2(0.97,0.24),
                          vec2(0.03,0.69),
                          vec2(0.97,0.69) }
                
    -- apply the texture coordinates to each triangle
    local cubetexCoords = {
      -- Front
      texvertices[1], texvertices[2], texvertices[4],
      texvertices[1], texvertices[4], texvertices[3],
      -- Right
      texvertices[1], texvertices[2], texvertices[4],
      texvertices[1], texvertices[4], texvertices[3],
      -- Back
      texvertices[1], texvertices[2], texvertices[4],
      texvertices[1], texvertices[4], texvertices[3],
      -- Left
      texvertices[1], texvertices[2], texvertices[4],
      texvertices[1], texvertices[4], texvertices[3],
      -- Top
      texvertices[1], texvertices[2], texvertices[4],
      texvertices[1], texvertices[4], texvertices[3],
      -- Bottom
      texvertices[1], texvertices[2], texvertices[4],
      texvertices[1], texvertices[4], texvertices[3],
    }
    
    -- now we make our 3 different block types
    self.ms = mesh()
    self.ms.vertices = cubeverts
    self.ms.texture = "Planet Cute:Stone Block"
    self.ms.texCoords = cubetexCoords
    self.ms:setColors(255,255,255,255)
    
    self.md = mesh()
    self.md.vertices = cubeverts
    self.md.texture = "Planet Cute:Dirt Block"
    self.md.texCoords = cubetexCoords
    self.md:setColors(255,255,255,255)
    
    self.mg = mesh()
    self.mg.vertices = cubeverts
    self.mg.texture = "Planet Cute:Grass Block"
    self.mg.texCoords = cubetexCoords
    self.mg:setColors(255,255,255,255)   
    
    -- currently doesnt work properly without backfaces
    self.mw = mesh()
    self.mw.vertices = cubeverts
    self.mw.texture = "Planet Cute:Water Block"
    self.mw.texCoords = cubetexCoords
    self.mw:setColors(255,255,255,100)
    
    -- stick 'em in a table
    self.blocks = { self.mg, self.md, self.ms }
    
    -- our scene itself
    -- numbers correspond to block positions in the blockTypes table
    --             bottom      middle      top
    self.scene = {   { {3, 3, 0}, {2, 0, 0}, {0, 0, 0} },
                     { {3, 3, 3}, {2, 2, 0}, {1, 0, 0} },
                     { {3, 3, 3}, {2, 2, 2}, {1, 1, 0} } }
end

function Test2:draw()
    pushMatrix()
    pushStyle()
    
    -- Make a floor
    translate(0,-Size/2,0)
    rotate(Angle,0,1,0)
    rotate(90,1,0,0)
    sprite("SpaceCute:Background", 0, 0, 300, 300) 

    -- render each block in turn
    for zi,zv in ipairs(self.scene) do
        for yi,yv in ipairs(zv) do
            for xi, xv in ipairs(yv) do
                -- apply each transform  need - rotate, scale, translate to the correct place
                resetMatrix()
                rotate(Angle,0,1,0)
                
                local s = Size*0.25
                scale(s,s,s)
                
                translate(xi-2, yi-2, zi-2)    -- renders based on corner
                                               -- so -2 fudges it near center
                
                if xv > 0 then
                    self.blocks[xv]:draw()
                end
            end
        end
    end
    
    popStyle()
    popMatrix()
end

function Test2:touched(touch)
    
end

It’s pretty complicated, but it works.

This is awesome!! Thank you Sky!!

Btw, this crashes on my iPad when I run it from AirCode the moment I try to do any modifications. I need to dig into it further but curious if you are able to build on this in AirCode @SkyTheCoder

@SkyTheCoder this is awesome. Of course I tried holding my iPad in front of my face with a Smart Cover as the divider… :-B

Makes me want to get one of those cardboard vr kits, they’re only a few quid. Tho it would probably break your nose if you tried to wear it with a full-size iPad. When iOS 9 launches, apparently you won’t need a dev account to test apps on your devices (ie more wearable devices like iPhones)… Or if Codea went universal, it would be awesome for this kind of development.