astro-viewer 3.2.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,29 @@
1
+ import { MeshHiPSShaderProgram } from '../../shader/MeshHiPSShaderProgram.js';
2
+ import type { MeshHiPSTileCoord } from './MeshHiPSTypes.js';
3
+ type Mat4 = Float32Array;
4
+ export declare class MeshHiPSTile {
5
+ readonly coord: MeshHiPSTileCoord;
6
+ private _url;
7
+ private _webgl;
8
+ private _shaderProgram;
9
+ private _gpuMesh;
10
+ private _ready;
11
+ private _loading;
12
+ private _failed;
13
+ private _lastUsedAt;
14
+ private _createdAt;
15
+ constructor(coord: MeshHiPSTileCoord, _url: string, _webgl: WebGL2RenderingContext, _shaderProgram: MeshHiPSShaderProgram);
16
+ get ready(): boolean;
17
+ get loading(): boolean;
18
+ get failed(): boolean;
19
+ get lastUsedAt(): number;
20
+ get createdAt(): number;
21
+ touch(): void;
22
+ draw(pMatrix: Mat4, vMatrix: Mat4, mMatrix: Mat4, color: [number, number, number, number], wireframe: boolean): boolean;
23
+ dispose(): void;
24
+ private load;
25
+ private uploadMesh;
26
+ private buildLineIndices;
27
+ private get key();
28
+ }
29
+ export {};
@@ -0,0 +1,148 @@
1
+ /*
2
+ * AstroViewer
3
+ * Copyright (C) Fabrizio Giordano
4
+ * SPDX-License-Identifier: LicenseRef-AstroViewer-Dual-License
5
+ */
6
+ import { OBJMeshParser } from './OBJMeshParser.js';
7
+ export class MeshHiPSTile {
8
+ coord;
9
+ _url;
10
+ _webgl;
11
+ _shaderProgram;
12
+ _gpuMesh = null;
13
+ _ready = false;
14
+ _loading = false;
15
+ _failed = false;
16
+ _lastUsedAt = 0;
17
+ _createdAt = Date.now();
18
+ constructor(coord, _url, _webgl, _shaderProgram) {
19
+ this.coord = coord;
20
+ this._url = _url;
21
+ this._webgl = _webgl;
22
+ this._shaderProgram = _shaderProgram;
23
+ void this.load();
24
+ }
25
+ get ready() {
26
+ return this._ready;
27
+ }
28
+ get loading() {
29
+ return this._loading;
30
+ }
31
+ get failed() {
32
+ return this._failed;
33
+ }
34
+ get lastUsedAt() {
35
+ return this._lastUsedAt;
36
+ }
37
+ get createdAt() {
38
+ return this._createdAt;
39
+ }
40
+ touch() {
41
+ this._lastUsedAt = Date.now();
42
+ }
43
+ draw(pMatrix, vMatrix, mMatrix, color, wireframe) {
44
+ this.touch();
45
+ if (!this._ready || !this._gpuMesh)
46
+ return false;
47
+ const gl = this._webgl;
48
+ this._shaderProgram.enableShaders(pMatrix, vMatrix, mMatrix, color);
49
+ gl.bindBuffer(gl.ARRAY_BUFFER, this._gpuMesh.positionBuffer);
50
+ gl.vertexAttribPointer(this._shaderProgram.locations.vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
51
+ gl.enableVertexAttribArray(this._shaderProgram.locations.vertexPositionAttribute);
52
+ if (this._shaderProgram.locations.vertexNormalAttribute >= 0) {
53
+ gl.bindBuffer(gl.ARRAY_BUFFER, this._gpuMesh.normalBuffer);
54
+ gl.vertexAttribPointer(this._shaderProgram.locations.vertexNormalAttribute, 3, gl.FLOAT, false, 0, 0);
55
+ gl.enableVertexAttribArray(this._shaderProgram.locations.vertexNormalAttribute);
56
+ }
57
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, wireframe ? this._gpuMesh.lineIndexBuffer : this._gpuMesh.indexBuffer);
58
+ gl.drawElements(wireframe ? gl.LINES : gl.TRIANGLES, wireframe ? this._gpuMesh.lineIndexCount : this._gpuMesh.indexCount, this._gpuMesh.indexType, 0);
59
+ gl.disableVertexAttribArray(this._shaderProgram.locations.vertexPositionAttribute);
60
+ if (this._shaderProgram.locations.vertexNormalAttribute >= 0) {
61
+ gl.disableVertexAttribArray(this._shaderProgram.locations.vertexNormalAttribute);
62
+ }
63
+ return true;
64
+ }
65
+ dispose() {
66
+ const gl = this._webgl;
67
+ if (this._gpuMesh?.positionBuffer)
68
+ gl.deleteBuffer(this._gpuMesh.positionBuffer);
69
+ if (this._gpuMesh?.normalBuffer)
70
+ gl.deleteBuffer(this._gpuMesh.normalBuffer);
71
+ if (this._gpuMesh?.indexBuffer)
72
+ gl.deleteBuffer(this._gpuMesh.indexBuffer);
73
+ if (this._gpuMesh?.lineIndexBuffer)
74
+ gl.deleteBuffer(this._gpuMesh.lineIndexBuffer);
75
+ this._gpuMesh = null;
76
+ this._ready = false;
77
+ this._loading = false;
78
+ }
79
+ async load() {
80
+ if (this._loading || this._ready)
81
+ return;
82
+ this._loading = true;
83
+ try {
84
+ const resp = await fetch(this._url);
85
+ if (!resp.ok)
86
+ throw new Error(`HTTP ${resp.status} fetching ${this._url}`);
87
+ const mesh = OBJMeshParser.parse(await resp.text());
88
+ this._gpuMesh = this.uploadMesh(mesh);
89
+ this._ready = true;
90
+ this._failed = false;
91
+ }
92
+ catch (error) {
93
+ console.warn('[MeshHiPSTile] load failed', this._url, error);
94
+ this._failed = true;
95
+ this._ready = false;
96
+ }
97
+ finally {
98
+ this._loading = false;
99
+ }
100
+ }
101
+ uploadMesh(mesh) {
102
+ const gl = this._webgl;
103
+ const positionBuffer = gl.createBuffer();
104
+ const normalBuffer = gl.createBuffer();
105
+ const indexBuffer = gl.createBuffer();
106
+ const lineIndexBuffer = gl.createBuffer();
107
+ if (!positionBuffer || !normalBuffer || !indexBuffer || !lineIndexBuffer) {
108
+ throw new Error(`Could not create MeshHiPS buffers for ${this.key}`);
109
+ }
110
+ const lineIndices = this.buildLineIndices(mesh.indices);
111
+ gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
112
+ gl.bufferData(gl.ARRAY_BUFFER, mesh.positions, gl.STATIC_DRAW);
113
+ gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
114
+ gl.bufferData(gl.ARRAY_BUFFER, mesh.normals, gl.STATIC_DRAW);
115
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
116
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, mesh.indices, gl.STATIC_DRAW);
117
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, lineIndexBuffer);
118
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, lineIndices, gl.STATIC_DRAW);
119
+ return {
120
+ positionBuffer,
121
+ normalBuffer,
122
+ indexBuffer,
123
+ lineIndexBuffer,
124
+ indexCount: mesh.indices.length,
125
+ lineIndexCount: lineIndices.length,
126
+ indexType: gl.UNSIGNED_INT,
127
+ };
128
+ }
129
+ buildLineIndices(indices) {
130
+ const lines = new Uint32Array(indices.length * 2);
131
+ let out = 0;
132
+ for (let i = 0; i < indices.length; i += 3) {
133
+ const a = indices[i];
134
+ const b = indices[i + 1];
135
+ const c = indices[i + 2];
136
+ lines[out++] = a;
137
+ lines[out++] = b;
138
+ lines[out++] = b;
139
+ lines[out++] = c;
140
+ lines[out++] = c;
141
+ lines[out++] = a;
142
+ }
143
+ return lines;
144
+ }
145
+ get key() {
146
+ return `${this.coord.order}/${this.coord.ipix}`;
147
+ }
148
+ }
@@ -0,0 +1,41 @@
1
+ export type MeshHiPSTileCoord = {
2
+ order: number;
3
+ ipix: number;
4
+ };
5
+ export type MeshHiPSConfig = {
6
+ baseUrl: string;
7
+ name?: string;
8
+ meshRadius?: number;
9
+ order?: number;
10
+ minOrder?: number;
11
+ maxOrder?: number;
12
+ maxCachedTiles?: number;
13
+ color?: [number, number, number, number];
14
+ wireframe?: boolean;
15
+ };
16
+ export type MeshHiPSDebugStats = {
17
+ activeBaseLayer: 'meships';
18
+ meshHiPSName: string;
19
+ meshHiPSUrl: string;
20
+ currentOrder: number;
21
+ visibleTileCount: number;
22
+ coverageTileCount: number;
23
+ cacheSize: number;
24
+ readyTileCount: number;
25
+ loadingTileCount: number;
26
+ failedTileCount: number;
27
+ };
28
+ export type MeshHiPSMesh = {
29
+ positions: Float32Array;
30
+ normals: Float32Array;
31
+ indices: Uint32Array;
32
+ };
33
+ export type MeshHiPSGpuMesh = {
34
+ positionBuffer: WebGLBuffer | null;
35
+ normalBuffer: WebGLBuffer | null;
36
+ indexBuffer: WebGLBuffer | null;
37
+ lineIndexBuffer: WebGLBuffer | null;
38
+ indexCount: number;
39
+ lineIndexCount: number;
40
+ indexType: number;
41
+ };
@@ -0,0 +1,6 @@
1
+ /*
2
+ * AstroViewer
3
+ * Copyright (C) Fabrizio Giordano
4
+ * SPDX-License-Identifier: LicenseRef-AstroViewer-Dual-License
5
+ */
6
+ export {};
@@ -0,0 +1,6 @@
1
+ import type { MeshHiPSMesh } from './MeshHiPSTypes.js';
2
+ export declare class OBJMeshParser {
3
+ static parse(text: string): MeshHiPSMesh;
4
+ private static computeVertexNormals;
5
+ private static resolveIndex;
6
+ }
@@ -0,0 +1,92 @@
1
+ /*
2
+ * AstroViewer
3
+ * Copyright (C) Fabrizio Giordano
4
+ * SPDX-License-Identifier: LicenseRef-AstroViewer-Dual-License
5
+ */
6
+ export class OBJMeshParser {
7
+ static parse(text) {
8
+ const vertices = [];
9
+ const indices = [];
10
+ const normals = [];
11
+ const lines = text.split(/\r\n|\n/);
12
+ for (const raw of lines) {
13
+ const line = raw.trim();
14
+ if (!line || line.startsWith('#'))
15
+ continue;
16
+ if (line.startsWith('v ')) {
17
+ const parts = line.split(/\s+/);
18
+ if (parts.length < 4)
19
+ continue;
20
+ vertices.push(Number(parts[1]), Number(parts[2]), Number(parts[3]));
21
+ normals.push(0, 0, 0);
22
+ continue;
23
+ }
24
+ if (line.startsWith('f ')) {
25
+ const face = line
26
+ .slice(2)
27
+ .trim()
28
+ .split(/\s+/)
29
+ .map((part) => Number(part.split('/')[0]))
30
+ .filter((idx) => Number.isInteger(idx) && idx !== 0);
31
+ if (face.length < 3)
32
+ continue;
33
+ const first = OBJMeshParser.resolveIndex(face[0], vertices.length / 3);
34
+ for (let i = 1; i < face.length - 1; i++) {
35
+ indices.push(first, OBJMeshParser.resolveIndex(face[i], vertices.length / 3), OBJMeshParser.resolveIndex(face[i + 1], vertices.length / 3));
36
+ }
37
+ }
38
+ }
39
+ OBJMeshParser.computeVertexNormals(vertices, indices, normals);
40
+ return {
41
+ positions: new Float32Array(vertices),
42
+ normals: new Float32Array(normals),
43
+ indices: new Uint32Array(indices),
44
+ };
45
+ }
46
+ static computeVertexNormals(vertices, indices, normals) {
47
+ for (let i = 0; i < indices.length; i += 3) {
48
+ const ia = indices[i];
49
+ const ib = indices[i + 1];
50
+ const ic = indices[i + 2];
51
+ const ax = vertices[ia * 3];
52
+ const ay = vertices[ia * 3 + 1];
53
+ const az = vertices[ia * 3 + 2];
54
+ const bx = vertices[ib * 3];
55
+ const by = vertices[ib * 3 + 1];
56
+ const bz = vertices[ib * 3 + 2];
57
+ const cx = vertices[ic * 3];
58
+ const cy = vertices[ic * 3 + 1];
59
+ const cz = vertices[ic * 3 + 2];
60
+ const abx = bx - ax;
61
+ const aby = by - ay;
62
+ const abz = bz - az;
63
+ const acx = cx - ax;
64
+ const acy = cy - ay;
65
+ const acz = cz - az;
66
+ const nx = aby * acz - abz * acy;
67
+ const ny = abz * acx - abx * acz;
68
+ const nz = abx * acy - aby * acx;
69
+ normals[ia * 3] += nx;
70
+ normals[ia * 3 + 1] += ny;
71
+ normals[ia * 3 + 2] += nz;
72
+ normals[ib * 3] += nx;
73
+ normals[ib * 3 + 1] += ny;
74
+ normals[ib * 3 + 2] += nz;
75
+ normals[ic * 3] += nx;
76
+ normals[ic * 3 + 1] += ny;
77
+ normals[ic * 3 + 2] += nz;
78
+ }
79
+ for (let i = 0; i < normals.length; i += 3) {
80
+ const nx = normals[i];
81
+ const ny = normals[i + 1];
82
+ const nz = normals[i + 2];
83
+ const len = Math.hypot(nx, ny, nz) || 1;
84
+ normals[i] = nx / len;
85
+ normals[i + 1] = ny / len;
86
+ normals[i + 2] = nz / len;
87
+ }
88
+ }
89
+ static resolveIndex(objIndex, vertexCount) {
90
+ return objIndex > 0 ? objIndex - 1 : vertexCount + objIndex;
91
+ }
92
+ }
@@ -0,0 +1,20 @@
1
+ type MeshHiPSLocations = {
2
+ pMatrix: WebGLUniformLocation | null;
3
+ mMatrix: WebGLUniformLocation | null;
4
+ vMatrix: WebGLUniformLocation | null;
5
+ color: WebGLUniformLocation | null;
6
+ vertexPositionAttribute: number;
7
+ vertexNormalAttribute: number;
8
+ };
9
+ export declare class MeshHiPSShaderProgram {
10
+ private _webgl;
11
+ readonly locations: MeshHiPSLocations;
12
+ private _shaderProgram?;
13
+ constructor(_webgl: WebGL2RenderingContext);
14
+ get shaderProgram(): WebGLProgram;
15
+ enableProgram(): void;
16
+ enableShaders(pMatrix: Float32Array, vMatrix: Float32Array, mMatrix: Float32Array, color: [number, number, number, number]): void;
17
+ private initShaders;
18
+ private compileShader;
19
+ }
20
+ export {};
@@ -0,0 +1,98 @@
1
+ /*
2
+ * AstroViewer
3
+ * Copyright (C) Fabrizio Giordano
4
+ * SPDX-License-Identifier: LicenseRef-AstroViewer-Dual-License
5
+ */
6
+ export class MeshHiPSShaderProgram {
7
+ _webgl;
8
+ locations;
9
+ _shaderProgram;
10
+ constructor(_webgl) {
11
+ this._webgl = _webgl;
12
+ this.locations = {
13
+ pMatrix: null,
14
+ mMatrix: null,
15
+ vMatrix: null,
16
+ color: null,
17
+ vertexPositionAttribute: -1,
18
+ vertexNormalAttribute: -1,
19
+ };
20
+ }
21
+ get shaderProgram() {
22
+ const gl = this._webgl;
23
+ if (!this._shaderProgram) {
24
+ const program = gl.createProgram();
25
+ if (!program)
26
+ throw new Error('Could not create MeshHiPS shader program');
27
+ this._shaderProgram = program;
28
+ this.initShaders();
29
+ }
30
+ gl.useProgram(this._shaderProgram);
31
+ return this._shaderProgram;
32
+ }
33
+ enableProgram() {
34
+ this._webgl.useProgram(this.shaderProgram);
35
+ }
36
+ enableShaders(pMatrix, vMatrix, mMatrix, color) {
37
+ const gl = this._webgl;
38
+ const program = this.shaderProgram;
39
+ gl.useProgram(program);
40
+ this.locations.pMatrix = gl.getUniformLocation(program, 'uPMatrix');
41
+ this.locations.vMatrix = gl.getUniformLocation(program, 'uVMatrix');
42
+ this.locations.mMatrix = gl.getUniformLocation(program, 'uMMatrix');
43
+ this.locations.color = gl.getUniformLocation(program, 'uColor');
44
+ this.locations.vertexPositionAttribute = gl.getAttribLocation(program, 'aVertexPosition');
45
+ this.locations.vertexNormalAttribute = gl.getAttribLocation(program, 'aVertexNormal');
46
+ gl.uniformMatrix4fv(this.locations.pMatrix, false, pMatrix);
47
+ gl.uniformMatrix4fv(this.locations.vMatrix, false, vMatrix);
48
+ gl.uniformMatrix4fv(this.locations.mMatrix, false, mMatrix);
49
+ gl.uniform4fv(this.locations.color, color);
50
+ }
51
+ initShaders() {
52
+ const gl = this._webgl;
53
+ const vertexShader = this.compileShader(gl.VERTEX_SHADER, `#version 300 es
54
+ precision mediump float;
55
+ in vec3 aVertexPosition;
56
+ in vec3 aVertexNormal;
57
+ uniform mat4 uPMatrix;
58
+ uniform mat4 uVMatrix;
59
+ uniform mat4 uMMatrix;
60
+ out vec3 vNormal;
61
+ void main(void) {
62
+ vec3 worldPos = (uMMatrix * vec4(aVertexPosition, 1.0)).xyz;
63
+ vNormal = normalize((uMMatrix * vec4(aVertexNormal, 0.0)).xyz);
64
+ gl_Position = uPMatrix * uVMatrix * vec4(worldPos, 1.0);
65
+ }`);
66
+ const fragmentShader = this.compileShader(gl.FRAGMENT_SHADER, `#version 300 es
67
+ precision mediump float;
68
+ in vec3 vNormal;
69
+ uniform vec4 uColor;
70
+ out vec4 outColor;
71
+ void main(void) {
72
+ vec3 normal = normalize(vNormal);
73
+ vec3 lightDir = normalize(vec3(0.45, 0.8, 0.35));
74
+ float diffuse = max(dot(normal, lightDir), 0.0);
75
+ float rim = pow(1.0 - max(dot(normal, vec3(0.0, 0.0, 1.0)), 0.0), 2.0);
76
+ vec3 color = uColor.rgb * (0.24 + diffuse * 0.78) + vec3(0.16, 0.24, 0.20) * rim;
77
+ outColor = vec4(color, uColor.a);
78
+ }`);
79
+ gl.attachShader(this._shaderProgram, vertexShader);
80
+ gl.attachShader(this._shaderProgram, fragmentShader);
81
+ gl.linkProgram(this._shaderProgram);
82
+ if (!gl.getProgramParameter(this._shaderProgram, gl.LINK_STATUS)) {
83
+ throw new Error(gl.getProgramInfoLog(this._shaderProgram) || 'Could not initialise MeshHiPS shaders');
84
+ }
85
+ }
86
+ compileShader(type, source) {
87
+ const gl = this._webgl;
88
+ const shader = gl.createShader(type);
89
+ if (!shader)
90
+ throw new Error('Could not create MeshHiPS shader');
91
+ gl.shaderSource(shader, source);
92
+ gl.compileShader(shader);
93
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
94
+ throw new Error(gl.getShaderInfoLog(shader) || 'MeshHiPS shader compile error');
95
+ }
96
+ return shader;
97
+ }
98
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "astro-viewer",
3
3
  "description": "Astrobrowser 3d engine.",
4
- "version": "3.2.0",
4
+ "version": "3.3.0",
5
5
  "keywords": [
6
6
  "HiPS",
7
7
  "HiPS cutout",
@@ -48,9 +48,10 @@
48
48
  "dev:lib": "tsc -w -p tsconfig.build.json",
49
49
  "prod": "npm run clean && tsc -p tsconfig.build.json && webpack --mode=production && npm run web",
50
50
  "build": "npm run clean && tsc -p tsconfig.build.json && webpack --mode=production",
51
- "web": "cp dist/* public/javascripts/; cp -R src/html/javascripts/ public/javascripts/; cp -R src/html/css/ public/css/; cp src/html/*.html public/",
52
- "start-server": "npm run web; python3 -m http.server 8080 -d public",
53
- "all": "npm run clean; npm run prod; npm run start-server",
51
+ "test": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js",
52
+ "web": "cp dist/* public/javascripts/ && cp -R src/html/javascripts/ public/javascripts/ && cp -R src/html/css/ public/css/ && cp src/html/*.html public/",
53
+ "start-server": "npm run build && npm run web && python3 -m http.server 8080 -d public",
54
+ "all": "npm run start-server",
54
55
  "prepublishOnly": "npm run build"
55
56
  },
56
57
  "engines": {