Skip to content

Instantly share code, notes, and snippets.

@Skaruts
Last active June 14, 2020 05:41
Show Gist options
  • Save Skaruts/26f011fbae8f302c5bab7966a8b755f9 to your computer and use it in GitHub Desktop.
Save Skaruts/26f011fbae8f302c5bab7966a8b755f9 to your computer and use it in GitHub Desktop.
import rlw
import cell
import colors
import tileset
import vector2 # my own Vector struct
import rect
import nimex
import vertex # my own Vertex struct
import raylib/rlgl
# import common
# import deque
# import debugger
const BG_GLYPH = 219 # background glyph
const VOID = 32 # empty glyph
const DEF_GLYPH = 0 # default char glyph
const DEF_BG = Blank # default bg color
const DEF_FG = Blank # default fg color
type
BackendKinds* = enum
BE_TextureBuffer, BE_VertexBuffer #, BE_OpenGL
var renderer* = BE_TextureBuffer
type # this is much like a C struct
Backend* = ref object
x*, y*:int
w*, h*:int
scale*:float
# render_states:RenderStates
tileset:Tileset
# render_proc:proc(self:Backend, cells, buffer:seq[Cell])
# render_proc:proc
case kind*:BackendKinds
of BE_VertexBuffer:
# for a terraria like thing you don't need two sets of quads
bg_quads:seq[Vertex] # background quads (the color around text characters)
fg_quads:seq[Vertex] # foreground quads (text characters)
of BE_TextureBuffer:
render_tex:RenderTexture
glyph_rects:array[256, Recti]
flip_rect_s:Recti # to flip the render texture on render
# else: discard
template cs*(self:Backend):Vector2i = self.tileset.ts # cell size, same as tile size from tileset
template cw*(self:Backend):int = self.tileset.tw # cell width
template ch*(self:Backend):int = self.tileset.th # cell height
proc get_idx(self:Backend, x, y:int):int = # TODO: this should be a template
x+y*self.w
# some forward declarations
# TODO: organize this file so I don't need these...
proc init_rects(self:Backend)
proc init_texture_buffer(self:Backend)
proc init_vertex_buffers(self:Backend)
proc texture_render(self:Backend, cells, buffer:var seq[Cell])
proc render_quads(self:Backend)
proc update_quads(self:Backend, cells, buffer:var seq[Cell])
proc init(self:Backend) =
case self.kind
of BE_VertexBuffer:
# self.render_proc = texture_render
self.init_vertex_buffers()
of BE_TextureBuffer:
self.init_rects()
# self.render_proc = update_quads
self.init_texture_buffer()
# else: discard
proc new_backend*(x, y, w, h:int, tileset:Tileset):Backend =
result = Backend(kind:renderer)
result.x = x
result.y = y
result.w = w
result.h = h
result.tileset = tileset
result.init()
proc render*(self:Backend, cells, buffer:var seq[Cell]) =
# TODO: store these in the 'render_proc' property to avoid having to use a case-of
case self.kind
of BE_VertexBuffer:
self.update_quads(cells, buffer)
# self.render_tris()
self.render_quads()
of BE_TextureBuffer:
self.texture_render(cells, buffer)
# self.render_proc(cells, buffer)
proc unload*(self:Backend) =
case self.kind
of BE_VertexBuffer:
discard
of BE_TextureBuffer:
unloadRenderTexture(self.render_tex)
# precomputed rects for each tile in the tileset
proc init_rects(self:Backend) =
for j in 0..<16:
for i in 0..<16:
self.glyph_rects[i+j*16] = rect(i*self.cw, j*self.ch, self.cw, self.ch)
proc init_texture_buffer(self:Backend) =
self.render_tex = loadRenderTexture(self.w*self.cw, self.h*self.ch)
self.flip_rect_s = rect(0, 0, self.render_tex.texture.w, -self.render_tex.texture.h)
# this is a call to a nim template I made for convenience, which internally
# does the usual Begin/EndTextureMode calls, so it's equivalent to that
withTextureMode(self.render_tex):
clearBackground(Blank)
proc init_vertex_buffers(self:Backend) =
self.bg_quads = @[] # reset sequences if they have anything already
self.fg_quads = @[]
let (CW, CH) = (self.cw, self.ch) # cell width / height
let COLS = self.tileset.cols # tileset width, in tiles
let UVS = 1.0/float(COLS) # UV size - TODO: should account for different width and height (UVW, UVH)
for j in 0..<self.h:
for i in 0..<self.w:
var u = float(self.tileset.bg_glyph mod COLS) * UVS
var v = float(self.tileset.bg_glyph div COLS) * UVS
var u2 = u + UVS
var v2 = v + UVS
# A --- B
# | |
# D --- C
self.bg_quads.add( new_vertex(vecf( i * CW, j * CH), DEF_BG, vecf( u, v)) ) # a
self.bg_quads.add( new_vertex(vecf((i+1) * CW, j * CH), DEF_BG, vecf(u2, v)) ) # b
self.bg_quads.add( new_vertex(vecf((i+1) * CW, (j+1) * CH), DEF_BG, vecf(u2, v2)) ) # c
self.bg_quads.add( new_vertex(vecf( i * CW, (j+1) * CH), DEF_BG, vecf( u, v2)) ) # d
u = float(DEF_GLYPH mod COLS) * UVS
v = float(DEF_GLYPH div COLS) * UVS
u2 = u + UVS
v2 = v + UVS
self.fg_quads.add( new_vertex(vecf( i * CW, j * CH), DEF_FG, vecf( u, v)) ) # a
self.fg_quads.add( new_vertex(vecf((i+1) * CW, j * CH), DEF_FG, vecf(u2, v)) ) # b
self.fg_quads.add( new_vertex(vecf((i+1) * CW, (j+1) * CH), DEF_FG, vecf(u2, v2)) ) # c
self.fg_quads.add( new_vertex(vecf( i * CW, (j+1) * CH), DEF_FG, vecf( u, v2)) ) # d
proc texture_render(self:Backend, cells, buffer:var seq[Cell]) =
block:
# benchmark "Backend Rendering":
# this is a call to a nim template I made for convenience, which internally
# does the usual Begin/EndTextureMode calls, so it's equivalent to that
withTextureMode(self.render_tex):
for j in 0..<self.h:
for i in 0..<self.w:
let idx = self.get_idx(i, j)
let pos = vec(i*self.cw, j*self.ch)
let cell = cells[idx]
let buffer_cell = buffer[idx]
if buffer_cell.glyph < 0 or buffer_cell.glyph > 255: #self.tileset.max_glyphs:
# if out of bounds, make it zero
# TODO: make it the tileset's empty char instead
buffer_cell.glyph = DEF_GLYPH
if buffer_cell.fg.a == 0:
buffer_cell.glyph = DEF_GLYPH # no alpha, no glyph
if buffer_cell.glyph == DEF_GLYPH:
buffer_cell.fg = Blank
if buffer_cell != cell:
cell.glyph = buffer_cell.glyph
cell.fg = buffer_cell.fg
cell.bg = buffer_cell.bg
# Correct y for scissor mode (will be fixed in the future.
# See: https://github.com/raysan5/raylib/commit/6264c4901b67fb49f829165bdb806c11be40b29a)
let corrected_y = pos.y + getScreenHeight() - (self.h*self.ch)
# clear the tile's portion of the RenderTexture in scissor mode to work around to the inability
# to copy a fully transparent tile from a texture to a render texture (which means I have
# to do this for all the changed tiles, even if they're not fully transparent. Not much a fan of that, but whatever.)
withScissorMode(pos.x, corrected_y, self.cw, self.ch):
clearBackground(Blank)
drawTextureRec(self.tileset.texture, self.glyph_rects[219], pos, cell.bg)
drawTextureRec(self.tileset.texture, self.glyph_rects[cell.glyph], pos, cell.fg)
drawTextureRec(self.render_tex.texture, self.flip_rect_s, vec(self.x, self.y) * self.cs, White)
template add_vertex(self:Backend, v:Vertex):untyped =
rlColor4ub(v.color.r, v.color.g, v.color.b, v.color.a)
rlTexCoord2f(v.uv.x, v.uv.y)
rlVertex2f(v.pos.x, v.pos.y)
proc render_quads(self:Backend) =
let (CW, CH) = (self.cw, self.ch) # cell width / height
if rlCheckBufferLimit(self.w*self.h*8): # prevent MAX_BATCH_ELEMENTS overflow error - https://github.com/raysan5/raylib/issues/800
rlglDraw()
rlEnableTexture(self.tileset.texture.id)
rlPushMatrix(); block:
rlTranslatef(float(self.x*CW), float(self.y*CH), 0.0)
# A --- B
# | |
# D --- C
rlBegin(RL_QUADS); block:
var idx = 0
for i in 0 ..< self.w*self.h:
idx = i*4
# background
self.add_vertex( self.bg_quads[idx ] ) # A
self.add_vertex( self.bg_quads[idx + 3] ) # D
self.add_vertex( self.bg_quads[idx + 2] ) # C
self.add_vertex( self.bg_quads[idx + 1] ) # B
# foreground
self.add_vertex( self.fg_quads[idx ] ) # A
self.add_vertex( self.fg_quads[idx + 3] ) # D
self.add_vertex( self.fg_quads[idx + 2] ) # C
self.add_vertex( self.fg_quads[idx + 1] ) # B
rlEnd()
rlPopMatrix()
rlDisableTexture()
proc update_quads(self:Backend, cells, buffer:var seq[Cell]) =
let (CW, CH) = (self.cw, self.ch) # cell width / height
let COLS = self.tileset.cols # tileset width, in tiles
let UVS = 1.0/float(COLS) # UV size - TODO: should account for different width and height (UVW, UVH)
var idx = 0
for i in 0..<self.w*self.h:
idx = i*4
let b_cell = buffer[i]
let cell = cells[i]
if b_cell.glyph < 0 or b_cell.glyph > 255: #self.tileset.max_glyphs:
# if out of bounds, make it zero
# TODO: make it the tileset's empty chard instead
b_cell.glyph = DEF_GLYPH
if b_cell.fg.a == 0:
b_cell.glyph = DEF_GLYPH # no alpha, no glyph
if b_cell.glyph == DEF_GLYPH:
b_cell.fg = Blank
if cell != b_cell:
let (fg_a, bg_a) = (self.fg_quads[idx ], self.bg_quads[idx ]) # a
let (fg_b, bg_b) = (self.fg_quads[idx+1], self.bg_quads[idx+1]) # b
let (fg_c, bg_c) = (self.fg_quads[idx+2], self.bg_quads[idx+2]) # c
let (fg_d, bg_d) = (self.fg_quads[idx+3], self.bg_quads[idx+3]) # d
# if cell.bg != Blank:
if b_cell.bg != cell.bg:
cell.bg = b_cell.bg
bg_a.color = cell.bg
bg_b.color = cell.bg
bg_c.color = cell.bg
bg_d.color = cell.bg
# if cell.fg != Blank:
if b_cell.glyph != cell.glyph:
cell.glyph = b_cell.glyph
let u = float(cell.glyph mod COLS) * UVS
let v = float(cell.glyph div COLS) * UVS
let u2 = u + UVS
let v2 = v + UVS
fg_a.uv = vecf(u , v )
fg_b.uv = vecf(u2, v )
fg_c.uv = vecf(u2, v2)
fg_d.uv = vecf(u , v2)
if b_cell.fg != cell.fg:
cell.fg = b_cell.fg
fg_a.color = cell.fg
fg_b.color = cell.fg
fg_c.color = cell.fg
fg_d.color = cell.fg
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment