Compare commits

..

No commits in common. "dev" and "master" have entirely different histories.
dev ... master

58 changed files with 812 additions and 3187 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -1,3 +0,0 @@
For a description of the contents of this pack, usage rights, and other useful details, go here:
http://blog.spiralgraphics.biz/2011/02/nine-cartoon-backdrops.html

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 368 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 511 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 411 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 337 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

74
common.js Normal file
View File

@ -0,0 +1,74 @@
function initializeContext(canvasId) {
const canvas = document.getElementById(canvasId);
const ctx = canvas.getContext("webgl2", { antialias: false });
return ctx;
}
class Vec2 {
x;
y;
constructor(x, y) {
this.x = x;
this.y = y;
}
add(other) {
this.x += other.x;
this.y += other.y;
}
addScalar(scalar) {
this.x += scalar;
this.y += scalar;
}
addNew(other) {
return new Vec2(this.x + other.x, this.y + other.y);
}
addScalarNew(scalar) {
return new Vec2(this.x + scalar, this.y + scalar);
}
sub(other) {
this.x -= other.x;
this.y -= other.y;
}
mult(scalar) {
this.x *= scalar;
this.y *= scalar;
}
multNew(scalar) {
return new Vec2(this.x * scalar, this.y * scalar);
}
splatToArray() {
return [this.x, this.y];
}
static angle(angle) {
const eps = 1e-6;
let x = Math.cos(angle);
let y = Math.sin(angle);
if ((x > 0 && x < eps)
|| (x < 0 && x > -eps))
x = 0;
if ((y > 0 && y < eps)
|| (y < 0 && y > -eps))
y = 0;
return new Vec2(x, y);
}
}
class Vec3 {
x;
y;
z;
constructor(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
add(other) {
this.x += other.x;
this.y += other.y;
this.z += other.z;
}
sub(other) {
this.x -= other.x;
this.y -= other.y;
this.z -= other.z;
}
}
export { initializeContext, Vec2, Vec3 };

96
common.ts Normal file
View File

@ -0,0 +1,96 @@
function initializeContext(canvasId: string): WebGL2RenderingContext | null {
const canvas = document.getElementById(canvasId) as HTMLCanvasElement;
const ctx = canvas.getContext("webgl2", {antialias: false});
return ctx;
}
type Color = [number, number, number, number]
class Vec2 {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
add(other: Vec2) {
this.x += other.x;
this.y += other.y;
}
addScalar(scalar: number) {
this.x += scalar;
this.y += scalar;
}
addNew(other: Vec2): Vec2 {
return new Vec2(this.x + other.x, this.y + other.y);
}
addScalarNew(scalar: number): Vec2 {
return new Vec2(this.x + scalar, this.y + scalar);
}
sub(other: Vec2) {
this.x -= other.x;
this.y -= other.y;
}
mult(scalar: number) {
this.x *= scalar;
this.y *= scalar;
}
multNew(scalar: number): Vec2 {
return new Vec2(this.x * scalar, this.y * scalar);
}
splatToArray(): Array<number> {
return [this.x, this.y];
}
static angle(angle: number): Vec2 {
const eps = 1e-6;
let x = Math.cos(angle);
let y = Math.sin(angle);
if ((x > 0 && x < eps)
|| (x < 0 && x > -eps))
x = 0;
if ((y > 0 && y < eps)
|| (y < 0 && y > -eps))
y = 0;
return new Vec2(x, y);
}
}
class Vec3 {
x: number;
y: number;
z: number;
constructor(x: number, y: number, z: number) {
this.x = x;
this.y = y;
this.z = z;
}
add(other: Vec3) {
this.x += other.x;
this.y += other.y;
this.z += other.z;
}
sub(other: Vec3) {
this.x -= other.x;
this.y -= other.y;
this.z -= other.z;
}
}
export { initializeContext, Vec2, Vec3, Color };

92
draw.js Normal file
View File

@ -0,0 +1,92 @@
import { Vec2 } from "./common.js";
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);
}
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.x + exts.x, position.y,
position.x, position.y + exts.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);
}
function drawRectangle(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.x + exts.x, position.y,
position.x, position.y + exts.y,
position.x + exts.x, position.y + exts.y,
position.x + exts.x, position.y,
position.x, position.y + exts.y,
];
const colors = [
color,
color,
color,
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, 6);
}
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.mult(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);
}
}
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);
}
export { drawTriangle, drawTriangleExts, drawRectangle, drawCircle, drawLine };

116
draw.ts Normal file
View File

@ -0,0 +1,116 @@
import { Vec2 } from "./common.js"
import { Graphics } from "./graphics.js";
import { Color } from "./common.js";
function drawTriangle(gfx: Graphics, positions: [Vec2, Vec2, Vec2], color: Color) {
const a_position = gfx.getAttribute("a_position");
const a_color = gfx.getAttribute("a_color");
const points: Array<number> = [
positions[0].x, positions[0].y,
positions[1].x, positions[1].y,
positions[2].x, positions[2].y,
]
const colors: Array<number[]> = [
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);
}
function drawTriangleExts(gfx: Graphics, position: Vec2, exts: Vec2, color: Color) {
const a_position = gfx.getAttribute("a_position");
const a_color = gfx.getAttribute("a_color");
const points: Array<number> = [
position.x, position.y,
position.x + exts.x, position.y,
position.x, position.y + exts.y,
]
const colors: Array<number[]> = [
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);
}
function drawRectangle(gfx: Graphics, position: Vec2, exts: Vec2, color: Color) {
const a_position = gfx.getAttribute("a_position");
const a_color = gfx.getAttribute("a_color");
const points: Array<number> = [
position.x, position.y,
position.x + exts.x, position.y,
position.x, position.y + exts.y,
position.x + exts.x, position.y + exts.y,
position.x + exts.x, position.y,
position.x, position.y + exts.y,
]
const colors: Array<number[]> = [
color,
color,
color,
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, 6);
}
function drawCircle(gfx: Graphics, position: Vec2, radius: number, color: Color) {
const points: Array<Vec2> = new Array<Vec2>();
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.mult(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);
}
}
function drawLine(gfx: Graphics, A: Vec2, B: Vec2, color: Color) {
const a_position = gfx.getAttribute("a_position");
const a_color = gfx.getAttribute("a_color");
let points: Array<number> = [
A.x, A.y,
B.x, B.y,
];
const colors: Array<number[]> = [
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);
}
export { drawTriangle, drawTriangleExts, drawRectangle, drawCircle, drawLine }

110
graphics.js Normal file
View File

@ -0,0 +1,110 @@
function fullscreenCanvas(gfx, id) {
const canvas = document.getElementById(id);
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
gfx.ctx.viewport(0, 0, canvas.width, canvas.height);
}
function createShader(ctx, type, source) {
var shader = ctx.createShader(type);
if (!shader) {
throw new Error("Couldn't create shader: " + type);
}
ctx.shaderSource(shader, source);
ctx.compileShader(shader);
var success = ctx.getShaderParameter(shader, ctx.COMPILE_STATUS);
if (!success) {
console.log(ctx.getShaderInfoLog(shader));
ctx.deleteShader(shader);
throw new Error("Couldn't compile shader: " + type);
}
return shader;
}
function createProgram(ctx, vertexShaderSource, fragmentShaderSource) {
var program = ctx.createProgram();
if (vertexShaderSource !== null) {
const vs = createShader(ctx, ctx.VERTEX_SHADER, vertexShaderSource);
ctx.attachShader(program, vs);
}
if (fragmentShaderSource !== null) {
const fs = createShader(ctx, ctx.FRAGMENT_SHADER, fragmentShaderSource);
ctx.attachShader(program, fs);
}
ctx.linkProgram(program);
var success = ctx.getProgramParameter(program, ctx.LINK_STATUS);
if (!success) {
console.log(ctx.getProgramInfoLog(program));
ctx.deleteProgram(program);
throw new Error("Failed to create program!");
}
return program;
}
class Graphics {
ctx;
program;
attribs = new Map();
uniforms = new Map();
vao;
constructor(ctx, vs, fs) {
this.ctx = ctx;
this.program = createProgram(ctx, vs, fs);
this.vao = ctx.createVertexArray();
ctx.bindVertexArray(this.vao);
ctx.viewport(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.useProgram(this.program);
}
clear(r, g, b, a) {
this.ctx.clearColor(r, g, b, a);
this.ctx.clear(this.ctx.COLOR_BUFFER_BIT);
}
createAttribute(name) {
const attrib = new Attribute(this.ctx, this.program, name);
this.attribs.set(name, attrib);
return attrib;
}
getAttribute(name) {
const attrib = this.attribs.get(name);
if (attrib === undefined)
throw new Error("Tried to get uninitialized attribute: " + name);
return attrib;
}
createUniform(name) {
const loc = this.ctx.getUniformLocation(this.program, name);
if (loc === null)
throw new Error("Couldn't get location for uniform: " + name);
this.uniforms.set(name, loc);
}
getUniform(name) {
const loc = this.uniforms.get(name);
if (loc === undefined)
throw new Error("Tried to get uninitialized uniform: " + name);
return loc;
}
}
class Attribute {
loc;
buffer;
// TODO: maybe use undefined as default value?
size = 0;
type = 0;
normalized = false;
stride = 0;
offset = 0;
constructor(ctx, program, name) {
this.loc = ctx.getAttribLocation(program, name);
this.buffer = ctx.createBuffer();
ctx.enableVertexAttribArray(this.loc);
}
format(size, type, normalized, stride, offset) {
this.size = size;
this.type = type;
this.normalized = normalized;
this.stride = stride;
this.offset = offset;
}
data(ctx, data, usage) {
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);
}
}
export { fullscreenCanvas, Graphics, Attribute };

148
graphics.ts Normal file
View File

@ -0,0 +1,148 @@
import { Vec2 } from "./common.js"
function fullscreenCanvas(gfx: Graphics, id: string) {
const canvas = document.getElementById(id) as HTMLCanvasElement;
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
gfx.ctx.viewport(0, 0, canvas.width, canvas.height);
}
function createShader(ctx: WebGL2RenderingContext, type: GLenum, source: string): WebGLShader {
var shader = ctx.createShader(type);
if (!shader) {
throw new Error("Couldn't create shader: " + type);
}
ctx.shaderSource(shader, source);
ctx.compileShader(shader);
var success = ctx.getShaderParameter(shader, ctx.COMPILE_STATUS);
if (!success) {
console.log(ctx.getShaderInfoLog(shader));
ctx.deleteShader(shader);
throw new Error("Couldn't compile shader: " + type);
}
return shader;
}
function createProgram(ctx: WebGL2RenderingContext, vertexShaderSource: string | null, fragmentShaderSource: string | null): WebGLProgram {
var program = ctx.createProgram();
if (vertexShaderSource !== null) {
const vs = createShader(ctx, ctx.VERTEX_SHADER, vertexShaderSource);
ctx.attachShader(program, vs);
}
if (fragmentShaderSource !== null) {
const fs = createShader(ctx, ctx.FRAGMENT_SHADER, fragmentShaderSource);
ctx.attachShader(program, fs);
}
ctx.linkProgram(program);
var success = ctx.getProgramParameter(program, ctx.LINK_STATUS);
if (!success) {
console.log(ctx.getProgramInfoLog(program));
ctx.deleteProgram(program);
throw new Error("Failed to create program!");
}
return program;
}
class Graphics {
ctx: WebGL2RenderingContext;
program: WebGLProgram;
attribs: Map<string, Attribute> = new Map();
uniforms: Map<string, WebGLUniformLocation> = new Map();
vao: WebGLVertexArrayObject;
constructor(ctx: WebGL2RenderingContext, vs: string, fs: string) {
this.ctx = ctx;
this.program = createProgram(ctx, vs, fs);
this.vao = ctx.createVertexArray();
ctx.bindVertexArray(this.vao);
ctx.viewport(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.useProgram(this.program);
}
clear(r: number, g: number, b: number, a: number) {
this.ctx.clearColor(r, g, b, a);
this.ctx.clear(this.ctx.COLOR_BUFFER_BIT);
}
createAttribute(name: string): Attribute {
const attrib = new Attribute(this.ctx, this.program, name);
this.attribs.set(name, attrib);
return attrib;
}
getAttribute(name: string): Attribute {
const attrib = this.attribs.get(name);
if (attrib === undefined)
throw new Error("Tried to get uninitialized attribute: " + name);
return attrib;
}
createUniform(name: string) {
const loc = this.ctx.getUniformLocation(this.program, name);
if (loc === null)
throw new Error("Couldn't get location for uniform: " + name);
this.uniforms.set(name, loc);
}
getUniform(name: string): WebGLUniformLocation {
const loc = this.uniforms.get(name);
if (loc === undefined)
throw new Error("Tried to get uninitialized uniform: " + name);
return loc;
}
}
class Attribute {
loc: GLint;
buffer: WebGLBuffer;
// TODO: maybe use undefined as default value?
size: GLint = 0;
type: GLenum = 0;
normalized: GLboolean = false;
stride: GLsizei = 0;
offset: GLintptr = 0;
constructor(ctx: WebGL2RenderingContext, program: WebGLProgram, name: string) {
this.loc = ctx.getAttribLocation(program, name);
this.buffer = ctx.createBuffer();
ctx.enableVertexAttribArray(this.loc);
}
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<number>, usage: GLenum) {
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);
}
}
export { fullscreenCanvas, Graphics, Attribute }

View File

@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width,initial-scale=1" /> <meta name="viewport" content="width=device-width,initial-scale=1" />
<meta name="description" content="" /> <meta name="description" content="" />
<link rel="stylesheet" href="style.css"></link> <link rel="stylesheet" href="style.css"></link>
<script type="module" src="src/js/script.js" defer> var exports = {}; </script> <script type="module" src="script.js" defer> var exports = {}; </script>
</head> </head>
<body> <body>
<canvas id="game"></canvas> <canvas id="game"></canvas>

77
script.js Normal file
View File

@ -0,0 +1,77 @@
import { initializeContext, Vec2 } from "./common.js";
import { Graphics, fullscreenCanvas } from "./graphics.js";
import * as drawing from "./draw.js";
const vertexShader = `#version 300 es
in vec2 a_position;
in vec4 a_color;
out vec4 color;
uniform vec2 u_resolution;
void main() {
vec2 clipSpace = (a_position / u_resolution) * 2.0 - 1.0;
color = a_color;
gl_Position = vec4(clipSpace.xy, 0.0, 1.0);
}
`;
const fragmentShader = `#version 300 es
precision highp float;
in vec4 color;
out vec4 outColor;
void main() {
outColor = color;
}
`;
function draw(gfx, dt, pos, velocity) {
gfx.clear(0, 0, 0, 0);
gfx.ctx.uniform2f(gfx.getUniform("u_resolution"), gfx.ctx.canvas.width, gfx.ctx.canvas.height);
drawing.drawCircle(gfx, pos, 200, [1, 0, 0, 1]);
if (pos.x + 200 >= gfx.ctx.canvas.width) {
pos.x = gfx.ctx.canvas.width - 200;
velocity.x *= -1;
}
else if (pos.x - 200 <= 0) {
pos.x = 200;
velocity.x *= -1;
}
if (pos.y + 200 >= gfx.ctx.canvas.height) {
pos.y = gfx.ctx.canvas.height - 200;
velocity.y *= -1;
}
else if (pos.y - 200 <= 0) {
pos.y = 200;
velocity.y *= -1;
}
pos.add(velocity.multNew(dt));
}
(() => {
const canvasId = "game";
const ctx = initializeContext(canvasId);
if (ctx === null)
return;
const gfx = new Graphics(ctx, vertexShader, fragmentShader);
fullscreenCanvas(gfx, canvasId);
const a_position = gfx.createAttribute("a_position");
a_position.format(2, gfx.ctx.FLOAT, false, 0, 0);
const a_color = gfx.createAttribute("a_color");
a_color.format(4, gfx.ctx.FLOAT, false, 0, 0);
gfx.createUniform("u_resolution");
let pos = new Vec2(300, 300);
let velocity = new Vec2(200, 200);
let prevTimestamp = 0;
const frame = (timestamp) => {
const deltaTime = (timestamp - prevTimestamp) / 1000;
prevTimestamp = timestamp;
fullscreenCanvas(gfx, "game");
draw(gfx, deltaTime, pos, velocity);
window.requestAnimationFrame(frame);
};
window.requestAnimationFrame((timestamp) => {
prevTimestamp = timestamp;
window.requestAnimationFrame(frame);
});
})();

98
script.ts Normal file
View File

@ -0,0 +1,98 @@
import { initializeContext, Vec2 } from "./common.js";
import { Graphics, fullscreenCanvas } from "./graphics.js";
import * as drawing from "./draw.js";
const vertexShader =
`#version 300 es
in vec2 a_position;
in vec4 a_color;
out vec4 color;
uniform vec2 u_resolution;
void main() {
vec2 clipSpace = (a_position / u_resolution) * 2.0 - 1.0;
color = a_color;
gl_Position = vec4(clipSpace.xy, 0.0, 1.0);
}
`;
const fragmentShader =
`#version 300 es
precision highp float;
in vec4 color;
out vec4 outColor;
void main() {
outColor = color;
}
`;
function draw(gfx: Graphics, dt: number, pos: Vec2, velocity: Vec2) {
gfx.clear(0, 0, 0, 0);
gfx.ctx.uniform2f(gfx.getUniform("u_resolution"), gfx.ctx.canvas.width, gfx.ctx.canvas.height);
drawing.drawCircle(gfx, pos, 200, [1, 0, 0, 1]);
if (pos.x + 200 >= gfx.ctx.canvas.width)
{
pos.x = gfx.ctx.canvas.width - 200;
velocity.x *= -1;
} else if (pos.x - 200 <= 0)
{
pos.x = 200;
velocity.x *= -1;
}
if (pos.y + 200 >= gfx.ctx.canvas.height)
{
pos.y = gfx.ctx.canvas.height - 200;
velocity.y *= -1;
} else if (pos.y - 200 <= 0)
{
pos.y = 200;
velocity.y *= -1;
}
pos.add(velocity.multNew(dt));
}
(() => {
const canvasId = "game";
const ctx = initializeContext(canvasId);
if (ctx === null) return;
const gfx = new Graphics(ctx, vertexShader, fragmentShader);
fullscreenCanvas(gfx, canvasId);
const a_position = gfx.createAttribute("a_position");
a_position.format(2, gfx.ctx.FLOAT, false, 0, 0);
const a_color = gfx.createAttribute("a_color");
a_color.format(4, gfx.ctx.FLOAT, false, 0, 0);
gfx.createUniform("u_resolution");
let pos = new Vec2(300, 300);
let velocity = new Vec2(200, 200);
let prevTimestamp = 0;
const frame = (timestamp: number) => {
const deltaTime = (timestamp - prevTimestamp)/1000;
prevTimestamp = timestamp;
fullscreenCanvas(gfx, "game");
draw(gfx, deltaTime, pos, velocity);
window.requestAnimationFrame(frame);
}
window.requestAnimationFrame((timestamp) => {
prevTimestamp = timestamp;
window.requestAnimationFrame(frame);
});
})();

View File

@ -1,27 +0,0 @@
import { Vec2 } from "./common.js";
import { Texture, Spritesheet } from "./graphics.js";
export const Colors = {
Red: [1, 0, 0, 1],
Green: [0, 1, 0, 1],
Blue: [0, 0, 1, 1],
Brown: [0.341, 0.337, 0.204, 1],
Gray: [0.66, 0.66, 0.66, 1],
};
export class Assets {
assets = new Map();
push(name, asset) {
if (this.assets.get(name) !== undefined)
throw new Error("Asset name occupied!");
this.assets.set(name, asset);
}
get(name) {
let a = this.assets.get(name);
if (a === undefined)
throw new Error("Couldn't find asset: " + name);
return a;
}
async load(gfx) {
assets.push("sprites", new Spritesheet(await Texture.load(gfx, "../../assets/sprites.png"), new Vec2(16, 16)));
}
}
export const assets = new Assets();

View File

@ -1,39 +0,0 @@
import {Vec2} from "./common.js";
import {Graphics, Texture, Spritesheet} from "./graphics.js";
export type Color = [number, number, number, number]
export const Colors = {
Red : [1, 0, 0, 1] as Color,
Green : [0, 1, 0, 1] as Color,
Blue : [0, 0, 1, 1] as Color,
Brown : [0.341, 0.337, 0.204, 1] as Color,
Gray : [0.66, 0.66, 0.66, 1] as Color,
}
export type Asset = Texture | Spritesheet;
export class Assets {
assets: Map<string, Asset> = new Map();
push(name: string, asset: Asset) {
if (this.assets.get(name) !== undefined)
throw new Error("Asset name occupied!");
this.assets.set(name, asset);
}
get(name: string): Asset {
let a = this.assets.get(name);
if (a === undefined)
throw new Error("Couldn't find asset: " + name);
return a;
}
async load(gfx: Graphics) {
assets.push("sprites", new Spritesheet(await Texture.load(gfx, "../../assets/sprites.png"), new Vec2(16, 16)));
}
}
export const assets = new Assets();

View File

@ -1,464 +0,0 @@
export function initializeContext(canvasId) {
const canvas = document.getElementById(canvasId);
const ctx = canvas.getContext("webgl2", { antialias: false });
return ctx;
}
export class Vec2 {
x;
y;
constructor(x, y) {
this.x = x;
this.y = y;
}
from(scalar) {
return new Vec2(scalar, scalar);
}
static ZERO() {
return new Vec2(0, 0);
}
static ID() {
return new Vec2(1, 1);
}
copy() {
return new Vec2(this.x, this.y);
}
add(other) {
this.x += other.x;
this.y += other.y;
}
addScalar(scalar) {
this.x += scalar;
this.y += scalar;
}
addNew(other) {
return new Vec2(this.x + other.x, this.y + other.y);
}
addScalarNew(scalar) {
return new Vec2(this.x + scalar, this.y + scalar);
}
sub(other) {
this.x -= other.x;
this.y -= other.y;
}
subNew(other) {
let vec = this.copy();
vec.x -= other.x;
vec.y -= other.y;
return vec;
}
multScalar(scalar) {
this.x *= scalar;
this.y *= scalar;
}
multScalarNew(scalar) {
return new Vec2(this.x * scalar, this.y * scalar);
}
div(other) {
if (other.x == 0 ||
other.y == 0) {
throw new Error("Division by zero in Vec4");
}
this.x /= other.x;
this.y /= other.y;
}
divNew(other) {
if (other.x == 0 ||
other.y == 0) {
throw new Error("Division by zero in Vec4");
}
let vec = this.copy();
vec.x /= other.x;
vec.y /= other.y;
return vec;
}
reduce() {
throw new Error("Can't reduce Vec2!");
}
extend(value) {
return new Vec3(this.x, this.y, value);
}
splatToArray() {
return [this.x, this.y];
}
static angle(angle) {
const eps = 1e-6;
let x = Math.cos(angle);
let y = Math.sin(angle);
if ((x > 0 && x < eps)
|| (x < 0 && x > -eps))
x = 0;
if ((y > 0 && y < eps)
|| (y < 0 && y > -eps))
y = 0;
return new Vec2(x, y);
}
}
export class Vec3 {
x;
y;
z;
constructor(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
from(scalar) {
return new Vec3(scalar, scalar, scalar);
}
addScalar(scalar) {
this.x += scalar;
this.y += scalar;
this.z += scalar;
}
addScalarNew(scalar) {
let vec = this.copy();
vec.x += scalar;
vec.y += scalar;
vec.z += scalar;
return vec;
}
static ZERO() {
return new Vec3(0, 0, 0);
}
static ID() {
return new Vec3(1, 1, 1);
}
copy() {
return new Vec3(this.x, this.y, this.z);
}
add(other) {
this.x += other.x;
this.y += other.y;
this.z += other.z;
}
addNew(other) {
let vec = this.copy();
vec.x += other.x;
vec.y += other.y;
vec.z += other.z;
return vec;
}
sub(other) {
this.x -= other.x;
this.y -= other.y;
this.z -= other.z;
}
subNew(other) {
let vec = this.copy();
vec.x -= other.x;
vec.y -= other.y;
vec.z -= other.z;
return vec;
}
multScalar(scalar) {
this.x *= scalar;
this.y *= scalar;
this.z *= scalar;
}
multScalarNew(scalar) {
let vec = this.copy();
vec.x *= scalar;
vec.y *= scalar;
vec.z *= scalar;
return vec;
}
div(other) {
if (other.x == 0 ||
other.y == 0 ||
other.z == 0) {
throw new Error("Division by zero in Vec4");
}
this.x /= other.x;
this.y /= other.y;
this.z /= other.z;
}
divNew(other) {
if (other.x == 0 ||
other.y == 0 ||
other.z == 0) {
throw new Error("Division by zero in Vec4");
}
let vec = this.copy();
vec.x /= other.x;
vec.y /= other.y;
vec.z /= other.z;
return vec;
}
reduce() {
return new Vec2(this.x, this.y);
}
extend(value) {
return new Vec4(this.x, this.y, this.z, value);
}
splatToArray() {
return [this.x, this.y, this.z];
}
}
export class Vec4 {
x;
y;
z;
w;
constructor(x, y, z, w) {
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
from(scalar) {
return new Vec4(scalar, scalar, scalar, scalar);
}
static ZERO() {
return new Vec4(0, 0, 0, 0);
}
static ID() {
return new Vec4(1, 1, 1, 1);
}
copy() {
return new Vec4(this.x, this.y, this.z, this.w);
}
add(other) {
this.x += other.x;
this.y += other.y;
this.z += other.z;
this.w += other.w;
}
addNew(other) {
let vec = this.copy();
vec.x += other.x;
vec.y += other.y;
vec.z += other.z;
vec.w += other.w;
return vec;
}
addScalar(scalar) {
this.x += scalar;
this.y += scalar;
this.z += scalar;
this.w += scalar;
}
addScalarNew(scalar) {
let vec = this.copy();
vec.x += scalar;
vec.y += scalar;
vec.z += scalar;
vec.w += scalar;
return vec;
}
sub(other) {
this.x -= other.x;
this.y -= other.y;
this.z -= other.z;
this.w -= other.w;
}
subNew(other) {
let vec = this.copy();
vec.x -= other.x;
vec.y -= other.y;
vec.z -= other.z;
vec.w -= other.w;
return vec;
}
multScalar(scalar) {
this.x *= scalar;
this.y *= scalar;
this.z *= scalar;
this.w *= scalar;
}
multScalarNew(scalar) {
let vec = this.copy();
vec.x *= scalar;
vec.y *= scalar;
vec.z *= scalar;
vec.w *= scalar;
return vec;
}
div(other) {
if (other.x == 0 ||
other.y == 0 ||
other.z == 0 ||
other.w == 0) {
throw new Error("Division by zero in Vec4");
}
this.x /= other.x;
this.y /= other.y;
this.z /= other.z;
this.w /= other.w;
}
divNew(other) {
if (other.x == 0 ||
other.y == 0 ||
other.z == 0 ||
other.w == 0) {
throw new Error("Division by zero in Vec4");
}
let vec = this.copy();
vec.x /= other.x;
vec.y /= other.y;
vec.z /= other.z;
vec.w /= other.w;
return vec;
}
reduce() {
return new Vec3(this.x, this.y, this.z);
}
extend(_value) {
throw new Error("Can't extend Vec4");
}
}
export class Mat4 {
data;
constructor(i) {
if (i instanceof Float32Array) {
console.assert(i.length == 16, "Mat4 has to have 16 elements");
this.data = i;
return;
}
else if (typeof (i) === 'number') {
this.data = new Float32Array(16).fill(i);
return;
}
this.data = new Float32Array(16);
}
static IDENTITY() {
return new Mat4(new Float32Array([
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
]));
}
static orthographic(left, right, bottom, top, near, far) {
let data = new Float32Array([
2 / (right - left), 0, 0, 0,
0, 2 / (top - bottom), 0, 0,
0, 0, -2 / (far - near), 0,
-(right + left) / (right - left),
-(top + bottom) / (top - bottom),
-(far + near) / (far - near),
1,
]);
return new Mat4(data);
}
static isometric() {
return new Mat4(new Float32Array([
1, -1, 0, 0,
1, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
]));
}
static isometric_inverse() {
return new Mat4(new Float32Array([
0.5, -0.5, 0, 0,
0.5, 0.5, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
]));
}
static rotation_x(angle) {
let data = new Float32Array([
1, 0, 0, 0,
0, Math.cos(angle), -Math.sin(angle), 0,
0, Math.sin(angle), Math.cos(angle), 0,
0, 0, 0, 1,
]);
return new Mat4(data);
}
static rotation_y(angle) {
let data = new Float32Array([
Math.cos(angle), 0, Math.sin(angle), 0,
0, 1, 0, 0,
-Math.sin(angle), 0, Math.cos(angle), 0,
0, 0, 0, 1,
]);
return new Mat4(data);
}
static rotation_z(angle) {
let data = new Float32Array([
Math.cos(angle), -Math.sin(angle), 0, 0,
Math.sin(angle), Math.cos(angle), 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
]);
return new Mat4(data);
}
static translate(t) {
return new Mat4(new Float32Array([
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
t.x, t.y, t.z, 1,
]));
}
static scale(scales) {
return new Mat4(new Float32Array([
scales.x, 0, 0, 0,
0, scales.y, 0, 0,
0, 0, scales.z, 0,
0, 0, 0, 1,
]));
}
x(n) {
return this.data[n];
}
y(n) {
return this.data[n + 4];
}
z(n) {
return this.data[n + 8];
}
w(n) {
return this.data[n + 12];
}
splat() {
return Array.from(this.data);
}
row(row, data) {
for (let i = 0; i < data.length; ++i)
this.data[i + 4 * row] = data[i];
}
col(col, data) {
for (let i = 0; i < data.length; ++i)
this.data[i * 4 + col] = data[i];
}
transform(v) {
let x = v.x * this.x(0) + v.y * this.y(0) + v.z * this.z(0) + v.w * this.w(0);
let y = v.x * this.x(1) + v.y * this.y(1) + v.z * this.z(1) + v.w * this.w(1);
let z = v.x * this.x(2) + v.y * this.y(2) + v.z * this.z(2) + v.w * this.w(2);
let w = v.x * this.x(3) + v.y * this.y(3) + v.z * this.z(3) + v.w * this.w(3);
v.x = x;
v.y = y;
v.z = z;
v.w = w;
}
transformNew(v) {
let vec = v.copy();
let x = v.x * this.x(0) + v.y * this.y(0) + v.z * this.z(0) + v.w * this.w(0);
let y = v.x * this.x(1) + v.y * this.y(1) + v.z * this.z(1) + v.w * this.w(1);
let z = v.x * this.x(2) + v.y * this.y(2) + v.z * this.z(2) + v.w * this.w(2);
let w = v.x * this.x(3) + v.y * this.y(3) + v.z * this.z(3) + v.w * this.w(3);
vec.x = x;
vec.y = y;
vec.z = z;
vec.w = w;
return vec;
}
multNew(other) {
let m = new Mat4(0);
for (let i = 0; i < 4; ++i) {
for (let j = 0; j < 4; ++j) {
m.data[(i * 4) + j] += this.data[(i * 4) + 0] * other.data[j + 0];
m.data[(i * 4) + j] += this.data[(i * 4) + 1] * other.data[j + 4];
m.data[(i * 4) + j] += this.data[(i * 4) + 2] * other.data[j + 8];
m.data[(i * 4) + j] += this.data[(i * 4) + 3] * other.data[j + 12];
}
}
return m;
}
multScalar(scalar) {
for (let i = 0; i < this.data.length; ++i) {
this.data[i] *= scalar;
}
}
}

View File

@ -1,629 +0,0 @@
export function initializeContext(canvasId: string): WebGL2RenderingContext | null {
const canvas = document.getElementById(canvasId) as HTMLCanvasElement;
const ctx = canvas.getContext("webgl2", {antialias: false});
return ctx;
}
// TODO: Make all vectors follow one interface
interface Vector<P, T, N> {
copy(): T;
from(scalar: number): T;
add(other: T): void;
addNew(other: T): T;
addScalar(scalar: number): void;
addScalarNew(scalar: number): T;
sub(other: T): void;
subNew(other: T): T;
multScalar(scalar: number): void;
multScalarNew(scalar: number): T;
div(other: T): void;
divNew(other: T): T;
reduce(): P;
extend(value: number): N;
}
export class Vec2 implements Vector<void, Vec2, Vec3> {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
from(scalar: number): Vec2 {
return new Vec2(scalar, scalar);
}
static ZERO(): Vec2 {
return new Vec2(0, 0);
}
static ID(): Vec2 {
return new Vec2(1, 1);
}
copy(): Vec2 {
return new Vec2(this.x, this.y);
}
add(other: Vec2) {
this.x += other.x;
this.y += other.y;
}
addScalar(scalar: number) {
this.x += scalar;
this.y += scalar;
}
addNew(other: Vec2): Vec2 {
return new Vec2(this.x + other.x, this.y + other.y);
}
addScalarNew(scalar: number): Vec2 {
return new Vec2(this.x + scalar, this.y + scalar);
}
sub(other: Vec2) {
this.x -= other.x;
this.y -= other.y;
}
subNew(other: Vec2): Vec2 {
let vec = this.copy();
vec.x -= other.x;
vec.y -= other.y;
return vec;
}
multScalar(scalar: number) {
this.x *= scalar;
this.y *= scalar;
}
multScalarNew(scalar: number): Vec2 {
return new Vec2(this.x * scalar, this.y * scalar);
}
div(other: Vec2) {
if ( other.x == 0 ||
other.y == 0)
{
throw new Error("Division by zero in Vec4");
}
this.x /= other.x;
this.y /= other.y;
}
divNew(other: Vec2): Vec2 {
if ( other.x == 0 ||
other.y == 0)
{
throw new Error("Division by zero in Vec4");
}
let vec = this.copy();
vec.x /= other.x;
vec.y /= other.y;
return vec;
}
reduce(): null {
throw new Error("Can't reduce Vec2!");
}
extend(value: number): Vec3 {
return new Vec3(this.x, this.y, value);
}
splatToArray(): Array<number> {
return [this.x, this.y];
}
static angle(angle: number): Vec2 {
const eps = 1e-6;
let x = Math.cos(angle);
let y = Math.sin(angle);
if ((x > 0 && x < eps)
|| (x < 0 && x > -eps))
x = 0;
if ((y > 0 && y < eps)
|| (y < 0 && y > -eps))
y = 0;
return new Vec2(x, y);
}
}
export class Vec3 implements Vector<Vec2, Vec3, Vec4> {
x: number;
y: number;
z: number;
constructor(x: number, y: number, z: number) {
this.x = x;
this.y = y;
this.z = z;
}
from(scalar: number): Vec3 {
return new Vec3(scalar, scalar, scalar);
}
addScalar(scalar: number): void {
this.x += scalar;
this.y += scalar;
this.z += scalar;
}
addScalarNew(scalar: number): Vec3 {
let vec = this.copy();
vec.x += scalar;
vec.y += scalar;
vec.z += scalar;
return vec;
}
static ZERO(): Vec3 {
return new Vec3(0, 0, 0);
}
static ID(): Vec3 {
return new Vec3(1, 1, 1);
}
copy(): Vec3 {
return new Vec3(this.x, this.y, this.z);
}
add(other: Vec3) {
this.x += other.x;
this.y += other.y;
this.z += other.z;
}
addNew(other: Vec3): Vec3 {
let vec = this.copy();
vec.x += other.x;
vec.y += other.y;
vec.z += other.z;
return vec;
}
sub(other: Vec3) {
this.x -= other.x;
this.y -= other.y;
this.z -= other.z;
}
subNew(other: Vec3): Vec3 {
let vec = this.copy();
vec.x -= other.x;
vec.y -= other.y;
vec.z -= other.z;
return vec;
}
multScalar(scalar: number) {
this.x *= scalar;
this.y *= scalar;
this.z *= scalar;
}
multScalarNew(scalar: number): Vec3 {
let vec = this.copy();
vec.x *= scalar;
vec.y *= scalar;
vec.z *= scalar;
return vec;
}
div(other: Vec3) {
if ( other.x == 0 ||
other.y == 0 ||
other.z == 0)
{
throw new Error("Division by zero in Vec4");
}
this.x /= other.x;
this.y /= other.y;
this.z /= other.z;
}
divNew(other: Vec3): Vec3 {
if ( other.x == 0 ||
other.y == 0 ||
other.z == 0)
{
throw new Error("Division by zero in Vec4");
}
let vec = this.copy();
vec.x /= other.x;
vec.y /= other.y;
vec.z /= other.z;
return vec;
}
reduce(): Vec2 {
return new Vec2(this.x, this.y);
}
extend(value: number): Vec4 {
return new Vec4(this.x, this.y, this.z, value);
}
splatToArray(): Array<number> {
return [this.x, this.y, this.z];
}
}
export class Vec4 implements Vector<Vec3, Vec4, void> {
x: number;
y: number;
z: number;
w: number;
constructor(x: number, y: number, z: number, w: number) {
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
from(scalar: number): Vec4 {
return new Vec4(scalar, scalar, scalar, scalar);
}
static ZERO(): Vec4 {
return new Vec4(0, 0, 0, 0);
}
static ID(): Vec4 {
return new Vec4(1, 1, 1, 1);
}
copy(): Vec4 {
return new Vec4(this.x, this.y, this.z, this.w);
}
add(other: Vec4) {
this.x += other.x;
this.y += other.y;
this.z += other.z;
this.w += other.w;
}
addNew(other: Vec4): Vec4 {
let vec = this.copy();
vec.x += other.x;
vec.y += other.y;
vec.z += other.z;
vec.w += other.w;
return vec;
}
addScalar(scalar: number): void {
this.x += scalar;
this.y += scalar;
this.z += scalar;
this.w += scalar;
}
addScalarNew(scalar: number): Vec4 {
let vec = this.copy();
vec.x += scalar;
vec.y += scalar;
vec.z += scalar;
vec.w += scalar;
return vec;
}
sub(other: Vec4) {
this.x -= other.x;
this.y -= other.y;
this.z -= other.z;
this.w -= other.w;
}
subNew(other: Vec4): Vec4 {
let vec = this.copy();
vec.x -= other.x;
vec.y -= other.y;
vec.z -= other.z;
vec.w -= other.w;
return vec;
}
multScalar(scalar: number) {
this.x *= scalar;
this.y *= scalar;
this.z *= scalar;
this.w *= scalar;
}
multScalarNew(scalar: number): Vec4 {
let vec = this.copy();
vec.x *= scalar;
vec.y *= scalar;
vec.z *= scalar;
vec.w *= scalar;
return vec;
}
div(other: Vec4) {
if ( other.x == 0 ||
other.y == 0 ||
other.z == 0 ||
other.w == 0)
{
throw new Error("Division by zero in Vec4");
}
this.x /= other.x;
this.y /= other.y;
this.z /= other.z;
this.w /= other.w;
}
divNew(other: Vec4): Vec4 {
if ( other.x == 0 ||
other.y == 0 ||
other.z == 0 ||
other.w == 0)
{
throw new Error("Division by zero in Vec4");
}
let vec = this.copy();
vec.x /= other.x;
vec.y /= other.y;
vec.z /= other.z;
vec.w /= other.w;
return vec;
}
reduce(): Vec3 {
return new Vec3(this.x, this.y, this.z);
}
extend(_value: number): void {
throw new Error("Can't extend Vec4");
}
}
type Mat4Init = number | Float32Array;
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
export class Mat4 {
data: Float32Array;
constructor(i: Mat4Init) {
if (i instanceof Float32Array) {
console.assert(i.length == 16, "Mat4 has to have 16 elements");
this.data = i;
return;
} else if (typeof(i) === 'number') {
this.data = new Float32Array(16).fill(i);
return;
}
this.data = new Float32Array(16);
}
static IDENTITY(): Mat4 {
return new Mat4(new Float32Array([
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
]));
}
static orthographic(left: number, right: number, bottom: number, top: number, near: number, far: number): Mat4 {
let data = new Float32Array([
2 / (right - left), 0, 0, 0,
0, 2 / (top - bottom), 0, 0,
0, 0, -2 / (far - near), 0,
-(right + left)/(right - left),
-(top + bottom)/(top - bottom),
-(far + near)/(far - near),
1,
]);
return new Mat4(data);
}
static isometric(): Mat4 {
return new Mat4(new Float32Array([
1, -1, 0, 0,
1, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
]));
}
static isometric_inverse(): Mat4 {
return new Mat4(new Float32Array([
0.5, -0.5, 0, 0,
0.5, 0.5, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
]));
}
static rotation_x(angle: number): Mat4 {
let data = new Float32Array([
1, 0, 0, 0,
0, Math.cos(angle), -Math.sin(angle), 0,
0, Math.sin(angle), Math.cos(angle), 0,
0, 0, 0, 1,
]);
return new Mat4(data);
}
static rotation_y(angle: number): Mat4 {
let data = new Float32Array([
Math.cos(angle), 0, Math.sin(angle), 0,
0, 1, 0, 0,
-Math.sin(angle), 0, Math.cos(angle), 0,
0, 0, 0, 1,
]);
return new Mat4(data);
}
static rotation_z(angle: number): Mat4 {
let data = new Float32Array([
Math.cos(angle), -Math.sin(angle), 0, 0,
Math.sin(angle), Math.cos(angle), 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
]);
return new Mat4(data);
}
static translate(t: Vec3): Mat4 {
return new Mat4(new Float32Array([
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
t.x, t.y, t.z, 1,
]));
}
static scale(scales: Vec3) {
return new Mat4(new Float32Array([
scales.x, 0, 0, 0,
0, scales.y, 0, 0,
0, 0, scales.z, 0,
0, 0, 0, 1,
]));
}
x(n: Mat4X) {
return this.data[n];
}
y(n: Mat4Y) {
return this.data[n + 4];
}
z(n: Mat4Z) {
return this.data[n + 8];
}
w(n: Mat4W) {
return this.data[n + 12];
}
splat() {
return Array.from(this.data);
}
row(row: Mat4Row, data: [number, number, number, number]) {
for (let i = 0; i < data.length; ++i)
this.data[i + 4 * row] = data[i];
}
col(col: Mat4Col, data: [number, number, number, number]) {
for (let i = 0; i < data.length; ++i)
this.data[i*4 + col] = data[i];
}
transform(v: Vec4) {
let x = v.x * this.x(0) + v.y * this.y(0) + v.z * this.z(0) + v.w * this.w(0);
let y = v.x * this.x(1) + v.y * this.y(1) + v.z * this.z(1) + v.w * this.w(1);
let z = v.x * this.x(2) + v.y * this.y(2) + v.z * this.z(2) + v.w * this.w(2);
let w = v.x * this.x(3) + v.y * this.y(3) + v.z * this.z(3) + v.w * this.w(3);
v.x = x;
v.y = y;
v.z = z;
v.w = w;
}
transformNew(v: Vec4): Vec4 {
let vec = v.copy();
let x = v.x * this.x(0) + v.y * this.y(0) + v.z * this.z(0) + v.w * this.w(0);
let y = v.x * this.x(1) + v.y * this.y(1) + v.z * this.z(1) + v.w * this.w(1);
let z = v.x * this.x(2) + v.y * this.y(2) + v.z * this.z(2) + v.w * this.w(2);
let w = v.x * this.x(3) + v.y * this.y(3) + v.z * this.z(3) + v.w * this.w(3);
vec.x = x;
vec.y = y;
vec.z = z;
vec.w = w;
return vec;
}
multNew(other: Mat4): Mat4 {
let m = new Mat4(0);
for (let i = 0; i < 4; ++i) {
for (let j = 0; j < 4; ++j) {
m.data[(i * 4) + j] += this.data[(i * 4) + 0] * other.data[j + 0]
m.data[(i * 4) + j] += this.data[(i * 4) + 1] * other.data[j + 4]
m.data[(i * 4) + j] += this.data[(i * 4) + 2] * other.data[j + 8]
m.data[(i * 4) + j] += this.data[(i * 4) + 3] * other.data[j + 12]
}
}
return m;
}
multScalar(scalar: number) {
for (let i = 0; i < this.data.length; ++i) {
this.data[i] *= scalar;
}
}
}

View File

@ -1,182 +0,0 @@
import { Vec3, Mat4, Vec4 } from "./common.js";
import { DrawTag, Sprite } from "./graphics.js";
import { TileEdge } from "./world.js";
import * as Assets from "./assets.js";
// Attrib format
// position color uv
// (3) (4) (3) => 3 + 4 + 3 = 10 <=> vertexSize == 10
export class Rectangle {
attribs = ["a_position", "a_color", "a_tex"];
tags = [];
data = [];
vertexSize = 10;
sprites = Assets.assets.get("sprites");
constructor(tags, sprites, attribs) {
if (sprites !== undefined) {
this.sprites = sprites;
}
if (attribs !== undefined) {
this.attribs = attribs;
}
if (tags !== undefined) {
this.tags = tags;
}
}
commit(gfx) {
gfx.toRender.push(this);
}
draw(corners, fill) {
if (fill instanceof Sprite) {
let uvs = Assets.assets.get("sprites").getUVs(fill.id);
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;
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);
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;
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]);
}
}
}
}
export function drawIsometricCube(position, exts, r, fill, edge) {
let points = [
// Left Top
position,
//Top
new Vec3(position.x, position.y + exts.y, position.z),
//Mid
new Vec3(position.x + exts.x, position.y, position.z),
//Right Top
new Vec3(position.x + exts.x, position.y + exts.y, position.z),
//Bottom
new Vec3(position.x + 1.5 * exts.x, position.y - 0.5 * exts.y, position.z + 0.25 * exts.z),
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),
];
// Top
r.draw([
points[0],
points[1],
points[3],
points[2],
], fill.top);
// Right Edge
if (edge == TileEdge.Right || edge == TileEdge.Both) {
r.draw([
points[3],
points[2],
points[4],
points[5],
], fill.right);
}
// Left Edge
if (edge == TileEdge.Left || edge == TileEdge.Both) {
r.draw([
points[0],
points[2],
points[4],
points[6],
], fill.left);
}
}
// TODO: Optimize the shit out of this function
export function drawIsometricGrid(gfx, camera, grid) {
let position = grid.position.copy();
let exts = new Vec3(grid.tileSize, grid.tileSize, 0);
let tileCoord = new Vec3(0, 0, 0);
let rect = new Rectangle([
DrawTag.ISO,
]);
let mt = Mat4.translate(camera.position.multScalarNew(-1.0));
let ms = Mat4.scale(new Vec3(1 / camera.scale, 1 / camera.scale, 0));
let mc = Mat4.translate(new Vec3((gfx.ctx.canvas.width / 2 - camera.position.x), (gfx.ctx.canvas.height / 2 - camera.position.y), 1.0));
let mr = Mat4.translate(new Vec3(-(gfx.ctx.canvas.width / 2 - camera.position.x), -(gfx.ctx.canvas.height / 2 - camera.position.y), 1.0));
let mi = Mat4.isometric();
let bias = 4 * grid.tileSize;
let bb = [
new Vec3(-bias, -bias, 0),
new Vec3(gfx.width() + bias, -bias, 0),
new Vec3(gfx.width() + bias, gfx.height() + bias, 0),
new Vec3(-bias, gfx.height() + bias, 0),
];
for (let i = 0; i < bb.length; ++i) {
bb[i] = mt.transformNew(bb[i].extend(1.0)).reduce();
bb[i] = mr.transformNew(bb[i].extend(1.0)).reduce();
bb[i] = ms.transformNew(bb[i].extend(1.0)).reduce();
bb[i] = mc.transformNew(bb[i].extend(1.0)).reduce();
}
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;
tileCoord.z = k;
let tile = grid.getTile(tileCoord);
if (tile === null) {
position.x += grid.tileSize;
continue;
}
const ipos = mi.transformNew(new Vec4(position.x - grid.tileSize / 2 * k, position.y + grid.tileSize / 2 * k, 1.0, 1.0)).reduce();
if (ipos.x >= bb[2].x || ipos.x <= bb[0].x ||
ipos.y >= bb[2].y || ipos.y <= bb[0].y) {
position.x += grid.tileSize;
continue;
}
drawIsometricCube(new Vec3(position.x - grid.tileSize / 2 * k, position.y + grid.tileSize / 2 * k, position.z), exts, rect, tile.fill, tile.edge);
position.x += grid.tileSize;
}
position.y -= grid.tileSize;
position.x = grid.position.x;
}
position.y = grid.position.y;
}
rect.commit(gfx);
}

