Move responsibility for drawing out of Tileset and into the renderer

This commit is contained in:
Eevee (Evelyn Woods) 2020-09-16 14:10:10 -06:00
parent 2e0519f802
commit e70e92b931
3 changed files with 56 additions and 58 deletions

View File

@ -229,11 +229,6 @@ class Player extends PrimaryView {
this._redraw();
});
// Populate inventory
this._inventory_tiles = {};
let floor_tile = this.render_inventory_tile('floor');
this.inventory_el.style.backgroundImage = `url(${floor_tile})`;
this.renderer = new CanvasRenderer(this.conductor.tileset);
this.level_el.append(this.renderer.canvas);
this.renderer.canvas.addEventListener('auxclick', ev => {
@ -244,6 +239,11 @@ class Player extends PrimaryView {
this.level.move_to(this.level.player, this.level.cells[y][x], 1);
});
// Populate inventory
this._inventory_tiles = {};
let floor_tile = this.render_inventory_tile('floor');
this.inventory_el.style.backgroundImage = `url(${floor_tile})`;
let last_key;
this.pending_player_move = null;
this.next_player_move = null;
@ -526,10 +526,8 @@ class Player extends PrimaryView {
render_inventory_tile(name) {
if (! this._inventory_tiles[name]) {
// TODO put this on the renderer
// TODO reuse the canvas for data urls
let canvas = mk('canvas', {width: this.conductor.tileset.size_x, height: this.conductor.tileset.size_y});
this.conductor.tileset.draw({type: TILE_TYPES[name]}, null, canvas.getContext('2d'), 0, 0);
let canvas = this.renderer.create_tile_type_canvas(name);
this._inventory_tiles[name] = canvas.toDataURL();
}
return this._inventory_tiles[name];
@ -1026,13 +1024,9 @@ class Editor extends PrimaryView {
let section_el = mk('section');
palette_el.append(mk('h2', sectiondef.title), section_el);
for (let name of sectiondef.tiles) {
let entry = mk('canvas.palette-entry', {
width: this.conductor.tileset.size_x,
height: this.conductor.tileset.size_y,
'data-tile-name': name,
});
let ctx = entry.getContext('2d');
this.conductor.tileset.draw_type(name, null, null, ctx, 0, 0);
let entry = this.renderer.create_tile_type_canvas(name);
entry.setAttribute('data-tile-name', name);
entry.classList = 'palette-entry';
this.palette[name] = entry;
section_el.append(entry);
}

View File

@ -22,6 +22,7 @@ export class CanvasRenderer {
this.canvas = mk('canvas', {width: tileset.size_x * this.viewport_size_x, height: tileset.size_y * this.viewport_size_y});
this.canvas.style.setProperty('--viewport-width', this.viewport_size_x);
this.canvas.style.setProperty('--viewport-height', this.viewport_size_y);
this.ctx = this.canvas.getContext('2d');
this.viewport_x = 0;
this.viewport_y = 0;
}
@ -40,6 +41,16 @@ export class CanvasRenderer {
return [x, y];
}
// Draw to a canvas using tile coordinates
blit(ctx, sx, sy, dx, dy, w = 1, h = w) {
let tw = this.tileset.size_x;
let th = this.tileset.size_y;
ctx.drawImage(
this.tileset.image,
sx * tw, sy * th, w * tw, h * th,
dx * tw, dy * th, w * tw, h * th);
}
draw(tic_offset = 0) {
if (! this.level) {
console.warn("CanvasRenderer.draw: No level to render");
@ -48,9 +59,9 @@ export class CanvasRenderer {
// TODO StoredLevel may not have a tic_counter
let tic = (this.level.tic_counter ?? 0) + tic_offset;
let ctx = this.canvas.getContext('2d');
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
let tw = this.tileset.size_x;
let th = this.tileset.size_y;
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
// TODO only recompute if the player moved?
// TODO what about levels smaller than the viewport...? shrink the canvas in set_level?
@ -68,8 +79,8 @@ export class CanvasRenderer {
let x0 = Math.max(0, Math.min(this.level.size_x - this.viewport_size_x, px - xmargin));
let y0 = Math.max(0, Math.min(this.level.size_y - this.viewport_size_y, py - ymargin));
// Round to the pixel grid
x0 = Math.floor(x0 * this.tileset.size_x + 0.5) / this.tileset.size_x;
y0 = Math.floor(y0 * this.tileset.size_y + 0.5) / this.tileset.size_y;
x0 = Math.floor(x0 * tw + 0.5) / tw;
y0 = Math.floor(y0 * th + 0.5) / th;
this.viewport_x = x0;
this.viewport_y = y0;
// The viewport might not be aligned to the grid, so split off any fractional part.
@ -89,10 +100,11 @@ export class CanvasRenderer {
let y1 = Math.min(this.level.size_y - 1, Math.ceil(y0 + this.viewport_size_y));
// Draw one layer at a time, so animated objects aren't overdrawn by
// neighboring terrain
let x, y;
// XXX layer count hardcoded here
for (let layer = 0; layer < 4; layer++) {
for (let x = xf0; x <= x1; x++) {
for (let y = yf0; y <= y1; y++) {
for (x = xf0; x <= x1; x++) {
for (y = yf0; y <= y1; y++) {
for (let tile of this.level.cells[y][x]) {
if (tile.type.draw_layer !== layer)
continue;
@ -104,19 +116,29 @@ export class CanvasRenderer {
// Handle smooth scrolling
let [vx, vy] = tile.visual_position(tic_offset);
// Round this to the pixel grid too!
vx = Math.floor(vx * this.tileset.size_x + 0.5) / this.tileset.size_x;
vy = Math.floor(vy * this.tileset.size_y + 0.5) / this.tileset.size_y;
this.tileset.draw(tile, tic, ctx, vx - x0, vy - y0);
vx = Math.floor(vx * tw + 0.5) / tw;
vy = Math.floor(vy * th + 0.5) / th;
this.tileset.draw(tile, tic, (sx, sy, dx = 0, dy = 0, w = 1, h = w) =>
this.blit(this.ctx, sx, sy, vx - x0 + dx, vy - y0 + dy, w, h));
}
else {
// Non-actors can't move
this.tileset.draw(tile, tic, ctx, x - x0, y - y0);
this.tileset.draw(tile, tic, (sx, sy, dx = 0, dy = 0, w = 1, h = w) =>
this.blit(this.ctx, sx, sy, x - x0 + dx, y - y0 + dy, w, h));
}
}
}
}
}
}
create_tile_type_canvas(name) {
let canvas = mk('canvas', {width: this.tileset.size_x, height: this.tileset.size_y});
let ctx = canvas.getContext('2d');
this.tileset.draw_type(name, null, 0, (sx, sy, dx = 0, dy = 0, w = 1, h = w) =>
this.blit(ctx, sx, sy, dx, dy, w, h));
return canvas;
}
}
export default CanvasRenderer;

View File

@ -491,23 +491,13 @@ export class Tileset {
this.size_y = size_y;
}
// Helper to draw to a canvas using tile coordinates
blit(ctx, sx, sy, dx, dy, scale_x = 1, scale_y = scale_x) {
let w = this.size_x * scale_x;
let h = this.size_y * scale_y;
ctx.drawImage(
this.image,
sx * this.size_x, sy * this.size_y, w, h,
dx * this.size_x, dy * this.size_y, w, h);
}
draw(tile, tic, ctx, x, y) {
this.draw_type(tile.type.name, tile, tic, ctx, x, y);
draw(tile, tic, blit) {
this.draw_type(tile.type.name, tile, tic, blit);
}
// Draws a tile type, given by name. Passing in a tile is optional, but
// without it you'll get defaults.
draw_type(name, tile, tic, ctx, x, y) {
draw_type(name, tile, tic, blit) {
let drawspec = this.layout[name];
if (! drawspec) {
console.error(`Don't know how to draw tile type ${name}!`);
@ -519,9 +509,9 @@ export class Tileset {
// southeast thin walls. Draw the base (a type name), then draw
// the overlay (either a type name or a regular draw spec).
// TODO chance of infinite recursion here
this.draw_type(drawspec.base, tile, tic, ctx, x, y);
this.draw_type(drawspec.base, tile, tic, blit);
if (typeof drawspec.overlay === 'string') {
this.draw_type(drawspec.overlay, tile, tic, ctx, x, y);
this.draw_type(drawspec.overlay, tile, tic, blit);
return;
}
else {
@ -539,7 +529,7 @@ export class Tileset {
if (tile && tile.wire_directions !== undefined && tile.wire_directions !== 0) {
// TODO all four is a different thing entirely
// Draw the appropriate wire underlay
this.draw_type('#unpowered', tile, tic, ctx, x, y);
this.draw_type('#unpowered', tile, tic, blit);
// Draw a masked part of the base tile
let wiredir = tile.wire_directions;
@ -548,16 +538,16 @@ export class Tileset {
let wire1 = 0.5 + wire_radius;
let [bx, by] = drawspec.base;
if ((wiredir & DIRECTIONS['north'].bit) === 0) {
this.blit(ctx, bx, by, x, y, 1, wire0);
blit(bx, by, 0, 0, 1, wire0);
}
if ((wiredir & DIRECTIONS['south'].bit) === 0) {
this.blit(ctx, bx, by + wire1, x, y + wire1, 1, wire0);
blit(bx, by + wire1, 0, wire1, 1, wire0);
}
if ((wiredir & DIRECTIONS['west'].bit) === 0) {
this.blit(ctx, bx, by, x, y, wire0, 1);
blit(bx, by, 0, 0, wire0, 1);
}
if ((wiredir & DIRECTIONS['east'].bit) === 0) {
this.blit(ctx, bx + wire1, by, x + wire1, y, wire0, 1);
blit(bx + wire1, by, wire1, 0, wire0, 1);
}
// Then draw the wired tile as normal
@ -571,7 +561,7 @@ export class Tileset {
coords = drawspec.base;
}
else {
this.blit(ctx, drawspec.base[0], drawspec.base[1], x, y);
blit(drawspec.base[0], drawspec.base[1]);
coords = drawspec.wired;
}
}
@ -621,17 +611,11 @@ export class Tileset {
// Continue on with masking
coords = drawspec.tile;
let [x0, y0, w, h] = drawspec.mask;
this.blit(
ctx,
coords[0] + x0,
coords[1] + y0,
x + x0,
y + y0,
w, h);
blit(coords[0] + x0, coords[1] + y0, x0, y0, w, h);
}
else {
if (!coords) console.error(name, tile);
this.blit(ctx, coords[0], coords[1], x, y);
blit(coords[0], coords[1]);
}
// Special behavior for special objects
@ -661,9 +645,7 @@ export class Tileset {
sy = (letter_spec.y0 + Math.floor(n / w)) * scale;
}
let offset = (1 - scale) / 2;
this.blit(
ctx, sx, sy,
x + offset, y + offset, scale);
blit(sx, sy, offset, offset, scale, scale);
}
}
}