From b5910d8c9751281d32a1f003f792c09ee9904cc5 Mon Sep 17 00:00:00 2001 From: Maciej Samborski Date: Mon, 6 Jan 2025 22:44:41 +0100 Subject: [PATCH] Optimized batching and drawing --- src/js/common.js | 11 +++++----- src/js/common.ts | 24 ++++++++++------------ src/js/draw.js | 44 +++++++++++++++++++++++++--------------- src/js/draw.ts | 50 ++++++++++++++++++++++++++++++---------------- src/js/graphics.js | 2 +- src/js/graphics.ts | 4 ++-- src/js/script.js | 9 ++++----- src/js/script.ts | 10 +++++----- src/js/world.js | 24 +++++++++++++--------- src/js/world.ts | 28 +++++++++++++++++--------- 10 files changed, 123 insertions(+), 83 deletions(-) diff --git a/src/js/common.js b/src/js/common.js index 81687d0..87bb1d5 100644 --- a/src/js/common.js +++ b/src/js/common.js @@ -1,9 +1,9 @@ -function initializeContext(canvasId) { +export function initializeContext(canvasId) { const canvas = document.getElementById(canvasId); const ctx = canvas.getContext("webgl2", { antialias: false }); return ctx; } -class Vec2 { +export class Vec2 { x; y; constructor(x, y) { @@ -84,7 +84,7 @@ class Vec2 { return new Vec2(x, y); } } -class Vec3 { +export class Vec3 { x; y; z; @@ -164,7 +164,7 @@ class Vec3 { return [this.x, this.y, this.z]; } } -class Vec4 { +export class Vec4 { x; y; z; @@ -253,7 +253,7 @@ class Vec4 { throw new Error("Can't extend Vec4"); } } -class Mat4 { +export class Mat4 { data; constructor(i) { if (i instanceof Float32Array) { @@ -409,4 +409,3 @@ class Mat4 { } } } -export { initializeContext, Mat4, Vec2, Vec3, Vec4 }; diff --git a/src/js/common.ts b/src/js/common.ts index fdf1768..76245f6 100644 --- a/src/js/common.ts +++ b/src/js/common.ts @@ -1,4 +1,4 @@ -function initializeContext(canvasId: string): WebGL2RenderingContext | null { +export function initializeContext(canvasId: string): WebGL2RenderingContext | null { const canvas = document.getElementById(canvasId) as HTMLCanvasElement; const ctx = canvas.getContext("webgl2", {antialias: false}); @@ -25,7 +25,7 @@ interface Vector { extend(value: number): N; } -class Vec2 implements Vector { +export class Vec2 implements Vector { x: number; y: number; @@ -134,7 +134,7 @@ class Vec2 implements Vector { } } -class Vec3 implements Vector { +export class Vec3 implements Vector { x: number; y: number; z: number; @@ -240,7 +240,7 @@ class Vec3 implements Vector { } } -class Vec4 implements Vector { +export class Vec4 implements Vector { x: number; y: number; z: number; @@ -356,14 +356,14 @@ class Vec4 implements Vector { type Mat4Init = number | Float32Array; -type Mat4Row = 0 | 1 | 2 | 3 -type Mat4Col = Mat4Row -type Mat4X = Mat4Row -type Mat4Y = Mat4Row -type Mat4Z = Mat4Row -type Mat4W = Mat4Row +export type Mat4Row = 0 | 1 | 2 | 3 +export type Mat4Col = Mat4Row +export type Mat4X = Mat4Row +export type Mat4Y = Mat4Row +export type Mat4Z = Mat4Row +export type Mat4W = Mat4Row -class Mat4 { +export class Mat4 { data: Float32Array; constructor(i: Mat4Init) { @@ -551,5 +551,3 @@ class Mat4 { } } } - -export { initializeContext, Mat4, Vec2, Vec3, Vec4 }; diff --git a/src/js/draw.js b/src/js/draw.js index f8899fb..d62f761 100644 --- a/src/js/draw.js +++ b/src/js/draw.js @@ -28,49 +28,61 @@ export class Rectangle { draw(corners, fill) { if (fill instanceof Sprite) { let uvs = Assets.assets.get("sprites").getUVs(fill.id); - this.data.push([ + let data = [ corners[0].x, corners[0].y, corners[0].z, 0, 0, 0, 0, uvs[0].x, uvs[0].y, 1, corners[1].x, corners[1].y, corners[1].z, 0, 0, 0, 0, uvs[1].x, uvs[1].y, 1, corners[3].x, corners[3].y, corners[3].z, 0, 0, 0, 0, uvs[3].x, uvs[3].y, 1, corners[2].x, corners[2].y, corners[2].z, 0, 0, 0, 0, uvs[2].x, uvs[2].y, 1, corners[1].x, corners[1].y, corners[1].z, 0, 0, 0, 0, uvs[1].x, uvs[1].y, 1, corners[3].x, corners[3].y, corners[3].z, 0, 0, 0, 0, uvs[3].x, uvs[3].y, 1, - ]); + ]; + for (let i = 0; i < data.length; ++i) { + this.data.push(data[i]); + } } else { let color = fill; - this.data.push([ + let data = [ corners[0].x, corners[0].y, corners[0].z, color[0], color[1], color[2], color[3], 0, 0, 0, corners[1].x, corners[1].y, corners[1].z, color[0], color[1], color[2], color[3], 0, 0, 0, corners[3].x, corners[3].y, corners[3].z, color[0], color[1], color[2], color[3], 0, 0, 0, corners[2].x, corners[2].y, corners[2].z, color[0], color[1], color[2], color[3], 0, 0, 0, corners[1].x, corners[1].y, corners[1].z, color[0], color[1], color[2], color[3], 0, 0, 0, corners[3].x, corners[3].y, corners[3].z, color[0], color[1], color[2], color[3], 0, 0, 0, - ]); + ]; + for (let i = 0; i < data.length; ++i) { + this.data.push(data[i]); + } } } drawExts(position, exts, fill) { if (fill instanceof Sprite) { let uvs = Assets.assets.get("sprites").getUVs(fill.id); - this.data.push([ + let data = [ position.x, position.y, position.z, 0, 0, 0, 0, uvs[0].x, uvs[0].y, 1, position.x + exts.x, position.y, position.z, 0, 0, 0, 0, uvs[1].x, uvs[1].y, 1, position.x, position.y + exts.y, position.z, 0, 0, 0, 0, uvs[3].x, uvs[3].y, 1, position.x + exts.x, position.y + exts.y, position.z, 0, 0, 0, 0, uvs[2].x, uvs[2].y, 1, position.x + exts.x, position.y, position.z, 0, 0, 0, 0, uvs[1].x, uvs[1].y, 1, position.x, position.y + exts.y, position.z, 0, 0, 0, 0, uvs[3].x, uvs[3].y, 1, - ]); + ]; + for (let i = 0; i < data.length; ++i) { + this.data.push(data[i]); + } } else { let color = fill; - this.data.push([ - position.x, position.y, position.z, color[0], color[1], color[2], color[3], 0, 0, - position.x + exts.x, position.y, position.z, color[0], color[1], color[2], color[3], 0, 0, - position.x, position.y + exts.y, position.z, color[0], color[1], color[2], color[3], 0, 0, - position.x + exts.x, position.y + exts.y, position.z, color[0], color[1], color[2], color[3], 0, 0, - position.x + exts.x, position.y, position.z, color[0], color[1], color[2], color[3], 0, 0, - position.x, position.y + exts.y, position.z, color[0], color[1], color[2], color[3], 0, 0, - ]); + let data = [ + position.x, position.y, position.z, color[0], color[1], color[2], color[3], 0, 0, 0, + position.x + exts.x, position.y, position.z, color[0], color[1], color[2], color[3], 0, 0, 0, + position.x, position.y + exts.y, position.z, color[0], color[1], color[2], color[3], 0, 0, 0, + position.x + exts.x, position.y + exts.y, position.z, color[0], color[1], color[2], color[3], 0, 0, 0, + position.x + exts.x, position.y, position.z, color[0], color[1], color[2], color[3], 0, 0, 0, + position.x, position.y + exts.y, position.z, color[0], color[1], color[2], color[3], 0, 0, 0, + ]; + for (let i = 0; i < data.length; ++i) { + this.data.push(data[i]); + } } } } @@ -125,8 +137,8 @@ export function drawIsometricGrid(gfx, grid) { // TODO: Optimize this // 1. Grid based occlusion culling // 2. frustum culling - for (let k = 0; k < grid.height; ++k) { - for (let j = 0; j < grid.length; ++j) { + for (let k = 0; k < grid.topHeight; ++k) { + for (let j = 0; j < grid.breadth; ++j) { for (let i = 0; i < grid.width; ++i) { tileCoord.x = i; tileCoord.y = j; diff --git a/src/js/draw.ts b/src/js/draw.ts index 7d45d0f..1d359a7 100644 --- a/src/js/draw.ts +++ b/src/js/draw.ts @@ -11,7 +11,7 @@ export class Rectangle implements Drawable { attribs: string[] = ["a_position", "a_color", "a_tex"]; tags: Array = []; - data: number[][] = []; + data: number[] = []; vertexSize: number = 10; sprites: Spritesheet = Assets.assets.get("sprites") as Spritesheet; @@ -38,7 +38,7 @@ export class Rectangle implements Drawable { if (fill instanceof Sprite) { let uvs = (Assets.assets.get("sprites") as Spritesheet).getUVs(fill.id); - this.data.push([ + let data = [ corners[0].x, corners[0].y, corners[0].z, 0, 0, 0, 0, uvs[0].x, uvs[0].y, 1, corners[1].x, corners[1].y, corners[1].z, 0, 0, 0, 0, uvs[1].x, uvs[1].y, 1, corners[3].x, corners[3].y, corners[3].z, 0, 0, 0, 0, uvs[3].x, uvs[3].y, 1, @@ -46,11 +46,15 @@ export class Rectangle implements Drawable { corners[2].x, corners[2].y, corners[2].z, 0, 0, 0, 0, uvs[2].x, uvs[2].y, 1, corners[1].x, corners[1].y, corners[1].z, 0, 0, 0, 0, uvs[1].x, uvs[1].y, 1, corners[3].x, corners[3].y, corners[3].z, 0, 0, 0, 0, uvs[3].x, uvs[3].y, 1, - ]); + ]; + + for (let i = 0; i < data.length; ++i) { + this.data.push(data[i]); + } } else { let color = fill; - this.data.push([ + let data = [ corners[0].x, corners[0].y, corners[0].z, color[0], color[1], color[2], color[3], 0, 0, 0, corners[1].x, corners[1].y, corners[1].z, color[0], color[1], color[2], color[3], 0, 0, 0, corners[3].x, corners[3].y, corners[3].z, color[0], color[1], color[2], color[3], 0, 0, 0, @@ -58,7 +62,11 @@ export class Rectangle implements Drawable { corners[2].x, corners[2].y, corners[2].z, color[0], color[1], color[2], color[3], 0, 0, 0, corners[1].x, corners[1].y, corners[1].z, color[0], color[1], color[2], color[3], 0, 0, 0, corners[3].x, corners[3].y, corners[3].z, color[0], color[1], color[2], color[3], 0, 0, 0, - ]); + ]; + + for (let i = 0; i < data.length; ++i) { + this.data.push(data[i]); + } } } @@ -66,7 +74,7 @@ export class Rectangle implements Drawable { if (fill instanceof Sprite) { let uvs = (Assets.assets.get("sprites") as Spritesheet).getUVs(fill.id); - this.data.push([ + let data = [ position.x, position.y, position.z, 0, 0, 0, 0, uvs[0].x, uvs[0].y, 1, position.x + exts.x, position.y, position.z, 0, 0, 0, 0, uvs[1].x, uvs[1].y, 1, position.x, position.y + exts.y, position.z, 0, 0, 0, 0, uvs[3].x, uvs[3].y, 1, @@ -74,19 +82,27 @@ export class Rectangle implements Drawable { position.x + exts.x, position.y + exts.y, position.z, 0, 0, 0, 0, uvs[2].x, uvs[2].y, 1, position.x + exts.x, position.y, position.z, 0, 0, 0, 0, uvs[1].x, uvs[1].y, 1, position.x, position.y + exts.y, position.z, 0, 0, 0, 0, uvs[3].x, uvs[3].y, 1, - ]); + ]; + + for (let i = 0; i < data.length; ++i) { + this.data.push(data[i]); + } } else { let color = fill; - this.data.push([ - position.x, position.y, position.z, color[0], color[1], color[2], color[3], 0, 0, - position.x + exts.x, position.y, position.z, color[0], color[1], color[2], color[3], 0, 0, - position.x, position.y + exts.y, position.z, color[0], color[1], color[2], color[3], 0, 0, + let data = [ + position.x, position.y, position.z, color[0], color[1], color[2], color[3], 0, 0, 0, + position.x + exts.x, position.y, position.z, color[0], color[1], color[2], color[3], 0, 0, 0, + position.x, position.y + exts.y, position.z, color[0], color[1], color[2], color[3], 0, 0, 0, - position.x + exts.x, position.y + exts.y, position.z, color[0], color[1], color[2], color[3], 0, 0, - position.x + exts.x, position.y, position.z, color[0], color[1], color[2], color[3], 0, 0, - position.x, position.y + exts.y, position.z, color[0], color[1], color[2], color[3], 0, 0, - ]); + position.x + exts.x, position.y + exts.y, position.z, color[0], color[1], color[2], color[3], 0, 0, 0, + position.x + exts.x, position.y, position.z, color[0], color[1], color[2], color[3], 0, 0, 0, + position.x, position.y + exts.y, position.z, color[0], color[1], color[2], color[3], 0, 0, 0, + ]; + + for (let i = 0; i < data.length; ++i) { + this.data.push(data[i]); + } } } } @@ -156,8 +172,8 @@ export function drawIsometricGrid(gfx: Graphics, grid: Grid) { // TODO: Optimize this // 1. Grid based occlusion culling // 2. frustum culling - for (let k = 0; k < grid.height; ++k) { - for (let j = 0; j < grid.length; ++j) { + for (let k = 0; k < grid.topHeight; ++k) { + for (let j = 0; j < grid.breadth; ++j) { for (let i = 0; i < grid.width; ++i) { tileCoord.x = i; tileCoord.y = j; diff --git a/src/js/graphics.js b/src/js/graphics.js index 2e98a43..bf68c8f 100644 --- a/src/js/graphics.js +++ b/src/js/graphics.js @@ -92,7 +92,7 @@ export class Graphics { } draw() { for (let o of this.toRender) { - const data = o.data.flat(); + const data = o.data; this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.vbo); this.ctx.bufferData(this.ctx.ARRAY_BUFFER, new Float32Array(data), this.ctx.STATIC_DRAW); let aid = 0; diff --git a/src/js/graphics.ts b/src/js/graphics.ts index 891e270..a16c83d 100644 --- a/src/js/graphics.ts +++ b/src/js/graphics.ts @@ -60,7 +60,7 @@ export interface Drawable { attribs: string[]; tags: Array; - data: number[][]; + data: number[]; vertexSize: number; sprites: Spritesheet | undefined; @@ -131,7 +131,7 @@ export class Graphics { draw() { for (let o of this.toRender) { - const data = o.data.flat(); + const data = o.data; this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.vbo); this.ctx.bufferData(this.ctx.ARRAY_BUFFER, new Float32Array(data), this.ctx.STATIC_DRAW); diff --git a/src/js/script.js b/src/js/script.js index 6f82926..02b3798 100644 --- a/src/js/script.js +++ b/src/js/script.js @@ -140,11 +140,10 @@ function addDefaultKeybinds(input, camera) { right: Assets.Colors.Brown, left: Assets.Colors.Brown, }), 0); - tree(grid, new Vec2(5, 5)); - //for (let i = 0; i < 10; i++) { - // tree(grid, new Vec2(Math.floor(Math.random() * size - 1), Math.floor(Math.random() * size - 1))); - //} - //grid.setTile(new Tile(Assets.AssetToTileFill("log"), TileEdge.Both), new Vec3(0, 29, 1)); + //grid.setTile(new Tile(Sprite.tile(0), TileEdge.Both), new Vec3(1, 1, 1)); + for (let i = 0; i <= size / 2; i++) { + tree(grid, new Vec2(Math.floor(Math.random() * size - 1), Math.floor(Math.random() * size - 1))); + } let prevTimestamp = 0; const frame = (timestamp) => { const deltaTime = (timestamp - prevTimestamp) / 1000; diff --git a/src/js/script.ts b/src/js/script.ts index c1b49a7..071b87a 100644 --- a/src/js/script.ts +++ b/src/js/script.ts @@ -192,12 +192,12 @@ function addDefaultKeybinds(input: Input, camera: Camera) { right: Assets.Colors.Brown, left: Assets.Colors.Brown, }), 0); - tree(grid, new Vec2(5, 5)); - //for (let i = 0; i < 10; i++) { - // tree(grid, new Vec2(Math.floor(Math.random() * size - 1), Math.floor(Math.random() * size - 1))); - //} - //grid.setTile(new Tile(Assets.AssetToTileFill("log"), TileEdge.Both), new Vec3(0, 29, 1)); + //grid.setTile(new Tile(Sprite.tile(0), TileEdge.Both), new Vec3(1, 1, 1)); + + for (let i = 0; i <= size / 2; i++) { + tree(grid, new Vec2(Math.floor(Math.random() * size - 1), Math.floor(Math.random() * size - 1))); + } let prevTimestamp = 0; const frame = (timestamp: number) => { diff --git a/src/js/world.js b/src/js/world.js index 46b47e4..43837ba 100644 --- a/src/js/world.js +++ b/src/js/world.js @@ -33,36 +33,42 @@ export class Grid { tiles3d; tileSize; width; - length; + breadth; height; - constructor(position, tileSize, width, length, height) { + topHeight; + constructor(position, tileSize, width, breadth, height) { this.tiles3d = new Array(height); this.position = position; this.tileSize = tileSize; this.width = width; - this.length = length; + this.breadth = breadth; this.height = height; - let layer = new Array(width * length); + this.topHeight = 0; + let layer = new Array(width * breadth); for (let i = 0; i < this.height; ++i) this.tiles3d[i] = { ...layer }; } fillLayer(tile, z) { + if (z + 1 > this.topHeight) + this.topHeight = z + 1; for (let i = 0; i < this.width; ++i) { - for (let j = 0; j < this.length; ++j) { + for (let j = 0; j < this.breadth; ++j) { this.tiles3d[z][i + j * this.width] = { ...tile }; } } for (let i = 0; i < this.width; ++i) { - this.tiles3d[z][this.length * i - 1] = { ...tile, edge: TileEdge.Right }; + this.tiles3d[z][this.breadth * i - 1] = { ...tile, edge: TileEdge.Right }; } - for (let i = 0; i < this.length - 1; ++i) { - this.tiles3d[z][this.width * (this.length - 1) + i] = { ...tile, edge: TileEdge.Left }; + for (let i = 0; i < this.breadth - 1; ++i) { + this.tiles3d[z][this.width * (this.breadth - 1) + i] = { ...tile, edge: TileEdge.Left }; } - this.tiles3d[z][this.width * this.length - 1] = { ...tile, edge: TileEdge.Both }; + this.tiles3d[z][this.width * this.breadth - 1] = { ...tile, edge: TileEdge.Both }; } setTile(tile, coord) { let index = coord.x + coord.y * this.width; this.tiles3d[coord.z][index] = { ...tile }; + if (coord.z + 1 > this.topHeight) + this.topHeight = coord.z + 1; } getTile(coord) { let index = coord.x + coord.y * this.width; diff --git a/src/js/world.ts b/src/js/world.ts index 2e29613..56198b8 100644 --- a/src/js/world.ts +++ b/src/js/world.ts @@ -47,46 +47,56 @@ export class Grid { tileSize: number; width: number; - length: number; + breadth: number; height: number; - constructor(position: Vec3, tileSize: number, width: number, length: number, height: number) { + topHeight: number; + + constructor(position: Vec3, tileSize: number, width: number, breadth: number, height: number) { this.tiles3d = new Array(height); this.position = position; this.tileSize = tileSize; this.width = width; - this.length = length; + this.breadth = breadth; this.height = height; - let layer = new Array(width * length); + this.topHeight = 0; + + let layer = new Array(width * breadth); for (let i = 0; i < this.height; ++i) this.tiles3d[i] = {...layer}; } fillLayer(tile: Tile, z: number) { + if (z + 1 > this.topHeight) + this.topHeight = z + 1; + for (let i = 0; i < this.width; ++i) { - for (let j = 0; j < this.length; ++j) { + for (let j = 0; j < this.breadth; ++j) { this.tiles3d[z][i + j * this.width] = {...tile}; } } for (let i = 0; i < this.width; ++i) { - this.tiles3d[z][this.length * i - 1] = {...tile, edge: TileEdge.Right}; + this.tiles3d[z][this.breadth * i - 1] = {...tile, edge: TileEdge.Right}; } - for (let i = 0; i < this.length - 1; ++i) { - this.tiles3d[z][this.width * (this.length - 1) + i] = {...tile, edge: TileEdge.Left}; + for (let i = 0; i < this.breadth - 1; ++i) { + this.tiles3d[z][this.width * (this.breadth - 1) + i] = {...tile, edge: TileEdge.Left}; } - this.tiles3d[z][this.width * this.length - 1] = {...tile, edge: TileEdge.Both}; + this.tiles3d[z][this.width * this.breadth - 1] = {...tile, edge: TileEdge.Both}; } setTile(tile: Tile, coord: Vec3) { let index = coord.x + coord.y * this.width; this.tiles3d[coord.z][index] = {...tile}; + + if (coord.z + 1 > this.topHeight) + this.topHeight = coord.z + 1; } getTile(coord: Vec3): Tile | null {