How to do split screen in 3D

Hey guys!
So I’m working on a two player game in 3D, but I’m getting stuck on the split screen part. I was able to get the camera to focus on player one on the bottom half of the screen, but I can’t figure out how to get the second camera to work.

Here’s my code so far


--# Main

-- Physics
PLAYER_ONE = 1
PLAYER_TWO = 2
PLATFORM = 3
displayMode(FULLSCREEN)
function setup()
    gameMode = 2
    
    platforms = {}
    p1 = Player(1)
    if gameMode == 2 then p2 = Player(2) end
    
    for i = 1,5 do
        table.insert(platforms,
        Platform(math.random(WIDTH),math.random(HEIGHT)))
    end
end

function draw()
    background(40,40,50)
    
    pushMatrix()
    ortho()
    translate(0,-HEIGHT/4)
    clip(0,0,WIDTH,HEIGHT/2)
    perspective(10)
    camera(p1.cam.x,p1.cam.y,5000,p1.body.x,p1.body.y,0,0,1,0)
    for i,p in pairs(platforms) do
        p:draw()
    end
    p1:draw()
    p2:draw()
    popMatrix()
    
    
    pushMatrix()
    ortho()
    translate(WIDTH,HEIGHT)
    rotate(180)
    translate(0,-HEIGHT/4)
    clip(0,HEIGHT/2,WIDTH,HEIGHT/2)
    perspective(10)
    camera(p2.cam.x,p2.cam.y,5000,p2.body.x,p2.body.y,0,0,1,0)
    for i,p in pairs(platforms) do
        p:draw()
    end
    p1:draw()
    p2:draw()
    popMatrix()
    
end

function touched(touch)
    p1:touched(touch)
    p2:touched(touch)
end


--# Player
Player = class()

function Player:init(id)
    self.body = physics.body(CIRCLE,50)
    self.body.position = vec2(100+200*id,600)
    -- self.body.linearDamping = 2
    self.body.restitution = 0
    self.body.friction = 1
    self.body.gravityScale = 1
    
    self.cam = vec2(x,y)
    self:createSphere(id)
end

function Player:draw()
    pushMatrix()
    -- fill(0,100,255)
    if self == p1 then
        stroke(0,100,255)
    else
        stroke(255,50,0)
    end
    strokeWidth(7)
    translate(self.body.x,self.body.y)
    -- ellipse(0,0,100)
    self.sphere:draw()
    if self.jump then
        line(0,0,self.jump.x,self.jump.y)
    end
    popMatrix()
    
    self.camTarget = self.body.position-self.body.linearVelocity/50+vec2(0,1)
    self.cam.x = self.cam.x + (self.camTarget.x-self.cam.x)/20
    self.cam.y = self.cam.y + (self.camTarget.y-self.cam.y)/40
    
end

function Player:touched(touch)
    if touch.state == BEGAN then self.jump = vec2(0,150) end
    if self.jump then
        if self == p1 then
            self.jump = self.jump:rotate(-touch.deltaX/100)
        else
            self.jump = self.jump:rotate(-touch.deltaX/100)
        end
    end
    if touch.state == ENDED and self.jump then 
        self.body:applyLinearImpulse(self.jump:normalize()*20)
        self.jump = nil
    end
end

