Skip to content

Instantly share code, notes, and snippets.

@ltucker
Created July 12, 2016 23:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ltucker/5636df69ba2b315009a5355daf56ea48 to your computer and use it in GitHub Desktop.
Save ltucker/5636df69ba2b315009a5355daf56ea48 to your computer and use it in GitHub Desktop.
local platform = require("platform")
require("opengl"):import{"*"}
require("sprites"):import{
"Image",
"init_program",
ortho_mat="glOrtho"
}
function setup()
fbsize = platform.screen_size()
platform.log("framebuffer size is " .. fbsize.width .. "x" .. fbsize.height)
device_width = fbsize.width
device_height = fbsize.height
base_width = 240
base_height = 160
width_ratio = device_width / base_width
height_ratio = device_height / base_height
-- don't take the floor for a non-integer scale
-- scale = math.floor(math.min(width_ratio, height_ratio))
scale = math.min(width_ratio, height_ratio)
-- size of the device in base pixels
base_screen_width = device_width / scale
base_screen_height = device_height / scale
-- offset of (0,0) on the device in base pixels
offset_x = (base_screen_width - base_width) / 2
offset_y = (base_screen_height - base_height) / 2
-- projection to apply to transform base pixels to
-- screen space.
device_projection = glOrtho(
-offset_x, base_screen_width-offset_x,
base_screen_height-offset_y, -offset_y,
1, -1
)
-- record the default framebuffer (not always 0, ie iOS)
device_framebuffer = glGetIntegerv(GL_FRAMEBUFFER_BINDING)
-- offscreen framebuffer scale
-- pick the next largest integer scale that is /larger/ than
-- the device scale (well, at least as large)
over_scale = math.ceil(scale)
-- base dimensions scaled up by the overscaling factor
fb_base_width = base_width * over_scale
fb_base_height = base_height * over_scale
-- calculate the width and height of the framebuffer texture.
-- these might be larger than the requested sizes
-- due to power-of-two texture size restrictions.
fb_tex_width, fb_tex_height = next_legal_texture_size(fb_base_width, fb_base_height)
-- set up the texture
fb_texture = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, fb_texture)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, fb_tex_width, fb_tex_height, 0, GL_RGB, GL_UNSIGNED_BYTE, nil)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) -- <-- this will be used since it's downscaling
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
-- make the texture the color attachment for the new offscreen framebuffer
framebuffer = glGenFramebuffers(1)
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb_texture, 0)
-- world -> offscreen framebuffer projection
fb_projection = glOrtho(0, fb_tex_width / over_scale, fb_tex_height / over_scale, 0, 1, -1)
BACKGROUND = Image:from_file("background.png")
TEST_IMAGE_1 = Image:from_file("test1.png")
TEST_IMAGE_2 = Image:from_file("test2.png")
end
function update()
-- prepare to draw to the offscreen framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer)
glViewport(0,0, fb_tex_width, fb_tex_height)
glScissor(0,0,fb_base_width,fb_base_height)
-- draw as usual, but draw onto the offscreen framebuffer
blit(BACKGROUND.texture,
0, 0, BACKGROUND.width, BACKGROUND.height,
0, 0, 1, 1,
fb_projection
)
blit(TEST_IMAGE_1.texture,
140, 90, TEST_IMAGE_1.width, TEST_IMAGE_1.height,
0, 0, 1, 1,
fb_projection
)
blit(TEST_IMAGE_2.texture,
10, 10, TEST_IMAGE_2.width, TEST_IMAGE_2.height,
0, 0, 1, 1,
fb_projection
)
-- finally, draw the framebuffer to the device by rendering it's texture
-- into a quad covering the screen as defined before. Since the offscreen
-- framebuffer is larger than the device framebuffer, downsampling using
-- linear interpolation (GL_LINEAR) will be used.
glBindFramebuffer(GL_FRAMEBUFFER, device_framebuffer)
glViewport(0, 0, device_width, device_height)
-- letter-box / pillar-box to restrict drawing to
-- the virtual screen area only (optional)
glDisable(GL_SCISSOR_TEST)
glClearColor(0,0,0,1)
glClear(GL_COLOR_BUFFER_BIT)
glEnable(GL_SCISSOR_TEST)
glScissor(
offset_x*scale,
offset_y*scale,
base_width*scale,
base_height*scale
)
-- draw the overscaled offscreen framebuffer onto the
-- device framebuffer.
blit(fb_texture,
0, 0, base_width, base_height,
0, 0, fb_base_width / fb_tex_width, fb_base_height / fb_tex_height,
device_projection
)
end
--- basic blit
blit_v_shader_src =
[[
uniform mat4 viewProjection;
attribute vec4 vPosition;
attribute vec4 texCoord_in;
varying vec2 texCoord;
void main()
{
texCoord = texCoord_in.xy;
gl_Position = viewProjection*vPosition;
}
]]
blit_f_shader_src =
[[
precision mediump float;
varying vec2 texCoord;
uniform sampler2D tex;
void main()
{
gl_FragColor.rgba = texture2D(tex, texCoord);
}
]]
_blit_program, _blit_vshader, _blit_fshader = init_program(blit_v_shader_src, blit_f_shader_src)
BLIT_ARG_VPOSITION = glGetAttribLocation(_blit_program, "vPosition")
BLIT_ARG_TEXCOORD = glGetAttribLocation(_blit_program, "texCoord_in")
BLIT_ARG_TEX = glGetUniformLocation(_blit_program, "tex")
BLIT_ARG_VIEWPROJECTION = glGetUniformLocation(_blit_program, "viewProjection")
function blit(tex, x, y, w, h, tx, ty, tw, th, projection)
glUseProgram(_blit_program)
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, tex)
glUniformMatrix4fv(BLIT_ARG_VIEWPROJECTION, 1, GL_FALSE, projection)
glVertexAttribPointer(
BLIT_ARG_VPOSITION, 2, GL_FLOAT, GL_FALSE, 0,
_quad(x,y,w,h)
)
glEnableVertexAttribArray(BLIT_ARG_VPOSITION)
glVertexAttribPointer(BLIT_ARG_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0,
_quad(tx,ty,tw,th)
)
glEnableVertexAttribArray(BLIT_ARG_TEXCOORD)
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
end
function _quad(x,y,w,h)
local x2 = x + w
local y2 = y + h
return GLfloatv(8, {x, y2, x2, y2, x, y, x2, y})
end
function next_legal_texture_size(width, height)
-- assuming power of 2 ...
local ptw = 256
while ptw < width do
ptw = ptw * 2
end
local pth = 256
while pth < height do
pth = pth * 2
end
return ptw, pth
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment