@@ -1,108 +0,0 @@
|
||||
const MessageType = {
|
||||
domEvent: 0,
|
||||
custom: 1,
|
||||
attachCanvas: 2,
|
||||
} as const;
|
||||
type MessageTypeI = typeof MessageType;
|
||||
|
||||
const EventTypes = {
|
||||
pointermove: 0,
|
||||
pointerdown: 1,
|
||||
pointerup: 2,
|
||||
keydown: 3,
|
||||
keyup: 4,
|
||||
onWheel: 5,
|
||||
} as const;
|
||||
type EventTypesI = typeof EventTypes;
|
||||
|
||||
/**
|
||||
* @param worker The worker you're running the canvas on
|
||||
* @returns DisconnectWorker()
|
||||
*/
|
||||
const connectWorker = (worker: Worker) => {
|
||||
const eventPassers = {
|
||||
[EventTypes.pointermove]: (e: PointerEvent) => {
|
||||
worker.postMessage([
|
||||
MessageType.domEvent,
|
||||
EventTypes.pointermove,
|
||||
e.clientX,
|
||||
e.clientY,
|
||||
]);
|
||||
},
|
||||
[EventTypes.pointerup]: (e: PointerEvent) => {
|
||||
worker.postMessage([
|
||||
MessageType.domEvent,
|
||||
EventTypes.pointerup,
|
||||
e.clientX,
|
||||
e.clientY,
|
||||
]);
|
||||
},
|
||||
[EventTypes.pointerdown]: (e: PointerEvent) => {
|
||||
worker.postMessage([
|
||||
MessageType.domEvent,
|
||||
EventTypes.pointerdown,
|
||||
e.clientX,
|
||||
e.clientY,
|
||||
]);
|
||||
},
|
||||
[EventTypes.keydown]: (e: KeyboardEvent) => {
|
||||
worker.postMessage([MessageType.domEvent, EventTypes.keydown, e.key]);
|
||||
},
|
||||
[EventTypes.keyup]: (e: KeyboardEvent) => {
|
||||
worker.postMessage([MessageType.domEvent, EventTypes.keyup, e.key]);
|
||||
},
|
||||
[EventTypes.onWheel]: () => {
|
||||
worker.postMessage([MessageType.domEvent, EventTypes.onWheel]);
|
||||
},
|
||||
} as const;
|
||||
|
||||
document.addEventListener(
|
||||
"pointermove",
|
||||
eventPassers[EventTypes.pointermove],
|
||||
);
|
||||
document.addEventListener(
|
||||
"pointerdown",
|
||||
eventPassers[EventTypes.pointerdown],
|
||||
);
|
||||
document.addEventListener("pointerup", eventPassers[EventTypes.pointerup]);
|
||||
//
|
||||
document.addEventListener("keydown", eventPassers[EventTypes.keydown]);
|
||||
document.addEventListener("keyup", eventPassers[EventTypes.keyup]);
|
||||
|
||||
const disconnectWorker = () => {
|
||||
document.removeEventListener(
|
||||
"pointermove",
|
||||
eventPassers[EventTypes.pointermove],
|
||||
);
|
||||
document.removeEventListener(
|
||||
"pointerdown",
|
||||
eventPassers[EventTypes.pointerdown],
|
||||
);
|
||||
document.removeEventListener(
|
||||
"pointerup",
|
||||
eventPassers[EventTypes.pointerup],
|
||||
);
|
||||
//
|
||||
document.removeEventListener("keydown", eventPassers[EventTypes.keydown]);
|
||||
document.removeEventListener("keyup", eventPassers[EventTypes.keyup]);
|
||||
};
|
||||
|
||||
return disconnectWorker;
|
||||
};
|
||||
|
||||
const attachCanvas = (worker: Worker, canvas: HTMLCanvasElement | string) => {
|
||||
if (typeof canvas === "string")
|
||||
canvas = document.getElementById("rendering-canvas") as HTMLCanvasElement;
|
||||
if (!canvas) {
|
||||
throw new Error("Fatal: Canvas Not Found!");
|
||||
}
|
||||
|
||||
canvas.width = canvas.clientWidth;
|
||||
canvas.height = canvas.clientHeight;
|
||||
|
||||
const canvasWorker = canvas.transferControlToOffscreen();
|
||||
worker.postMessage([MessageType.attachCanvas, canvasWorker], [canvasWorker]);
|
||||
};
|
||||
|
||||
export type { EventTypesI, MessageTypeI };
|
||||
export { EventTypes, connectWorker, attachCanvas, MessageType };
|
||||
@@ -1,42 +0,0 @@
|
||||
import { Engine } from "../renderer/engine";
|
||||
import { MessageType } from "./mainThread";
|
||||
|
||||
const cubeVertices = [
|
||||
1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1,
|
||||
-1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1,
|
||||
1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1,
|
||||
-1, -1, -1, 1, -1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, 1,
|
||||
1, -1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1,
|
||||
];
|
||||
|
||||
const handleConnection = (msg: MessageEvent<any>) => {
|
||||
const { data } = msg;
|
||||
|
||||
if (!(data instanceof Array)) return;
|
||||
if (!data.length) return;
|
||||
|
||||
switch (data[0]) {
|
||||
case MessageType.attachCanvas:
|
||||
const canvas = data[1];
|
||||
|
||||
const engine = new Engine(canvas);
|
||||
const scene = engine.createScene();
|
||||
scene.addMesh("box", cubeVertices);
|
||||
scene.addCamera("cam", true);
|
||||
|
||||
const raf = () => {
|
||||
requestAnimationFrame(() => {
|
||||
scene.runSystems();
|
||||
raf();
|
||||
});
|
||||
};
|
||||
|
||||
raf();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
console.log(...data);
|
||||
};
|
||||
|
||||
export { handleConnection };
|
||||
-110
@@ -1,110 +0,0 @@
|
||||
type ComponentMap<CT> = {
|
||||
uid: (number | null)[];
|
||||
} & {
|
||||
[K in keyof CT]?: Array<CT[K] | null>;
|
||||
};
|
||||
|
||||
namespace ECS {
|
||||
export type System<ComponentsType extends Record<string | number, any>> = (
|
||||
arg: ComponentsType & { uid: (number | null)[] },
|
||||
) => Partial<ComponentsType> | null;
|
||||
}
|
||||
|
||||
class ECS<ComponentsType extends Record<string | number, any>> {
|
||||
private components: ComponentMap<ComponentsType> = { uid: [] };
|
||||
private componentDefaults: Record<
|
||||
string,
|
||||
null | ComponentsType[keyof ComponentsType]
|
||||
> = { uid: null };
|
||||
private systems: Record<string, ECS.System<ComponentsType>> = {};
|
||||
|
||||
/**
|
||||
* System
|
||||
*/
|
||||
runSystems() {
|
||||
const limit = this.components.uid.length;
|
||||
for (let i = 0; i < limit; i++) {
|
||||
const obj = {} as Record<string, unknown>;
|
||||
|
||||
for (const key in this.components) {
|
||||
obj[key] = this.components[key]![i];
|
||||
}
|
||||
|
||||
const systems = Object.values(this.systems);
|
||||
for (let j = 0; j < systems.length; j++) {
|
||||
const res = systems[j](
|
||||
obj as ComponentsType & { uid: (number | null)[] },
|
||||
);
|
||||
if (!res) continue;
|
||||
|
||||
for (const key in res) {
|
||||
if (!this.components[key]) continue;
|
||||
this.components[key][i] = res[key]!;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addSystem(system: ECS.System<ComponentsType>) {
|
||||
const id = crypto.randomUUID();
|
||||
this.systems[id] = system;
|
||||
return id;
|
||||
}
|
||||
|
||||
removeSystem(id: string) {
|
||||
delete this.systems[id];
|
||||
}
|
||||
|
||||
getById(uid: number) {
|
||||
if (this.components.uid.length <= uid) return {};
|
||||
|
||||
const obj = {} as Record<string, unknown>;
|
||||
|
||||
for (const key in this.components) {
|
||||
obj[key] = this.components[key]![uid];
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Entity
|
||||
*/
|
||||
addEntity(components: ComponentsType) {
|
||||
for (const key in this.components) {
|
||||
if (key === "uid") {
|
||||
this.components[key].push(this.components[key].length);
|
||||
continue;
|
||||
}
|
||||
|
||||
this.components[key]!.push(
|
||||
components[key] ?? this.componentDefaults[key],
|
||||
);
|
||||
}
|
||||
|
||||
return this.components.uid.length - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Component
|
||||
*/
|
||||
addComponent(
|
||||
name: keyof ComponentsType,
|
||||
fillValue = null as null | ComponentsType[keyof ComponentsType],
|
||||
defaultValue = null as null | ComponentsType[keyof ComponentsType],
|
||||
) {
|
||||
if (this.components[name]) throw new Error("Component already exists");
|
||||
|
||||
this.components[name] = new Array(this.components.uid.length).fill(
|
||||
fillValue,
|
||||
) as ComponentMap<ComponentsType>[keyof ComponentsType];
|
||||
|
||||
this.componentDefaults[name as string] = defaultValue;
|
||||
}
|
||||
|
||||
removeComponent(name: keyof ComponentsType) {
|
||||
delete this.components[name];
|
||||
}
|
||||
}
|
||||
|
||||
export { ECS };
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
/// <reference types="vite-plugin-glsl/ext" />
|
||||
@@ -1,31 +0,0 @@
|
||||
import { Scene } from "./scene";
|
||||
|
||||
class Engine {
|
||||
canvas: HTMLCanvasElement;
|
||||
ctx: WebGL2RenderingContext;
|
||||
scenes = [] as Scene[];
|
||||
|
||||
constructor(canvas: string | HTMLCanvasElement) {
|
||||
/** get canvas by id */
|
||||
if (typeof canvas === "string") {
|
||||
canvas = document.getElementById(canvas)! as HTMLCanvasElement;
|
||||
}
|
||||
|
||||
if (!canvas) {
|
||||
throw new Error("canvas not found");
|
||||
}
|
||||
this.canvas = canvas;
|
||||
this.ctx = canvas.getContext("webgl2")!;
|
||||
if (!this.ctx) {
|
||||
throw new Error("can't get webgl2 context");
|
||||
}
|
||||
}
|
||||
|
||||
createScene() {
|
||||
const scene = new Scene(this.canvas, this.ctx);
|
||||
this.scenes.push(scene);
|
||||
return scene;
|
||||
}
|
||||
}
|
||||
|
||||
export { Engine };
|
||||
@@ -1,107 +0,0 @@
|
||||
import { mat4 } from "gl-matrix";
|
||||
|
||||
export const compileShader = (
|
||||
gl: WebGL2RenderingContext,
|
||||
type: number,
|
||||
source: string,
|
||||
) => {
|
||||
const shader = gl.createShader(type);
|
||||
if (!shader) {
|
||||
throw new Error(`couldn't make the shader ${source}`);
|
||||
}
|
||||
|
||||
gl.shaderSource(shader, source);
|
||||
gl.compileShader(shader);
|
||||
|
||||
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
||||
throw new Error(
|
||||
`Error in ${source}: ${gl.getShaderInfoLog(shader) ?? "no error returned"}`,
|
||||
);
|
||||
}
|
||||
|
||||
return shader;
|
||||
};
|
||||
|
||||
export const createProgram = (
|
||||
gl: WebGL2RenderingContext,
|
||||
vs: string,
|
||||
fs: string,
|
||||
): WebGLProgram => {
|
||||
const program = gl.createProgram()!;
|
||||
|
||||
const vShader = compileShader(gl, gl.VERTEX_SHADER, vs);
|
||||
const fShader = compileShader(gl, gl.FRAGMENT_SHADER, fs);
|
||||
|
||||
gl.attachShader(program, vShader);
|
||||
gl.attachShader(program, fShader);
|
||||
|
||||
gl.linkProgram(program);
|
||||
|
||||
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
||||
throw new Error(gl.getProgramInfoLog(program) || "");
|
||||
}
|
||||
|
||||
return program;
|
||||
};
|
||||
|
||||
export type AttributeCollection = {
|
||||
name: string;
|
||||
data: Float32Array;
|
||||
size: number;
|
||||
};
|
||||
|
||||
export const setupVAO = (
|
||||
gl: WebGL2RenderingContext,
|
||||
program: WebGLProgram,
|
||||
attributeCollections: AttributeCollection[],
|
||||
) => {
|
||||
const vao = gl.createVertexArray()!;
|
||||
gl.bindVertexArray(vao);
|
||||
|
||||
for (const attr of attributeCollections) {
|
||||
const loc = gl.getAttribLocation(program, attr.name);
|
||||
if (loc >= 0) {
|
||||
const buf = gl.createBuffer()!;
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, attr.data, gl.STATIC_DRAW);
|
||||
gl.enableVertexAttribArray(loc);
|
||||
gl.vertexAttribPointer(loc, attr.size, gl.FLOAT, false, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
gl.bindVertexArray(null);
|
||||
return vao;
|
||||
};
|
||||
|
||||
export const updateVAO = (
|
||||
gl: WebGL2RenderingContext,
|
||||
program: WebGLProgram,
|
||||
vao: WebGLVertexArrayObject,
|
||||
attributeCollections: AttributeCollection[],
|
||||
) => {
|
||||
gl.bindVertexArray(vao);
|
||||
|
||||
for (const attr of attributeCollections) {
|
||||
const loc = gl.getAttribLocation(program, attr.name);
|
||||
if (loc >= 0) {
|
||||
const buf = gl.createBuffer()!;
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, attr.data, gl.STATIC_DRAW);
|
||||
gl.enableVertexAttribArray(loc);
|
||||
gl.vertexAttribPointer(loc, attr.size, gl.FLOAT, false, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
gl.bindVertexArray(null);
|
||||
return vao;
|
||||
};
|
||||
|
||||
export const setMat4 = (
|
||||
gl: WebGL2RenderingContext,
|
||||
program: WebGLProgram,
|
||||
name: string,
|
||||
mat: mat4,
|
||||
) => {
|
||||
const loc = gl.getUniformLocation(program, name);
|
||||
if (loc) gl.uniformMatrix4fv(loc, false, mat);
|
||||
};
|
||||
@@ -1,197 +0,0 @@
|
||||
import { mat4, vec3 } from "gl-matrix";
|
||||
import { ECS } from "../ecs/ecs";
|
||||
import { createProgram, setMat4, setupVAO, updateVAO } from "./gl";
|
||||
import defaultVertexSrc from "../shaders/triangle/vertex.glsl";
|
||||
import defaultFragSrc from "../shaders/triangle/frag.glsl";
|
||||
|
||||
class Scene {
|
||||
canvas: HTMLCanvasElement;
|
||||
gl: WebGL2RenderingContext;
|
||||
ecs: ECS<Record<string | number, any>>;
|
||||
activeCamera: number;
|
||||
|
||||
constructor(canvas: HTMLCanvasElement, ctx: WebGL2RenderingContext) {
|
||||
this.canvas = canvas;
|
||||
this.gl = ctx;
|
||||
this.activeCamera = -1;
|
||||
|
||||
/** make ecs */
|
||||
this.ecs = new ECS();
|
||||
this.initECS();
|
||||
}
|
||||
|
||||
private initECS() {
|
||||
const projection = mat4.create();
|
||||
mat4.perspective(
|
||||
projection,
|
||||
45,
|
||||
this.canvas.width / this.canvas.height,
|
||||
0.1,
|
||||
100.0,
|
||||
);
|
||||
|
||||
this.ecs.addComponent("isMesh");
|
||||
this.ecs.addComponent("isCamera");
|
||||
this.ecs.addComponent("isMaterial");
|
||||
this.ecs.addComponent("program");
|
||||
this.ecs.addComponent("vao");
|
||||
this.ecs.addComponent("name", "deafult", "default");
|
||||
this.ecs.addComponent("attr_pos", [], []);
|
||||
this.ecs.addComponent("attr_normals", [], []);
|
||||
this.ecs.addComponent("u_model_matrix", mat4.create(), mat4.create());
|
||||
this.ecs.addComponent("u_projection_matrix", projection, projection);
|
||||
|
||||
const defaultMaterial = this.addMaterial(
|
||||
"default-material",
|
||||
defaultVertexSrc,
|
||||
defaultFragSrc,
|
||||
);
|
||||
this.ecs.addComponent("applied_material", defaultMaterial, defaultMaterial);
|
||||
|
||||
this.ecs.addSystem((data) => {
|
||||
this.render(data);
|
||||
|
||||
const model = mat4.create();
|
||||
const angle = Date.now() * 0.001;
|
||||
mat4.translate(model, model, vec3.fromValues(0, 0, -5));
|
||||
mat4.rotate(model, model, angle, [0.5, 1, 0]);
|
||||
|
||||
if (data["isMesh"]) {
|
||||
return {
|
||||
u_model_matrix: model,
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
runSystems() {
|
||||
this.ecs.runSystems();
|
||||
}
|
||||
|
||||
private render(data: any) {
|
||||
if (this.activeCamera === -1) return;
|
||||
const camera = this.ecs.getById(this.activeCamera);
|
||||
|
||||
const view = mat4.create();
|
||||
mat4.invert(view, camera["u_model_matrix"] as mat4);
|
||||
|
||||
const projection = camera["u_projection_matrix"] as mat4;
|
||||
|
||||
const gl = this.gl;
|
||||
gl.enable(gl.DEPTH_TEST);
|
||||
|
||||
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
|
||||
|
||||
gl.clearColor(0.0, 0.0, 0.0, 1.0);
|
||||
// gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||
|
||||
if (data["isMesh"]) {
|
||||
const { program, vao } = this.ecs.getById(data["applied_material"]);
|
||||
|
||||
const attrCollection = [
|
||||
{
|
||||
name: "attr_pos",
|
||||
data: data["attr_pos"],
|
||||
size: 3,
|
||||
},
|
||||
{
|
||||
name: "attr_normals",
|
||||
data: data["attr_normals"],
|
||||
size: 3,
|
||||
},
|
||||
];
|
||||
|
||||
updateVAO(gl, program as any, vao as any, attrCollection);
|
||||
gl.useProgram(program!);
|
||||
|
||||
const model = data["u_model_matrix"];
|
||||
setMat4(gl, program as any, "model", model);
|
||||
setMat4(gl, program as any, "view", view);
|
||||
setMat4(gl, program as any, "projection", projection);
|
||||
|
||||
gl.bindVertexArray(vao as any);
|
||||
const vertexCount = data["attr_pos"].length / 3;
|
||||
gl.drawArrays(gl.TRIANGLES, 0, vertexCount);
|
||||
console.log(vertexCount);
|
||||
}
|
||||
|
||||
gl.bindVertexArray(null);
|
||||
gl.useProgram(null);
|
||||
}
|
||||
|
||||
addMaterial(name: string, vs: string, fs: string) {
|
||||
const program = createProgram(this.gl, vs, fs);
|
||||
const vao = setupVAO(this.gl, program, []);
|
||||
return this.ecs.addEntity({
|
||||
name,
|
||||
isMaterial: true,
|
||||
program,
|
||||
vao,
|
||||
});
|
||||
}
|
||||
|
||||
addCamera(name: string, setActive = true) {
|
||||
const uid = this.ecs.addEntity({
|
||||
name,
|
||||
});
|
||||
if (setActive) this.activeCamera = uid;
|
||||
}
|
||||
|
||||
addMesh(name: string, positions: number[], normals?: number[]) {
|
||||
const numVerts = Math.floor(positions.length / 3);
|
||||
const numTris = Math.floor(numVerts / 3);
|
||||
positions.length = numTris * 9;
|
||||
|
||||
if (!normals) {
|
||||
normals = [];
|
||||
const a = vec3.create();
|
||||
const b = vec3.create();
|
||||
const c = vec3.create();
|
||||
const e1 = vec3.create();
|
||||
const e2 = vec3.create();
|
||||
|
||||
for (let i = 0; i < numTris; i++) {
|
||||
const base = i * 9;
|
||||
|
||||
vec3.set(
|
||||
a,
|
||||
positions[base + 0],
|
||||
positions[base + 1],
|
||||
positions[base + 2],
|
||||
);
|
||||
vec3.set(
|
||||
b,
|
||||
positions[base + 3],
|
||||
positions[base + 4],
|
||||
positions[base + 5],
|
||||
);
|
||||
vec3.set(
|
||||
c,
|
||||
positions[base + 6],
|
||||
positions[base + 7],
|
||||
positions[base + 8],
|
||||
);
|
||||
|
||||
vec3.subtract(e1, b, a);
|
||||
vec3.subtract(e2, c, a);
|
||||
vec3.cross(e1, e2, e1);
|
||||
vec3.normalize(e1, e1);
|
||||
|
||||
normals.push(e1[0], e1[1], e1[2]);
|
||||
normals.push(e1[0], e1[1], e1[2]);
|
||||
normals.push(e1[0], e1[1], e1[2]);
|
||||
}
|
||||
}
|
||||
|
||||
this.ecs.addEntity({
|
||||
name,
|
||||
attr_pos: new Float32Array(positions),
|
||||
attr_normals: new Float32Array(normals),
|
||||
isMesh: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export { Scene };
|
||||
@@ -1,16 +0,0 @@
|
||||
#version 300 es
|
||||
|
||||
precision highp float;
|
||||
|
||||
in vec3 vNormal;
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
vec3 normal = normalize(vNormal);
|
||||
vec3 lightDir = normalize(vec3(0.0, 0.0, 1.0));
|
||||
float diff = max(dot(normal, lightDir), 0.0);
|
||||
|
||||
vec3 diffuseColor = vec3(1.0, 0.5, 0.3) * diff;
|
||||
|
||||
fragColor = vec4(diffuseColor, 1.0);
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
#version 300 es
|
||||
|
||||
precision highp float;
|
||||
|
||||
in vec3 attr_pos;
|
||||
in vec3 attr_normals;
|
||||
|
||||
uniform mat4 model;
|
||||
uniform mat4 view;
|
||||
uniform mat4 projection;
|
||||
|
||||
out vec3 vNormal;
|
||||
|
||||
void main() {
|
||||
mat3 normalMatrix = transpose(inverse(mat3(model)));
|
||||
|
||||
vNormal = normalize(normalMatrix * attr_normals);
|
||||
|
||||
gl_Position = projection * view * model * vec4(attr_pos, 1.0);
|
||||
}
|
||||
Reference in New Issue
Block a user