initial implementation of WebGL-based brush renderer

This commit is contained in:
りき萌 2025-09-05 20:20:45 +02:00
parent b4c3260f49
commit bb55e23979
14 changed files with 385 additions and 247 deletions

View file

@ -2,6 +2,8 @@ import { listen, Pool } from "rkgk/framework.js";
import { Viewport } from "rkgk/viewport.js";
import { Wall, chunkKey } from "rkgk/wall.js";
import { AtlasAllocator } from "rkgk/chunk-allocator.js";
import { compileProgram } from "rkgk/webgl.js";
import { BrushRenderer } from "rkgk/brush-renderer.js";
class CanvasRenderer extends HTMLElement {
viewport = new Viewport();
@ -74,7 +76,7 @@ class CanvasRenderer extends HTMLElement {
// Renderer initialization
#initializeRenderer() {
console.groupCollapsed("initializeRenderer");
console.group("initializeRenderer");
console.info("vendor", this.gl.getParameter(this.gl.VENDOR));
console.info("renderer", this.gl.getParameter(this.gl.RENDERER));
@ -93,7 +95,9 @@ class CanvasRenderer extends HTMLElement {
// We also realistically don't need anymore, because (at least at the time I'm writing this)
// we store (8 * 8 = 64) chunks per texture atlas, so we can't batch more than that.
const maxRects = 64;
let renderChunksProgramId = this.#compileProgram(
let renderChunksProgramId = compileProgram(
this.gl,
// Vertex
`#version 300 es
@ -200,47 +204,13 @@ class CanvasRenderer extends HTMLElement {
this.batches = [];
this.batchPool = new Pool();
this.brushRenderer = new BrushRenderer(this.gl, this.atlasAllocator.canvasSource());
console.debug("GL error state", this.gl.getError());
console.groupEnd();
}
#compileShader(kind, source) {
let shader = this.gl.createShader(kind);
this.gl.shaderSource(shader, source);
this.gl.compileShader(shader);
if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {
let error = new Error(`failed to compile shader: ${this.gl.getShaderInfoLog(shader)}`);
this.gl.deleteShader(shader);
throw error;
} else {
return shader;
}
}
#compileProgram(vertexSource, fragmentSource) {
let vertexShader = this.#compileShader(this.gl.VERTEX_SHADER, vertexSource);
let fragmentShader = this.#compileShader(this.gl.FRAGMENT_SHADER, fragmentSource);
let program = this.gl.createProgram();
this.gl.attachShader(program, vertexShader);
this.gl.attachShader(program, fragmentShader);
this.gl.linkProgram(program);
this.gl.deleteShader(vertexShader);
this.gl.deleteShader(fragmentShader);
if (!this.gl.getProgramParameter(program, this.gl.LINK_STATUS)) {
let error = new Error(`failed to link program: ${this.gl.getProgramInfoLog(program)}`);
this.gl.deleteProgram(program);
throw error;
} else {
return program;
}
}
// Renderer
#render() {
@ -256,6 +226,7 @@ class CanvasRenderer extends HTMLElement {
}
this.gl.viewport(0, 0, this.canvas.width, this.canvas.height);
this.gl.scissor(0, 0, this.canvas.width, this.canvas.height);
this.gl.clearColor(1, 1, 1, 1);
this.gl.clear(this.gl.COLOR_BUFFER_BIT);
@ -303,8 +274,9 @@ class CanvasRenderer extends HTMLElement {
this.#resetRectBuffer();
for (let chunk of chunks) {
let { i, allocation } = chunk.allocation;
let atlas = this.atlasAllocator.atlases[i];
let atlasIndex = this.atlasAllocator.getAtlasIndex(chunk.id);
let allocation = this.atlasAllocator.getAllocation(chunk.id);
let atlas = this.atlasAllocator.atlases[atlasIndex];
this.#pushRect(
chunk.x * this.wall.chunkSize,
chunk.y * this.wall.chunkSize,
@ -358,15 +330,14 @@ class CanvasRenderer extends HTMLElement {
for (let chunkX = left; chunkX < right; ++chunkX) {
let chunk = layer.getChunk(chunkX, chunkY);
if (chunk != null) {
let allocation = chunk.id;
let array = batch.get(allocation.i);
let atlasIndex = this.atlasAllocator.getAtlasIndex(chunk.id);
let array = batch.get(atlasIndex);
if (array == null) {
array = [];
batch.set(allocation.i, array);
batch.set(atlasIndex, array);
}
array.push({ layerId: layer.id, x: chunkX, y: chunkY, allocation });
array.push({ layerId: layer.id, x: chunkX, y: chunkY, id: chunk.id });
}
}
}