Last active
June 14, 2020 05:41
-
-
Save Skaruts/26f011fbae8f302c5bab7966a8b755f9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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