Optimized batching and drawing

This commit is contained in:
Maciej Samborski 2025-01-06 22:44:41 +01:00
parent 37a91de848
commit b5910d8c97
10 changed files with 123 additions and 83 deletions

View File

@ -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 };

View File

@ -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<P, T, N> {
extend(value: number): N;
}
class Vec2 implements Vector<void, Vec2, Vec3> {
export class Vec2 implements Vector<void, Vec2, Vec3> {
x: number;
y: number;
@ -134,7 +134,7 @@ class Vec2 implements Vector<void, Vec2, Vec3> {
}
}
class Vec3 implements Vector<Vec2, Vec3, Vec4> {
export class Vec3 implements Vector<Vec2, Vec3, Vec4> {
x: number;
y: number;
z: number;
@ -240,7 +240,7 @@ class Vec3 implements Vector<Vec2, Vec3, Vec4> {
}
}
class Vec4 implements Vector<Vec3, Vec4, void> {
export class Vec4 implements Vector<Vec3, Vec4, void> {
x: number;
y: number;
z: number;
@ -356,14 +356,14 @@ class Vec4 implements Vector<Vec3, Vec4, void> {
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 };

View File

@ -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;

View File

@ -11,7 +11,7 @@ export class Rectangle implements Drawable {
attribs: string[] = ["a_position", "a_color", "a_tex"];
tags: Array<DrawTag> = [];
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;

View File

@ -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;

View File

@ -60,7 +60,7 @@ export interface Drawable {
attribs: string[];
tags: Array<DrawTag>;
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);

View File

@ -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;

View File

@ -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) => {

View File

@ -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;

View File

@ -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<Tile[]>(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 {