diff --git a/assets/sprites.png b/assets/sprites.png new file mode 100644 index 0000000..22dda09 Binary files /dev/null and b/assets/sprites.png differ diff --git a/src/js/assets.js b/src/js/assets.js index b497e32..411a306 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -26,12 +26,10 @@ export class Assets { throw new Error("Tried to assess assets without loading them!"); return this.assets.get(name); } - async load(ctx) { - assets.push("city", await Texture.load(ctx, "../../assets/genetica/rt/City Night.jpg")); - assets.push("wall", await Texture.load(ctx, "../../assets/wall.png")); - assets.push("grass", await Texture.load(ctx, "../../assets/grass.png")); - assets.push("leaves", await Texture.load(ctx, "../../assets/greenary.png")); - assets.push("log", await Texture.load(ctx, "../../assets/log.png")); + async load(gfx) { + assets.push("grass", await Texture.load(gfx, "../../assets/grass.png")); + assets.push("leaves", await Texture.load(gfx, "../../assets/greenary.png")); + assets.push("log", await Texture.load(gfx, "../../assets/log.png")); this.loaded = true; } } diff --git a/src/js/assets.ts b/src/js/assets.ts index ee62f3b..d2ca573 100644 --- a/src/js/assets.ts +++ b/src/js/assets.ts @@ -1,4 +1,4 @@ -import {Texture} from "./graphics.js"; +import {Graphics, Texture} from "./graphics.js"; import {TileFill} from "./world.js"; export type Color = [number, number, number, number] @@ -40,12 +40,11 @@ export class Assets { return this.assets.get(name)!; } - async load(ctx: WebGL2RenderingContext) { - assets.push("city", await Texture.load(ctx, "../../assets/genetica/rt/City Night.jpg")); - assets.push("wall", await Texture.load(ctx, "../../assets/wall.png")); - assets.push("grass", await Texture.load(ctx, "../../assets/grass.png")); - assets.push("leaves", await Texture.load(ctx, "../../assets/greenary.png")); - assets.push("log", await Texture.load(ctx, "../../assets/log.png")); + async load(gfx: Graphics) { + assets.push("grass", await Texture.load(gfx, "../../assets/grass.png")); + assets.push("leaves", await Texture.load(gfx, "../../assets/greenary.png")); + assets.push("log", await Texture.load(gfx, "../../assets/log.png")); + this.loaded = true; } } diff --git a/src/js/draw.js b/src/js/draw.js index 1bc9d10..7b6c3d6 100644 --- a/src/js/draw.js +++ b/src/js/draw.js @@ -1,5 +1,5 @@ -import { Vec3, Vec2, Vec4 } from "./common.js"; -import { Texture } from "./graphics.js"; +import { Vec3 } from "./common.js"; +import { Texture, DrawTag } from "./graphics.js"; import { TileEdge } from "./world.js"; // TODO: Don't assume tile size is same in all directions function cull(point, screen, tileSize) { @@ -13,76 +13,80 @@ function cull(point, screen, tileSize) { return true; return false; } -export function drawTriangle(gfx, positions, color) { - const a_position = gfx.getAttribute("a_position"); - const a_color = gfx.getAttribute("a_color"); - const points = [ - positions[0].x, positions[0].y, - positions[1].x, positions[1].y, - positions[2].x, positions[2].y, - ]; - const colors = [ - color, - color, - color, - ]; - a_position.data(gfx.ctx, points, gfx.ctx.STATIC_DRAW); - a_color.data(gfx.ctx, colors.flat(), gfx.ctx.STATIC_DRAW); - gfx.ctx.drawArrays(gfx.ctx.TRIANGLES, 0, 3); -} -export function drawTriangleExts(gfx, position, exts, color) { - const a_position = gfx.getAttribute("a_position"); - const a_color = gfx.getAttribute("a_color"); - const points = [ - position.x, position.y, position.z, - position.x + exts.x, position.y, position.z, - position.x, position.y + exts.y, position.z, - ]; - const colors = [ - color, - color, - color, - ]; - a_position.data(gfx.ctx, points, gfx.ctx.STATIC_DRAW); - a_color.data(gfx.ctx, colors.flat(), gfx.ctx.STATIC_DRAW); - gfx.ctx.drawArrays(gfx.ctx.TRIANGLES, 0, 3); -} -export function drawRectangle(gfx, corners, color) { - const a_position = gfx.getAttribute("a_position"); - const points = [ - corners[0].x, corners[0].y, corners[0].z, - corners[1].x, corners[1].y, corners[1].z, - corners[3].x, corners[3].y, corners[3].z, - corners[2].x, corners[2].y, corners[2].z, - corners[1].x, corners[1].y, corners[1].z, - corners[3].x, corners[3].y, corners[3].z, - ]; - if (color instanceof Texture) { - const a_tex_position = gfx.getAttribute("a_tex_position"); - const uv = [ - 0.0, 0.0, - -1.0, 0.0, - 0.0, -1.0, - -1.0, -1.0, - -1.0, 0.0, - 0.0, -1.0, - ]; - a_position.data(gfx.ctx, points, gfx.ctx.STATIC_DRAW); - a_tex_position.data(gfx.ctx, uv, gfx.ctx.STATIC_DRAW); - color.bind(gfx); - gfx.ctx.drawArrays(gfx.ctx.TRIANGLES, 0, 6); - color.unbind(gfx); +// Attrib format +// position color uv +// (3) (4) (2) => 3 + 4 + 2 = 9 <=> data.len % 9 == 0 +export class Rectangle { + fill; + attribs = ["a_position", "a_color", "a_tex_position"]; + tags = []; + data = []; + stride = 0; + vertexStride = 0; + constructor(fill, tags, attribs) { + this.fill = fill; + if (attribs !== undefined) { + this.attribs = attribs; + } + if (tags !== undefined) { + this.tags = tags; + } } - else { - const a_color = gfx.getAttribute("a_color"); - const colors = new Array(6); - colors.fill(color); - a_position.data(gfx.ctx, points, gfx.ctx.STATIC_DRAW); - a_color.data(gfx.ctx, colors.flat(), gfx.ctx.STATIC_DRAW); - gfx.ctx.drawArrays(gfx.ctx.TRIANGLES, 0, 6); + overrideDraw(draw) { + this.draw = draw; + } + draw(gfx, corners) { + if (this.fill instanceof Texture) { + this.data = [ + corners[0].x, corners[0].y, corners[0].z, 0, 0, 0, 0, 0, 0, + corners[1].x, corners[1].y, corners[1].z, 0, 0, 0, 0, -1, 0, + corners[3].x, corners[3].y, corners[3].z, 0, 0, 0, 0, 0, -1, + corners[2].x, corners[2].y, corners[2].z, 0, 0, 0, 0, -1, -1, + corners[1].x, corners[1].y, corners[1].z, 0, 0, 0, 0, -1, 0, + corners[3].x, corners[3].y, corners[3].z, 0, 0, 0, 0, 0, -1, + ]; + } + else { + this.data = [ + corners[0].x, corners[0].y, corners[0].z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0, + corners[1].x, corners[1].y, corners[1].z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0, + corners[3].x, corners[3].y, corners[3].z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0, + corners[2].x, corners[2].y, corners[2].z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0, + corners[1].x, corners[1].y, corners[1].z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0, + corners[3].x, corners[3].y, corners[3].z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0, + ]; + } + this.vertexStride = 9; + this.stride = this.vertexStride * 4; + gfx.toRender.push({ ...this }); + } + drawExts(gfx, position, exts) { + if (this.fill instanceof Texture) { + this.data = [ + position.x, position.y, position.z, 0, 0, 0, 0, 0, 0, + position.x + exts.x, position.y, position.z, 0, 0, 0, 0, -1, 0, + position.x, position.y + exts.y, position.z, 0, 0, 0, 0, 0, -1, + position.x + exts.x, position.y + exts.y, position.z, 0, 0, 0, 0, -1, -1, + position.x + exts.x, position.y, position.z, 0, 0, 0, 0, -1, 0, + position.x, position.y + exts.y, position.z, 0, 0, 0, 0, 0, -1, + ]; + } + else { + this.data = [ + position.x, position.y, position.z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0, + position.x + exts.x, position.y, position.z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0, + position.x, position.y + exts.y, position.z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0, + position.x + exts.x, position.y + exts.y, position.z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0, + position.x + exts.x, position.y, position.z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0, + position.x, position.y + exts.y, position.z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0, + ]; + } + this.vertexStride = 9; + this.stride = this.vertexStride * 4; + gfx.toRender.push(this); } } -export function drawIsometricCube(gfx, camera, position, exts, color, edge) { +export function drawIsometricCube(gfx, position, exts, color, edge) { let points = [ // Left Top position, @@ -97,36 +101,38 @@ export function drawIsometricCube(gfx, camera, position, exts, color, edge) { new Vec3(position.x + 1.5 * exts.x, position.y + 0.5 * exts.y, position.z + 0.25 * exts.z), new Vec3(position.x + 0.5 * exts.x, position.y - 0.5 * exts.y, position.z + 0.25 * exts.z), ]; - let screen = new Vec4(-camera.position.x, -camera.position.y, gfx.ctx.canvas.width, gfx.ctx.canvas.height); - gfx.ctx.uniform1i(gfx.getUniform("u_isIso"), 1); + let r = new Rectangle(color.top, [ + DrawTag.ISO, + ]); // Top - drawRectangle(gfx, [ + r.draw(gfx, [ points[0], points[1], points[3], points[2], - ], color.top); + ]); // Right Edge if (edge == TileEdge.Right || edge == TileEdge.Both) { - drawRectangle(gfx, [ + r.fill = color.right; + r.draw(gfx, [ points[3], points[2], points[4], points[5], - ], color.right); + ]); } // Left Edge if (edge == TileEdge.Left || edge == TileEdge.Both) { - drawRectangle(gfx, [ + r.fill = color.left; + r.draw(gfx, [ points[0], points[2], points[4], points[6], - ], color.left); + ]); } - gfx.ctx.uniform1i(gfx.getUniform("u_isIso"), 0); } -export function drawIsometricGrid(gfx, camera, grid) { +export function drawIsometricGrid(gfx, grid) { let position = { ...grid.position }; let exts = new Vec3(grid.tileSize, grid.tileSize, 0); let tileCoord = new Vec3(0, 0, 0); @@ -142,7 +148,7 @@ export function drawIsometricGrid(gfx, camera, grid) { position.x += grid.tileSize; continue; } - drawIsometricCube(gfx, camera, new Vec3(position.x - grid.tileSize / 2 * k, position.y + grid.tileSize / 2 * k, position.z), exts, tile.fill, tile.edge); + drawIsometricCube(gfx, new Vec3(position.x - grid.tileSize / 2 * k, position.y + grid.tileSize / 2 * k, position.z), exts, tile.fill, tile.edge); position.x += grid.tileSize; } position.y -= grid.tileSize; @@ -151,71 +157,3 @@ export function drawIsometricGrid(gfx, camera, grid) { position.y = grid.position.y; } } -export function drawRectangleExts(gfx, position, exts, color) { - const a_position = gfx.getAttribute("a_position"); - const points = [ - position.x, position.y, position.z, - position.x + exts.x, position.y, position.z, - position.x, position.y + exts.y, position.z, - position.x + exts.x, position.y + exts.y, position.z, - position.x + exts.x, position.y, position.z, - position.x, position.y + exts.y, position.z, - ]; - if (color instanceof Texture) { - const a_tex_position = gfx.getAttribute("a_tex_position"); - const uv = [ - 0.0, 0.0, - -1.0, 0.0, - 0.0, -1.0, - -1.0, -1.0, - -1.0, 0.0, - 0.0, -1.0, - ]; - a_position.data(gfx.ctx, points, gfx.ctx.STATIC_DRAW); - a_tex_position.data(gfx.ctx, uv, gfx.ctx.STATIC_DRAW); - color.bind(gfx); - gfx.ctx.drawArrays(gfx.ctx.TRIANGLES, 0, 6); - color.unbind(gfx); - } - else { - const a_color = gfx.getAttribute("a_color"); - const colors = new Array(6); - colors.fill(color); - a_position.data(gfx.ctx, points, gfx.ctx.STATIC_DRAW); - a_color.data(gfx.ctx, colors.flat(), gfx.ctx.STATIC_DRAW); - gfx.ctx.drawArrays(gfx.ctx.TRIANGLES, 0, 6); - } -} -export function drawCircle(gfx, position, radius, color) { - const points = new Array(); - const precision = 40; - const angle = 2.0 * Math.PI / precision; - let a = 0; - for (let i = 0; i < precision; ++i) { - var vec = Vec2.angle(a); - vec.multScalar(radius); - a += angle; - points.push(vec); - } - for (let i = 0; i < points.length; i++) { - const current = points[i]; - const next = points[(i + 1) % points.length]; - let center = position; - drawTriangle(gfx, [center, center.addNew(current), center.addNew(next)], color); - } -} -export function drawLine(gfx, A, B, color) { - const a_position = gfx.getAttribute("a_position"); - const a_color = gfx.getAttribute("a_color"); - let points = [ - A.x, A.y, - B.x, B.y, - ]; - const colors = [ - color, - color, - ]; - a_position.data(gfx.ctx, points, gfx.ctx.STATIC_DRAW); - a_color.data(gfx.ctx, colors.flat(), gfx.ctx.STATIC_DRAW); - gfx.ctx.drawArrays(gfx.ctx.LINES, 0, 2); -} diff --git a/src/js/draw.ts b/src/js/draw.ts index a27978a..dbf247b 100644 --- a/src/js/draw.ts +++ b/src/js/draw.ts @@ -1,6 +1,6 @@ -import { Vec3, Vec2, Vec4, Mat4 } from "./common.js" -import { Camera, Graphics, Texture } from "./graphics.js"; -import {Grid, TileEdge, TileFill} from "./world.js"; +import { Vec3, Vec2, Vec4 } from "./common.js" +import { Graphics, Texture, Drawable, DrawTag } from "./graphics.js"; +import { TileEdge, TileFill, Grid } from "./world.js"; import * as Assets from "./assets.js"; // TODO: Don't assume tile size is same in all directions @@ -20,93 +20,93 @@ function cull(point: Vec3, screen: Vec4, tileSize: number): boolean { return false; } -export function drawTriangle(gfx: Graphics, positions: [Vec2, Vec2, Vec2], color: Assets.Color) { - const a_position = gfx.getAttribute("a_position"); - const a_color = gfx.getAttribute("a_color"); +// Attrib format +// position color uv +// (3) (4) (2) => 3 + 4 + 2 = 9 <=> data.len % 9 == 0 - const points: Array = [ - positions[0].x, positions[0].y, - positions[1].x, positions[1].y, - positions[2].x, positions[2].y, - ] +export class Rectangle implements Drawable { + fill: Texture | Assets.Color; + attribs: string[] = ["a_position", "a_color", "a_tex_position"]; + tags: Array = []; - const colors: Array = [ - color, - color, - color, - ]; + data: number[] = []; + stride: number = 0; + vertexStride: number = 0; - a_position.data(gfx.ctx, points, gfx.ctx.STATIC_DRAW); - a_color.data(gfx.ctx, colors.flat(), gfx.ctx.STATIC_DRAW); - gfx.ctx.drawArrays(gfx.ctx.TRIANGLES, 0, 3); -} + constructor(fill: Texture | Assets.Color, tags?: Array, attribs?: string[]) { + this.fill = fill; -export function drawTriangleExts(gfx: Graphics, position: Vec3, exts: Vec2, color: Assets.Color) { - const a_position = gfx.getAttribute("a_position"); - const a_color = gfx.getAttribute("a_color"); + if (attribs !== undefined) { + this.attribs = attribs; + } - const points: Array = [ - position.x, position.y, position.z, - position.x + exts.x, position.y, position.z, - position.x, position.y + exts.y, position.z, - ] - - const colors: Array = [ - color, - color, - color, - ]; - - a_position.data(gfx.ctx, points, gfx.ctx.STATIC_DRAW); - a_color.data(gfx.ctx, colors.flat(), gfx.ctx.STATIC_DRAW); - gfx.ctx.drawArrays(gfx.ctx.TRIANGLES, 0, 3); -} - -export function drawRectangle(gfx: Graphics, corners: [Vec3, Vec3, Vec3, Vec3], color: Assets.Color | Texture) { - const a_position = gfx.getAttribute("a_position"); - - const points: Array = [ - corners[0].x, corners[0].y, corners[0].z, - corners[1].x, corners[1].y, corners[1].z, - corners[3].x, corners[3].y, corners[3].z, - - corners[2].x, corners[2].y, corners[2].z, - corners[1].x, corners[1].y, corners[1].z, - corners[3].x, corners[3].y, corners[3].z, - - ] - - if (color instanceof Texture) { - const a_tex_position = gfx.getAttribute("a_tex_position"); - - const uv: Array = [ - 0.0, 0.0, - -1.0, 0.0, - 0.0, -1.0, - -1.0, -1.0, - -1.0, 0.0, - 0.0, -1.0, - ]; - - a_position.data(gfx.ctx, points, gfx.ctx.STATIC_DRAW); - a_tex_position.data(gfx.ctx, uv, gfx.ctx.STATIC_DRAW); - color.bind(gfx); - gfx.ctx.drawArrays(gfx.ctx.TRIANGLES, 0, 6); - color.unbind(gfx); - } else { - const a_color = gfx.getAttribute("a_color"); - - const colors: Array = new Array(6); - colors.fill(color); - - a_position.data(gfx.ctx, points, gfx.ctx.STATIC_DRAW); - a_color.data(gfx.ctx, colors.flat(), gfx.ctx.STATIC_DRAW); - gfx.ctx.drawArrays(gfx.ctx.TRIANGLES, 0, 6); + if (tags !== undefined) { + this.tags = tags; + } } + overrideDraw(draw: (...args: any[]) => void) { + this.draw = draw; + } + + draw(gfx: Graphics, corners: [Vec3, Vec3, Vec3, Vec3]) { + if (this.fill instanceof Texture) { + this.data = [ + corners[0].x, corners[0].y, corners[0].z, 0, 0, 0, 0, 0, 0, + corners[1].x, corners[1].y, corners[1].z, 0, 0, 0, 0, -1, 0, + corners[3].x, corners[3].y, corners[3].z, 0, 0, 0, 0, 0, -1, + + corners[2].x, corners[2].y, corners[2].z, 0, 0, 0, 0, -1, -1, + corners[1].x, corners[1].y, corners[1].z, 0, 0, 0, 0, -1, 0, + corners[3].x, corners[3].y, corners[3].z, 0, 0, 0, 0, 0, -1, + ] + } else { + this.data = [ + corners[0].x, corners[0].y, corners[0].z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0, + corners[1].x, corners[1].y, corners[1].z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0, + corners[3].x, corners[3].y, corners[3].z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0, + + corners[2].x, corners[2].y, corners[2].z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0, + corners[1].x, corners[1].y, corners[1].z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0, + corners[3].x, corners[3].y, corners[3].z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0, + ] + } + + this.vertexStride = 9; + this.stride = this.vertexStride * 4 + gfx.toRender.push({...this}); + } + + drawExts(gfx: Graphics, position: Vec3, exts: Vec2) { + if (this.fill instanceof Texture) { + this.data = [ + position.x, position.y, position.z, 0, 0, 0, 0, 0, 0, + position.x + exts.x, position.y, position.z, 0, 0, 0, 0, -1, 0, + position.x, position.y + exts.y, position.z, 0, 0, 0, 0, 0, -1, + + position.x + exts.x, position.y + exts.y, position.z, 0, 0, 0, 0, -1, -1, + position.x + exts.x, position.y, position.z, 0, 0, 0, 0, -1, 0, + position.x, position.y + exts.y, position.z, 0, 0, 0, 0, 0, -1, + ] + } else { + this.data = [ + position.x, position.y, position.z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0, + position.x + exts.x, position.y, position.z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0, + position.x, position.y + exts.y, position.z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0, + + position.x + exts.x, position.y + exts.y, position.z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0, + position.x + exts.x, position.y, position.z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0, + position.x, position.y + exts.y, position.z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0, + ] + } + + this.vertexStride = 9; + this.stride = this.vertexStride * 4 + gfx.toRender.push(this); + } } -export function drawIsometricCube(gfx: Graphics, camera: Camera, position: Vec3, exts: Vec3, color: TileFill, edge: TileEdge) { +export function drawIsometricCube(gfx: Graphics, position: Vec3, exts: Vec3, color: TileFill, edge: TileEdge) { let points = [ // Left Top position, @@ -122,16 +122,12 @@ export function drawIsometricCube(gfx: Graphics, camera: Camera, position: Vec3, new Vec3(position.x + 0.5 * exts.x, position.y - 0.5 * exts.y, position.z + 0.25 * exts.z), ]; - let screen = new Vec4( - -camera.position.x, - -camera.position.y, - gfx.ctx.canvas.width, - gfx.ctx.canvas.height); - - gfx.ctx.uniform1i(gfx.getUniform("u_isIso"), 1); + let r = new Rectangle(color.top, [ + DrawTag.ISO, + ]); // Top - drawRectangle( + r.draw( gfx, [ points[0], @@ -139,39 +135,41 @@ export function drawIsometricCube(gfx: Graphics, camera: Camera, position: Vec3, points[3], points[2], ], - color.top); + ); // Right Edge if (edge == TileEdge.Right || edge == TileEdge.Both) { - drawRectangle( + r.fill = color.right; + + r.draw( gfx, [ points[3], points[2], points[4], points[5], - ], - color.right); + ] + ); } // Left Edge if (edge == TileEdge.Left || edge == TileEdge.Both) { - drawRectangle( + r.fill = color.left; + + r.draw( gfx, [ points[0], points[2], points[4], points[6], - ], - color.left); + ] + ); } - - gfx.ctx.uniform1i(gfx.getUniform("u_isIso"), 0); } -export function drawIsometricGrid(gfx: Graphics, camera: Camera, grid: Grid) { +export function drawIsometricGrid(gfx: Graphics, grid: Grid) { let position = {...grid.position} as Vec3; let exts = new Vec3(grid.tileSize, grid.tileSize, 0); let tileCoord = new Vec3(0, 0, 0); @@ -191,7 +189,7 @@ export function drawIsometricGrid(gfx: Graphics, camera: Camera, grid: Grid) { continue; } - drawIsometricCube(gfx, camera, new Vec3(position.x - grid.tileSize / 2 * k, position.y + grid.tileSize / 2 * k, position.z), exts, tile.fill, tile.edge); + drawIsometricCube(gfx, new Vec3(position.x - grid.tileSize / 2 * k, position.y + grid.tileSize / 2 * k, position.z), exts, tile.fill, tile.edge); position.x += grid.tileSize; } @@ -201,86 +199,3 @@ export function drawIsometricGrid(gfx: Graphics, camera: Camera, grid: Grid) { position.y = grid.position.y; } } - -export function drawRectangleExts(gfx: Graphics, position: Vec3, exts: Vec2, color: Assets.Color | Texture) { - const a_position = gfx.getAttribute("a_position"); - - const points: Array = [ - position.x, position.y, position.z, - position.x + exts.x, position.y, position.z, - position.x, position.y + exts.y, position.z, - - position.x + exts.x, position.y + exts.y, position.z, - position.x + exts.x, position.y, position.z, - position.x, position.y + exts.y, position.z, - ] - - if (color instanceof Texture) { - const a_tex_position = gfx.getAttribute("a_tex_position"); - - const uv: Array = [ - 0.0, 0.0, - -1.0, 0.0, - 0.0, -1.0, - -1.0, -1.0, - -1.0, 0.0, - 0.0, -1.0, - ]; - - a_position.data(gfx.ctx, points, gfx.ctx.STATIC_DRAW); - a_tex_position.data(gfx.ctx, uv, gfx.ctx.STATIC_DRAW); - color.bind(gfx); - gfx.ctx.drawArrays(gfx.ctx.TRIANGLES, 0, 6); - color.unbind(gfx); - } else { - const a_color = gfx.getAttribute("a_color"); - - const colors: Array = new Array(6); - colors.fill(color); - - a_position.data(gfx.ctx, points, gfx.ctx.STATIC_DRAW); - a_color.data(gfx.ctx, colors.flat(), gfx.ctx.STATIC_DRAW); - gfx.ctx.drawArrays(gfx.ctx.TRIANGLES, 0, 6); - } - -} - -export function drawCircle(gfx: Graphics, position: Vec2, radius: number, color: Assets.Color) { - const points: Array = new Array(); - - const precision = 40; - const angle = 2.0*Math.PI/precision; - let a = 0; - for (let i = 0; i < precision; ++i) { - var vec = Vec2.angle(a); - vec.multScalar(radius); - a += angle; - points.push(vec); - } - - for (let i = 0; i < points.length; i++) { - const current = points[i]; - const next = points[(i + 1) % points.length]; - let center = position; - drawTriangle(gfx, [center, center.addNew(current), center.addNew(next)], color); - } -} - -export function drawLine(gfx: Graphics, A: Vec2, B: Vec2, color: Assets.Color) { - const a_position = gfx.getAttribute("a_position"); - const a_color = gfx.getAttribute("a_color"); - - let points: Array = [ - A.x, A.y, - B.x, B.y, - ]; - - const colors: Array = [ - color, - color, - ]; - - a_position.data(gfx.ctx, points, gfx.ctx.STATIC_DRAW); - a_color.data(gfx.ctx, colors.flat(), gfx.ctx.STATIC_DRAW); - gfx.ctx.drawArrays(gfx.ctx.LINES, 0, 2); -} diff --git a/src/js/graphics.js b/src/js/graphics.js index 7ab1873..2086534 100644 --- a/src/js/graphics.js +++ b/src/js/graphics.js @@ -39,16 +39,24 @@ function createProgram(ctx, vertexShaderSource, fragmentShaderSource) { } return program; } +export var DrawTag; +(function (DrawTag) { + DrawTag[DrawTag["ISO"] = 0] = "ISO"; +})(DrawTag || (DrawTag = {})); export class Graphics { ctx; program; attribs = new Map(); uniforms = new Map(); vao; + vbo; + toRender = []; + texCount = 0; constructor(ctx, vs, fs) { this.ctx = ctx; this.program = createProgram(ctx, vs, fs); this.vao = ctx.createVertexArray(); + this.vbo = ctx.createBuffer(); ctx.bindVertexArray(this.vao); ctx.viewport(0, 0, ctx.canvas.width, ctx.canvas.height); ctx.useProgram(this.program); @@ -82,60 +90,97 @@ export class Graphics { throw new Error("Tried to get uninitialized uniform: " + name); return loc; } + draw() { + for (let o of this.toRender) { + this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.vbo); + this.ctx.bufferData(this.ctx.ARRAY_BUFFER, new Float32Array(o.data), this.ctx.STATIC_DRAW); + let aid = 0; + for (let a of o.attribs) { + let attr = this.getAttribute(a); + if (!attr.formatted) + throw new Error("Tried to use unformatted attribute!"); + this.ctx.enableVertexAttribArray(attr.loc); + this.ctx.vertexAttribPointer(attr.loc, attr.size, attr.type, attr.normalized, o.stride, aid * 4); + aid += attr.size; + } + // Generalize the tag uniforms aka. don't hard code them + for (let t of o.tags) { + switch (t) { + case DrawTag.ISO: { + this.ctx.uniform1ui(this.getUniform("u_isIso"), 1); + break; + } + } + } + if (o.fill instanceof Texture) { + this.ctx.uniform1i(this.getUniform("u_isTex"), 1); + o.fill.bind(this); + } + this.ctx.drawArrays(this.ctx.TRIANGLES, 0, o.data.length / o.vertexStride); + if (o.fill instanceof Texture) { + this.ctx.uniform1i(this.getUniform("u_isTex"), 0); + o.fill.unbind(this); + } + for (let t of o.tags) { + switch (t) { + case DrawTag.ISO: { + this.ctx.uniform1ui(this.getUniform("u_isIso"), 0); + break; + } + } + } + } + // TODO: Maybe add persistent rendering? + this.toRender = []; + } } export class Attribute { loc; - buffer; - // TODO: maybe use undefined as default value? + formatted = false; size = 0; type = 0; - normalized = false; - stride = 0; offset = 0; + normalized = false; constructor(ctx, program, name) { this.loc = ctx.getAttribLocation(program, name); - this.buffer = ctx.createBuffer(); } - format(size, type, normalized, stride, offset) { + format(size, type, normalized, offset) { this.size = size; this.type = type; this.normalized = normalized; - this.stride = stride; this.offset = offset; - } - data(ctx, data, usage) { - ctx.enableVertexAttribArray(this.loc); - ctx.bindBuffer(ctx.ARRAY_BUFFER, this.buffer); - ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array(data), usage); - ctx.vertexAttribPointer(this.loc, this.size, this.type, this.normalized, this.stride, this.offset); + this.formatted = true; } } export class Texture { tex; + texId; width = 0; height = 0; - constructor(tex, width, height) { + constructor(texId, tex, width, height) { this.height = height; this.width = width; this.tex = tex; + this.texId = texId; } - static async load(ctx, path) { + // TODO: Load sprite sheet only once + // TODO: Allow changing sprite size + static async load(gfx, path) { let image = await loadTexture(path); - let tex = ctx.createTexture(); - ctx.bindTexture(ctx.TEXTURE_2D, tex); - ctx.texImage2D(ctx.TEXTURE_2D, 0, ctx.RGBA, ctx.RGBA, ctx.UNSIGNED_BYTE, image); - ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MAG_FILTER, ctx.NEAREST); - ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MIN_FILTER, ctx.NEAREST); - ctx.bindTexture(ctx.TEXTURE_2D, null); - return new Texture(tex, image.width, image.height); + let tex = gfx.ctx.createTexture(); + gfx.ctx.bindTexture(gfx.ctx.TEXTURE_2D, tex); + gfx.ctx.texImage2D(gfx.ctx.TEXTURE_2D, 0, gfx.ctx.RGBA, gfx.ctx.RGBA, gfx.ctx.UNSIGNED_BYTE, image); + gfx.ctx.texParameteri(gfx.ctx.TEXTURE_2D, gfx.ctx.TEXTURE_MAG_FILTER, gfx.ctx.NEAREST); + gfx.ctx.texParameteri(gfx.ctx.TEXTURE_2D, gfx.ctx.TEXTURE_MIN_FILTER, gfx.ctx.NEAREST); + gfx.ctx.bindTexture(gfx.ctx.TEXTURE_2D, null); + gfx.texCount += 1; + return new Texture(gfx.texCount, tex, image.width, image.height); } bind(gfx) { - gfx.ctx.uniform1i(gfx.getUniform("u_isTex"), 1); gfx.ctx.bindTexture(gfx.ctx.TEXTURE_2D, this.tex); } unbind(gfx) { gfx.ctx.bindTexture(gfx.ctx.TEXTURE_2D, null); - gfx.ctx.uniform1i(gfx.getUniform("u_isTex"), 0); } } async function loadTexture(path) { diff --git a/src/js/graphics.ts b/src/js/graphics.ts index 3d2b43f..fe09d75 100644 --- a/src/js/graphics.ts +++ b/src/js/graphics.ts @@ -1,3 +1,4 @@ +import * as Assets from "./assets.js"; import {Vec2, Vec3, Vec4} from "./common.js"; export function fullscreenCanvas(gfx: Graphics, id: string) { @@ -51,16 +52,36 @@ function createProgram(ctx: WebGL2RenderingContext, vertexShaderSource: string | return program; } +export enum DrawTag { + ISO, +} + +export interface Drawable { + fill: Texture | Assets.Color; + attribs: string[]; + tags: Array; + + data: Array; + vertexStride: number; + stride: number; +} + export class Graphics { ctx: WebGL2RenderingContext; program: WebGLProgram; attribs: Map = new Map(); uniforms: Map = new Map(); vao: WebGLVertexArrayObject; + vbo: WebGLBuffer; + + toRender: Array = []; + texCount: number = 0; + constructor(ctx: WebGL2RenderingContext, vs: string, fs: string) { this.ctx = ctx; this.program = createProgram(ctx, vs, fs); this.vao = ctx.createVertexArray(); + this.vbo = ctx.createBuffer(); ctx.bindVertexArray(this.vao); ctx.viewport(0, 0, ctx.canvas.width, ctx.canvas.height); @@ -107,78 +128,126 @@ export class Graphics { return loc; } + + draw() { + for (let o of this.toRender) { + this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.vbo); + this.ctx.bufferData(this.ctx.ARRAY_BUFFER, new Float32Array(o.data), this.ctx.STATIC_DRAW); + + let aid = 0; + for (let a of o.attribs) { + let attr = this.getAttribute(a); + if (!attr.formatted) + throw new Error("Tried to use unformatted attribute!"); + + this.ctx.enableVertexAttribArray(attr.loc); + this.ctx.vertexAttribPointer(attr.loc, attr.size, attr.type, attr.normalized, o.stride, aid * 4); + aid += attr.size; + } + + // Generalize the tag uniforms aka. don't hard code them + for (let t of o.tags) { + switch (t) { + case DrawTag.ISO: { + this.ctx.uniform1ui(this.getUniform("u_isIso"), 1); + break; + } + } + } + + if (o.fill instanceof Texture) + { + this.ctx.uniform1i(this.getUniform("u_isTex"), 1); + o.fill.bind(this); + } + + this.ctx.drawArrays(this.ctx.TRIANGLES, 0, o.data.length / o.vertexStride); + + if (o.fill instanceof Texture) + { + this.ctx.uniform1i(this.getUniform("u_isTex"), 0); + o.fill.unbind(this); + } + + for (let t of o.tags) { + switch (t) { + case DrawTag.ISO: { + this.ctx.uniform1ui(this.getUniform("u_isIso"), 0); + break; + } + } + } + + } + + // TODO: Maybe add persistent rendering? + this.toRender = []; + } } export class Attribute { loc: GLint; - buffer: WebGLBuffer; + formatted: boolean = false; - // TODO: maybe use undefined as default value? size: GLint = 0; type: GLenum = 0; - normalized: GLboolean = false; - stride: GLsizei = 0; offset: GLintptr = 0; + normalized: GLboolean = false; constructor(ctx: WebGL2RenderingContext, program: WebGLProgram, name: string) { this.loc = ctx.getAttribLocation(program, name); - this.buffer = ctx.createBuffer(); } format( size: GLint, type: GLenum, normalized: GLboolean, - stride: GLsizei, offset: GLintptr) { this.size = size; this.type = type; this.normalized = normalized; - this.stride = stride; this.offset = offset; - } - data(ctx: WebGL2RenderingContext, data: Array, usage: GLenum) { - ctx.enableVertexAttribArray(this.loc); - ctx.bindBuffer(ctx.ARRAY_BUFFER, this.buffer); - ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array(data), usage); - ctx.vertexAttribPointer(this.loc, this.size, this.type, this.normalized, this.stride, this.offset); + this.formatted = true; } } export class Texture { tex: WebGLTexture | null; + texId: number; width: number = 0; height: number = 0; - constructor(tex: WebGLTexture, width: number, height: number) { + constructor(texId: number, tex: WebGLTexture, width: number, height: number) { this.height = height; this.width = width; this.tex = tex; + this.texId = texId; } - static async load(ctx: WebGL2RenderingContext, path: string): Promise { + // TODO: Load sprite sheet only once + // TODO: Allow changing sprite size + static async load(gfx: Graphics, path: string): Promise { let image = await loadTexture(path); - let tex = ctx.createTexture(); - ctx.bindTexture(ctx.TEXTURE_2D, tex); - ctx.texImage2D(ctx.TEXTURE_2D, 0, ctx.RGBA, ctx.RGBA, ctx.UNSIGNED_BYTE, image); - ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MAG_FILTER, ctx.NEAREST); - ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MIN_FILTER, ctx.NEAREST); - ctx.bindTexture(ctx.TEXTURE_2D, null); + let tex = gfx.ctx.createTexture(); + gfx.ctx.bindTexture(gfx.ctx.TEXTURE_2D, tex); + gfx.ctx.texImage2D(gfx.ctx.TEXTURE_2D, 0, gfx.ctx.RGBA, gfx.ctx.RGBA, gfx.ctx.UNSIGNED_BYTE, image); + gfx.ctx.texParameteri(gfx.ctx.TEXTURE_2D, gfx.ctx.TEXTURE_MAG_FILTER, gfx.ctx.NEAREST); + gfx.ctx.texParameteri(gfx.ctx.TEXTURE_2D, gfx.ctx.TEXTURE_MIN_FILTER, gfx.ctx.NEAREST); + gfx.ctx.bindTexture(gfx.ctx.TEXTURE_2D, null); - return new Texture(tex, image.width, image.height); + gfx.texCount += 1; + return new Texture(gfx.texCount, tex, image.width, image.height); } bind(gfx: Graphics) { - gfx.ctx.uniform1i(gfx.getUniform("u_isTex"), 1); gfx.ctx.bindTexture(gfx.ctx.TEXTURE_2D, this.tex); } unbind(gfx: Graphics) { gfx.ctx.bindTexture(gfx.ctx.TEXTURE_2D, null); - gfx.ctx.uniform1i(gfx.getUniform("u_isTex"), 0); } } diff --git a/src/js/script.js b/src/js/script.js index f53fdad..6d79b8c 100644 --- a/src/js/script.js +++ b/src/js/script.js @@ -7,14 +7,16 @@ import { bush, Grid, Tile, tree } from "./world.js"; import * as Assets from "./assets.js"; const vertexShader = `#version 300 es - in vec3 a_position; - in vec2 a_tex_position; - out vec2 v_tex_position; - in vec4 a_color; + layout(location = 0) in vec3 a_position; + layout(location = 1) in vec2 a_tex_position; + layout(location = 2) in vec4 a_color; + out vec4 v_color; + out vec2 v_tex_position; + uniform mat4 u_matrix; - uniform bool u_isTex; uniform bool u_isIso; + uniform bool u_isTex; mat4 Iso = mat4( 1, -1, 0, 0, @@ -22,7 +24,7 @@ const vertexShader = `#version 300 es 0, 0, 1, 0, 0, 0, 0, 1 ); - + void main() { vec4 orthographic; @@ -47,12 +49,12 @@ const fragmentShader = `#version 300 es precision highp float; - in vec2 v_tex_position; in vec4 v_color; + in vec2 v_tex_position; out vec4 outColor; - uniform bool u_isTex; uniform sampler2D u_texture; + uniform bool u_isTex; void main() { if (u_isTex) { @@ -76,7 +78,20 @@ function draw(gfx, camera, dt, grid) { let mt = Mat4.translate(camera.position); let m = mt.multNew(mo); gfx.ctx.uniformMatrix4fv(gfx.getUniform("u_matrix"), false, m.splat()); - drawing.drawIsometricGrid(gfx, camera, grid); + drawing.drawIsometricGrid(gfx, grid); + //let r = new drawing.Rectangle(Assets.assets.get("grass"), [ + // DrawTag.ISO, + // DrawTag.TEXTURED, + //]); + //r.draw( + // gfx, + // [ + // new Vec3(0, 0, 0), + // new Vec3(100, 0, 0), + // new Vec3(100, 100, 0), + // new Vec3(0, 100, 0), + // ]); + gfx.draw(); } function addDefaultKeybinds(input, camera) { input.addKeyAction("KeyA", [], camera, (c) => { @@ -107,19 +122,19 @@ function addDefaultKeybinds(input, camera) { return; const gfx = new Graphics(ctx, vertexShader, fragmentShader); fullscreenCanvas(gfx, canvasId); - const a_position = gfx.createAttribute("a_position"); - a_position.format(3, gfx.ctx.FLOAT, false, 0, 0); - const a_color = gfx.createAttribute("a_color"); - a_color.format(4, gfx.ctx.FLOAT, false, 0, 0); - const a_tex_position = gfx.createAttribute("a_tex_position"); - a_tex_position.format(2, gfx.ctx.FLOAT, false, 0, 0); + let a = gfx.createAttribute("a_position"); + a.format(3, ctx.FLOAT, false, 0); + a = gfx.createAttribute("a_color"); + a.format(4, ctx.FLOAT, false, 0); + a = gfx.createAttribute("a_tex_position"); + a.format(2, ctx.FLOAT, false, 0); gfx.createUniform("u_matrix"); - gfx.createUniform("u_isTex"); gfx.createUniform("u_isIso"); + gfx.createUniform("u_isTex"); let camera = new Camera(new Vec3(0, 0, -1)); - await Assets.assets.load(ctx); + await Assets.assets.load(gfx); let m = Mat4.isometric(); - let size = 20; + let size = 100; let grid = new Grid(m.transformNew(new Vec4(ctx.canvas.width / 4, ctx.canvas.height / 2, 0, 1)).reduce(), 24, size, size, 10); grid.fillLayer(new Tile({ top: Assets.assets.get("grass"), diff --git a/src/js/script.ts b/src/js/script.ts index 735030d..a012e7d 100644 --- a/src/js/script.ts +++ b/src/js/script.ts @@ -1,22 +1,24 @@ import { initializeContext, Vec3, Mat4, Vec4, Vec2 } from "./common.js"; -import { Graphics, fullscreenCanvas, Camera } from "./graphics.js"; +import { Graphics, fullscreenCanvas, Camera, DrawTag } from "./graphics.js"; import * as drawing from "./draw.js"; import * as wasm from "./wasm.js"; import { Input } from "./input.js"; -import {bush, Grid, Tile, tree} from "./world.js"; +import {bush, Grid, Tile, TileEdge, tree} from "./world.js"; import * as Assets from "./assets.js"; const vertexShader = `#version 300 es - in vec3 a_position; - in vec2 a_tex_position; - out vec2 v_tex_position; - in vec4 a_color; + layout(location = 0) in vec3 a_position; + layout(location = 1) in vec2 a_tex_position; + layout(location = 2) in vec4 a_color; + out vec4 v_color; + out vec2 v_tex_position; + uniform mat4 u_matrix; - uniform bool u_isTex; uniform bool u_isIso; + uniform bool u_isTex; mat4 Iso = mat4( 1, -1, 0, 0, @@ -24,7 +26,7 @@ const vertexShader = 0, 0, 1, 0, 0, 0, 0, 1 ); - + void main() { vec4 orthographic; @@ -51,12 +53,12 @@ const fragmentShader = precision highp float; - in vec2 v_tex_position; in vec4 v_color; + in vec2 v_tex_position; out vec4 outColor; - uniform bool u_isTex; uniform sampler2D u_texture; + uniform bool u_isTex; void main() { if (u_isTex) { @@ -89,7 +91,23 @@ function draw(gfx: Graphics, camera: Camera, dt: number, grid: Grid) { m.splat() ); - drawing.drawIsometricGrid(gfx, camera, grid); + drawing.drawIsometricGrid(gfx, grid); + + //let r = new drawing.Rectangle(Assets.assets.get("grass"), [ + // DrawTag.ISO, + // DrawTag.TEXTURED, + //]); + + //r.draw( + // gfx, + // [ + // new Vec3(0, 0, 0), + // new Vec3(100, 0, 0), + // new Vec3(100, 100, 0), + // new Vec3(0, 100, 0), + // ]); + + gfx.draw(); } function addDefaultKeybinds(input: Input, camera: Camera) { @@ -134,26 +152,24 @@ function addDefaultKeybinds(input: Input, camera: Camera) { const gfx = new Graphics(ctx, vertexShader, fragmentShader); fullscreenCanvas(gfx, canvasId); - const a_position = gfx.createAttribute("a_position"); - a_position.format(3, gfx.ctx.FLOAT, false, 0, 0); - - const a_color = gfx.createAttribute("a_color"); - a_color.format(4, gfx.ctx.FLOAT, false, 0, 0); - - const a_tex_position = gfx.createAttribute("a_tex_position"); - a_tex_position.format(2, gfx.ctx.FLOAT, false, 0, 0); + let a = gfx.createAttribute("a_position"); + a.format(3, ctx.FLOAT, false, 0) + a = gfx.createAttribute("a_color"); + a.format(4, ctx.FLOAT, false, 0) + a = gfx.createAttribute("a_tex_position"); + a.format(2, ctx.FLOAT, false, 0) gfx.createUniform("u_matrix"); - gfx.createUniform("u_isTex"); gfx.createUniform("u_isIso"); + gfx.createUniform("u_isTex"); let camera = new Camera(new Vec3(0, 0, -1)); - await Assets.assets.load(ctx); + await Assets.assets.load(gfx); let m = Mat4.isometric(); - let size = 20; + let size = 100; let grid = new Grid(m.transformNew(new Vec4(ctx.canvas.width / 4, ctx.canvas.height / 2, 0, 1)).reduce(), 24, size, size, 10); grid.fillLayer(new Tile({ top: Assets.assets.get("grass"),