View File

@ -1,244 +0,0 @@
import { Vec3, Vec2, Mat4, Vec4 } from "./common.js"
import { Graphics, Drawable, DrawTag, Sprite, Spritesheet, Camera } from "./graphics.js";
import { TileEdge, TileFill, TileFillament, Grid } from "./world.js";
import * as Assets from "./assets.js";
import {Input} from "./input.js";
// Attrib format
// position color uv
// (3) (4) (3) => 3 + 4 + 3 = 10 <=> vertexSize == 10
export class Rectangle implements Drawable {
attribs: string[] = ["a_position", "a_color", "a_tex"];
tags: Array<DrawTag> = [];
data: number[] = [];
vertexSize: number = 10;
sprites: Spritesheet = Assets.assets.get("sprites") as Spritesheet;
constructor(tags?: Array<DrawTag>, sprites?: Spritesheet, attribs?: string[]) {
if (sprites !== undefined) {
this.sprites = sprites;
}
if (attribs !== undefined) {
this.attribs = attribs;
}
if (tags !== undefined) {
this.tags = tags;
}
}
commit(gfx: Graphics) {
gfx.toRender.push(this);
}
draw(corners: [Vec3, Vec3, Vec3, Vec3], fill: TileFillament) {
if (fill instanceof Sprite) {
let uvs = (Assets.assets.get("sprites") as Spritesheet).getUVs(fill.id);
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;
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: Vec3, exts: Vec2, fill: TileFillament) {
if (fill instanceof Sprite) {
let uvs = (Assets.assets.get("sprites") as Spritesheet).getUVs(fill.id);
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;
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]);
}
}
}
}
export function drawIsometricCube(position: Vec3, exts: Vec3, r: Rectangle, fill: TileFill, edge: TileEdge) {
let points = [
// Left Top
position,
//Top
new Vec3(position.x, position.y + exts.y, position.z),
//Mid
new Vec3(position.x + exts.x, position.y, position.z),
//Right Top
new Vec3(position.x + exts.x, position.y + exts.y, position.z),
//Bottom
new Vec3(position.x + 1.5 * exts.x, position.y - 0.5 * exts.y, position.z + 0.25 * exts.z),
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),
];
// Top
r.draw(
[
points[0],
points[1],
points[3],
points[2],
],
fill.top
);
// Right Edge
if (edge == TileEdge.Right || edge == TileEdge.Both) {
r.draw(
[
points[3],
points[2],
points[4],
points[5],
],
fill.right
);
}
// Left Edge
if (edge == TileEdge.Left || edge == TileEdge.Both) {
r.draw(
[
points[0],
points[2],
points[4],
points[6],
],
fill.left
);
}
}
// TODO: Optimize the shit out of this function
export function drawIsometricGrid(gfx: Graphics, camera: Camera, grid: Grid) {
let position = grid.position.copy();
let exts = new Vec3(grid.tileSize, grid.tileSize, 0);
let tileCoord = new Vec3(0, 0, 0);
let rect = new Rectangle([
DrawTag.ISO,
]);
let mt = Mat4.translate(camera.position.multScalarNew(-1.0));
let ms = Mat4.scale(new Vec3(1 / camera.scale, 1 /camera.scale, 0));
let mc = Mat4.translate(
new Vec3(
(gfx.ctx.canvas.width / 2 - camera.position.x),
(gfx.ctx.canvas.height / 2 - camera.position.y),
1.0));
let mr = Mat4.translate(
new Vec3(
-(gfx.ctx.canvas.width / 2 - camera.position.x),
-(gfx.ctx.canvas.height / 2 - camera.position.y),
1.0));
let mi = Mat4.isometric();
let bias = 4*grid.tileSize;
let bb: [Vec3, Vec3, Vec3, Vec3] = [
new Vec3(-bias, -bias, 0),
new Vec3(gfx.width() + bias, -bias, 0),
new Vec3(gfx.width() + bias, gfx.height() + bias, 0),
new Vec3(-bias, gfx.height() + bias, 0),
];
for (let i = 0; i < bb.length; ++i) {
bb[i] = mt.transformNew(bb[i].extend(1.0)).reduce();
bb[i] = mr.transformNew(bb[i].extend(1.0)).reduce();
bb[i] = ms.transformNew(bb[i].extend(1.0)).reduce();
bb[i] = mc.transformNew(bb[i].extend(1.0)).reduce();
}
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;
tileCoord.z = k;
let tile = grid.getTile(tileCoord);
if (tile === null) {
position.x += grid.tileSize;
continue;
}
const ipos = mi.transformNew(new Vec4(position.x - grid.tileSize / 2 * k, position.y + grid.tileSize / 2 * k, 1.0, 1.0)).reduce();
if (ipos.x >= bb[2].x || ipos.x <= bb[0].x ||
ipos.y >= bb[2].y || ipos.y <= bb[0].y) {
position.x += grid.tileSize;
continue;
}
drawIsometricCube(
new Vec3(position.x - grid.tileSize / 2 * k, position.y + grid.tileSize / 2 * k, position.z),
exts,
rect,
tile.fill,
tile.edge
);
position.x += grid.tileSize;
}
position.y -= grid.tileSize;
position.x = grid.position.x;
}
position.y = grid.position.y;
}
rect.commit(gfx);
}

