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.