Compare commits
11 Commits
master
...
instancing
Author | SHA1 | Date |
---|---|---|
|
87ad6fbd9d | |
|
912487d264 | |
|
14d5db166e | |
|
e936b626f3 | |
|
95257a4ef3 | |
|
2a1fe1d0a9 | |
|
bdde494b5b | |
|
ab532a2608 | |
|
2c81bc0684 | |
|
129cf05b90 | |
|
3e9bdcc469 |
After Width: | Height: | Size: 1.0 KiB |
|
@ -0,0 +1,3 @@
|
|||
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
|
After Width: | Height: | Size: 90 KiB |
After Width: | Height: | Size: 114 KiB |
After Width: | Height: | Size: 67 KiB |
After Width: | Height: | Size: 105 KiB |
After Width: | Height: | Size: 132 KiB |
After Width: | Height: | Size: 79 KiB |
After Width: | Height: | Size: 109 KiB |
After Width: | Height: | Size: 137 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 368 B |
After Width: | Height: | Size: 511 B |
After Width: | Height: | Size: 411 B |
After Width: | Height: | Size: 337 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 4.2 KiB |
74
common.js
|
@ -1,74 +0,0 @@
|
|||
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
|
@ -1,96 +0,0 @@
|
|||
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
|
@ -1,92 +0,0 @@
|
|||
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
|
@ -1,116 +0,0 @@
|
|||
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
|
@ -1,110 +0,0 @@
|
|||
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
|
@ -1,148 +0,0 @@
|
|||
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 }
|
|
@ -6,7 +6,7 @@
|
|||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<meta name="description" content="" />
|
||||
<link rel="stylesheet" href="style.css"></link>
|
||||
<script type="module" src="script.js" defer> var exports = {}; </script>
|
||||
<script type="module" src="src/js/script.js" defer> var exports = {}; </script>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="game"></canvas>
|
||||
|
|
77
script.js
|
@ -1,77 +0,0 @@
|
|||
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
|
@ -1,98 +0,0 @@
|
|||
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);
|
||||
});
|
||||
})();
|
|
@ -0,0 +1,36 @@
|
|||
import { Texture } 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],
|
||||
};
|
||||
export function AssetToTileFill(name) {
|
||||
let asset = assets.get(name);
|
||||
return {
|
||||
left: asset,
|
||||
top: asset,
|
||||
right: asset,
|
||||
};
|
||||
}
|
||||
export class Assets {
|
||||
assets = new Map();
|
||||
loaded = false;
|
||||
push(name, asset) {
|
||||
if (this.assets.get(name) !== undefined)
|
||||
throw new Error("Asset name occupied!");
|
||||
this.assets.set(name, asset);
|
||||
}
|
||||
get(name) {
|
||||
if (!this.loaded)
|
||||
throw new Error("Tried to assess assets without loading them!");
|
||||
return this.assets.get(name);
|
||||
}
|
||||
async load(gfx) {
|
||||
assets.push("grass", await Texture.load(gfx, "../../assets/grass2.png"));
|
||||
assets.push("leaves", await Texture.load(gfx, "../../assets/greenary.png"));
|
||||
assets.push("log", await Texture.load(gfx, "../../assets/log.png"));
|
||||
this.loaded = true;
|
||||
}
|
||||
}
|
||||
export const assets = new Assets();
|
|
@ -0,0 +1,52 @@
|
|||
import {Graphics, Texture} from "./graphics.js";
|
||||
import {TileFill} from "./world.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,
|
||||
}
|
||||
|
||||
export type Asset = Texture;
|
||||
|
||||
export function AssetToTileFill(name: string): TileFill {
|
||||
let asset = assets.get(name);
|
||||
|
||||
return {
|
||||
left: asset,
|
||||
top: asset,
|
||||
right: asset,
|
||||
};
|
||||
}
|
||||
|
||||
export class Assets {
|
||||
assets: Map<string, Asset> = new Map();
|
||||
loaded: boolean = false;
|
||||
|
||||
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 {
|
||||
if (!this.loaded)
|
||||
throw new Error("Tried to assess assets without loading them!");
|
||||
|
||||
return this.assets.get(name)!;
|
||||
}
|
||||
|
||||
async load(gfx: Graphics) {
|
||||
assets.push("grass", await Texture.load(gfx, "../../assets/grass2.png"));
|
||||
assets.push("leaves", await Texture.load(gfx, "../../assets/greenary.png"));
|
||||
assets.push("log", await Texture.load(gfx, "../../assets/log.png"));
|
||||
|
||||
this.loaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
export const assets = new Assets();
|
|
@ -0,0 +1,412 @@
|
|||
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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
class Vec3 {
|
||||
x;
|
||||
y;
|
||||
z;
|
||||
constructor(x, y, z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
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];
|
||||
}
|
||||
}
|
||||
class Vec4 {
|
||||
x;
|
||||
y;
|
||||
z;
|
||||
w;
|
||||
constructor(x, y, z, w) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.w = w;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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");
|
||||
}
|
||||
}
|
||||
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.x(1) + v.z * this.x(2) + v.w * this.x(3);
|
||||
let y = v.x * this.y(0) + v.y * this.y(1) + v.z * this.y(2) + v.w * this.y(3);
|
||||
let z = v.x * this.z(0) + v.y * this.z(1) + v.z * this.z(2) + v.w * this.z(3);
|
||||
let w = v.x * this.w(0) + v.y * this.w(1) + v.z * this.w(2) + 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.x(1) + v.z * this.x(2) + v.w * this.x(3);
|
||||
let y = v.x * this.y(0) + v.y * this.y(1) + v.z * this.y(2) + v.w * this.y(3);
|
||||
let z = v.x * this.z(0) + v.y * this.z(1) + v.z * this.z(2) + v.w * this.z(3);
|
||||
let w = v.x * this.w(0) + v.y * this.w(1) + v.z * this.w(2) + 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
export { initializeContext, Mat4, Vec2, Vec3, Vec4 };
|
|
@ -0,0 +1,555 @@
|
|||
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;
|
||||
|
||||
add(other: T): void;
|
||||
addNew(other: T): 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;
|
||||
}
|
||||
|
||||
class Vec2 implements Vector<void, Vec2, Vec3> {
|
||||
x: number;
|
||||
y: number;
|
||||
|
||||
constructor(x: number, y: number) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
type Mat4Row = 0 | 1 | 2 | 3
|
||||
type Mat4Col = Mat4Row
|
||||
type Mat4X = Mat4Row
|
||||
type Mat4Y = Mat4Row
|
||||
type Mat4Z = Mat4Row
|
||||
type Mat4W = Mat4Row
|
||||
|
||||
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.x(1) + v.z * this.x(2) + v.w * this.x(3);
|
||||
let y = v.x * this.y(0) + v.y * this.y(1) + v.z * this.y(2) + v.w * this.y(3);
|
||||
let z = v.x * this.z(0) + v.y * this.z(1) + v.z * this.z(2) + v.w * this.z(3);
|
||||
let w = v.x * this.w(0) + v.y * this.w(1) + v.z * this.w(2) + 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.x(1) + v.z * this.x(2) + v.w * this.x(3);
|
||||
let y = v.x * this.y(0) + v.y * this.y(1) + v.z * this.y(2) + v.w * this.y(3);
|
||||
let z = v.x * this.z(0) + v.y * this.z(1) + v.z * this.z(2) + v.w * this.z(3);
|
||||
let w = v.x * this.w(0) + v.y * this.w(1) + v.z * this.w(2) + 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { initializeContext, Mat4, Vec2, Vec3, Vec4 };
|
|
@ -0,0 +1,159 @@
|
|||
import { Vec3 } from "./common.js";
|
||||
import { Texture, DrawTag } from "./graphics.js";
|
||||
import { TileEdge } from "./world.js";
|
||||
// TODO: Don't assume tile size is same in all directions
|
||||
function cull(point, screen, tileSize) {
|
||||
if (point.x + tileSize < screen.x)
|
||||
return true;
|
||||
if (point.x + tileSize > screen.x + screen.z)
|
||||
return true;
|
||||
if (point.y - tileSize < screen.y)
|
||||
return true;
|
||||
if (point.y + tileSize > screen.y + screen.w)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
// Attrib format
|
||||
// position color uv
|
||||
// (3) (4) (2) => 3 + 4 + 2 = 9 <=> data.len % 9 == 0
|
||||
export class Rectangle {
|
||||
fill;
|
||||
attribs = ["a_position", "a_color", "a_tex_position"];
|
||||
tags = [];
|
||||
data = [];
|
||||
stride = 0;
|
||||
vertexStride = 0;
|
||||
constructor(fill, tags, attribs) {
|
||||
this.fill = fill;
|
||||
if (attribs !== undefined) {
|
||||
this.attribs = attribs;
|
||||
}
|
||||
if (tags !== undefined) {
|
||||
this.tags = tags;
|
||||
}
|
||||
}
|
||||
overrideDraw(draw) {
|
||||
this.draw = draw;
|
||||
}
|
||||
draw(gfx, corners) {
|
||||
if (this.fill instanceof Texture) {
|
||||
this.data = [
|
||||
corners[0].x, corners[0].y, corners[0].z, 0, 0, 0, 0, 0, 0,
|
||||
corners[1].x, corners[1].y, corners[1].z, 0, 0, 0, 0, -1, 0,
|
||||
corners[3].x, corners[3].y, corners[3].z, 0, 0, 0, 0, 0, -1,
|
||||
corners[2].x, corners[2].y, corners[2].z, 0, 0, 0, 0, -1, -1,
|
||||
corners[1].x, corners[1].y, corners[1].z, 0, 0, 0, 0, -1, 0,
|
||||
corners[3].x, corners[3].y, corners[3].z, 0, 0, 0, 0, 0, -1,
|
||||
];
|
||||
}
|
||||
else {
|
||||
this.data = [
|
||||
corners[0].x, corners[0].y, corners[0].z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0,
|
||||
corners[1].x, corners[1].y, corners[1].z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0,
|
||||
corners[3].x, corners[3].y, corners[3].z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0,
|
||||
corners[2].x, corners[2].y, corners[2].z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0,
|
||||
corners[1].x, corners[1].y, corners[1].z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0,
|
||||
corners[3].x, corners[3].y, corners[3].z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0,
|
||||
];
|
||||
}
|
||||
this.vertexStride = 9;
|
||||
this.stride = this.vertexStride * 4;
|
||||
gfx.toRender.push({ ...this });
|
||||
}
|
||||
drawExts(gfx, position, exts) {
|
||||
if (this.fill instanceof Texture) {
|
||||
this.data = [
|
||||
position.x, position.y, position.z, 0, 0, 0, 0, 0, 0,
|
||||
position.x + exts.x, position.y, position.z, 0, 0, 0, 0, -1, 0,
|
||||
position.x, position.y + exts.y, position.z, 0, 0, 0, 0, 0, -1,
|
||||
position.x + exts.x, position.y + exts.y, position.z, 0, 0, 0, 0, -1, -1,
|
||||
position.x + exts.x, position.y, position.z, 0, 0, 0, 0, -1, 0,
|
||||
position.x, position.y + exts.y, position.z, 0, 0, 0, 0, 0, -1,
|
||||
];
|
||||
}
|
||||
else {
|
||||
this.data = [
|
||||
position.x, position.y, position.z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0,
|
||||
position.x + exts.x, position.y, position.z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0,
|
||||
position.x, position.y + exts.y, position.z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0,
|
||||
position.x + exts.x, position.y + exts.y, position.z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0,
|
||||
position.x + exts.x, position.y, position.z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0,
|
||||
position.x, position.y + exts.y, position.z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0,
|
||||
];
|
||||
}
|
||||
this.vertexStride = 9;
|
||||
this.stride = this.vertexStride * 4;
|
||||
gfx.toRender.push(this);
|
||||
}
|
||||
}
|
||||
export function drawIsometricCube(gfx, position, exts, color, 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),
|
||||
];
|
||||
let r = new Rectangle(color.top, [
|
||||
DrawTag.ISO,
|
||||
]);
|
||||
// Top
|
||||
r.draw(gfx, [
|
||||
points[0],
|
||||
points[1],
|
||||
points[3],
|
||||
points[2],
|
||||
]);
|
||||
// Right Edge
|
||||
if (edge == TileEdge.Right || edge == TileEdge.Both) {
|
||||
r.fill = color.right;
|
||||
r.draw(gfx, [
|
||||
points[3],
|
||||
points[2],
|
||||
points[4],
|
||||
points[5],
|
||||
]);
|
||||
}
|
||||
// Left Edge
|
||||
if (edge == TileEdge.Left || edge == TileEdge.Both) {
|
||||
r.fill = color.left;
|
||||
r.draw(gfx, [
|
||||
points[0],
|
||||
points[2],
|
||||
points[4],
|
||||
points[6],
|
||||
]);
|
||||
}
|
||||
}
|
||||
export function drawIsometricGrid(gfx, grid) {
|
||||
let position = { ...grid.position };
|
||||
let exts = new Vec3(grid.tileSize, grid.tileSize, 0);
|
||||
let tileCoord = new Vec3(0, 0, 0);
|
||||
// TODO: Optimize this
|
||||
for (let k = 0; k < grid.height; ++k) {
|
||||
for (let j = 0; j < grid.length; ++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;
|
||||
}
|
||||
drawIsometricCube(gfx, new Vec3(position.x - grid.tileSize / 2 * k, position.y + grid.tileSize / 2 * k, position.z), exts, tile.fill, tile.edge);
|
||||
position.x += grid.tileSize;
|
||||
}
|
||||
position.y -= grid.tileSize;
|
||||
position.x = grid.position.x;
|
||||
}
|
||||
position.y = grid.position.y;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
import { Vec3, Vec2, Vec4 } from "./common.js"
|
||||
import { Graphics, Texture, Drawable, DrawTag } from "./graphics.js";
|
||||
import { TileEdge, TileFill, Grid } from "./world.js";
|
||||
import * as Assets from "./assets.js";
|
||||
|
||||
// TODO: Don't assume tile size is same in all directions
|
||||
function cull(point: Vec3, screen: Vec4, tileSize: number): boolean {
|
||||
if (point.x + tileSize < screen.x)
|
||||
return true;
|
||||
|
||||
if (point.x + tileSize > screen.x + screen.z)
|
||||
return true;
|
||||
|
||||
if (point.y - tileSize < screen.y)
|
||||
return true;
|
||||
|
||||
if (point.y + tileSize > screen.y + screen.w)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attrib format
|
||||
// position color uv
|
||||
// (3) (4) (2) => 3 + 4 + 2 = 9 <=> data.len % 9 == 0
|
||||
|
||||
export class Rectangle implements Drawable {
|
||||
fill: Texture | Assets.Color;
|
||||
attribs: string[] = ["a_position", "a_color", "a_tex_position"];
|
||||
tags: Array<DrawTag> = [];
|
||||
|
||||
data: number[] = [];
|
||||
stride: number = 0;
|
||||
vertexStride: number = 0;
|
||||
|
||||
constructor(fill: Texture | Assets.Color, tags?: Array<DrawTag>, attribs?: string[]) {
|
||||
this.fill = fill;
|
||||
|
||||
if (attribs !== undefined) {
|
||||
this.attribs = attribs;
|
||||
}
|
||||
|
||||
if (tags !== undefined) {
|
||||
this.tags = tags;
|
||||
}
|
||||
}
|
||||
|
||||
overrideDraw(draw: (...args: any[]) => void) {
|
||||
this.draw = draw;
|
||||
}
|
||||
|
||||
draw(gfx: Graphics, corners: [Vec3, Vec3, Vec3, Vec3]) {
|
||||
if (this.fill instanceof Texture) {
|
||||
this.data = [
|
||||
corners[0].x, corners[0].y, corners[0].z, 0, 0, 0, 0, 0, 0,
|
||||
corners[1].x, corners[1].y, corners[1].z, 0, 0, 0, 0, -1, 0,
|
||||
corners[3].x, corners[3].y, corners[3].z, 0, 0, 0, 0, 0, -1,
|
||||
|
||||
corners[2].x, corners[2].y, corners[2].z, 0, 0, 0, 0, -1, -1,
|
||||
corners[1].x, corners[1].y, corners[1].z, 0, 0, 0, 0, -1, 0,
|
||||
corners[3].x, corners[3].y, corners[3].z, 0, 0, 0, 0, 0, -1,
|
||||
]
|
||||
} else {
|
||||
this.data = [
|
||||
corners[0].x, corners[0].y, corners[0].z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0,
|
||||
corners[1].x, corners[1].y, corners[1].z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0,
|
||||
corners[3].x, corners[3].y, corners[3].z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0,
|
||||
|
||||
corners[2].x, corners[2].y, corners[2].z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0,
|
||||
corners[1].x, corners[1].y, corners[1].z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0,
|
||||
corners[3].x, corners[3].y, corners[3].z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0,
|
||||
]
|
||||
}
|
||||
|
||||
this.vertexStride = 9;
|
||||
this.stride = this.vertexStride * 4
|
||||
gfx.toRender.push({...this});
|
||||
}
|
||||
|
||||
drawExts(gfx: Graphics, position: Vec3, exts: Vec2) {
|
||||
if (this.fill instanceof Texture) {
|
||||
this.data = [
|
||||
position.x, position.y, position.z, 0, 0, 0, 0, 0, 0,
|
||||
position.x + exts.x, position.y, position.z, 0, 0, 0, 0, -1, 0,
|
||||
position.x, position.y + exts.y, position.z, 0, 0, 0, 0, 0, -1,
|
||||
|
||||
position.x + exts.x, position.y + exts.y, position.z, 0, 0, 0, 0, -1, -1,
|
||||
position.x + exts.x, position.y, position.z, 0, 0, 0, 0, -1, 0,
|
||||
position.x, position.y + exts.y, position.z, 0, 0, 0, 0, 0, -1,
|
||||
]
|
||||
} else {
|
||||
this.data = [
|
||||
position.x, position.y, position.z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0,
|
||||
position.x + exts.x, position.y, position.z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0,
|
||||
position.x, position.y + exts.y, position.z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0,
|
||||
|
||||
position.x + exts.x, position.y + exts.y, position.z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0,
|
||||
position.x + exts.x, position.y, position.z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0,
|
||||
position.x, position.y + exts.y, position.z, this.fill[0], this.fill[1], this.fill[2], this.fill[3], 0, 0,
|
||||
]
|
||||
}
|
||||
|
||||
this.vertexStride = 9;
|
||||
this.stride = this.vertexStride * 4
|
||||
gfx.toRender.push(this);
|
||||
}
|
||||
}
|
||||
|
||||
export function drawIsometricCube(gfx: Graphics, position: Vec3, exts: Vec3, color: 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),
|
||||
];
|
||||
|
||||
let r = new Rectangle(color.top, [
|
||||
DrawTag.ISO,
|
||||
]);
|
||||
|
||||
// Top
|
||||
r.draw(
|
||||
gfx,
|
||||
[
|
||||
points[0],
|
||||
points[1],
|
||||
points[3],
|
||||
points[2],
|
||||
],
|
||||
);
|
||||
|
||||
// Right Edge
|
||||
|
||||
if (edge == TileEdge.Right || edge == TileEdge.Both) {
|
||||
r.fill = color.right;
|
||||
|
||||
r.draw(
|
||||
gfx,
|
||||
[
|
||||
points[3],
|
||||
points[2],
|
||||
points[4],
|
||||
points[5],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// Left Edge
|
||||
if (edge == TileEdge.Left || edge == TileEdge.Both) {
|
||||
r.fill = color.left;
|
||||
|
||||
r.draw(
|
||||
gfx,
|
||||
[
|
||||
points[0],
|
||||
points[2],
|
||||
points[4],
|
||||
points[6],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function drawIsometricGrid(gfx: Graphics, grid: Grid) {
|
||||
let position = {...grid.position} as Vec3;
|
||||
let exts = new Vec3(grid.tileSize, grid.tileSize, 0);
|
||||
let tileCoord = new Vec3(0, 0, 0);
|
||||
|
||||
// TODO: Optimize this
|
||||
for (let k = 0; k < grid.height; ++k) {
|
||||
for (let j = 0; j < grid.length; ++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;
|
||||
}
|
||||
|
||||
drawIsometricCube(gfx, new Vec3(position.x - grid.tileSize / 2 * k, position.y + grid.tileSize / 2 * k, position.z), exts, tile.fill, tile.edge);
|
||||
|
||||
position.x += grid.tileSize;
|
||||
}
|
||||
position.y -= grid.tileSize;
|
||||
position.x = grid.position.x;
|
||||
}
|
||||
position.y = grid.position.y;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,220 @@
|
|||
import { 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;
|
||||
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) {
|
||||
this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.vbo);
|
||||
this.ctx.bufferData(this.ctx.ARRAY_BUFFER, new Float32Array(o.data), this.ctx.STATIC_DRAW);
|
||||
let aid = 0;
|
||||
for (let a of o.attribs) {
|
||||
let attr = this.getAttribute(a);
|
||||
if (!attr.formatted)
|
||||
throw new Error("Tried to use unformatted attribute!");
|
||||
this.ctx.enableVertexAttribArray(attr.loc);
|
||||
this.ctx.vertexAttribPointer(attr.loc, attr.size, attr.type, attr.normalized, o.stride, aid * 4);
|
||||
aid += attr.size;
|
||||
}
|
||||
// Generalize the tag uniforms aka. don't hard code them
|
||||
for (let t of o.tags) {
|
||||
switch (t) {
|
||||
case DrawTag.ISO: {
|
||||
this.ctx.uniform1ui(this.getUniform("u_isIso"), 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (o.fill instanceof Texture) {
|
||||
this.ctx.uniform1i(this.getUniform("u_isTex"), 1);
|
||||
o.fill.bind(this);
|
||||
}
|
||||
this.ctx.drawArrays(this.ctx.TRIANGLES, 0, o.data.length / o.vertexStride);
|
||||
if (o.fill instanceof Texture) {
|
||||
this.ctx.uniform1i(this.getUniform("u_isTex"), 0);
|
||||
o.fill.unbind(this);
|
||||
}
|
||||
for (let t of o.tags) {
|
||||
switch (t) {
|
||||
case DrawTag.ISO: {
|
||||
this.ctx.uniform1ui(this.getUniform("u_isIso"), 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: Maybe add persistent rendering?
|
||||
this.toRender = [];
|
||||
}
|
||||
}
|
||||
export class Attribute {
|
||||
loc;
|
||||
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);
|
||||
}
|
||||
}
|
||||
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 = 0.0;
|
||||
scaleSpeed = 1.5;
|
||||
constructor(position) {
|
||||
this.position = position;
|
||||
this.movement = new Vec4(0, 0, 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 * this.dt * this.scaleSpeed;
|
||||
if (this.scale < 0.5) {
|
||||
this.scale = 0.5;
|
||||
}
|
||||
if (this.scale > 1.5) {
|
||||
this.scale = 1.5;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,298 @@
|
|||
import * as Assets from "./assets.js";
|
||||
import {Vec2, Vec3, Vec4} from "./common.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 {
|
||||
fill: Texture | Assets.Color;
|
||||
attribs: string[];
|
||||
tags: Array<DrawTag>;
|
||||
|
||||
data: Array<number>;
|
||||
vertexStride: number;
|
||||
stride: number;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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) {
|
||||
this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.vbo);
|
||||
this.ctx.bufferData(this.ctx.ARRAY_BUFFER, new Float32Array(o.data), this.ctx.STATIC_DRAW);
|
||||
|
||||
let aid = 0;
|
||||
for (let a of o.attribs) {
|
||||
let attr = this.getAttribute(a);
|
||||
if (!attr.formatted)
|
||||
throw new Error("Tried to use unformatted attribute!");
|
||||
|
||||
this.ctx.enableVertexAttribArray(attr.loc);
|
||||
this.ctx.vertexAttribPointer(attr.loc, attr.size, attr.type, attr.normalized, o.stride, aid * 4);
|
||||
aid += attr.size;
|
||||
}
|
||||
|
||||
// Generalize the tag uniforms aka. don't hard code them
|
||||
for (let t of o.tags) {
|
||||
switch (t) {
|
||||
case DrawTag.ISO: {
|
||||
this.ctx.uniform1ui(this.getUniform("u_isIso"), 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (o.fill instanceof Texture)
|
||||
{
|
||||
this.ctx.uniform1i(this.getUniform("u_isTex"), 1);
|
||||
o.fill.bind(this);
|
||||
}
|
||||
|
||||
this.ctx.drawArrays(this.ctx.TRIANGLES, 0, o.data.length / o.vertexStride);
|
||||
|
||||
if (o.fill instanceof Texture)
|
||||
{
|
||||
this.ctx.uniform1i(this.getUniform("u_isTex"), 0);
|
||||
o.fill.unbind(this);
|
||||
}
|
||||
|
||||
for (let t of o.tags) {
|
||||
switch (t) {
|
||||
case DrawTag.ISO: {
|
||||
this.ctx.uniform1ui(this.getUniform("u_isIso"), 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO: Maybe add persistent rendering?
|
||||
this.toRender = [];
|
||||
}
|
||||
}
|
||||
|
||||
export class Attribute {
|
||||
loc: GLint;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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: number = 0.0;
|
||||
scaleSpeed: number = 1.5;
|
||||
|
||||
constructor(position: Vec3) {
|
||||
this.position = position;
|
||||
this.movement = new Vec4(0, 0, 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 * this.dt * this.scaleSpeed;
|
||||
|
||||
if (this.scale < 0.5) {
|
||||
this.scale = 0.5;
|
||||
}
|
||||
|
||||
if (this.scale > 1.5) {
|
||||
this.scale = 1.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
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();
|
||||
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);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
//TODO: add modifier key support
|
||||
addKeyAction(key, modifiers, data, keydown, keyup) {
|
||||
this.handlers.set(key, new KeyAction(key, keydown, keyup, data));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
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();
|
||||
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);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//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));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
import { initializeContext, Vec3, Mat4, Vec4, Vec2 } from "./common.js";
|
||||
import { Graphics, fullscreenCanvas, Camera } from "./graphics.js";
|
||||
import * as drawing from "./draw.js";
|
||||
import * as wasm from "./wasm.js";
|
||||
import { Input } from "./input.js";
|
||||
import { bush, Grid, Tile, tree } from "./world.js";
|
||||
import * as Assets from "./assets.js";
|
||||
const vertexShader = `#version 300 es
|
||||
|
||||
layout(location = 0) in vec3 a_position;
|
||||
layout(location = 1) in vec2 a_tex_position;
|
||||
layout(location = 2) in vec4 a_color;
|
||||
|
||||
out vec4 v_color;
|
||||
out vec2 v_tex_position;
|
||||
|
||||
uniform mat4 u_matrix;
|
||||
uniform bool u_isIso;
|
||||
uniform bool u_isTex;
|
||||
|
||||
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;
|
||||
|
||||
if (u_isTex) {
|
||||
v_tex_position = a_tex_position;
|
||||
} else {
|
||||
v_color = a_color;
|
||||
}
|
||||
}
|
||||
`;
|
||||
const fragmentShader = `#version 300 es
|
||||
|
||||
precision highp float;
|
||||
|
||||
in vec4 v_color;
|
||||
in vec2 v_tex_position;
|
||||
out vec4 outColor;
|
||||
|
||||
uniform sampler2D u_texture;
|
||||
uniform bool u_isTex;
|
||||
|
||||
void main() {
|
||||
if (u_isTex) {
|
||||
outColor = texture(u_texture, v_tex_position);
|
||||
} 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, 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 = 0.0;
|
||||
}, (c) => {
|
||||
c.scaling = 1.0;
|
||||
});
|
||||
input.addKeyAction("KeyE", [], camera, (c) => {
|
||||
c.scaling = 0.0;
|
||||
}, (c) => {
|
||||
c.scaling = -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_position");
|
||||
a.format(2, ctx.FLOAT, false, 0);
|
||||
gfx.createUniform("u_matrix");
|
||||
gfx.createUniform("u_isIso");
|
||||
gfx.createUniform("u_isTex");
|
||||
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(), 24, size, size, 10);
|
||||
grid.fillLayer(new Tile({
|
||||
top: Assets.assets.get("grass"),
|
||||
right: Assets.Colors.Brown,
|
||||
left: Assets.Colors.Brown,
|
||||
}), 0);
|
||||
tree(grid, new Vec2(size / 2, size / 2));
|
||||
bush(grid, new Vec2(size / 2 + 4, size / 2 + 4));
|
||||
//for (let i = 0; i < 10; i++) {
|
||||
// tree(grid, new Vec2(Math.floor(Math.random() * size - 1), Math.floor(Math.random() * size - 1)));
|
||||
//}
|
||||
//grid.setTile(new Tile(Assets.AssetToTileFill("log"), TileEdge.Both), new Vec3(0, 29, 1));
|
||||
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 input = new Input();
|
||||
addDefaultKeybinds(input, camera);
|
||||
const wasmgl = new wasm.WASMGL(await wasm.loadWasmModule("./src/wasm/module.wasm"));
|
||||
})();
|
|
@ -0,0 +1,230 @@
|
|||
import { initializeContext, Vec3, Mat4, Vec4, Vec2 } from "./common.js";
|
||||
import { Graphics, fullscreenCanvas, Camera, DrawTag } from "./graphics.js";
|
||||
import * as drawing from "./draw.js";
|
||||
import * as wasm from "./wasm.js";
|
||||
import { Input } from "./input.js";
|
||||
import {bush, Grid, Tile, 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 vec2 a_tex_position;
|
||||
layout(location = 2) in vec4 a_color;
|
||||
|
||||
out vec4 v_color;
|
||||
out vec2 v_tex_position;
|
||||
|
||||
uniform mat4 u_matrix;
|
||||
uniform bool u_isIso;
|
||||
uniform bool u_isTex;
|
||||
|
||||
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;
|
||||
|
||||
if (u_isTex) {
|
||||
v_tex_position = a_tex_position;
|
||||
} else {
|
||||
v_color = a_color;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const fragmentShader =
|
||||
`#version 300 es
|
||||
|
||||
precision highp float;
|
||||
|
||||
in vec4 v_color;
|
||||
in vec2 v_tex_position;
|
||||
out vec4 outColor;
|
||||
|
||||
uniform sampler2D u_texture;
|
||||
uniform bool u_isTex;
|
||||
|
||||
void main() {
|
||||
if (u_isTex) {
|
||||
outColor = texture(u_texture, v_tex_position);
|
||||
} 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, 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 = 0.0;
|
||||
},
|
||||
(c) => {
|
||||
c.scaling = 1.0;
|
||||
});
|
||||
|
||||
input.addKeyAction("KeyE", [], camera,
|
||||
(c) => {
|
||||
c.scaling = 0.0;
|
||||
},
|
||||
(c) => {
|
||||
c.scaling = -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_position");
|
||||
a.format(2, ctx.FLOAT, false, 0)
|
||||
|
||||
gfx.createUniform("u_matrix");
|
||||
gfx.createUniform("u_isIso");
|
||||
gfx.createUniform("u_isTex");
|
||||
|
||||
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(), 24, size, size, 10);
|
||||
grid.fillLayer(new Tile({
|
||||
top: Assets.assets.get("grass"),
|
||||
right: Assets.Colors.Brown,
|
||||
left: Assets.Colors.Brown,
|
||||
}), 0);
|
||||
|
||||
tree(grid, new Vec2(size / 2, size / 2));
|
||||
bush(grid, new Vec2(size / 2 + 4, size / 2 + 4));
|
||||
//for (let i = 0; i < 10; i++) {
|
||||
// tree(grid, new Vec2(Math.floor(Math.random() * size - 1), Math.floor(Math.random() * size - 1)));
|
||||
//}
|
||||
|
||||
//grid.setTile(new Tile(Assets.AssetToTileFill("log"), TileEdge.Both), new Vec3(0, 29, 1));
|
||||
|
||||
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 input = new Input();
|
||||
addDefaultKeybinds(input, camera);
|
||||
|
||||
const wasmgl: wasm.WASMGL = new wasm.WASMGL(await wasm.loadWasmModule("./src/wasm/module.wasm"));
|
||||
})();
|
|
@ -0,0 +1,37 @@
|
|||
export class WASMGLvalue {
|
||||
ptr;
|
||||
size;
|
||||
constructor(ptr, size) {
|
||||
this.ptr = ptr;
|
||||
this.size = size;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
export class WASMGLvalue {
|
||||
ptr: number;
|
||||
size: number;
|
||||
|
||||
constructor(ptr: number, size: number) {
|
||||
this.ptr = ptr;
|
||||
this.size = size;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
import * as Assets from "./assets.js";
|
||||
import { Vec3 } from "./common.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;
|
||||
length;
|
||||
height;
|
||||
constructor(position, tileSize, width, length, height) {
|
||||
this.tiles3d = new Array(height);
|
||||
this.position = position;
|
||||
this.tileSize = tileSize;
|
||||
this.width = width;
|
||||
this.length = length;
|
||||
this.height = height;
|
||||
let layer = new Array(width * length);
|
||||
for (let i = 0; i < this.height; ++i)
|
||||
this.tiles3d[i] = { ...layer };
|
||||
}
|
||||
fillLayer(tile, z) {
|
||||
for (let i = 0; i < this.width; ++i) {
|
||||
for (let j = 0; j < this.length; ++j) {
|
||||
this.tiles3d[z][i + j * this.width] = { ...tile };
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < this.width; ++i) {
|
||||
this.tiles3d[z][this.length * i - 1] = { ...tile, edge: TileEdge.Right };
|
||||
}
|
||||
for (let i = 0; i < this.length - 1; ++i) {
|
||||
this.tiles3d[z][this.width * (this.length - 1) + i] = { ...tile, edge: TileEdge.Left };
|
||||
}
|
||||
this.tiles3d[z][this.width * this.length - 1] = { ...tile, edge: TileEdge.Both };
|
||||
}
|
||||
setTile(tile, coord) {
|
||||
let index = coord.x + coord.y * this.width;
|
||||
this.tiles3d[coord.z][index] = { ...tile };
|
||||
}
|
||||
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) {
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("log"), TileEdge.Both), new Vec3(position.x, position.y, 1));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("log"), TileEdge.Both), new Vec3(position.x, position.y, 2));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("log"), TileEdge.Both), new Vec3(position.x, position.y, 3));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("log"), TileEdge.Both), new Vec3(position.x, position.y, 4));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("leaves"), TileEdge.None), new Vec3(position.x, position.y, 4));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("leaves"), TileEdge.Both), new Vec3(position.x, position.y + 1, 4));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("leaves"), TileEdge.Both), new Vec3(position.x, position.y + 1, 5));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("leaves"), TileEdge.Both), new Vec3(position.x, position.y, 5));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("leaves"), TileEdge.Both), new Vec3(position.x + 1, position.y, 4));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("leaves"), TileEdge.Both), new Vec3(position.x + 1, position.y, 5));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("leaves"), TileEdge.Both), new Vec3(position.x + 1, position.y + 1, 4));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("leaves"), TileEdge.Both), new Vec3(position.x + 1, position.y + 1, 5));
|
||||
}
|
||||
export function bush(grid, position) {
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("leaves"), TileEdge.Both), position.extend(1));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("leaves"), TileEdge.Both), new Vec3(position.x + 1, position.y, 1));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("leaves"), TileEdge.Both), new Vec3(position.x, position.y + 1, 1));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("leaves"), TileEdge.Both), new Vec3(position.x + 1, position.y + 1, 1));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("leaves"), TileEdge.Both), position.extend(2));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("leaves"), TileEdge.Both), new Vec3(position.x + 1, position.y, 2));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("leaves"), TileEdge.Both), new Vec3(position.x, position.y + 1, 2));
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
import * as Assets from "./assets.js";
|
||||
import {Vec2, Vec3} from "./common.js";
|
||||
import {Texture} from "./graphics.js";
|
||||
|
||||
export enum TileEdge {
|
||||
None,
|
||||
Left,
|
||||
Right,
|
||||
Both,
|
||||
}
|
||||
|
||||
export type TileFillament = Texture | Assets.Color;
|
||||
export type TileFill = { left: TileFillament, top: TileFillament, right: 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;
|
||||
length: number;
|
||||
height: number;
|
||||
|
||||
constructor(position: Vec3, tileSize: number, width: number, length: number, height: number) {
|
||||
this.tiles3d = new Array<Tile[]>(height);
|
||||
this.position = position;
|
||||
|
||||
this.tileSize = tileSize;
|
||||
|
||||
this.width = width;
|
||||
this.length = length;
|
||||
this.height = height;
|
||||
|
||||
let layer = new Array(width * length);
|
||||
|
||||
for (let i = 0; i < this.height; ++i)
|
||||
this.tiles3d[i] = {...layer};
|
||||
}
|
||||
|
||||
fillLayer(tile: Tile, z: number) {
|
||||
for (let i = 0; i < this.width; ++i) {
|
||||
for (let j = 0; j < this.length; ++j) {
|
||||
this.tiles3d[z][i + j * this.width] = {...tile};
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.width; ++i) {
|
||||
this.tiles3d[z][this.length * i - 1] = {...tile, edge: TileEdge.Right};
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.length - 1; ++i) {
|
||||
this.tiles3d[z][this.width * (this.length - 1) + i] = {...tile, edge: TileEdge.Left};
|
||||
}
|
||||
|
||||
this.tiles3d[z][this.width * this.length - 1] = {...tile, edge: TileEdge.Both};
|
||||
}
|
||||
|
||||
setTile(tile: Tile, coord: Vec3) {
|
||||
let index = coord.x + coord.y * this.width;
|
||||
this.tiles3d[coord.z][index] = {...tile};
|
||||
}
|
||||
|
||||
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: Vec2) {
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("log"), TileEdge.Both), new Vec3(position.x, position.y, 1));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("log"), TileEdge.Both), new Vec3(position.x, position.y, 2));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("log"), TileEdge.Both), new Vec3(position.x, position.y, 3));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("log"), TileEdge.Both), new Vec3(position.x, position.y, 4));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("leaves"), TileEdge.None), new Vec3(position.x, position.y, 4));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("leaves"), TileEdge.Both), new Vec3(position.x, position.y + 1, 4));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("leaves"), TileEdge.Both), new Vec3(position.x, position.y + 1, 5));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("leaves"), TileEdge.Both), new Vec3(position.x, position.y, 5));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("leaves"), TileEdge.Both), new Vec3(position.x + 1, position.y, 4));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("leaves"), TileEdge.Both), new Vec3(position.x + 1, position.y, 5));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("leaves"), TileEdge.Both), new Vec3(position.x + 1, position.y + 1, 4));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("leaves"), TileEdge.Both), new Vec3(position.x + 1, position.y + 1, 5));
|
||||
}
|
||||
|
||||
export function bush(grid: Grid, position: Vec2) {
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("leaves"), TileEdge.Both), position.extend(1));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("leaves"), TileEdge.Both), new Vec3(position.x + 1, position.y, 1));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("leaves"), TileEdge.Both), new Vec3(position.x, position.y + 1, 1));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("leaves"), TileEdge.Both), new Vec3(position.x + 1, position.y + 1, 1));
|
||||
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("leaves"), TileEdge.Both), position.extend(2));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("leaves"), TileEdge.Both), new Vec3(position.x + 1, position.y, 2));
|
||||
grid.setTile(new Tile(Assets.AssetToTileFill("leaves"), TileEdge.Both), new Vec3(position.x, position.y + 1, 2));
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
#!/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
|
|
@ -0,0 +1,12 @@
|
|||
<!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>
|
|
@ -0,0 +1,20 @@
|
|||
#include "wasmgl.h"
|
||||
|
||||
void doubel(WASMGLvalue(0))
|
||||
{
|
||||
for (int i = 0; i < size_0; ++i)
|
||||
WASMGLset(ptr_0 + i, WASMGLmemory[ptr_0 + i] * 2.0);
|
||||
}
|
||||
|
||||
void uppercase(WASMGLvalue(0)) {
|
||||
int val = 0;
|
||||
|
||||
for (int i = 0; i < size_0; ++i)
|
||||
{
|
||||
val = WASMGLmemory[ptr_0 + i] - 32;
|
||||
|
||||
if (val < 65 || val > 122) val = WASMGLmemory[ptr_0 + i];
|
||||
|
||||
WASMGLset(ptr_0 + i, val);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
(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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
#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]);
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
#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(n) WASMGLptr ptr_##n, WASMGLsize size_##n
|
||||
|
||||
WASMGLptr WASMGLmalloc(WASMGLsize size);
|
||||
void WASMGLset(WASMGLptr ptr, int value);
|
||||
void WASMGLsetstr(WASMGLptr ptr, const char * cstr, WASMGLsize size);
|
||||
#endif // WASMGL_H_
|