378 lines
8.5 KiB
TypeScript
378 lines
8.5 KiB
TypeScript
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]
|
|
|
|
// TODO: Make all vectors follow one interface
|
|
interface Vector {
|
|
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
multScalar(scalar: number) {
|
|
this.x *= scalar;
|
|
this.y *= scalar;
|
|
this.z *= scalar;
|
|
}
|
|
|
|
multScalarNew(scalar: number): Vec3 {
|
|
let vec = new Vec3(this.x, this.y, this.z);
|
|
|
|
vec.x *= scalar;
|
|
vec.y *= scalar;
|
|
vec.z *= scalar;
|
|
|
|
return vec;
|
|
}
|
|
|
|
splatToArray(): Array<number> {
|
|
return [this.x, this.y, this.z];
|
|
}
|
|
}
|
|
|
|
class Vec4 {
|
|
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;
|
|
}
|
|
|
|
add(other: Vec4) {
|
|
this.x += other.x;
|
|
this.y += other.y;
|
|
this.z += other.z;
|
|
this.w += other.w;
|
|
}
|
|
|
|
sub(other: Vec4) {
|
|
this.x -= other.x;
|
|
this.y -= other.y;
|
|
this.z -= other.z;
|
|
this.w -= other.w;
|
|
}
|
|
|
|
multScalar(scalar: number) {
|
|
this.x *= scalar;
|
|
this.y *= scalar;
|
|
this.z *= scalar;
|
|
this.w *= scalar;
|
|
}
|
|
|
|
multScalarNew(scalar: number): Vec4 {
|
|
let vec = new Vec4(this.x, this.y, this.z, this.w);
|
|
|
|
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 = new Vec4(this.x, this.y, this.z, this.w);
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
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) {
|
|
this.data = i;
|
|
return;
|
|
} else if (typeof(i) === 'number') {
|
|
this.data = new Float32Array(16).fill(i);
|
|
return;
|
|
}
|
|
|
|
this.data = new Float32Array(16);
|
|
}
|
|
|
|
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 {
|
|
let m = new Mat4(new Float32Array([
|
|
1, -1, 0, 0,
|
|
1, 1, 0, 0,
|
|
0, 0, 1, 0,
|
|
0, 0, 0, 1,
|
|
]));
|
|
|
|
return m;
|
|
}
|
|
|
|
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,
|
|
]));
|
|
}
|
|
|
|
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.x * this.x(1) + v.x * this.x(2) + v.x * this.x(3);
|
|
let y = v.y * this.y(0) + v.y * this.y(1) + v.y * this.y(2) + v.y * this.y(3);
|
|
let z = v.z * this.z(0) + v.z * this.z(1) + v.z * this.z(2) + v.z * this.z(3);
|
|
let w = v.w * this.w(0) + v.w * this.w(1) + v.w * 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 = new Vec4(v.x, v.y, v.z, v.w);
|
|
|
|
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, Color };
|