function Player:createSphere(id)
    self.sphere = mesh()
    local verts = {}
    local colors = {}
    
    local res = 10
    local s = 50
    local long,lat = 360/res,180/res
    for la = 0,lat-1 do
        for lo = 1,long do
            local c1,c2
            if id == 1 then
                c1 = color(0,50-la*lat/5,255-la*lat/5)
                c2 = color(0,50-la*lat/5,255-(la+1)*lat/5)
            else
                c1 = color(255-la*lat/5,0,0)
                c2 = color(255-(la+1)*lat/5,0,0)
            end
            
            local x,y,z
            x = math.cos(math.rad(lo*res))*math.cos(math.rad(90-la*res))*s
            y = math.sin(math.rad(90-la*res))*s
            z = math.sin(math.rad(lo*res))*math.cos(math.rad(90-la*res))*s
            local v1 = vec3(x,y,z)
            x = math.cos(math.rad((lo+1)*res))*math.cos(math.rad(90-la*res))*s
            y = math.sin(math.rad(90-la*res))*s
            z = math.sin(math.rad((lo+1)*res))*math.cos(math.rad(90-la*res))*s
            local v2 = vec3(x,y,z)
            x = math.cos(math.rad((lo+1)*res))*
                math.cos(math.rad(90-(la+1)*res))*s
            y = math.sin(math.rad(90-(la+1)*res))*s
            z = math.sin(math.rad((lo+1)*res))*
                math.cos(math.rad(90-(la+1)*res))*s
            local v3 = vec3(x,y,z)
            x = math.cos(math.rad(lo*res))*math.cos(math.rad(90-(la+1)*res))*s
            y = math.sin(math.rad(90-(la+1)*res))*s
            z = math.sin(math.rad(lo*res))*math.cos(math.rad(90-(la+1)*res))*s
            local v4 = vec3(x,y,z)
            local v = {v1,v2,v3,v3,v4,v1}
            local c = {c1,c1,c2,c2,c2,c1}
            for i,vv in pairs(v) do table.insert(verts,vv) end
            for i,cc in pairs(c) do table.insert(colors,cc) end
        end
    end
    self.sphere.vertices = verts
    self.sphere.colors = colors
    print(#verts,#colors)
end


--# Platform
Platform = class()

function Platform:init(x,y)
    self.x = x
    self.y = y
    self.w = math.random(150,200)
    self.h = math.random(25,50)
    local w,h = self.w/2,self.h/2
    self.pivot = physics.body(CIRCLE,10)
    self.pivot.position = vec2(x,y)
    self.pivot.type = STATIC
    self.platform = physics.body(POLYGON,
    vec2(w,h),vec2(w,-h),vec2(-w,-h),vec2(-w,h))
    self.platform.position = vec2(x,y)
    self.platform.restitution = 0
    self.platform.friction = 1
    self.joint = 
    physics.joint(WELD,self.pivot,self.platform,vec2(x,y))
    self.joint.frequency = 0.9
    self.joint.dampingRatio = 0.1
    
    self.m = mesh()
    local z = 150
    local v1,v2,v3,v4 = vec3(-w,h,z),vec3(w,h,z),vec3(w,-h,z),vec3(-w,-h,z)
    local v5,v6,v7,v8 = vec3(-w,h,-z),vec3(w,h,-z),vec3(w,-h,-z),vec3(-w,-h,-z)
    self.m.vertices = {
        v1,v2,v3,v3,v4,v1,
        v1,v2,v6,v6,v5,v1,
        v5,v6,v7,v7,v8,v5,
        v3,v4,v8,v8,v7,v3,
        v1,v4,v8,v8,v5,v1,
        v2,v3,v7,v7,v6,v2}
    local c1,c2,c3 = color(0,50,255),color(0,45,230),color(0,35,200)
    local c4,c5,c6 = color(0,30,190),color(0,40,210),color(0,25,170)
    self.m.colors = {
        c1,c1,c1,c1,c1,c1,
        c2,c2,c2,c2,c2,c2,
        c3,c3,c3,c3,c3,c3,
        c4,c4,c4,c4,c4,c4,
        c5,c5,c5,c5,c5,c5,
        c6,c6,c6,c6,c6,c6}
end

function Platform:draw()
    pushMatrix()
    translate(self.x,self.y)
    noStroke()
    fill(0,100,255)
    rotate(self.platform.angle)
    local w,h = self.w/2,self.h/2
    self.m:draw()
    popMatrix()
end

function Platform:touched(touch)
    
end

I did also try rendering it onto two separate images, but that didn’t work either. Is there a way to save the camera view to an image?

Thanks for the help!

Codea Craft will make 3D easier. As now, I only can think of

  • mesh()'es
  • translate(X, y, z) – Translates the view to X, y, z
  • rotate(X, y, z) – Rotates the view to X, y, z

@Dwins you’re on the right lines. I did split-screen 3D for Banjax. I created a Viewport class for each view (to cut out code repetition, keep it DRY).

The order you need for each viewport draw is (starting in 2D mode) is:

  • clip
  • perspective
  • camera
  • draw all objects:
    • pushMatrix
    • translate
    • rotate
    • draw
    • popMatrix
  • ortho()
  • viewMatrix(matrix())
  • draw any 2D overlays

Then do it again for the other viewport.

@yojimbo2000 thank you!
The problem was actually in this line
camera(p2.cam.x,p2.cam.y,2000,p2.body.x,p2.body.y,0,0,1,0)
Changing it to this fixed it
camera(WIDTH-p2.cam.x,HEIGHT-p2.cam.y,2000,WIDTH-p2.body.x,HEIGHT-p2.body.y,0,0,1,0)