View File

@ -1,264 +0,0 @@
import { Vec2, Vec4 } from "./common.js";
export function fullscreenCanvas(gfx, id) {
const canvas = document.getElementById(id);
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
gfx.ctx.viewport(0, 0, canvas.width, canvas.height);
}
function createShader(ctx, type, source) {
var shader = ctx.createShader(type);
if (!shader) {
throw new Error("Couldn't create shader: " + type);
}
ctx.shaderSource(shader, source);
ctx.compileShader(shader);
var success = ctx.getShaderParameter(shader, ctx.COMPILE_STATUS);
if (!success) {
console.log(ctx.getShaderInfoLog(shader));
ctx.deleteShader(shader);
throw new Error("Couldn't compile shader: " + type);
}
return shader;
}
function createProgram(ctx, vertexShaderSource, fragmentShaderSource) {
var program = ctx.createProgram();
if (vertexShaderSource !== null) {
const vs = createShader(ctx, ctx.VERTEX_SHADER, vertexShaderSource);
ctx.attachShader(program, vs);
}
if (fragmentShaderSource !== null) {
const fs = createShader(ctx, ctx.FRAGMENT_SHADER, fragmentShaderSource);
ctx.attachShader(program, fs);
}
ctx.linkProgram(program);
var success = ctx.getProgramParameter(program, ctx.LINK_STATUS);
if (!success) {
console.log(ctx.getProgramInfoLog(program));
ctx.deleteProgram(program);
throw new Error("Failed to create program!");
}
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;
width() {
return this.ctx.canvas.width;
}
height() {
return this.ctx.canvas.height;
}
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);
ctx.blendFunc(ctx.SRC_ALPHA, ctx.ONE_MINUS_SRC_ALPHA);
ctx.enable(ctx.BLEND);
}
clear(r, g, b, a) {
this.ctx.clearColor(r, g, b, a);
this.ctx.clear(this.ctx.COLOR_BUFFER_BIT);
}
createAttribute(name) {
const attrib = new Attribute(this.ctx, this.program, name);
this.attribs.set(name, attrib);
return attrib;
}
getAttribute(name) {
const attrib = this.attribs.get(name);
if (attrib === undefined)
throw new Error("Tried to get uninitialized attribute: " + name);
return attrib;
}
createUniform(name) {
const loc = this.ctx.getUniformLocation(this.program, name);
if (loc === null)
throw new Error("Couldn't get location for uniform: " + name);
this.uniforms.set(name, loc);
}
getUniform(name) {
const loc = this.uniforms.get(name);
if (loc === undefined)
throw new Error("Tried to get uninitialized uniform: " + name);
return loc;
}
draw() {
for (let o of this.toRender) {
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;
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.vertexSize * 4, 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;
}
}
}
o.sprites?.bind(this);
this.ctx.drawArrays(this.ctx.TRIANGLES, 0, data.length / o.vertexSize);
o.sprites?.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;
formatted = false;
size = 0;
type = 0;
offset = 0;
normalized = false;
constructor(ctx, program, name) {
this.loc = ctx.getAttribLocation(program, name);
}
format(size, type, normalized, offset) {
this.size = size;
this.type = type;
this.normalized = normalized;
this.offset = offset;
this.formatted = true;
}
}
export class Texture {
tex;
texId;
width = 0;
height = 0;
constructor(texId, tex, width, height) {
this.height = height;
this.width = width;
this.tex = tex;
this.texId = texId;
}
// TODO: Load sprite sheet only once
// TODO: Allow changing sprite size
static async load(gfx, path) {
let image = await loadTexture(path);
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.bindTexture(gfx.ctx.TEXTURE_2D, this.tex);
}
unbind(gfx) {
gfx.ctx.bindTexture(gfx.ctx.TEXTURE_2D, null);
}
}
export class Sprite {
id = 0;
constructor(id) {
this.id = id;
}
static id(id) {
return new Sprite(id);
}
static tile(id) {
let s = new Sprite(id);
return {
left: s,
top: s,
right: s,
};
}
}
;
export class Spritesheet {
texture; // Texture is a horizontal spritesheet
spriteSize; // width and height of one sprite
spriteCount; // number of sprites
constructor(texture, spriteSize) {
this.texture = texture;
this.spriteSize = spriteSize;
this.spriteCount = texture.width / spriteSize.x;
}
getUVs(id) {
return [
new Vec2((this.spriteSize.x * id) / this.texture.width, 0),
new Vec2((this.spriteSize.x * (id + 1)) / this.texture.width, 0),
new Vec2((this.spriteSize.x * (id + 1)) / this.texture.width, -1),
new Vec2((this.spriteSize.x * id) / this.texture.width, -1),
];
}
bind(gfx) {
this.texture.bind(gfx);
}
unbind(gfx) {
this.texture.unbind(gfx);
}
}
async function loadTexture(path) {
return new Promise(resolve => {
const img = new Image();
img.addEventListener("load", () => {
resolve(img);
});
img.src = path;
});
}
export class Camera {
dt = 0;
position;
movement;
speed = 600;
scale = 1.0;
scaling;
scaleSpeed = 1.5;
constructor(position) {
this.position = position;
this.movement = new Vec4(0, 0, 0, 0);
this.scaling = new Vec2(0, 0);
}
update(dt) {
this.dt = dt;
let newPosition = this.movement.multScalarNew(this.dt);
this.position.x += (newPosition.x + newPosition.y) * this.speed;
this.position.y += (newPosition.z + newPosition.w) * this.speed;
this.scale += (this.scaling.x + this.scaling.y) * this.dt * this.scaleSpeed;
if (this.scale < 0.5) {
this.scale = 0.5;
}
if (this.scale > 1.5) {
this.scale = 1.5;
}
}
}

