Initial commit
This commit is contained in:
commit
4803f11739
|
|
@ -0,0 +1,34 @@
|
|||
# dependencies (bun install)
|
||||
node_modules
|
||||
|
||||
# output
|
||||
out
|
||||
dist
|
||||
*.tgz
|
||||
|
||||
# code coverage
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# logs
|
||||
logs
|
||||
_.log
|
||||
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# caches
|
||||
.eslintcache
|
||||
.cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# IntelliJ based IDEs
|
||||
.idea
|
||||
|
||||
# Finder (MacOS) folder config
|
||||
.DS_Store
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
# stars
|
||||
|
||||
To install dependencies:
|
||||
|
||||
```bash
|
||||
bun install
|
||||
```
|
||||
|
||||
To run:
|
||||
|
||||
```bash
|
||||
bun run index.ts
|
||||
```
|
||||
|
||||
This project was created using `bun init` in bun v1.2.20. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"lockfileVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "stars",
|
||||
"dependencies": {
|
||||
"@webgpu/types": "^0.1.64",
|
||||
"stats.ts": "^1.1.0",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.2.20",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@types/bun": ["@types/bun@1.2.20", "", { "dependencies": { "bun-types": "1.2.20" } }, "sha512-dX3RGzQ8+KgmMw7CsW4xT5ITBSCrSbfHc36SNT31EOUg/LA9JWq0VDdEXDRSe1InVWpd2yLUM1FUF/kEOyTzYA=="],
|
||||
|
||||
"@types/node": ["@types/node@24.3.0", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow=="],
|
||||
|
||||
"@types/react": ["@types/react@19.1.10", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg=="],
|
||||
|
||||
"@webgpu/types": ["@webgpu/types@0.1.64", "", {}, "sha512-84kRIAGV46LJTlJZWxShiOrNL30A+9KokD7RB3dRCIqODFjodS5tCD5yyiZ8kIReGVZSDfA3XkkwyyOIF6K62A=="],
|
||||
|
||||
"bun-types": ["bun-types@1.2.20", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-pxTnQYOrKvdOwyiyd/7sMt9yFOenN004Y6O4lCcCUoKVej48FS5cvTw9geRaEcB9TsDZaJKAxPTVvi8tFsVuXA=="],
|
||||
|
||||
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
||||
|
||||
"stats.ts": ["stats.ts@1.1.0", "", {}, "sha512-mokWb6xGU0elmDlUT6Y5E4zsUs/Enlq5fpyE7pfffpJg9Lg83GlL7B9zHEPA0eDvDC3Ko0ZZ50cjIN8yYJRPoQ=="],
|
||||
|
||||
"typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
|
||||
|
||||
"undici-types": ["undici-types@7.10.0", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="],
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,8 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Stars</title>
|
||||
<script module src="main.ts"></script>
|
||||
</head>
|
||||
<body style="width: 100vw; height: 100vh; margin: 0; padding: 0; overflow: hidden;"></body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import index from './index.html';
|
||||
|
||||
const server = Bun.serve({
|
||||
routes: {
|
||||
'/': index
|
||||
},
|
||||
development: false
|
||||
});
|
||||
console.log(server.url.href);
|
||||
|
|
@ -0,0 +1,537 @@
|
|||
import untypedStars from './data/stars.json';
|
||||
import untypedConstellations from './data/constellations.json';
|
||||
import { Stats } from 'stats.ts';
|
||||
|
||||
const shaderCode = `
|
||||
struct VertexOut {
|
||||
@builtin(position) position: vec4f,
|
||||
@location(0) color: vec4f,
|
||||
@location(1) uv: vec2f
|
||||
}
|
||||
struct Star {
|
||||
pos: vec3f,
|
||||
lum: f32
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<uniform> _ViewProjectionMatrix: mat4x4f;
|
||||
@group(0) @binding(1) var<storage, read> stars: array<Star>;
|
||||
|
||||
const quad: array<vec4f, 4> = array(
|
||||
vec4f(0, 0, 0, 0),
|
||||
vec4f(0, 1, 0, 0),
|
||||
vec4f(1, 0, 0, 0),
|
||||
vec4f(1, 1, 0, 0),
|
||||
);
|
||||
|
||||
@vertex
|
||||
fn vertex_main(@builtin(vertex_index) vertex_index: u32, @builtin(instance_index) instance_index: u32) -> VertexOut
|
||||
{
|
||||
var star: Star = stars[instance_index];
|
||||
var output: VertexOut;
|
||||
|
||||
output.position = vec4f(star.pos, 0) + quad[vertex_index];
|
||||
output.color = vec4(star.lum, star.lum, star.lum, 1);
|
||||
output.uv = vec2f(quad[vertex_index].x, quad[vertex_index].y);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fragment_main(fragData: VertexOut) -> @location(0) vec4f
|
||||
{
|
||||
return fragData.color * distance(fragData.uv, vec2f(0.5, 0.5));
|
||||
}
|
||||
`;
|
||||
|
||||
type StarID = number;
|
||||
type Star = {
|
||||
id: StarID;
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
ci: number;
|
||||
lum: number;
|
||||
};
|
||||
type Constellation = {
|
||||
con: string;
|
||||
con_id: string;
|
||||
lines: StarID[][];
|
||||
certainty: string | null;
|
||||
description: string | null;
|
||||
semantics: string[] | null;
|
||||
};
|
||||
|
||||
class Matrix4x4
|
||||
{
|
||||
private _buffer: ArrayBuffer;
|
||||
private _view: Float32Array;
|
||||
constructor()
|
||||
{
|
||||
this._view = new Float32Array(16);
|
||||
this._buffer = this._view.buffer as ArrayBuffer;
|
||||
}
|
||||
|
||||
static translation(x: number, y: number, z: number): Matrix4x4
|
||||
{
|
||||
const mat = new Matrix4x4();
|
||||
|
||||
mat.view[ 0] = 1; mat.view[ 1] = 0; mat.view[ 2] = 0; mat.view[ 3] = 0;
|
||||
mat.view[ 4] = 0; mat.view[ 5] = 1; mat.view[ 6] = 0; mat.view[ 7] = 0;
|
||||
mat.view[ 8] = 0; mat.view[ 9] = 0; mat.view[10] = 1; mat.view[11] = 0;
|
||||
mat.view[12] = x; mat.view[13] = y; mat.view[14] = z; mat.view[15] = 1;
|
||||
|
||||
return mat;
|
||||
}
|
||||
static rotationX(angle: number): Matrix4x4
|
||||
{
|
||||
const c = Math.cos(angle);
|
||||
const s = Math.sin(angle);
|
||||
|
||||
const mat = new Matrix4x4();
|
||||
|
||||
mat.view[ 0] = 1; mat.view[ 1] = 0; mat.view[ 2] = 0; mat.view[ 3] = 0;
|
||||
mat.view[ 4] = 0; mat.view[ 5] = c; mat.view[ 6] = s; mat.view[ 7] = 0;
|
||||
mat.view[ 8] = 0; mat.view[ 9] = -s; mat.view[10] = c; mat.view[11] = 0;
|
||||
mat.view[12] = 0; mat.view[13] = 0; mat.view[14] = 0; mat.view[15] = 1;
|
||||
|
||||
return mat;
|
||||
}
|
||||
static rotationY(angle: number): Matrix4x4
|
||||
{
|
||||
const c = Math.cos(angle);
|
||||
const s = Math.sin(angle);
|
||||
|
||||
const mat = new Matrix4x4();
|
||||
|
||||
mat.view[ 0] = c; mat.view[ 1] = 0; mat.view[ 2] = -s; mat.view[ 3] = 0;
|
||||
mat.view[ 4] = 0; mat.view[ 5] = 1; mat.view[ 6] = 0; mat.view[ 7] = 0;
|
||||
mat.view[ 8] = s; mat.view[ 9] = 0; mat.view[10] = c; mat.view[11] = 0;
|
||||
mat.view[12] = 0; mat.view[13] = 0; mat.view[14] = 0; mat.view[15] = 1;
|
||||
|
||||
return mat;
|
||||
}
|
||||
static rotationZ(angle: number): Matrix4x4
|
||||
{
|
||||
const c = Math.cos(angle);
|
||||
const s = Math.sin(angle);
|
||||
|
||||
const mat = new Matrix4x4();
|
||||
|
||||
mat.view[ 0] = c; mat.view[ 1] = s; mat.view[ 2] = 0; mat.view[ 3] = 0;
|
||||
mat.view[ 4] = -s; mat.view[ 5] = c; mat.view[ 6] = 0; mat.view[ 7] = 0;
|
||||
mat.view[ 8] = 0; mat.view[ 9] = 0; mat.view[10] = 1; mat.view[11] = 0;
|
||||
mat.view[12] = 0; mat.view[13] = 0; mat.view[14] = 0; mat.view[15] = 1;
|
||||
|
||||
return mat;
|
||||
}
|
||||
static scaling(sx: number, sy: number, sz: number): Matrix4x4
|
||||
{
|
||||
const mat = new Matrix4x4();
|
||||
|
||||
mat.view[ 0] = sx; mat.view[ 1] = 0; mat.view[ 2] = 0; mat.view[ 3] = 0;
|
||||
mat.view[ 4] = 0; mat.view[ 5] = sy; mat.view[ 6] = 0; mat.view[ 7] = 0;
|
||||
mat.view[ 8] = 0; mat.view[ 9] = 0; mat.view[10] = sz; mat.view[11] = 0;
|
||||
mat.view[12] = 0; mat.view[13] = 0; mat.view[14] = 0; mat.view[15] = 1;
|
||||
|
||||
return mat;
|
||||
}
|
||||
|
||||
translate(x: number, y: number, z: number): this
|
||||
{
|
||||
return this.multiply(this, Matrix4x4.translation(x, y, z));
|
||||
}
|
||||
rotateX(angle: number): this
|
||||
{
|
||||
return this.multiply(this, Matrix4x4.rotationX(angle));
|
||||
}
|
||||
rotateY(angle: number): this
|
||||
{
|
||||
return this.multiply(this, Matrix4x4.rotationY(angle));
|
||||
}
|
||||
rotateZ(angle: number): this
|
||||
{
|
||||
return this.multiply(this, Matrix4x4.rotationZ(angle));
|
||||
}
|
||||
scale(sx: number, sy: number, sz: number): this
|
||||
{
|
||||
return this.multiply(this, Matrix4x4.scaling(sx, sy, sz));
|
||||
}
|
||||
|
||||
multiply(a: Matrix4x4, b: Matrix4x4): this
|
||||
{
|
||||
const b00 = b.view[0 * 4 + 0]!;
|
||||
const b01 = b.view[0 * 4 + 1]!;
|
||||
const b02 = b.view[0 * 4 + 2]!;
|
||||
const b03 = b.view[0 * 4 + 3]!;
|
||||
const b10 = b.view[1 * 4 + 0]!;
|
||||
const b11 = b.view[1 * 4 + 1]!;
|
||||
const b12 = b.view[1 * 4 + 2]!;
|
||||
const b13 = b.view[1 * 4 + 3]!;
|
||||
const b20 = b.view[2 * 4 + 0]!;
|
||||
const b21 = b.view[2 * 4 + 1]!;
|
||||
const b22 = b.view[2 * 4 + 2]!;
|
||||
const b23 = b.view[2 * 4 + 3]!;
|
||||
const b30 = b.view[3 * 4 + 0]!;
|
||||
const b31 = b.view[3 * 4 + 1]!;
|
||||
const b32 = b.view[3 * 4 + 2]!;
|
||||
const b33 = b.view[3 * 4 + 3]!;
|
||||
const a00 = a.view[0 * 4 + 0]!;
|
||||
const a01 = a.view[0 * 4 + 1]!;
|
||||
const a02 = a.view[0 * 4 + 2]!;
|
||||
const a03 = a.view[0 * 4 + 3]!;
|
||||
const a10 = a.view[1 * 4 + 0]!;
|
||||
const a11 = a.view[1 * 4 + 1]!;
|
||||
const a12 = a.view[1 * 4 + 2]!;
|
||||
const a13 = a.view[1 * 4 + 3]!;
|
||||
const a20 = a.view[2 * 4 + 0]!;
|
||||
const a21 = a.view[2 * 4 + 1]!;
|
||||
const a22 = a.view[2 * 4 + 2]!;
|
||||
const a23 = a.view[2 * 4 + 3]!;
|
||||
const a30 = a.view[3 * 4 + 0]!;
|
||||
const a31 = a.view[3 * 4 + 1]!;
|
||||
const a32 = a.view[3 * 4 + 2]!;
|
||||
const a33 = a.view[3 * 4 + 3]!;
|
||||
|
||||
this._view[0] = b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30;
|
||||
this._view[1] = b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31;
|
||||
this._view[2] = b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32;
|
||||
this._view[3] = b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33;
|
||||
|
||||
this._view[4] = b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30;
|
||||
this._view[5] = b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31;
|
||||
this._view[6] = b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32;
|
||||
this._view[7] = b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33;
|
||||
|
||||
this._view[8] = b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30;
|
||||
this._view[9] = b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31;
|
||||
this._view[10] = b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32;
|
||||
this._view[11] = b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33;
|
||||
|
||||
this._view[12] = b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30;
|
||||
this._view[13] = b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31;
|
||||
this._view[14] = b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32;
|
||||
this._view[15] = b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33;
|
||||
|
||||
return this;
|
||||
}
|
||||
inverse()
|
||||
{
|
||||
const m00 = this._view[0 * 4 + 0]!;
|
||||
const m01 = this._view[0 * 4 + 1]!;
|
||||
const m02 = this._view[0 * 4 + 2]!;
|
||||
const m03 = this._view[0 * 4 + 3]!;
|
||||
const m10 = this._view[1 * 4 + 0]!;
|
||||
const m11 = this._view[1 * 4 + 1]!;
|
||||
const m12 = this._view[1 * 4 + 2]!;
|
||||
const m13 = this._view[1 * 4 + 3]!;
|
||||
const m20 = this._view[2 * 4 + 0]!;
|
||||
const m21 = this._view[2 * 4 + 1]!;
|
||||
const m22 = this._view[2 * 4 + 2]!;
|
||||
const m23 = this._view[2 * 4 + 3]!;
|
||||
const m30 = this._view[3 * 4 + 0]!;
|
||||
const m31 = this._view[3 * 4 + 1]!;
|
||||
const m32 = this._view[3 * 4 + 2]!;
|
||||
const m33 = this._view[3 * 4 + 3]!;
|
||||
|
||||
const tmp0 = m22 * m33;
|
||||
const tmp1 = m32 * m23;
|
||||
const tmp2 = m12 * m33;
|
||||
const tmp3 = m32 * m13;
|
||||
const tmp4 = m12 * m23;
|
||||
const tmp5 = m22 * m13;
|
||||
const tmp6 = m02 * m33;
|
||||
const tmp7 = m32 * m03;
|
||||
const tmp8 = m02 * m23;
|
||||
const tmp9 = m22 * m03;
|
||||
const tmp10 = m02 * m13;
|
||||
const tmp11 = m12 * m03;
|
||||
const tmp12 = m20 * m31;
|
||||
const tmp13 = m30 * m21;
|
||||
const tmp14 = m10 * m31;
|
||||
const tmp15 = m30 * m11;
|
||||
const tmp16 = m10 * m21;
|
||||
const tmp17 = m20 * m11;
|
||||
const tmp18 = m00 * m31;
|
||||
const tmp19 = m30 * m01;
|
||||
const tmp20 = m00 * m21;
|
||||
const tmp21 = m20 * m01;
|
||||
const tmp22 = m00 * m11;
|
||||
const tmp23 = m10 * m01;
|
||||
|
||||
const t0 = (tmp0 * m11 + tmp3 * m21 + tmp4 * m31) -
|
||||
(tmp1 * m11 + tmp2 * m21 + tmp5 * m31);
|
||||
const t1 = (tmp1 * m01 + tmp6 * m21 + tmp9 * m31) -
|
||||
(tmp0 * m01 + tmp7 * m21 + tmp8 * m31);
|
||||
const t2 = (tmp2 * m01 + tmp7 * m11 + tmp10 * m31) -
|
||||
(tmp3 * m01 + tmp6 * m11 + tmp11 * m31);
|
||||
const t3 = (tmp5 * m01 + tmp8 * m11 + tmp11 * m21) -
|
||||
(tmp4 * m01 + tmp9 * m11 + tmp10 * m21);
|
||||
|
||||
const d = 1 / (m00 * t0 + m10 * t1 + m20 * t2 + m30 * t3);
|
||||
|
||||
this._view[0] = d * t0;
|
||||
this._view[1] = d * t1;
|
||||
this._view[2] = d * t2;
|
||||
this._view[3] = d * t3;
|
||||
|
||||
this._view[4] = d * ((tmp1 * m10 + tmp2 * m20 + tmp5 * m30) - (tmp0 * m10 + tmp3 * m20 + tmp4 * m30));
|
||||
this._view[5] = d * ((tmp0 * m00 + tmp7 * m20 + tmp8 * m30) - (tmp1 * m00 + tmp6 * m20 + tmp9 * m30));
|
||||
this._view[6] = d * ((tmp3 * m00 + tmp6 * m10 + tmp11 * m30) - (tmp2 * m00 + tmp7 * m10 + tmp10 * m30));
|
||||
this._view[7] = d * ((tmp4 * m00 + tmp9 * m10 + tmp10 * m20) - (tmp5 * m00 + tmp8 * m10 + tmp11 * m20));
|
||||
|
||||
this._view[8] = d * ((tmp12 * m13 + tmp15 * m23 + tmp16 * m33) - (tmp13 * m13 + tmp14 * m23 + tmp17 * m33));
|
||||
this._view[9] = d * ((tmp13 * m03 + tmp18 * m23 + tmp21 * m33) - (tmp12 * m03 + tmp19 * m23 + tmp20 * m33));
|
||||
this._view[10] = d * ((tmp14 * m03 + tmp19 * m13 + tmp22 * m33) - (tmp15 * m03 + tmp18 * m13 + tmp23 * m33));
|
||||
this._view[11] = d * ((tmp17 * m03 + tmp20 * m13 + tmp23 * m23) - (tmp16 * m03 + tmp21 * m13 + tmp22 * m23));
|
||||
|
||||
this._view[12] = d * ((tmp14 * m22 + tmp17 * m32 + tmp13 * m12) - (tmp16 * m32 + tmp12 * m12 + tmp15 * m22));
|
||||
this._view[13] = d * ((tmp20 * m32 + tmp12 * m02 + tmp19 * m22) - (tmp18 * m22 + tmp21 * m32 + tmp13 * m02));
|
||||
this._view[14] = d * ((tmp18 * m12 + tmp23 * m32 + tmp15 * m02) - (tmp22 * m32 + tmp14 * m02 + tmp19 * m12));
|
||||
this._view[15] = d * ((tmp22 * m22 + tmp16 * m02 + tmp21 * m12) - (tmp20 * m12 + tmp23 * m22 + tmp17 * m02));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
camera(transform: {
|
||||
position: {
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
},
|
||||
yaw: number,
|
||||
pitch: number
|
||||
}): this
|
||||
{
|
||||
const cosPitch = Math.cos(transform.pitch);
|
||||
const sinPitch = Math.sin(transform.pitch);
|
||||
const cosYaw = Math.cos(transform.yaw);
|
||||
const sinYaw = Math.sin(transform.yaw);
|
||||
|
||||
this._view[0] = cosYaw;
|
||||
this._view[1] = 0;
|
||||
this._view[2] = -sinYaw;
|
||||
this._view[3] = 0;
|
||||
|
||||
this._view[4] = sinYaw * sinPitch;
|
||||
this._view[5] = cosPitch;
|
||||
this._view[6] = cosYaw * sinPitch;
|
||||
this._view[7] = 0;
|
||||
|
||||
this._view[8] = sinYaw * cosPitch;
|
||||
this._view[9] = -sinPitch;
|
||||
this._view[10] = cosYaw * cosPitch;
|
||||
this._view[11] = 0;
|
||||
|
||||
this._view[12] = transform.position.x;
|
||||
this._view[13] = transform.position.y;
|
||||
this._view[14] = transform.position.z;
|
||||
this._view[15] = 1;
|
||||
|
||||
return this;
|
||||
}
|
||||
perspective(aspect: number, fovY: number, near: number, far: number): this
|
||||
{
|
||||
const f = Math.tan(Math.PI * 0.5 - 0.5 * fovY);
|
||||
const rangeInv = 1 / (near - far);
|
||||
|
||||
this._view[0] = f / aspect;
|
||||
this._view[1] = 0;
|
||||
this._view[2] = 0;
|
||||
this._view[3] = 0;
|
||||
|
||||
this._view[4] = 0;
|
||||
this._view[5] = f;
|
||||
this._view[6] = 0;
|
||||
this._view[7] = 0;
|
||||
|
||||
this._view[8] = 0;
|
||||
this._view[9] = 0;
|
||||
this._view[10] = far * rangeInv;
|
||||
this._view[11] = -1;
|
||||
|
||||
this._view[12] = 0;
|
||||
this._view[13] = 0;
|
||||
this._view[14] = near * far * rangeInv;
|
||||
this._view[15] = 0;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
copy(buffer: ArrayBuffer, offset?: number)
|
||||
{
|
||||
new Float32Array(buffer).set(this._view, offset);
|
||||
}
|
||||
get view()
|
||||
{
|
||||
return this._view;
|
||||
}
|
||||
}
|
||||
|
||||
//@ts-ignore
|
||||
const stars: Star[] = untypedStars;
|
||||
const constellations: Constellation[] = untypedConstellations;
|
||||
let canvas: HTMLCanvasElement, device: GPUDevice | undefined, pipeline: GPURenderPipeline, context: GPUCanvasContext | null, bindGroup: GPUBindGroup, viewport: DOMRect;
|
||||
let camera = { projection: new Matrix4x4(), view: new Matrix4x4(), transform: { position: { x: 0, y: 0, z: 0 }, yaw: 0, pitch: 0 }, final: new Matrix4x4(), _viewDirty: true, _projectionDirty: true };
|
||||
let stats: Stats;
|
||||
|
||||
function fillBuffer(buffer: ArrayBuffer)
|
||||
{
|
||||
const array = new Float32Array(buffer);
|
||||
|
||||
for(let i = 0; i < stars.length; i++)
|
||||
{
|
||||
array[i * 5 + 0] = stars[i]!.x;
|
||||
array[i * 5 + 1] = stars[i]!.y;
|
||||
array[i * 5 + 2] = stars[i]!.z;
|
||||
array[i * 5 + 3] = stars[i]!.lum;
|
||||
//array[i * 5 + 4] = stars[i]!.ci;
|
||||
}
|
||||
}
|
||||
function resize()
|
||||
{
|
||||
viewport = document.body.getBoundingClientRect();
|
||||
canvas!.width = viewport.width; canvas!.height = viewport.height;
|
||||
camera.projection.perspective(viewport.width / viewport.height, 60, 0.1, 2000);
|
||||
}
|
||||
function computeViewProjectionMatrix()
|
||||
{
|
||||
let _dirty = false;
|
||||
if(camera._viewDirty)
|
||||
{
|
||||
_dirty = true;
|
||||
camera.view.camera(camera.transform).inverse();
|
||||
camera._viewDirty = false;
|
||||
}
|
||||
if(camera._projectionDirty)
|
||||
{
|
||||
_dirty = true;
|
||||
camera.projection.perspective(viewport.width / viewport.height, 60, 0.1, 2000);
|
||||
camera._projectionDirty = false;
|
||||
}
|
||||
if(_dirty)
|
||||
{
|
||||
camera.final.multiply(camera.projection, camera.view);
|
||||
}
|
||||
}
|
||||
async function init()
|
||||
{
|
||||
canvas = document.createElement("canvas");
|
||||
context = canvas.getContext('webgpu');
|
||||
|
||||
document.body.appendChild(canvas);
|
||||
resize();
|
||||
document.addEventListener('resize', resize);
|
||||
|
||||
stats = new Stats();
|
||||
document.body.appendChild(stats.dom);
|
||||
|
||||
const adapter = await navigator.gpu.requestAdapter({ });
|
||||
device = await adapter?.requestDevice({ label: 'Device' });
|
||||
|
||||
if(device && context)
|
||||
{
|
||||
context.configure({
|
||||
device,
|
||||
format: navigator.gpu.getPreferredCanvasFormat(),
|
||||
alphaMode: 'premultiplied',
|
||||
});
|
||||
|
||||
const shader = device.createShaderModule({
|
||||
code: shaderCode,
|
||||
label: 'default-shader'
|
||||
});
|
||||
|
||||
const buffer = device.createBuffer({ size: stars.length * 4 * 4, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, mappedAtCreation: true, label: 'instanced_buffer' });
|
||||
const arrayBuffer = buffer.getMappedRange();
|
||||
|
||||
fillBuffer(arrayBuffer);
|
||||
|
||||
buffer.unmap();
|
||||
device.queue.writeBuffer(buffer, 0, arrayBuffer);
|
||||
|
||||
const uniformBuffer = device.createBuffer({ size: 16 * 4 /* matrix4x4 */, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, mappedAtCreation: true, label: 'uniform_buffer' });
|
||||
const uniformArrayBuffer = uniformBuffer.getMappedRange();
|
||||
|
||||
computeViewProjectionMatrix();
|
||||
camera.final.copy(uniformArrayBuffer);
|
||||
|
||||
uniformBuffer.unmap();
|
||||
device.queue.writeBuffer(uniformBuffer, 0, uniformArrayBuffer);
|
||||
|
||||
const bindGroupLayout = device.createBindGroupLayout({
|
||||
entries: [{
|
||||
binding: 0,
|
||||
visibility: GPUShaderStage.VERTEX,
|
||||
buffer: {
|
||||
type: 'uniform',
|
||||
}
|
||||
}, {
|
||||
binding: 1,
|
||||
visibility: GPUShaderStage.VERTEX,
|
||||
buffer: {
|
||||
type: 'read-only-storage',
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
bindGroup = device.createBindGroup({
|
||||
layout: bindGroupLayout,
|
||||
entries: [{
|
||||
binding: 0,
|
||||
resource: { buffer: uniformBuffer, }
|
||||
}, {
|
||||
binding: 1,
|
||||
resource: { buffer: buffer, }
|
||||
}]
|
||||
});
|
||||
|
||||
pipeline = device.createRenderPipeline({
|
||||
layout: device.createPipelineLayout({
|
||||
bindGroupLayouts: [ bindGroupLayout ],
|
||||
}),
|
||||
vertex: {
|
||||
module: shader,
|
||||
entryPoint: 'vertex_main'
|
||||
},
|
||||
fragment: {
|
||||
targets: [{ format: navigator.gpu.getPreferredCanvasFormat() }],
|
||||
module: shader,
|
||||
entryPoint: 'fragment_main'
|
||||
},
|
||||
primitive: {
|
||||
topology: 'triangle-list',
|
||||
cullMode: 'front',
|
||||
}
|
||||
});
|
||||
|
||||
requestAnimationFrame(render);
|
||||
}
|
||||
else
|
||||
{
|
||||
document.body.append('Erreur de chargement (aucun GPU disponible)');
|
||||
}
|
||||
}
|
||||
function render(time: DOMHighResTimeStamp)
|
||||
{
|
||||
stats.begin();
|
||||
const commandEncoder = device!.createCommandEncoder({ label: 'command-encoder' });
|
||||
const pass = commandEncoder.beginRenderPass({ label: 'render-pass', colorAttachments: [{
|
||||
loadOp: "clear",
|
||||
storeOp: "store",
|
||||
clearValue: { r: 0, g: 0, b: 0, a: 1 },
|
||||
view: context!.getCurrentTexture().createView({ label: 'render-target', usage: GPUTextureUsage.RENDER_ATTACHMENT }),
|
||||
}], });
|
||||
|
||||
pass.setPipeline(pipeline!);
|
||||
pass.setBindGroup(0, bindGroup);
|
||||
pass.setViewport(viewport.x, viewport.y, viewport.width, viewport.height, 0, 1);
|
||||
pass.draw(4, stars.length, 0, 0);
|
||||
pass.end();
|
||||
|
||||
device!.queue.submit([ commandEncoder.finish() ]);
|
||||
|
||||
stats.end();
|
||||
requestAnimationFrame(render);
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"name": "stars",
|
||||
"module": "index.ts",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.2.20"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@webgpu/types": "^0.1.64",
|
||||
"stats.ts": "^1.1.0"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
// Environment setup & latest features
|
||||
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
||||
"target": "ESNext",
|
||||
"module": "Preserve",
|
||||
"moduleDetection": "force",
|
||||
"allowJs": true,
|
||||
"types": [ "@webgpu/types", "@types/bun" ],
|
||||
|
||||
// Bundler mode
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"noEmit": true,
|
||||
|
||||
// Best practices
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitOverride": true,
|
||||
|
||||
// Some stricter flags (disabled by default)
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noPropertyAccessFromIndexSignature": false
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue