@pirireis/webglobeplugins 0.8.6 → 0.8.8

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.
Files changed (61) hide show
  1. package/bearing-line/plugin.js +51 -43
  2. package/circle-line-chain/plugin.js +52 -44
  3. package/compass-rose/compass-rose-padding-flat.js +3 -0
  4. package/compassrose/compassrose.js +1 -8
  5. package/heatwave/isobar/plugin.js +4 -1
  6. package/heatwave/isobar/quadtreecontours.js +0 -2
  7. package/heatwave/plugins/heatwaveglobeshell.js +2 -0
  8. package/package.json +1 -1
  9. package/partialrings/goals.md +2 -0
  10. package/partialrings/plugin.js +2 -1
  11. package/point-glow-line-to-earth/draw-subset-obj.js +27 -0
  12. package/point-glow-line-to-earth/keymethod.js +0 -0
  13. package/point-glow-line-to-earth/plugin.js +439 -0
  14. package/point-glow-line-to-earth/types.js +26 -0
  15. package/point-heat-map/index.js +0 -3
  16. package/point-heat-map/plugin-webworker.js +4 -9
  17. package/point-heat-map/point-to-heat-map-flow.js +0 -6
  18. package/point-tracks/plugin.js +4 -4
  19. package/programs/arrowfield/logic.js +6 -4
  20. package/programs/arrowfield/object.js +1 -1
  21. package/programs/float2legendwithratio/object.js +5 -4
  22. package/programs/globe-util/is-globe-moved.js +27 -0
  23. package/programs/globeshell/wiggle/logic.js +3 -7
  24. package/programs/globeshell/wiggle/object.js +1 -2
  25. package/programs/line-on-globe/circle-accurate-3d.js +16 -23
  26. package/programs/line-on-globe/circle-accurate-flat.js +21 -21
  27. package/programs/line-on-globe/lines-color-instanced-flat.js +6 -7
  28. package/programs/line-on-globe/naive-accurate-flexible.js +239 -0
  29. package/programs/line-on-globe/to-the-surface.js +129 -0
  30. package/programs/picking/pickable-renderer.js +216 -0
  31. package/programs/point-on-globe/element-globe-surface-glow.js +168 -0
  32. package/programs/point-on-globe/element-point-glow.js +184 -0
  33. package/programs/point-on-globe/square-pixel-point.js +2 -4
  34. package/programs/programcache.js +9 -0
  35. package/programs/rings/partial-ring/piece-of-pie.js +1 -1
  36. package/programs/totems/camerauniformblock.js +23 -2
  37. package/rangerings/plugin.js +9 -6
  38. package/shaders/fragment-toy/firework.js +55 -0
  39. package/shaders/fragment-toy/singularity.js +59 -0
  40. package/types.js +16 -0
  41. package/util/account/single-attribute-buffer-management/buffer-manager.js +1 -5
  42. package/util/account/util.js +17 -7
  43. package/util/check/typecheck.js +11 -0
  44. package/util/gl-util/buffer/integrate-buffer.js +74 -0
  45. package/util/gl-util/draw-options/client.js +59 -0
  46. package/util/gl-util/draw-options/methods.js +46 -0
  47. package/util/gl-util/draw-options/types.js +18 -0
  48. package/util/gl-util/uniform-block/manager.js +176 -0
  49. package/util/gl-util/uniform-block/roadmap.md +70 -0
  50. package/util/gl-util/uniform-block/shader.js +0 -0
  51. package/util/gl-util/uniform-block/types.js +7 -0
  52. package/util/jshelpers/equality.js +17 -0
  53. package/util/picking/picker-displayer.js +1 -1
  54. package/util/programs/shapesonglobe.js +17 -19
  55. package/util/shaderfunctions/geometrytransformations.js +27 -63
  56. package/bearing-line/roadmap.md +0 -15
  57. package/point-heat-map/plugin.js +0 -132
  58. package/programs/line-on-globe/naive-accurate.js +0 -221
  59. package/programs/line-on-globe/to-the-origin.js +0 -164
  60. package/util/jshelpers/timemethods.js +0 -19
  61. /package/{programs/point-on-globe/element-draw-glow.js → point-glow-line-to-earth/adaptors.js} +0 -0
@@ -52,11 +52,11 @@ import { mapGetOrThrow } from "../util/check/get";
52
52
  import { populateFloat32Array } from "../util/jshelpers/data-filler";
53
53
  import { RingAccount, ringBigPaddingKeyMethod, ringKeyMethod } from "./ring-account";
54
54
  import { CirclePadding3DCache } from "../programs/line-on-globe/degree-padding-around-circle-3d";
55
- import { LineOnGlobeCache } from '../programs/line-on-globe/naive-accurate';
55
+ import { LineOnGlobeCache } from '../programs/line-on-globe/naive-accurate-flexible';
56
56
  import RangeRingAngleText from "./rangeringangletext";
57
57
  import { Z_ALPHA_MODE } from "../programs/line-on-globe/util";
58
58
  import { ENUM_HIDE } from "./enum";
59
-
59
+ import { opacityCheck, constraintFloat } from "../util/check/typecheck";
60
60
  const CIRCLE_FLAT_EDGE_COUNT = 362; // 360 + 2 for closing the circle and a cutting point
61
61
 
62
62
 
@@ -274,7 +274,7 @@ class RangeRings {
274
274
  * @method setOpacity @param { number } opacity
275
275
  */
276
276
  setOpacity(opacity) {
277
- if (typeof opacity !== "number" || opacity < 0 || opacity > 1) throw new Error("Invalid value for opacity");
277
+ opacityCheck(opacity);
278
278
  this._opacity = opacity;
279
279
  this.paddingTextPlugin?.setOpacity(opacity);
280
280
  this._textWritersMap.forEach((writer) => writer.setOpacity(opacity));
@@ -378,6 +378,7 @@ class RangeRings {
378
378
  );
379
379
 
380
380
  const obj = function (bufferManagerComp, divisor = 1) {
381
+ if (bufferManagerComp === null) return null;
381
382
  return { 'buffer': bufferManagerComp.bufferManager.buffer, 'stride': 0, 'offset': 0, divisor }
382
383
 
383
384
  };
@@ -431,8 +432,9 @@ class RangeRings {
431
432
  ]
432
433
  );
433
434
 
434
- this._bigPadding3dFlatVAO = this._bigPadding3dFlatProgram.createVAO(
435
- ...["circlePoint", "circlePoint3d", "paddingPoint", "paddingPoint3d", "dashRatio", "dashOpacity", "rgba",].map(key => obj(this.bufferManagersCompMapPadding.get(key)))
435
+ this._bigPadding3dFlatVAO = this._bigPadding3dFlatProgram.createVAO(//"dashRatio", "dashOpacity"
436
+ ...["circlePoint", "circlePoint3d", "paddingPoint", "paddingPoint3d", null, null, "rgba",].map(key =>
437
+ (key === null) ? null : obj(this.bufferManagersCompMapPadding.get(key)))
436
438
  );
437
439
 
438
440
  this._padding3dOneDegreeVao = this._padding3dProgram.createVAO(
@@ -575,7 +577,8 @@ class RangeRings {
575
577
  // _padding2dProgram.draw(bigPaddingVAO, paddingBufferOrchestrator.length, _opacity);
576
578
  if (_oneDegreePadding) _padding2dProgram.draw(_oneDegree2DPaddingVao, bufferOrchestrator.length * CIRCLE_FLAT_EDGE_COUNT, _opacity, _zAlphaOnDegreePadding);
577
579
  }
578
- _bigPadding3dFlatProgram.draw(_bigPadding3dFlatVAO, paddingBufferOrchestrator.length, _opacity);
580
+ const drawOptions = { drawRange: { first: 0, count: bufferOrchestrator.length } };
581
+ _bigPadding3dFlatProgram.draw(_bigPadding3dFlatVAO, drawOptions, _opacity);
579
582
  this._textWritersMap.forEach((textWriter) => textWriter.draw());
580
583
  gl.enable(gl.DEPTH_TEST);
581
584
  }
@@ -0,0 +1,55 @@
1
+ export const firework = `
2
+ float firework(vec2 point_coord, float phase) {
3
+ float t = phase + 5.0;
4
+ float z = 6.0;
5
+ const int n = 53; // particle count
6
+
7
+ vec3 startColor = vec3(0.0, 0.64, 0.2);
8
+ vec3 endColor = vec3(0.06, 0.35, 0.85);
9
+
10
+ float startRadius = 0.84;
11
+ float endRadius = 1.6;
12
+
13
+ float power = 0.51;
14
+ float duration = 4.0;
15
+
16
+ vec2 v = z * (2.0 * point_coord - vec2(1.0) );
17
+
18
+ vec3 col = vec3(0.0);
19
+ vec2 pm = v.yx * 2.8;
20
+
21
+ float dMax = duration;
22
+ float evo = (sin(phase * 0.01 + 400.0) * 0.5 + 0.5) * 99.0 + 1.0;
23
+
24
+ float mb = 0.0;
25
+ float mbRadius = 0.0;
26
+ float sum = 0.0;
27
+
28
+ for(int i = 0; i < n; i++) {
29
+ float d = fract(t * power + 48934.4238 * sin(float(i / int(evo)) * 692.7398));
30
+
31
+ float a = 6.28 * float(i) / float(n);
32
+ float x = d * cos(a) * duration;
33
+ float y = d * sin(a) * duration;
34
+
35
+ float distRatio = d / dMax;
36
+ mbRadius = mix(startRadius, endRadius, distRatio);
37
+
38
+ vec2 p = v - vec2(x, y);
39
+ mb = mbRadius / dot(p, p);
40
+
41
+ sum += mb;
42
+ col = mix(col, mix(startColor, endColor, distRatio), mb / sum);
43
+ }
44
+
45
+ sum /= float(n);
46
+ col = normalize(col) * sum;
47
+ sum = clamp(sum, 0.0, 0.4);
48
+
49
+ vec3 tex = vec3(1.0);
50
+ col *= smoothstep(tex, vec3(0.0), vec3(sum));
51
+
52
+ float alpha = sum * 2.5;
53
+ return clamp(alpha, 0.0, 1.0);
54
+ }
55
+ `
@@ -0,0 +1,59 @@
1
+
2
+
3
+ const singularity = `
4
+
5
+ vec3 palette(float d){
6
+ return mix(vec3(0.2, 0.7, 0.9), vec3(1.0, 0.0, 1.0), d);
7
+ }
8
+
9
+ vec2 rotate(vec2 p, float a){
10
+ float c = cos(a);
11
+ float s = sin(a);
12
+ return p * mat2(c, s, -s, c);
13
+ }
14
+
15
+ float map(vec3 p){
16
+ for (int i = 0; i < 4; ++i){
17
+ float t = u_phase * 0.2;
18
+ p.xz = rotate(p.xz, t);
19
+ p.xy = rotate(p.xy, t * 1.89);
20
+ p.xz = abs(p.xz);
21
+ p.xz -= 0.85;
22
+ }
23
+ return dot(sign(p), p) / 5.0;
24
+ }
25
+
26
+ vec4 rm(vec3 ro, vec3 rd){
27
+ float t = 0.0;
28
+ vec3 col = vec3(0.0);
29
+ float d;
30
+ for (float i = 0.0; i < 56.0; i++){
31
+ vec3 p = ro + rd * t;
32
+ d = map(p) * 0.5;
33
+ if (d < 0.02){
34
+ break;
35
+ }
36
+ if (d > 100.0){
37
+ break;
38
+ }
39
+ col += palette(length(p) * 0.1) / (400.0 * d);
40
+ t += d;
41
+ }
42
+ return vec4(col, 1.0 / d * 2.0 );
43
+ }
44
+
45
+ vec4 singularity(vec2 pointCoord, float u_phase) {
46
+ vec2 uv = (pointCoord - 0.5); // Normalized coordinates from gl_PointCoord
47
+ vec3 ro = vec3(0.0, 0.0, -50.0);
48
+ ro.xz = rotate(ro.xz, u_phase);
49
+ vec3 cf = normalize(-ro);
50
+ vec3 cs = normalize(cross(cf, vec3(0.0, 1.0, 0.0)));
51
+ vec3 cu = normalize(cross(cf, cs));
52
+
53
+ vec3 uuv = ro + cf * 3.0 + uv.x * cs + uv.y * cu;
54
+ vec3 rd = normalize(uuv - ro);
55
+
56
+ return rm(ro, rd);
57
+ }`;
58
+
59
+ export { singularity };
package/types.js ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @typedef DrawRange
3
+ * @type {Object}
4
+ * @property {int} first
5
+ * @property {int} count
6
+ *
7
+ * @typedef DrawRangeIndexParamsClient
8
+ * @type {Object}
9
+ * @property {null|DrawRange} drawRange
10
+ * @property {null|Int32List} indexes
11
+ */
12
+
13
+
14
+ /**
15
+ * * @typedef {Array<number>} Color rgba color 0-1 values
16
+ */
@@ -40,7 +40,6 @@ export class BufferManager {
40
40
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
41
41
  for (let offset of offsets) {
42
42
  if (offset !== undefined) {
43
-
44
43
  gl.bufferSubData(gl.ARRAY_BUFFER, offset * offsetMultiplier, emptyBlock);
45
44
  }
46
45
  }
@@ -108,7 +107,4 @@ export class BufferManager {
108
107
  this.gl = null;
109
108
  this.isFreed = true;
110
109
  }
111
- }
112
-
113
-
114
-
110
+ }
@@ -1,12 +1,22 @@
1
- export const vaoAttributeLoader = (gl, buffer, position, length, stride, offset, divisor = null, type = null) => {
2
- if (type === null) {
3
- type = gl.FLOAT;
1
+ export const vaoAttributeLoader = (gl, buffer, index, size, stride, offset, divisor = null, type = null) => {
2
+ if (!gl || !buffer) {
3
+ throw new Error("Invalid WebGL context or buffer");
4
4
  }
5
+ if (index < 0) {
6
+ throw new Error("Attribute index must be non-negative");
7
+ }
8
+ if (stride < 0 || offset < 0) {
9
+ throw new Error("Stride and offset must be non-negative");
10
+ }
11
+
12
+ const attribType = type === null ? gl.FLOAT : type;
5
13
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
6
- gl.enableVertexAttribArray(position);
7
- gl.vertexAttribPointer(position, length, type, false, stride, offset);
14
+ gl.enableVertexAttribArray(index);
15
+ gl.vertexAttribPointer(index, size, attribType, false, stride, offset);
8
16
  if (divisor !== null) {
9
- gl.vertexAttribDivisor(position, divisor);
17
+ gl.vertexAttribDivisor(index, divisor);
10
18
  }
11
19
 
12
- }
20
+ }
21
+
22
+
@@ -26,3 +26,14 @@ export const isTextFont = (textFont) => {
26
26
 
27
27
  }
28
28
 
29
+ export const constraintFloat = (x, lowerBound = null, upperBound = null) => {
30
+ if (typeof x !== "number") throw new Error("type must be numberic");
31
+ if (lowerBound === null && upperBound === null) return;
32
+ if (lowerBound !== null && lowerBound > x) throw new Error(`input must be greater than ${lowerBound}`);
33
+ if (upperBound !== null && x > upperBound) throw new Error(`input must be less than ${upperBound}`);
34
+ }
35
+
36
+
37
+ export const isBoolean = (x) => {
38
+ if (typeof x !== "boolean") throw new TypeError("type must be boolean");
39
+ }
@@ -0,0 +1,74 @@
1
+
2
+ /**
3
+ * @typedef BufferAndReadInfo Buffers can be intertwined or interleaved.
4
+ * This object forces user to adapt generic convention of buffer and read information.
5
+ * @type {Object}
6
+ * @property {WebGLBuffer} buffer
7
+ * @property {number} stride
8
+ * @property {number} offset
9
+ */
10
+
11
+ /**
12
+ *
13
+ * @param {WebGLBuffer} gl
14
+ * @param {BufferAndReadInfo} bufferAndReadInfo
15
+ * @param {number} index
16
+ * @param {number} size
17
+ * @param {Object} options
18
+ * @param {*} options.type | default gl.FLOAT, gl.UNSIGNED_BYTE, gl.SHORT, gl.UNSIGNED_SHORT, gl.INT, gl.UNSIGNED_INT
19
+ * @param {number} options.divisor
20
+ * @param {Array<number>} options.escapeValues
21
+ * @returns
22
+ */
23
+
24
+ const attributeLoader = (gl, bufferAndReadInfo, index, size, { divisor = null, type = null, escapeValues = null, normalized = false } = {}) => {
25
+ if (size < 1 || size > 4) throw new Error("Size must be between 1 and 4");
26
+ if (bufferAndReadInfo == null) {
27
+ if (escapeValues !== null) constantFunction(gl, index, size, escapeValues);
28
+ return;
29
+ }
30
+
31
+ const { buffer, stride, offset } = bufferAndReadInfo;
32
+ if (!gl || !buffer) {
33
+ throw new Error("Invalid WebGL context or buffer");
34
+ }
35
+ if (index < 0) {
36
+ throw new Error("Attribute index must be non-negative");
37
+ }
38
+ if (stride < 0 || offset < 0) {
39
+ throw new Error("Stride and offset must be non-negative");
40
+ }
41
+ console.log("stride", stride, "offset", offset);
42
+ const attribType = type === null ? gl.FLOAT : type;
43
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
44
+ gl.enableVertexAttribArray(index);
45
+ gl.vertexAttribPointer(index, size, attribType, normalized, stride, offset);
46
+ if (divisor !== null) {
47
+ gl.vertexAttribDivisor(index, divisor);
48
+ }
49
+
50
+ }
51
+
52
+ /**
53
+ *
54
+ * @param {WebGLBuffer} buffer
55
+ * @param {number} stride
56
+ * @param {number} offset
57
+ * @returns {BufferAndReadInfo}
58
+ */
59
+ const createBufferAndReadInfo = (buffer, stride = 0, offset = 0) => {
60
+ if (buffer == null) return null;
61
+ console.log("createBufferAndReadInfo", buffer, stride, offset);
62
+ return { buffer, stride, offset };
63
+ }
64
+
65
+
66
+
67
+ const constantFunction = (gl, index, size, escapeValues) => {
68
+ const func = `vertexAttrib${size}f`;
69
+ console.log(`Using constant function ${func}`, gl[func], escapeValues);
70
+ gl[func](index, ...escapeValues);
71
+ }
72
+
73
+
74
+ export { attributeLoader, createBufferAndReadInfo };
@@ -0,0 +1,59 @@
1
+
2
+
3
+ class DrawOptionsClient {
4
+
5
+ /**
6
+ * @typedef DrawOptionsClientAdaptor
7
+ * @type {Object}
8
+ * @property {function} setElements
9
+ * @property {function} setDrawRange
10
+ * @property {function} free
11
+ *
12
+ * @param {*} gl
13
+ * @param {*} adaptorsMap
14
+ */
15
+ constructor(gl, adaptorsMap) {
16
+ this.gl = gl;
17
+ this.adaptorsMap = adaptorsMap;
18
+ this.elementBuffers = new Map();
19
+ }
20
+
21
+ __init() {
22
+ const { gl, adaptorsMap } = this;
23
+ // create element buffer for each element buffer type
24
+ for (const [key, value] of Object.entries(adaptorsMap)) {
25
+ const elementBuffer = gl.createBuffer();
26
+ this.elementBuffers.set(key, elementBuffer);
27
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuffer);
28
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, value, gl.DYNAMIC_DRAW);
29
+ }
30
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
31
+
32
+
33
+ }
34
+
35
+
36
+ setDrawRange() {
37
+
38
+ }
39
+
40
+
41
+ /**
42
+ *
43
+ * @param {null|Int32List|Int32Array} elementIndexes
44
+ */
45
+ setElements(elementIndexes = null) {
46
+
47
+ }
48
+
49
+ getDrawOptions() {
50
+
51
+ }
52
+
53
+
54
+
55
+
56
+ free() {
57
+
58
+ }
59
+ }
@@ -0,0 +1,46 @@
1
+ import './types'
2
+
3
+ /**
4
+ *
5
+ * @param {WebGL2RenderingContext} gl
6
+ * @param {WebGL2RenderingContext.DrawMode} mode
7
+ * @param {DrawRangeIndexParams} drawOptions
8
+ */
9
+
10
+
11
+ // first and element effects the vertexID, which is unfortunately not effected
12
+ const drawInstanced = (gl, mode, drawOptions, vertexCount) => {
13
+ const { drawRange, indexType = gl.UNSIGNED_INT } = drawOptions;
14
+ const { first = 0, count: instanceCount = 1 } = drawRange;
15
+ if (first > 0 || drawOptions.elementBuffer) {
16
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, drawOptions.elementBuffer);
17
+ gl.drawElementsInstanced(mode, vertexCount, indexType, first, instanceCount);
18
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
19
+ } else {
20
+
21
+ gl.drawArraysInstanced(mode, first, vertexCount, instanceCount);
22
+ }
23
+ }
24
+
25
+
26
+ /**
27
+ *
28
+ * @param {WebGL2RenderingContext} gl
29
+ * @param {WebGL2RenderingContext.DrawMode} defaultMode
30
+ * @param {DrawRangeIndexParams} drawOptions
31
+ */
32
+
33
+ const drawArrays = (gl, defaultMode, drawOptions) => {
34
+ const { drawRange, elementBuffer, indexType = gl.UNSIGNED_INT, drawMode = null } = drawOptions;
35
+ const { first, count } = drawRange;
36
+ const mode = drawMode ?? defaultMode;
37
+ if (elementBuffer) {
38
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuffer);
39
+ gl.drawElements(mode, count, indexType, first);
40
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
41
+ } else {
42
+ gl.drawArrays(mode, first, count);
43
+ }
44
+ }
45
+
46
+ export { drawArrays, drawInstanced }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @typedef DrawRange
3
+ * @type {Object}
4
+ * @property {int} first
5
+ * @property {int} count
6
+ *
7
+ * @typedef DrawRangeIndexParams
8
+ * @type {Object}
9
+ * @property {null|DrawRange} drawRange
10
+ * @property {null|ElementBuffer} elementBuffer
11
+ * @property {null|indexType} indexType
12
+ * @property {null|int} drawMode
13
+ *
14
+ */
15
+
16
+
17
+
18
+
@@ -0,0 +1,176 @@
1
+ import "./types";
2
+
3
+ const typeSizes = {
4
+ 'float': 4,
5
+ 'vec2': 8,
6
+ 'vec3': 16, // vec3 takes up same space as vec4 due to alignment
7
+ 'vec4': 16,
8
+ 'mat2': 32, // 2 vec4s (16 * 2)
9
+ 'mat3': 48, // 3 vec4s (16 * 3)
10
+ 'mat4': 64, // 4 vec4s (16 * 4)
11
+ 'int': 4,
12
+ 'ivec2': 8,
13
+ 'ivec3': 16,
14
+ 'ivec4': 16,
15
+ 'bool': 4
16
+ };
17
+
18
+ const typeAlignments = {
19
+ 'float': 4,
20
+ 'vec2': 8,
21
+ 'vec3': 16,
22
+ 'vec4': 16,
23
+ 'mat2': 16, // aligns to vec4 boundary
24
+ 'mat3': 16, // aligns to vec4 boundary
25
+ 'mat4': 16, // aligns to vec4 boundary
26
+ 'int': 4,
27
+ 'ivec2': 8,
28
+ 'ivec3': 16,
29
+ 'ivec4': 16,
30
+ 'bool': 4
31
+ };
32
+
33
+
34
+ const typeArrayConstructors = {
35
+ 'float': Float32Array,
36
+ 'vec2': Float32Array,
37
+ 'vec3': Float32Array,
38
+ 'vec4': Float32Array,
39
+ 'mat2': Float32Array,
40
+ 'mat3': Float32Array,
41
+ 'mat4': Float32Array,
42
+ 'int': Int32Array,
43
+ 'ivec2': Int32Array,
44
+ 'ivec3': Int32Array,
45
+ 'ivec4': Int32Array,
46
+ 'bool': Float32Array
47
+ };
48
+
49
+
50
+ class UniformBlockManager {
51
+
52
+ /**
53
+ *
54
+ * @param {UniformBlockName} blockName
55
+ * @param {Array<UniformBlockMember} blockMembers
56
+ * @param {string} prefix usage name of block in the shader
57
+ */
58
+ constructor(blockName, blockMembers, bindingPoint, prefix = "") {
59
+ this.blockName = blockName;
60
+ this.blockMembers = blockMembers;
61
+ this.bindingPoint = bindingPoint;
62
+ this.prefix = prefix;
63
+ this.offsetMap = this.__create_LayoutSTD140_OffsetMap();
64
+ this.size = this.__calculateSize();
65
+ }
66
+
67
+
68
+
69
+ glslCode() {
70
+ let code = `layout(std140) uniform ${this.blockName} {`;
71
+ for (const member of this.blockMembers) {
72
+ const { name, type } = member;
73
+ code += `${type} ${this.prefix}${name};`;
74
+ }
75
+ if (this.prefix) {
76
+ code += `} ${this.prefix};`;
77
+ } else {
78
+ code += `};`;
79
+ }
80
+ return code;
81
+ }
82
+
83
+
84
+ createUBO(gl, bufferWriteType = "STATIC_DRAW") {
85
+ const ubo = gl.createBuffer();
86
+ gl.bindBuffer(gl.UNIFORM_BUFFER, ubo);
87
+ gl.bufferData(gl.UNIFORM_BUFFER, this.size, gl[bufferWriteType]);
88
+ gl.bindBufferBase(gl.UNIFORM_BUFFER, 0, ubo);
89
+
90
+ for (const member of this.blockMembers) {
91
+ const { name, type, value = null } = member;;
92
+ const offset = this.offsetMap.get(name);
93
+ const data = (value === null) ? new typeArrayConstructors[type](typeSizes[type] / 4).fill(NaN) : value;
94
+ gl.bufferSubData(gl.UNIFORM_BUFFER, offset, data);
95
+ }
96
+ gl.bindBuffer(gl.UNIFORM_BUFFER, null);
97
+
98
+ return { // TODO typedef this, encapsulate this in a class or something, ask ai
99
+ ubo,
100
+ update: this.updateUBO.bind(this, gl, ubo),
101
+ bind: this.bind.bind(this, gl, ubo),
102
+ unbind: this.unbind.bind(this, gl),
103
+ free: () => gl.deleteBuffer(ubo)
104
+ };
105
+ }
106
+
107
+
108
+ updateUBO(gl, ubo, nameValueMap) {
109
+ gl.bindBuffer(gl.UNIFORM_BUFFER, ubo);
110
+ for (const [name, value] of nameValueMap.entries()) {
111
+ const offset = this.offsetMap.get(name);
112
+ const type = this.blockMembers.find(member => member.name === name).type;
113
+ if (offset === undefined) {
114
+ throw new Error(`Uniform block member ${name} not found in offset map.`);
115
+ }
116
+ let data;
117
+ if (Array.isArray(value)) {
118
+ data = new typeArrayConstructors[type](value);
119
+ } else if (typeof value === 'number') {
120
+ data = new typeArrayConstructors[type]([value]);
121
+ } else if (value instanceof ArrayBuffer) {
122
+ data = new typeArrayConstructors[type](value);
123
+ } else if (ArrayBuffer.isView(value) && !(value instanceof DataView))
124
+ data = (typeof value === 'number') ? new typeArrayConstructors[type]([value]) : new typeArrayConstructors[type](value);
125
+ else {
126
+ throw new Error(`Unsupported value type for ${name}: ${typeof value}`);
127
+ }
128
+ gl.bufferSubData(gl.UNIFORM_BUFFER, offset, data);
129
+ }
130
+ gl.bindBuffer(gl.UNIFORM_BUFFER, null);
131
+ }
132
+
133
+ // call this after linking the program
134
+ assignBindingPoint(gl, program) {
135
+ const blockIndex = gl.getUniformBlockIndex(program, this.blockName);
136
+ gl.uniformBlockBinding(program, blockIndex, this.bindingPoint);
137
+ }
138
+
139
+ // call this before drawing
140
+ bind(gl, ubo) {
141
+ gl.bindBufferBase(gl.UNIFORM_BUFFER, this.bindingPoint, ubo);
142
+ }
143
+
144
+ // call this right after drawing
145
+ unbind(gl) {
146
+ gl.bindBufferBase(gl.UNIFORM_BUFFER, this.bindingPoint, null);
147
+ }
148
+
149
+
150
+ // implicit methods
151
+ __create_LayoutSTD140_OffsetMap() {
152
+ let offset = 0;
153
+ const offsetMap = new Map();
154
+ for (const member of this.blockMembers) {
155
+ const { name, type } = member;
156
+ const size = typeSizes[type];
157
+ const alignment = typeAlignments[type];
158
+ const padding = (alignment - (offset % alignment)) % alignment;
159
+ offset += padding;
160
+ offsetMap.set(name, offset);
161
+ offset += size;
162
+ }
163
+ return offsetMap;
164
+ }
165
+
166
+ // __create_LayoutSTD140_OffsetMap() should be called before this
167
+ __calculateSize() {
168
+ const lastOffset = Array.from(this.offsetMap.values()).pop();
169
+ const lastItemAlignment = typeAlignments[this.blockMembers[this.blockMembers.length - 1].type];
170
+ return lastOffset + lastItemAlignment;
171
+ }
172
+
173
+ }
174
+
175
+
176
+ export { UniformBlockManager };
@@ -0,0 +1,70 @@
1
+ # UniformBlock Wrapper
2
+
3
+ - Uniform shader string
4
+ - bind/unbind
5
+ - set/get uniform value generic
6
+
7
+ ## INPUT
8
+ - uniform block name
9
+ - Array<{type, name}> UniformBlockMembers
10
+
11
+
12
+ ## PROCESS
13
+
14
+ - Calculate and save starting offset of each member
15
+ - Create UBO
16
+ - Bind UBO
17
+ - Set uniform value
18
+
19
+ ### Calculate and save starting offset of each member
20
+
21
+ layout(std140) is a convention. The calculation should be done according to this convention.
22
+
23
+
24
+ type | size
25
+ --- | ---
26
+ float | 4
27
+ vec2 | 8
28
+ vec3 | 12
29
+ vec4 | 16
30
+ mat2 | 16
31
+ mat3 | 48
32
+ mat4 | 64
33
+
34
+ A line is 16 bytes aligned. Alignment can be shared if previous member is equal or bigger then the next member and the sum of the sizes are smaller then 16 bytes.
35
+
36
+
37
+
38
+
39
+
40
+
41
+
42
+
43
+
44
+
45
+ ## setMethod
46
+
47
+ update({
48
+ color = null,
49
+ bboxOffsetRad = null,
50
+ bboxSizeRad = null,
51
+ resolution = null,
52
+ tailLengthRatio = null,
53
+ wingLengthRatio = null,
54
+ height = null,
55
+ opacity = null,
56
+ noDataValue = null,
57
+ } = {}) {
58
+ const { gl, ubo } = this;
59
+ gl.bindBuffer(gl.UNIFORM_BUFFER, ubo);
60
+ if (color !== null) gl.bufferSubData(gl.UNIFORM_BUFFER, 0, new Float32Array(color));
61
+ if (opacity !== null) gl.bufferSubData(gl.UNIFORM_BUFFER, 12, new Float32Array([opacity]));
62
+ if (bboxOffsetRad !== null) gl.bufferSubData(gl.UNIFORM_BUFFER, 16, new Float32Array(bboxOffsetRad));
63
+ if (bboxSizeRad !== null) gl.bufferSubData(gl.UNIFORM_BUFFER, 24, new Float32Array(bboxSizeRad));
64
+ if (resolution !== null) gl.bufferSubData(gl.UNIFORM_BUFFER, 32, new Float32Array(resolution));
65
+ if (tailLengthRatio !== null) gl.bufferSubData(gl.UNIFORM_BUFFER, 40, new Float32Array([tailLengthRatio]));
66
+ if (wingLengthRatio !== null) gl.bufferSubData(gl.UNIFORM_BUFFER, 44, new Float32Array([wingLengthRatio]));
67
+ if (height !== null) gl.bufferSubData(gl.UNIFORM_BUFFER, 48, new Float32Array([height]));
68
+ if (noDataValue !== null) gl.bufferSubData(gl.UNIFORM_BUFFER, 52, new Float32Array([noDataValue]));
69
+ gl.bindBuffer(gl.UNIFORM_BUFFER, null);
70
+ }