View File

@ -1,353 +0,0 @@
import {Vec2, Vec3, Vec4} from "./common.js";
import { TileFillament, TileFill } from "./world.js";
export function fullscreenCanvas(gfx: Graphics, id: string) {
const canvas = document.getElementById(id) as HTMLCanvasElement;
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
gfx.ctx.viewport(0, 0, canvas.width, canvas.height);
}
function createShader(ctx: WebGL2RenderingContext, type: GLenum, source: string): WebGLShader {
var shader = ctx.createShader(type);
if (!shader) {
throw new Error("Couldn't create shader: " + type);
}
ctx.shaderSource(shader, source);
ctx.compileShader(shader);
var success = ctx.getShaderParameter(shader, ctx.COMPILE_STATUS);
if (!success) {
console.log(ctx.getShaderInfoLog(shader));
ctx.deleteShader(shader);
throw new Error("Couldn't compile shader: " + type);
}
return shader;
}
function createProgram(ctx: WebGL2RenderingContext, vertexShaderSource: string | null, fragmentShaderSource: string | null): WebGLProgram {
var program = ctx.createProgram();
if (vertexShaderSource !== null) {
const vs = createShader(ctx, ctx.VERTEX_SHADER, vertexShaderSource);
ctx.attachShader(program, vs);
}
if (fragmentShaderSource !== null) {
const fs = createShader(ctx, ctx.FRAGMENT_SHADER, fragmentShaderSource);
ctx.attachShader(program, fs);
}
ctx.linkProgram(program);
var success = ctx.getProgramParameter(program, ctx.LINK_STATUS);
if (!success) {
console.log(ctx.getProgramInfoLog(program));
ctx.deleteProgram(program);
throw new Error("Failed to create program!");
}
return program;
}
export enum DrawTag {
ISO,
}
export interface Drawable {
attribs: string[];
tags: Array<DrawTag>;
data: number[];
vertexSize: number;
sprites: Spritesheet | undefined;
}
export class Graphics {
ctx: WebGL2RenderingContext;
program: WebGLProgram;
attribs: Map<string, Attribute> = new Map();
uniforms: Map<string, WebGLUniformLocation> = new Map();
vao: WebGLVertexArrayObject;
vbo: WebGLBuffer;
toRender: Array<Drawable> = [];
texCount: number = 0;
width(): number {
return this.ctx.canvas.width;
}
height(): number {
return this.ctx.canvas.height;
}
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);
ctx.useProgram(this.program);
ctx.blendFunc(ctx.SRC_ALPHA, ctx.ONE_MINUS_SRC_ALPHA);
ctx.enable(ctx.BLEND);
}
clear(r: number, g: number, b: number, a: number) {
this.ctx.clearColor(r, g, b, a);
this.ctx.clear(this.ctx.COLOR_BUFFER_BIT);
}
createAttribute(name: string): Attribute {
const attrib = new Attribute(this.ctx, this.program, name);
this.attribs.set(name, attrib);
return attrib;
}
getAttribute(name: string): Attribute {
const attrib = this.attribs.get(name);
if (attrib === undefined)
throw new Error("Tried to get uninitialized attribute: " + name);
return attrib;
}
createUniform(name: string) {
const loc = this.ctx.getUniformLocation(this.program, name);
if (loc === null)
throw new Error("Couldn't get location for uniform: " + name);
this.uniforms.set(name, loc);
}
getUniform(name: string): WebGLUniformLocation {
const loc = this.uniforms.get(name);
if (loc === undefined)
throw new Error("Tried to get uninitialized uniform: " + name);
return loc;
}
draw() {
for (let o of this.toRender) {
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;
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.vertexSize * 4, 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;
}
}
}
o.sprites?.bind(this);
this.ctx.drawArrays(this.ctx.TRIANGLES, 0, data.length / o.vertexSize);
o.sprites?.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;
formatted: boolean = false;
size: GLint = 0;
type: GLenum = 0;
offset: GLintptr = 0;
normalized: GLboolean = false;
constructor(ctx: WebGL2RenderingContext, program: WebGLProgram, name: string) {
this.loc = ctx.getAttribLocation(program, name);
}
format(
size: GLint,
type: GLenum,
normalized: GLboolean,
offset: GLintptr)
{
this.size = size;
this.type = type;
this.normalized = normalized;
this.offset = offset;
this.formatted = true;
}
}
export class Texture {
tex: WebGLTexture | null;
texId: number;
width: number = 0;
height: number = 0;
constructor(texId: number, tex: WebGLTexture, width: number, height: number) {
this.height = height;
this.width = width;
this.tex = tex;
this.texId = texId;
}
// TODO: Load sprite sheet only once
// TODO: Allow changing sprite size
static async load(gfx: Graphics, path: string): Promise<Texture> {
let image = await loadTexture(path);
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: Graphics) {
gfx.ctx.bindTexture(gfx.ctx.TEXTURE_2D, this.tex);
}
unbind(gfx: Graphics) {
gfx.ctx.bindTexture(gfx.ctx.TEXTURE_2D, null);
}
}
export type SpriteId = number;
export class Sprite {
id: SpriteId = 0;
private constructor(id: SpriteId) {
this.id = id;
}
static id(id: SpriteId): Sprite {
return new Sprite(id);
}
static tile(id: SpriteId): TileFill {
let s = new Sprite(id);
return {
left: s,
top: s,
right: s,
}
}
};
export class Spritesheet {
texture: Texture; // Texture is a horizontal spritesheet
spriteSize: Vec2; // width and height of one sprite
spriteCount: number; // number of sprites
constructor(texture: Texture, spriteSize: Vec2) {
this.texture = texture;
this.spriteSize = spriteSize;
this.spriteCount = texture.width / spriteSize.x;
}
getUVs(id: number): [Vec2, Vec2, Vec2, Vec2] {
return [
new Vec2((this.spriteSize.x * id) / this.texture.width, 0),
new Vec2((this.spriteSize.x * (id + 1)) / this.texture.width, 0),
new Vec2((this.spriteSize.x * (id + 1)) / this.texture.width, -1),
new Vec2((this.spriteSize.x * id) / this.texture.width, -1),
];
}
bind(gfx: Graphics) {
this.texture.bind(gfx);
}
unbind(gfx: Graphics) {
this.texture.unbind(gfx);
}
}
async function loadTexture(path: string): Promise<HTMLImageElement> {
return new Promise(resolve => {
const img = new Image();
img.addEventListener("load", () => {
resolve(img);
})
img.src = path;
});
}
export class Camera {
dt: number = 0;
position: Vec3;
movement: Vec4;
speed: number = 600;
scale: number = 1.0;
scaling: Vec2;
scaleSpeed: number = 1.5;
constructor(position: Vec3) {
this.position = position;
this.movement = new Vec4(0, 0, 0, 0);
this.scaling = new Vec2(0, 0);
}
update(dt: number) {
this.dt = dt;
let newPosition = this.movement.multScalarNew(this.dt);
this.position.x += (newPosition.x + newPosition.y) * this.speed;
this.position.y += (newPosition.z + newPosition.w) * this.speed;
this.scale += (this.scaling.x + this.scaling.y) * this.dt * this.scaleSpeed;
if (this.scale < 0.5) {
this.scale = 0.5;
}
if (this.scale > 1.5) {
this.scale = 1.5;
}
}
}

View File

@ -1,41 +0,0 @@
import { Vec2 } from "./common.js";
class KeyAction {
key;
keyup;
keydown;
data;
constructor(key, ku, kd, data) {
this.key = key;
this.keyup = ku;
this.keydown = kd;
this.data = data;
}
}
export class Input {
handlers = new Map();
mousePosition = Vec2.ZERO();
constructor() {
window.addEventListener("keyup", e => {
this.handlers.forEach(ka => {
if (ka.keyup !== null && ka.key == e.code) {
ka.keyup(ka.data);
}
});
});
window.addEventListener("keydown", e => {
this.handlers.forEach(ka => {
if (ka.keydown !== null && ka.key == e.code) {
ka.keydown(ka.data);
}
});
});
window.addEventListener("mousemove", (e) => {
this.mousePosition.x = e.x;
this.mousePosition.y = e.y;
});
}
//TODO: add modifier key support
addKeyAction(key, modifiers, data, keydown, keyup) {
this.handlers.set(key, new KeyAction(key, keydown, keyup, data));
}
}

View File

@ -1,47 +0,0 @@
import { Vec2 } from "./common.js";
type Action = ((data: any) => void);
class KeyAction {
key: string;
keyup: Action | null;
keydown: Action | null;
data: any;
constructor(key: string, ku: Action | null, kd: Action | null, data: any) {
this.key = key;
this.keyup = ku;
this.keydown = kd;
this.data = data;
}
}
export class Input {
handlers: Map<string, KeyAction> = new Map();
mousePosition: Vec2 = Vec2.ZERO();
constructor() {
window.addEventListener("keyup", e => {
this.handlers.forEach(ka => {
if (ka.keyup !== null && ka.key == e.code) {
ka.keyup(ka.data);
}
});
});
window.addEventListener("keydown", e => {
this.handlers.forEach(ka => {
if (ka.keydown !== null && ka.key == e.code) {
ka.keydown(ka.data);
}
});
});
window.addEventListener("mousemove", (e) => {
this.mousePosition.x = e.x;
this.mousePosition.y = e.y;
})
}
//TODO: add modifier key support
addKeyAction(key: string, modifiers: [string] | [], data: any, keydown: Action | null, keyup: Action | null) {
this.handlers.set(key, new KeyAction(key, keydown, keyup, data));
}
}

View File

@ -1,171 +0,0 @@
import { initializeContext, Vec3, Mat4, Vec4 } from "./common.js";
import { Graphics, fullscreenCanvas, Camera, Sprite } from "./graphics.js";
import * as drawing from "./draw.js";
import * as wasm from "./wasm.js";
import { Input } from "./input.js";
import { Grid, Tile, tree } from "./world.js";
import * as Assets from "./assets.js";
const vertexShader = `#version 300 es
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec3 a_tex;
layout(location = 2) in vec4 a_color;
out vec4 v_color;
out vec3 v_tex;
uniform mat4 u_matrix;
uniform bool u_isIso;
mat4 Iso = mat4(
1, -1, 0, 0,
1, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
);
void main() {
vec4 orthographic;
if (u_isIso) {
vec4 isometric = Iso * vec4(a_position.xyz, 1.0);
orthographic = u_matrix * isometric;
//orthographic = u_matrix * vec4(a_position.xyz, 1.0);
} else {
orthographic = u_matrix * vec4(a_position.xyz, 1.0);
}
gl_Position = orthographic;
v_tex = a_tex;
v_color = a_color;
}
`;
const fragmentShader = `#version 300 es
precision highp float;
in vec4 v_color;
in vec3 v_tex;
out vec4 outColor;
uniform sampler2D u_texture;
void main() {
if (v_tex.z == 1.0) {
outColor = texture(u_texture, v_tex.xy);
} else {
outColor = v_color;
}
}
`;
function draw(gfx, camera, dt, grid) {
gfx.clear(0, 0, 0, 0);
camera.update(dt);
let right = gfx.ctx.canvas.width;
let left = 0;
let top = gfx.ctx.canvas.height;
let bottom = 0;
let near = -100;
let far = 100;
let m = Mat4.IDENTITY();
let mo = Mat4.orthographic(left, right, bottom, top, near, far);
let mc = Mat4.translate(new Vec3((gfx.ctx.canvas.width / 2 - camera.position.x), (gfx.ctx.canvas.height / 2 - camera.position.y), 1.0));
let mr = Mat4.translate(new Vec3(-(gfx.ctx.canvas.width / 2 - camera.position.x), -(gfx.ctx.canvas.height / 2 - camera.position.y), 1.0));
let mt = Mat4.translate(camera.position);
let ms = Mat4.scale(new Vec3(camera.scale, camera.scale, 1));
m = m.multNew(mr);
m = m.multNew(ms);
m = m.multNew(mc);
m = m.multNew(mt);
m = m.multNew(mo);
gfx.ctx.uniformMatrix4fv(gfx.getUniform("u_matrix"), false, m.splat());
drawing.drawIsometricGrid(gfx, camera, grid);
gfx.draw();
}
function addDefaultKeybinds(input, camera) {
input.addKeyAction("KeyA", [], camera, (c) => {
c.movement.x = 0;
}, (c) => {
c.movement.x = 1;
});
input.addKeyAction("KeyD", [], camera, (c) => {
c.movement.y = 0;
}, (c) => {
c.movement.y = -1;
});
input.addKeyAction("KeyW", [], camera, (c) => {
c.movement.z = 0;
}, (c) => {
c.movement.z = -1;
});
input.addKeyAction("KeyS", [], camera, (c) => {
c.movement.w = 0;
}, (c) => {
c.movement.w = 1;
});
input.addKeyAction("KeyQ", [], camera, (c) => {
c.scaling.x = 0.0;
}, (c) => {
c.scaling.x = 1.0;
});
input.addKeyAction("KeyE", [], camera, (c) => {
c.scaling.y = 0.0;
}, (c) => {
c.scaling.y = -1.0;
});
}
(async () => {
const canvasId = "game";
const ctx = initializeContext(canvasId);
if (ctx === null)
return;
const gfx = new Graphics(ctx, vertexShader, fragmentShader);
fullscreenCanvas(gfx, canvasId);
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");
a.format(3, ctx.FLOAT, false, 0);
gfx.createUniform("u_matrix");
gfx.createUniform("u_isIso");
let camera = new Camera(new Vec3(0, 0, -1));
await Assets.assets.load(gfx);
let m = Mat4.isometric();
let size = 100;
let grid = new Grid(m.transformNew(new Vec4(ctx.canvas.width / 4, ctx.canvas.height / 2, 0, 1)).reduce(), 32, size, size, 10);
grid.fillLayer(new Tile({
top: Assets.Colors.Gray,
right: Assets.Colors.Gray,
left: Assets.Colors.Gray,
}), 0);
grid.fillLayer(new Tile({
top: Assets.Colors.Gray,
right: Assets.Colors.Gray,
left: Assets.Colors.Gray,
}), 1);
grid.fillLayer(new Tile({
top: Sprite.id(0),
right: Assets.Colors.Brown,
left: Assets.Colors.Brown,
}), 2);
for (let i = 0; i <= size / 2; i++) {
tree(grid, new Vec3(Math.floor(Math.random() * size - 1), Math.floor(Math.random() * size - 1), 2));
}
const input = new Input();
addDefaultKeybinds(input, camera);
let prevTimestamp = 0;
const frame = (timestamp) => {
const deltaTime = (timestamp - prevTimestamp) / 1000;
prevTimestamp = timestamp;
fullscreenCanvas(gfx, "game");
draw(gfx, camera, deltaTime, grid);
window.requestAnimationFrame(frame);
};
window.requestAnimationFrame((timestamp) => {
prevTimestamp = timestamp;
window.requestAnimationFrame(frame);
});
const wasmgl = new wasm.WASMGL(await wasm.loadWasmModule("./src/wasm/module.wasm"));
})();

View File

@ -1,234 +0,0 @@
import { initializeContext, Vec3, Mat4, Vec4, Vec2 } from "./common.js";
import { Graphics, fullscreenCanvas, Camera, Sprite, DrawTag } from "./graphics.js";
import * as drawing from "./draw.js";
import * as wasm from "./wasm.js";
import { Input } from "./input.js";
import {Grid, Tile, TileEdge, tree} from "./world.js";
import * as Assets from "./assets.js";
const vertexShader =
`#version 300 es
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec3 a_tex;
layout(location = 2) in vec4 a_color;
out vec4 v_color;
out vec3 v_tex;
uniform mat4 u_matrix;
uniform bool u_isIso;
mat4 Iso = mat4(
1, -1, 0, 0,
1, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
);
void main() {
vec4 orthographic;
if (u_isIso) {
vec4 isometric = Iso * vec4(a_position.xyz, 1.0);
orthographic = u_matrix * isometric;
//orthographic = u_matrix * vec4(a_position.xyz, 1.0);
} else {
orthographic = u_matrix * vec4(a_position.xyz, 1.0);
}
gl_Position = orthographic;
v_tex = a_tex;
v_color = a_color;
}
`;
const fragmentShader =
`#version 300 es
precision highp float;
in vec4 v_color;
in vec3 v_tex;
out vec4 outColor;
uniform sampler2D u_texture;
void main() {
if (v_tex.z == 1.0) {
outColor = texture(u_texture, v_tex.xy);
} else {
outColor = v_color;
}
}
`;
function draw(gfx: Graphics, camera: Camera, dt: number, grid: Grid) {
gfx.clear(0, 0, 0, 0);
camera.update(dt);
let right = gfx.ctx.canvas.width;
let left = 0;
let top = gfx.ctx.canvas.height;
let bottom = 0;
let near = -100;
let far = 100;
let m = Mat4.IDENTITY();
let mo = Mat4.orthographic(left, right, bottom, top, near, far);
let mc = Mat4.translate(
new Vec3(
(gfx.ctx.canvas.width / 2 - camera.position.x),
(gfx.ctx.canvas.height / 2 - camera.position.y),
1.0));
let mr = Mat4.translate(
new Vec3(
-(gfx.ctx.canvas.width / 2 - camera.position.x),
-(gfx.ctx.canvas.height / 2 - camera.position.y),
1.0));
let mt = Mat4.translate(camera.position);
let ms = Mat4.scale(new Vec3(camera.scale, camera.scale, 1));
m = m.multNew(mr);
m = m.multNew(ms);
m = m.multNew(mc);
m = m.multNew(mt);
m = m.multNew(mo);
gfx.ctx.uniformMatrix4fv(
gfx.getUniform("u_matrix"),
false,
m.splat()
);
drawing.drawIsometricGrid(gfx, camera, grid);
gfx.draw();
}
function addDefaultKeybinds(input: Input, camera: Camera) {
input.addKeyAction("KeyA", [], camera,
(c) => {
c.movement.x = 0;
},
(c) => {
c.movement.x = 1;
});
input.addKeyAction("KeyD", [], camera,
(c) => {
c.movement.y = 0;
},
(c) => {
c.movement.y = -1;
});
input.addKeyAction("KeyW", [], camera,
(c) => {
c.movement.z = 0;
},
(c) => {
c.movement.z = -1;
});
input.addKeyAction("KeyS", [], camera,
(c) => {
c.movement.w = 0;
},
(c) => {
c.movement.w = 1;
});
input.addKeyAction("KeyQ", [], camera,
(c) => {
c.scaling.x = 0.0;
},
(c) => {
c.scaling.x = 1.0;
});
input.addKeyAction("KeyE", [], camera,
(c) => {
c.scaling.y = 0.0;
},
(c) => {
c.scaling.y = -1.0;
});
}
(async () => {
const canvasId = "game";
const ctx = initializeContext(canvasId);
if (ctx === null) return;
const gfx = new Graphics(ctx, vertexShader, fragmentShader);
fullscreenCanvas(gfx, canvasId);
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");
a.format(3, ctx.FLOAT, false, 0)
gfx.createUniform("u_matrix");
gfx.createUniform("u_isIso");
let camera = new Camera(new Vec3(0, 0, -1));
await Assets.assets.load(gfx);
let m = Mat4.isometric();
let size = 100;
let grid = new Grid(m.transformNew(new Vec4(ctx.canvas.width / 4, ctx.canvas.height / 2, 0, 1)).reduce(), 32, size, size, 10);
grid.fillLayer(new Tile({
top: Assets.Colors.Gray,
right: Assets.Colors.Gray,
left: Assets.Colors.Gray,
}), 0);
grid.fillLayer(new Tile({
top: Assets.Colors.Gray,
right: Assets.Colors.Gray,
left: Assets.Colors.Gray,
}), 1);
grid.fillLayer(new Tile({
top: Sprite.id(0),
right: Assets.Colors.Brown,
left: Assets.Colors.Brown,
}), 2);
for (let i = 0; i <= size / 2; i++) {
tree(grid, new Vec3(Math.floor(Math.random() * size - 1), Math.floor(Math.random() * size - 1), 2));
}
const input = new Input();
addDefaultKeybinds(input, camera);
let prevTimestamp = 0;
const frame = (timestamp: number) => {
const deltaTime = (timestamp - prevTimestamp)/1000;
prevTimestamp = timestamp;
fullscreenCanvas(gfx, "game");
draw(gfx, camera, deltaTime, grid);
window.requestAnimationFrame(frame);
}
window.requestAnimationFrame((timestamp) => {
prevTimestamp = timestamp;
window.requestAnimationFrame(frame);
});
const wasmgl: wasm.WASMGL = new wasm.WASMGL(await wasm.loadWasmModule("./src/wasm/module.wasm"));
})();

View File

@ -1,43 +0,0 @@
export async function loadWasmModule(path) {
return WebAssembly.instantiateStreaming(fetch(path));
}
export class WASMGL {
exports;
mem;
constructor(wasm) {
this.exports = wasm.instance.exports;
const memSize = new Float32Array(this.exports.memory.buffer, this.exports.WASMGLmemory.value + 4, 1).at(0);
this.mem = new Float32Array(this.exports.memory.buffer, this.exports.WASMGLmemory.value, memSize);
}
alloc(size) {
const head = this.mem[2];
this.mem[2] += size;
return head;
}
setstr(ptr, str) {
for (let i = 0; i < str.length; i++) {
this.mem[ptr] = str.charCodeAt(i);
++ptr;
}
}
set(ptr, data) {
data.forEach((v, i) => {
this.mem[ptr + i] = v;
});
return ptr;
}
push(data) {
let ptr = this.alloc(data.length);
data.forEach((v, i) => {
this.mem[ptr + i] = v;
});
return ptr;
}
get(ptr, length) {
let a = new Array(length);
for (let i = 0; i < length; ++i) {
a[i] = this.mem[ptr + i];
}
return a;
}
}

View File

@ -1,71 +0,0 @@
export interface WASMGLEnvironment {
exports: any,
mem: Float32Array,
}
export async function loadWasmModule(path: string): Promise<WebAssembly.WebAssemblyInstantiatedSource> {
return WebAssembly.instantiateStreaming(fetch(path));
}
export class WASMGL {
exports: any;
mem: Float32Array;
constructor(wasm: WebAssembly.WebAssemblyInstantiatedSource) {
this.exports = wasm.instance.exports;
const memSize = new Float32Array(
this.exports.memory.buffer,
this.exports.WASMGLmemory.value + 4,
1
).at(0);
this.mem = new Float32Array(
this.exports.memory.buffer,
this.exports.WASMGLmemory.value,
memSize
);
}
alloc(size: number): number {
const head = this.mem[2];
this.mem[2] += size;
return head;
}
setstr(ptr: number, str: string) {
for (let i = 0; i < str.length; i++) {
this.mem[ptr] = str.charCodeAt(i);
++ptr;
}
}
set(ptr: number, data: number[]): number {
data.forEach((v, i) => {
this.mem[ptr + i] = v;
});
return ptr;
}
push(data: number[]): number {
let ptr = this.alloc(data.length);
data.forEach((v, i) => {
this.mem[ptr + i] = v;
});
return ptr;
}
get(ptr: number, length: number): Array<number> {
let a = new Array(length);
for (let i = 0; i < length; ++i) {
a[i] = this.mem[ptr + i];
}
return a;
}
}

View File

@ -1,106 +0,0 @@
import { Vec3 } from "./common.js";
import { Sprite } from "./graphics.js";
export var TileEdge;
(function (TileEdge) {
TileEdge[TileEdge["None"] = 0] = "None";
TileEdge[TileEdge["Left"] = 1] = "Left";
TileEdge[TileEdge["Right"] = 2] = "Right";
TileEdge[TileEdge["Both"] = 3] = "Both";
})(TileEdge || (TileEdge = {}));
export class Tile {
fill = {
top: [1, 0, 1, 1],
left: [0, 0, 0, 1],
right: [0, 0, 0, 1],
};
edge = TileEdge.None;
constructor(fill, edge) {
if (fill !== undefined)
this.fill = fill;
if (edge !== undefined)
this.edge = edge;
}
}
export function ColorToTile(c) {
return {
left: c,
top: c,
right: c,
};
}
export class Grid {
position;
tiles3d;
tileSize;
width;
breadth;
height;
topHeight;
constructor(position, tileSize, width, breadth, height) {
this.tiles3d = new Array(height);
this.position = position;
this.tileSize = tileSize;
this.width = width;
this.breadth = breadth;
this.height = height;
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.breadth; ++j) {
this.tiles3d[z][i + j * this.width] = { ...tile };
}
}
for (let i = 0; i < this.width; ++i) {
this.tiles3d[z][this.breadth * i - 1] = { ...tile, edge: TileEdge.Right };
}
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.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;
let tile = this.tiles3d[coord.z][index];
if (tile === undefined)
return null;
return tile;
}
}
export function tree(grid, position) {
let log = Sprite.tile(2);
let leaves = Sprite.tile(1);
grid.setTile(new Tile(log, TileEdge.Both), new Vec3(position.x, position.y, position.z + 1));
grid.setTile(new Tile(log, TileEdge.Both), new Vec3(position.x, position.y, position.z + 2));
grid.setTile(new Tile(log, TileEdge.Both), new Vec3(position.x, position.y, position.z + 3));
grid.setTile(new Tile(log, TileEdge.Both), new Vec3(position.x, position.y, position.z + 4));
grid.setTile(new Tile(log, TileEdge.None), new Vec3(position.x, position.y, position.z + 4));
grid.setTile(new Tile(leaves, TileEdge.Both), new Vec3(position.x, position.y + 1, position.z + 4));
grid.setTile(new Tile(leaves, TileEdge.Both), new Vec3(position.x, position.y + 1, position.z + 5));
grid.setTile(new Tile(leaves, TileEdge.Both), new Vec3(position.x, position.y, position.z + 5));
grid.setTile(new Tile(leaves, TileEdge.Both), new Vec3(position.x + 1, position.y, position.z + 4));
grid.setTile(new Tile(leaves, TileEdge.Both), new Vec3(position.x + 1, position.y, position.z + 5));
grid.setTile(new Tile(leaves, TileEdge.Both), new Vec3(position.x + 1, position.y + 1, position.z + 4));
grid.setTile(new Tile(leaves, TileEdge.Both), new Vec3(position.x + 1, position.y + 1, position.z + 5));
}
export function bush(grid, position) {
let leaves = Sprite.tile(1);
grid.setTile(new Tile(leaves, TileEdge.Both), position.extend(1));
grid.setTile(new Tile(leaves, TileEdge.Both), new Vec3(position.x + 1, position.y, 1));
grid.setTile(new Tile(leaves, TileEdge.Both), new Vec3(position.x, position.y + 1, 1));
grid.setTile(new Tile(leaves, TileEdge.Both), new Vec3(position.x + 1, position.y + 1, 1));
grid.setTile(new Tile(leaves, TileEdge.Both), position.extend(2));
grid.setTile(new Tile(leaves, TileEdge.Both), new Vec3(position.x + 1, position.y, 2));
grid.setTile(new Tile(leaves, TileEdge.Both), new Vec3(position.x, position.y + 1, 2));
}

View File

@ -1,141 +0,0 @@
import * as Assets from "./assets.js";
import {Vec2, Vec3} from "./common.js";
import {Sprite} from "./graphics.js";
export enum TileEdge {
None,
Left,
Right,
Both,
}
export type TileFillament = Sprite | Assets.Color;
export type TileFill = {
right: TileFillament
left: TileFillament,
top: TileFillament,
};
export class Tile {
fill: TileFill = {
top : [1, 0, 1, 1],
left : [0, 0, 0, 1],
right : [0, 0, 0, 1],
};
edge: TileEdge = TileEdge.None;
constructor(fill?: TileFill, edge?: TileEdge) {
if (fill !== undefined)
this.fill = fill;
if (edge !== undefined)
this.edge = edge;
}
}
export function ColorToTile(c: Assets.Color) {
return {
left: c,
top: c,
right: c,
};
}
export class Grid {
position: Vec3;
tiles3d: Tile[][];
tileSize: number;
width: number;
breadth: 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.breadth = breadth;
this.height = height;
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.breadth; ++j) {
this.tiles3d[z][i + j * this.width] = {...tile};
}
}
for (let i = 0; i < this.width; ++i) {
this.tiles3d[z][this.breadth * i - 1] = {...tile, edge: TileEdge.Right};
}
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.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 {
let index = coord.x + coord.y * this.width;
let tile = this.tiles3d[coord.z][index];
if (tile === undefined) return null;
return tile;
}
}
export function tree(grid: Grid, position: Vec3) {
let log = Sprite.tile(2);
let leaves = Sprite.tile(1);
grid.setTile(new Tile(log, TileEdge.Both), new Vec3(position.x, position.y, position.z + 1));
grid.setTile(new Tile(log, TileEdge.Both), new Vec3(position.x, position.y, position.z + 2));
grid.setTile(new Tile(log, TileEdge.Both), new Vec3(position.x, position.y, position.z + 3));
grid.setTile(new Tile(log, TileEdge.Both), new Vec3(position.x, position.y, position.z + 4));
grid.setTile(new Tile(log, TileEdge.None), new Vec3(position.x, position.y, position.z + 4));
grid.setTile(new Tile(leaves, TileEdge.Both), new Vec3(position.x, position.y + 1, position.z + 4));
grid.setTile(new Tile(leaves, TileEdge.Both), new Vec3(position.x, position.y + 1, position.z + 5));
grid.setTile(new Tile(leaves, TileEdge.Both), new Vec3(position.x, position.y, position.z + 5));
grid.setTile(new Tile(leaves, TileEdge.Both), new Vec3(position.x + 1, position.y, position.z + 4));
grid.setTile(new Tile(leaves, TileEdge.Both), new Vec3(position.x + 1, position.y, position.z + 5));
grid.setTile(new Tile(leaves, TileEdge.Both), new Vec3(position.x + 1, position.y + 1, position.z + 4));
grid.setTile(new Tile(leaves, TileEdge.Both), new Vec3(position.x + 1, position.y + 1, position.z + 5));
}
export function bush(grid: Grid, position: Vec2) {
let leaves = Sprite.tile(1);
grid.setTile(new Tile(leaves, TileEdge.Both), position.extend(1));
grid.setTile(new Tile(leaves, TileEdge.Both), new Vec3(position.x + 1, position.y, 1));
grid.setTile(new Tile(leaves, TileEdge.Both), new Vec3(position.x, position.y + 1, 1));
grid.setTile(new Tile(leaves, TileEdge.Both), new Vec3(position.x + 1, position.y + 1, 1));
grid.setTile(new Tile(leaves, TileEdge.Both), position.extend(2));
grid.setTile(new Tile(leaves, TileEdge.Both), new Vec3(position.x + 1, position.y, 2));
grid.setTile(new Tile(leaves, TileEdge.Both), new Vec3(position.x, position.y + 1, 2));
}

View File

@ -1,10 +0,0 @@
#!/bin/bash
set -xe
CC="clang"
CLIBS=""
CFLAGS="--target=wasm32 -flto -Wl,--lto-O3 -nostdlib -Wl,--no-entry -Wl,--export-all"
$CC $CFLAGS -o module.wasm *.c $CLIBS

View File

@ -1,12 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>aaa</title>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<meta name="description" content="" />
</head>
<body>
<script src="test.js"></script>
</body>
</html>

View File

@ -1,20 +0,0 @@
#include "wasmgl.h"
void doubel(WASMGLvalue(data))
{
for (int i = 0; i < size_data; ++i)
WASMGLset(ptr_data + i, WASMGLmemory[ptr_data + i] * 2.0);
}
void uppercase(WASMGLvalue(data)) {
int val = 0;
for (int i = 0; i < size_data; ++i)
{
val = WASMGLmemory[ptr_data + i] - 32;
if (val < 65 || val > 122) val = WASMGLmemory[ptr_data + i];
WASMGLset(ptr_data + i, val);
}
}

Binary file not shown.

View File

@ -1,34 +0,0 @@
(async () => {
let instance = await WebAssembly.instantiateStreaming(fetch("module.wasm"));
const memSize = new Int32Array(
instance.instance.exports.memory.buffer,
instance.instance.exports.WASMGLmemory.value + 4,
1
)[0]
let mem = new Int32Array(
instance.instance.exports.memory.buffer,
instance.instance.exports.WASMGLmemory.value,
memSize
);
let ptr = alloc(mem, 4);
setstr(mem, ptr, 4, "game");
console.log(String.fromCharCode(instance.instance.exports.initialize(ptr, 4)));
})();
function alloc(mem, size) {
const head = mem[2];
console.log(head);
mem[2] += size;
return head;
}
function setstr(mem, ptr, size, str) {
for (let i = 0; i < size; i++) {
mem[ptr] = str.charCodeAt(i);
++ptr;
}
}

View File

@ -1,26 +0,0 @@
#include "wasmgl.h"
float WASMGLmemory[WASMGLmemory_size] = {
[MEM_SIZE] = WASMGLmemory_size,
[MEM_HEAD] = MEM_ELEM_COUNT,
0,
};
WASMGLptr WASMGLmalloc(WASMGLsize size)
{
WASMGLptr ptr = WASMGLmemory[MEM_HEAD];
WASMGLmemory[MEM_HEAD] += size;
return ptr;
}
void WASMGLset(WASMGLptr ptr, int value)
{
WASMGLmemory[ptr] = value;
}
void WASMGLsetstr(WASMGLptr ptr, const char * cstr, WASMGLsize size)
{
for (int i = 0; i < size; ++i)
WASMGLset(ptr + i, cstr[i]);
}

View File

@ -1,25 +0,0 @@
#ifndef WASMGL_H_
#define WASMGL_H_
#define main WASMGLmain
#define WASMGLmemory_size (1024 * 1024)
typedef enum {
MEM_NULL = 0,
MEM_SIZE = 1,
MEM_HEAD,
MEM_ELEM_COUNT,
} WASMGLmemory_layout;
extern float WASMGLmemory[WASMGLmemory_size];
typedef unsigned int WASMGLptr;
typedef unsigned int WASMGLsize;
#define WASMGLvalue(name) WASMGLptr ptr_##name, WASMGLsize size_##name
WASMGLptr WASMGLmalloc(WASMGLsize size);
void WASMGLset(WASMGLptr ptr, int value);
void WASMGLsetstr(WASMGLptr ptr, const char * cstr, WASMGLsize size);
#endif // WASMGL_H_