phoria2 1.2.0 → 1.4.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.
Files changed (69) hide show
  1. package/README.md +11 -9
  2. package/dist/bloom.js +4 -4
  3. package/dist/bloom.js.map +1 -1
  4. package/dist/charts.js +527 -510
  5. package/dist/charts.js.map +1 -1
  6. package/dist/core.js +1760 -1170
  7. package/dist/core.js.map +1 -1
  8. package/dist/debug.js +297 -0
  9. package/dist/debug.js.map +1 -0
  10. package/dist/edit.js +60 -60
  11. package/dist/edit.js.map +1 -1
  12. package/dist/explain.js +32 -13
  13. package/dist/explain.js.map +1 -1
  14. package/dist/gltf.js +155 -155
  15. package/dist/gltf.js.map +1 -1
  16. package/dist/keyboard.js +188 -0
  17. package/dist/keyboard.js.map +1 -0
  18. package/dist/motion.js +35 -35
  19. package/dist/motion.js.map +1 -1
  20. package/dist/particles.js +187 -114
  21. package/dist/particles.js.map +1 -1
  22. package/dist/passes.js +116 -104
  23. package/dist/passes.js.map +1 -1
  24. package/dist/phoria2.js +1 -1
  25. package/dist/picking.js +95 -62
  26. package/dist/picking.js.map +1 -1
  27. package/dist/snapshot.js +1 -1
  28. package/dist/tooltip.js +1 -1
  29. package/dist/types/View.d.ts +27 -0
  30. package/dist/types/area.d.ts +18 -0
  31. package/dist/types/debug.d.ts +74 -0
  32. package/dist/types/explain.d.ts +4 -0
  33. package/dist/types/frame.d.ts +5 -0
  34. package/dist/types/geometry/Geometry.d.ts +16 -0
  35. package/dist/types/index.d.ts +2 -0
  36. package/dist/types/material/Material.d.ts +67 -0
  37. package/dist/types/material/Texture.d.ts +18 -2
  38. package/dist/types/particles/Emitter.d.ts +39 -2
  39. package/dist/types/scene/drawables.d.ts +23 -2
  40. package/dist/types/watch.d.ts +8 -1
  41. package/dist/types/webgl/hooks.d.ts +48 -0
  42. package/dist/types/webgl/passes.d.ts +4 -0
  43. package/dist/types/webgl/programs.d.ts +15 -5
  44. package/dist/types/webgl/resources.d.ts +12 -2
  45. package/dist/types/webgl/shaders.d.ts +41 -5
  46. package/llms-full.txt +275 -1
  47. package/llms.txt +1 -1
  48. package/package.json +1 -1
  49. package/schema/scene.schema.json +75 -0
  50. package/src/View.js +121 -7
  51. package/src/area.js +131 -0
  52. package/src/charts/marks.js +29 -2
  53. package/src/debug.js +283 -0
  54. package/src/explain.js +19 -0
  55. package/src/frame.js +7 -2
  56. package/src/geometry/Geometry.js +16 -1
  57. package/src/index.js +1 -0
  58. package/src/material/Material.js +130 -4
  59. package/src/material/Texture.js +55 -2
  60. package/src/particles/Emitter.js +121 -10
  61. package/src/scene/drawables.js +89 -8
  62. package/src/watch.js +26 -3
  63. package/src/webgl/WebGLRenderer.js +55 -7
  64. package/src/webgl/hooks.js +121 -0
  65. package/src/webgl/passes.js +17 -2
  66. package/src/webgl/picking.js +45 -3
  67. package/src/webgl/programs.js +20 -9
  68. package/src/webgl/resources.js +169 -15
  69. package/src/webgl/shaders.js +545 -38
@@ -1 +1 @@
1
- {"version":3,"file":"passes.js","names":["mat4.invert","mat4.create","mat4.multiply","TONE_MAP","CLIP_ONLY_FRAGMENT","FULLSCREEN_VERTEX","invert","multiply","create","MESH_VERTEX","CLIP","EQUIRECT","SPOT_ATLAS_SIZE","POINT_SHADOW_SIZE","COMMON","uvMatrix","SHADOW_MAP_SIZE","irradianceSH","sh","sx","width","sy","height","dPhi","dTheta","y","theta","sinT","dy","weight","x","phi","dx","dz","basis","i","k","w","data","norm","band","out","c","BACKGROUND_FRAGMENT","PREFILTER_FRAGMENT","OUTLINE_MASK_FRAGMENT","OUTLINE_MAX","WIDEN","OUTLINE_WIDEN_FRAGMENT","OUTLINE_COMPOSITE_FRAGMENT","OUTLINE_COVER_FRAGMENT","SMOOTH","OUTLINE_SMOOTH_WIDEN_FRAGMENT","OUTLINE_SMOOTH_COMPOSITE_FRAGMENT","OUTLINE_MASK","OUTLINE_WIDEN","OUTLINE_COMPOSITE","OUTLINE_COVER","OUTLINE_SMOOTH_WIDEN","OUTLINE_SMOOTH_COMPOSITE","DEPTH","BACKGROUND","PREFILTER","ENV_WIDTH","ENV_LEVELS","Passes","gl","programs","resources","#environments","#shadow","#spotShadow","#pointShadow","#outline","texture","env","e","image","source","#floatTexture","prefiltered","program","framebuffer","level","hdr","blur","frame","tone","#depthTarget","#beginDepth","#drawDepth","shadow","#endDepth","toTexture","matrices","tiles","bias","half","spot","spots","toTile","#depthCube","point","face","item","casters","instances","defines","material","cutout","triplanar","viewProjection","map","targets","#outlineTargets","smooth","radius","outline","outlines","px","pixelRatio","attachment","target","widen","t","buffer","make","format","msaa","samples","size","framebuffers","levels"],"sources":["../src/irradiance.js"],"sourcesContent":["// Diffuse light from an environment image, for the renderer: computed once per environment.\n\n/** @import { DataImage } from './material/Texture.js' */\n\n/**\n * Diffuse light from an equirectangular environment, as nine spherical harmonic coefficients (RGB\n * each, flattened), scaled so the shader evaluates them with plain polynomial terms and gets\n * irradiance divided by PI - the light a white matte surface facing that way reflects.\n * @param {DataImage} image linear RGBA floats\n * @returns {Float32Array} 27 numbers\n */\nexport function irradianceSH({ data, width, height }) {\n const sh = new Float64Array(27)\n // sample a grid of at most 128 x 64 - plenty for the lowest frequencies\n const sx = Math.max(1, Math.floor(width / 128)), sy = Math.max(1, Math.floor(height / 64))\n const dPhi = (2 * Math.PI * sx) / width, dTheta = (Math.PI * sy) / height\n for (let y = 0; y < height; y += sy) {\n const theta = ((y + 0.5 * sy) / height) * Math.PI\n const sinT = Math.sin(theta), dy = Math.cos(theta)\n const weight = dPhi * dTheta * sinT\n for (let x = 0; x < width; x += sx) {\n const phi = ((x + 0.5 * sx) / width - 0.5) * 2 * Math.PI\n const dx = Math.cos(phi) * sinT, dz = Math.sin(phi) * sinT\n const basis = [1, dy, dz, dx, dx * dy, dy * dz, 3 * dz * dz - 1, dx * dz, dx * dx - dy * dy]\n const i = (y * width + x) * 4\n for (let k = 0; k < 9; k++) {\n const w = basis[k] * weight\n sh[k * 3] += data[i] * w\n sh[k * 3 + 1] += data[i + 1] * w\n sh[k * 3 + 2] += data[i + 2] * w\n }\n }\n }\n // projection uses the normalised basis Y = norm * polynomial, and evaluation multiplies by the\n // cosine lobe's band factors (PI, 2PI/3, PI/4) and norm again; then divide by PI\n const norm = [0.282095, 0.488603, 0.488603, 0.488603, 1.092548, 1.092548, 0.315392, 1.092548, 0.546274]\n const band = [1, 2 / 3, 2 / 3, 2 / 3, 1 / 4, 1 / 4, 1 / 4, 1 / 4, 1 / 4]\n const out = new Float32Array(27)\n for (let k = 0; k < 9; k++) {\n for (let c = 0; c < 3; c++) out[k * 3 + c] = sh[k * 3 + c] * norm[k] * norm[k] * band[k]\n }\n return out\n}\n"],"mappings":";;AAWA,SAAgBiB,EAAa,EAAE,SAAM,UAAO,aAAU;CACpD,IAAMC,oBAAK,IAAI,aAAa,EAAE,GAExBC,IAAK,KAAK,IAAI,GAAG,KAAK,MAAMC,IAAQ,GAAG,CAAC,GAAGC,IAAK,KAAK,IAAI,GAAG,KAAK,MAAMC,IAAS,EAAE,CAAC,GACnFC,IAAQ,IAAI,KAAK,KAAKJ,IAAMC,GAAOI,IAAU,KAAK,KAAKH,IAAMC;CACnE,KAAK,IAAIG,IAAI,GAAGA,IAAIH,GAAQ,KAAKD,GAAI;EACnC,IAAMK,KAAUD,IAAI,KAAMJ,KAAMC,IAAU,KAAK,IACzCK,IAAO,KAAK,IAAID,CAAK,GAAGE,IAAK,KAAK,IAAIF,CAAK,GAC3CG,IAASN,IAAOC,IAASG;EAC/B,KAAK,IAAIG,IAAI,GAAGA,IAAIV,GAAO,KAAKD,GAAI;GAClC,IAAMY,MAAQD,IAAI,KAAMX,KAAMC,IAAQ,MAAO,IAAI,KAAK,IAChDY,IAAK,KAAK,IAAID,CAAG,IAAIJ,GAAMM,IAAK,KAAK,IAAIF,CAAG,IAAIJ,GAChDO,IAAQ;IAAC;IAAGN;IAAIK;IAAID;IAAIA,IAAKJ;IAAIA,IAAKK;IAAI,IAAIA,IAAKA,IAAK;IAAGD,IAAKC;IAAID,IAAKA,IAAKJ,IAAKA;GAAE,GACrFO,KAAKV,IAAIL,IAAQU,KAAK;GAC5B,KAAK,IAAIM,IAAI,GAAGA,IAAI,GAAG,KAAK;IAC1B,IAAMC,IAAIH,EAAME,KAAKP;IAGrB,AAFA,EAAGO,IAAI,MAAME,EAAKH,KAAKE,GACvB,EAAGD,IAAI,IAAI,MAAME,EAAKH,IAAI,KAAKE,GAC/B,EAAGD,IAAI,IAAI,MAAME,EAAKH,IAAI,KAAKE;GACjC;EACF;CACF;CAGA,IAAME,IAAO;EAAC;EAAU;EAAU;EAAU;EAAU;EAAU;EAAU;EAAU;EAAU;CAAQ,GAChGC,IAAO;EAAC;EAAG,IAAI;EAAG,IAAI;EAAG,IAAI;EAAG,IAAI;EAAG,IAAI;EAAG,IAAI;EAAG,IAAI;EAAG,IAAI;CAAC,GACjEC,oBAAM,IAAI,aAAa,EAAE;CAC/B,KAAK,IAAIL,IAAI,GAAGA,IAAI,GAAG,KACrB,KAAK,IAAIM,IAAI,GAAGA,IAAI,GAAG,KAAK,EAAIN,IAAI,IAAIM,KAAKxB,EAAGkB,IAAI,IAAIM,KAAKH,EAAKH,KAAKG,EAAKH,KAAKI,EAAKJ;CAExF,OAAOK;AACT"}
1
+ {"version":3,"file":"passes.js","names":["mat4.invert","mat4.create","mat4.multiply","invert","passDefines","POINT_SHADOW_SIZE","COMMON","CLIP_ONLY_FRAGMENT","TONE_MAP","EQUIRECT","create","hookFor","CLIP","SPOT_ATLAS_SIZE","multiply","MESH_VERTEX","FULLSCREEN_VERTEX","setHookUniforms","SHADOW_MAP_SIZE","uvMatrix","irradianceSH","sh","sx","width","sy","height","dPhi","dTheta","y","theta","sinT","dy","weight","x","phi","dx","dz","basis","i","k","w","data","norm","band","out","c","BACKGROUND_FRAGMENT","PREFILTER_FRAGMENT","OUTLINE_MASK_FRAGMENT","OUTLINE_MAX","WIDEN","OUTLINE_WIDEN_FRAGMENT","OUTLINE_COMPOSITE_FRAGMENT","OUTLINE_COVER_FRAGMENT","SMOOTH","OUTLINE_SMOOTH_WIDEN_FRAGMENT","OUTLINE_SMOOTH_COMPOSITE_FRAGMENT","OUTLINE_MASK","OUTLINE_WIDEN","OUTLINE_COMPOSITE","OUTLINE_COVER","OUTLINE_SMOOTH_WIDEN","OUTLINE_SMOOTH_COMPOSITE","DEPTH","BACKGROUND","PREFILTER","ENV_WIDTH","ENV_LEVELS","Passes","gl","programs","resources","#environments","#shadow","#spotShadow","#pointShadow","#outline","texture","env","e","image","source","#floatTexture","prefiltered","program","framebuffer","level","hdr","blur","frame","tone","#depthTarget","#beginDepth","#drawDepth","shadow","#endDepth","toTexture","matrices","tiles","bias","half","spot","spots","toTile","#depthCube","point","face","item","casters","instances","defines","material","cutout","triplanar","hook","t","viewProjection","map","targets","#outlineTargets","smooth","radius","outline","outlines","px","pixelRatio","attachment","target","widen","buffer","make","format","msaa","samples","size","framebuffers","levels"],"sources":["../src/irradiance.js"],"sourcesContent":["// Diffuse light from an environment image, for the renderer: computed once per environment.\n\n/** @import { DataImage } from './material/Texture.js' */\n\n/**\n * Diffuse light from an equirectangular environment, as nine spherical harmonic coefficients (RGB\n * each, flattened), scaled so the shader evaluates them with plain polynomial terms and gets\n * irradiance divided by PI - the light a white matte surface facing that way reflects.\n * @param {DataImage} image linear RGBA floats\n * @returns {Float32Array} 27 numbers\n */\nexport function irradianceSH({ data, width, height }) {\n const sh = new Float64Array(27)\n // sample a grid of at most 128 x 64 - plenty for the lowest frequencies\n const sx = Math.max(1, Math.floor(width / 128)), sy = Math.max(1, Math.floor(height / 64))\n const dPhi = (2 * Math.PI * sx) / width, dTheta = (Math.PI * sy) / height\n for (let y = 0; y < height; y += sy) {\n const theta = ((y + 0.5 * sy) / height) * Math.PI\n const sinT = Math.sin(theta), dy = Math.cos(theta)\n const weight = dPhi * dTheta * sinT\n for (let x = 0; x < width; x += sx) {\n const phi = ((x + 0.5 * sx) / width - 0.5) * 2 * Math.PI\n const dx = Math.cos(phi) * sinT, dz = Math.sin(phi) * sinT\n const basis = [1, dy, dz, dx, dx * dy, dy * dz, 3 * dz * dz - 1, dx * dz, dx * dx - dy * dy]\n const i = (y * width + x) * 4\n for (let k = 0; k < 9; k++) {\n const w = basis[k] * weight\n sh[k * 3] += data[i] * w\n sh[k * 3 + 1] += data[i + 1] * w\n sh[k * 3 + 2] += data[i + 2] * w\n }\n }\n }\n // projection uses the normalised basis Y = norm * polynomial, and evaluation multiplies by the\n // cosine lobe's band factors (PI, 2PI/3, PI/4) and norm again; then divide by PI\n const norm = [0.282095, 0.488603, 0.488603, 0.488603, 1.092548, 1.092548, 0.315392, 1.092548, 0.546274]\n const band = [1, 2 / 3, 2 / 3, 2 / 3, 1 / 4, 1 / 4, 1 / 4, 1 / 4, 1 / 4]\n const out = new Float32Array(27)\n for (let k = 0; k < 9; k++) {\n for (let c = 0; c < 3; c++) out[k * 3 + c] = sh[k * 3 + c] * norm[k] * norm[k] * band[k]\n }\n return out\n}\n"],"mappings":";;AAWA,SAAgBoB,EAAa,EAAE,SAAM,UAAO,aAAU;CACpD,IAAMC,oBAAK,IAAI,aAAa,EAAE,GAExBC,IAAK,KAAK,IAAI,GAAG,KAAK,MAAMC,IAAQ,GAAG,CAAC,GAAGC,IAAK,KAAK,IAAI,GAAG,KAAK,MAAMC,IAAS,EAAE,CAAC,GACnFC,IAAQ,IAAI,KAAK,KAAKJ,IAAMC,GAAOI,IAAU,KAAK,KAAKH,IAAMC;CACnE,KAAK,IAAIG,IAAI,GAAGA,IAAIH,GAAQ,KAAKD,GAAI;EACnC,IAAMK,KAAUD,IAAI,KAAMJ,KAAMC,IAAU,KAAK,IACzCK,IAAO,KAAK,IAAID,CAAK,GAAGE,IAAK,KAAK,IAAIF,CAAK,GAC3CG,IAASN,IAAOC,IAASG;EAC/B,KAAK,IAAIG,IAAI,GAAGA,IAAIV,GAAO,KAAKD,GAAI;GAClC,IAAMY,MAAQD,IAAI,KAAMX,KAAMC,IAAQ,MAAO,IAAI,KAAK,IAChDY,IAAK,KAAK,IAAID,CAAG,IAAIJ,GAAMM,IAAK,KAAK,IAAIF,CAAG,IAAIJ,GAChDO,IAAQ;IAAC;IAAGN;IAAIK;IAAID;IAAIA,IAAKJ;IAAIA,IAAKK;IAAI,IAAIA,IAAKA,IAAK;IAAGD,IAAKC;IAAID,IAAKA,IAAKJ,IAAKA;GAAE,GACrFO,KAAKV,IAAIL,IAAQU,KAAK;GAC5B,KAAK,IAAIM,IAAI,GAAGA,IAAI,GAAG,KAAK;IAC1B,IAAMC,IAAIH,EAAME,KAAKP;IAGrB,AAFA,EAAGO,IAAI,MAAME,EAAKH,KAAKE,GACvB,EAAGD,IAAI,IAAI,MAAME,EAAKH,IAAI,KAAKE,GAC/B,EAAGD,IAAI,IAAI,MAAME,EAAKH,IAAI,KAAKE;GACjC;EACF;CACF;CAGA,IAAME,IAAO;EAAC;EAAU;EAAU;EAAU;EAAU;EAAU;EAAU;EAAU;EAAU;CAAQ,GAChGC,IAAO;EAAC;EAAG,IAAI;EAAG,IAAI;EAAG,IAAI;EAAG,IAAI;EAAG,IAAI;EAAG,IAAI;EAAG,IAAI;EAAG,IAAI;CAAC,GACjEC,oBAAM,IAAI,aAAa,EAAE;CAC/B,KAAK,IAAIL,IAAI,GAAGA,IAAI,GAAG,KACrB,KAAK,IAAIM,IAAI,GAAGA,IAAI,GAAG,KAAK,EAAIN,IAAI,IAAIM,KAAKxB,EAAGkB,IAAI,IAAIM,KAAKH,EAAKH,KAAKG,EAAKH,KAAKI,EAAKJ;CAExF,OAAOK;AACT"}
package/dist/phoria2.js CHANGED
@@ -1 +1 @@
1
- export { j as AmbientLight, xt as Box3, H as CAMERA_PRESETS, W as Camera, gt as Clip, _t as Color, r as Curve, M as DirectionalLight, X as Drawable, ht as Euler, R as Fog, ct as Geometry, nt as Group, N as HemisphereLight, Z as InstancedMesh, J as LIGHT_LIMITS, Q as Label, P as Light, $ as Lines, St as Mat4, at as Material, et as Mesh, it as Node, Y as NullRenderer, q as OrbitControl, G as OrthographicCamera, K as PerspectiveCamera, wt as PhoriaError, F as PointLight, tt as Points, lt as Quat, B as Ray, z as Raycaster, L as Scene, I as SpotLight, ot as Texture, Ct as Vec3, y as View, b as WebGLRenderer, c as axes, l as box, U as cameraPreset, t as create, i as curve, u as cylinder, d as edges, a as ellipse, f as extrude, o as fromPolygons, p as grid, m as icosphere, h as lathe, st as loadTexture, s as outline, g as plane, n as registerType, _ as sphere, v as torus } from "./core.js";
1
+ export { P as AmbientLight, Et as Box3, G as CAMERA_PRESETS, q as Camera, xt as Clip, St as Color, r as Curve, F as DirectionalLight, et as Drawable, bt as Euler, V as Fog, pt as Geometry, st as Group, I as HemisphereLight, tt as InstancedMesh, Z as LIGHT_LIMITS, nt as Label, L as Light, rt as Lines, Dt as Mat4, ut as Material, it as Mesh, lt as Node, Q as NullRenderer, X as OrbitControl, J as OrthographicCamera, Y as PerspectiveCamera, kt as PhoriaError, R as PointLight, at as Points, mt as Quat, U as Ray, H as Raycaster, B as Scene, z as SpotLight, dt as Texture, Ot as Vec3, y as View, b as WebGLRenderer, c as axes, l as box, K as cameraPreset, t as create, i as curve, u as cylinder, d as edges, a as ellipse, f as extrude, o as fromPolygons, p as grid, m as icosphere, h as lathe, ft as loadTexture, s as outline, g as plane, n as registerType, _ as sphere, v as torus } from "./core.js";
package/dist/picking.js CHANGED
@@ -1,30 +1,50 @@
1
- import { D as e, It as t, O as n, Pt as r, S as i, k as a, w as o } from "./core.js";
1
+ import { A as e, C as t, D as n, M as r, S as i, T as a, Vt as o, j as s, w as c, zt as l } from "./core.js";
2
2
  // src/webgl/picking.js
3
- var s = 33, c = 4, l = `precision highp float;
3
+ var u = 33, d = 4, f = `precision highp float;
4
4
  precision highp int;
5
5
  flat in uint vPickElement;
6
- #ifdef CLIP
6
+ #ifdef SPRITES
7
+ in vec2 vUv;
8
+ #endif
9
+ #if defined(CLIP) || defined(HOOK_FRAGMENT)
7
10
  in vec3 vWorldPosition;
8
11
  #endif
12
+ #ifdef HOOK_FRAGMENT
13
+ in vec3 vNormal;
14
+ in vec2 vUv;
15
+ in vec3 vColor;
16
+ #endif
9
17
  uniform uint uPickId;
10
18
  layout(location = 0) out uvec4 pickOut;
11
- ${o}
12
- ${i}
19
+ ${n}
20
+ ${a}
13
21
  void main() {
14
22
  #ifdef CLIP
15
23
  clipAway(vWorldPosition);
16
24
  #endif
25
+ #ifdef HOOK_FRAGMENT
26
+ {
27
+ vec4 base = vec4(vColor, 1.0);
28
+ vec3 emissive = vec3(0.0);
29
+ vec3 normal = normalize(vNormal);
30
+ if (base.a <= 0.0) discard;
31
+ }
32
+ #endif
17
33
  #ifdef ROUND
34
+ #ifdef SPRITES
35
+ vec2 p = vUv * 2.0 - 1.0;
36
+ #else
18
37
  vec2 p = gl_PointCoord * 2.0 - 1.0;
38
+ #endif
19
39
  if (dot(p, p) > 1.0) discard;
20
40
  #endif
21
41
  pickOut = uvec4(uPickId, vPickElement, floatBitsToUint(gl_FragCoord.z), 1u);
22
42
  }
23
- `, u = {
24
- mesh: [n, l],
25
- points: [a, l],
26
- lines: [e, l]
27
- }, d = class {
43
+ `, p = {
44
+ mesh: [s, f],
45
+ points: [r, f],
46
+ lines: [e, f]
47
+ }, m = class {
28
48
  #e = null;
29
49
  #t = /* @__PURE__ */ new Uint32Array(4356);
30
50
  #n = /* @__PURE__ */ new Float32Array(16);
@@ -39,79 +59,79 @@ pickOut = uvec4(uPickId, vPickElement, floatBitsToUint(gl_FragCoord.z), 1u);
39
59
  this.#e = null, this.#a = null, this.#o = !1;
40
60
  }
41
61
  pick(e, t, n, r, i, a) {
42
- let o = this.gl, c = this.#s(e, t, n, r, i, a);
43
- return c ? (o.readPixels(0, 0, s, s, o.RGBA_INTEGER, o.UNSIGNED_INT, this.#t), o.bindFramebuffer(o.FRAMEBUFFER, null), this.#c(c)) : null;
62
+ let o = this.gl, s = this.#s(e, t, n, r, i, a);
63
+ return s ? (o.readPixels(0, 0, u, u, o.RGBA_INTEGER, o.UNSIGNED_INT, this.#t), o.bindFramebuffer(o.FRAMEBUFFER, null), this.#c(s)) : null;
44
64
  }
45
65
  pickAsync(e, t, n, r, i, a) {
46
66
  if (this.#o) return null;
47
- let o = this.gl, c = this.#s(e, t, n, r, i, a);
48
- if (!c) return Promise.resolve(null);
49
- let l = this.#l();
50
- o.bindBuffer(o.PIXEL_PACK_BUFFER, l), o.readPixels(0, 0, s, s, o.RGBA_INTEGER, o.UNSIGNED_INT, 0), o.bindBuffer(o.PIXEL_PACK_BUFFER, null), o.bindFramebuffer(o.FRAMEBUFFER, null);
51
- let u = o.fenceSync(o.SYNC_GPU_COMMANDS_COMPLETE, 0);
67
+ let o = this.gl, s = this.#s(e, t, n, r, i, a);
68
+ if (!s) return Promise.resolve(null);
69
+ let c = this.#l();
70
+ o.bindBuffer(o.PIXEL_PACK_BUFFER, c), o.readPixels(0, 0, u, u, o.RGBA_INTEGER, o.UNSIGNED_INT, 0), o.bindBuffer(o.PIXEL_PACK_BUFFER, null), o.bindFramebuffer(o.FRAMEBUFFER, null);
71
+ let l = o.fenceSync(o.SYNC_GPU_COMMANDS_COMPLETE, 0);
52
72
  return o.flush(), this.#o = !0, new Promise((e) => {
53
73
  let t = () => {
54
- o.isContextLost() ? (this.#o = !1, e(null)) : o.clientWaitSync(u, 0, 0) === o.TIMEOUT_EXPIRED ? setTimeout(t, 1) : (o.deleteSync(u), o.bindBuffer(o.PIXEL_PACK_BUFFER, l), o.getBufferSubData(o.PIXEL_PACK_BUFFER, 0, this.#t), o.bindBuffer(o.PIXEL_PACK_BUFFER, null), this.#o = !1, e(this.#c(c)));
74
+ o.isContextLost() ? (this.#o = !1, e(null)) : o.clientWaitSync(l, 0, 0) === o.TIMEOUT_EXPIRED ? setTimeout(t, 1) : (o.deleteSync(l), o.bindBuffer(o.PIXEL_PACK_BUFFER, c), o.getBufferSubData(o.PIXEL_PACK_BUFFER, 0, this.#t), o.bindBuffer(o.PIXEL_PACK_BUFFER, null), this.#o = !1, e(this.#c(s)));
55
75
  };
56
76
  setTimeout(t, 0);
57
77
  });
58
78
  }
59
- #s(e, n, r, i, a, o) {
60
- let c = this.gl, l = f(e);
61
- if (!l.length) return null;
62
- let u = this.#d();
63
- c.bindFramebuffer(c.FRAMEBUFFER, u.framebuffer), c.viewport(0, 0, s, s), c.disable(c.BLEND), c.disable(c.SCISSOR_TEST), c.enable(c.DEPTH_TEST), c.depthFunc(c.LEQUAL), c.depthMask(!0), c.clearBufferuiv(c.COLOR, 0, [
79
+ #s(e, t, n, r, i, a) {
80
+ let s = this.gl, c = h(e);
81
+ if (!c.length) return null;
82
+ let l = this.#d();
83
+ s.bindFramebuffer(s.FRAMEBUFFER, l.framebuffer), s.viewport(0, 0, u, u), s.disable(s.BLEND), s.disable(s.SCISSOR_TEST), s.enable(s.DEPTH_TEST), s.depthFunc(s.LEQUAL), s.depthMask(!0), s.clearBufferuiv(s.COLOR, 0, [
64
84
  0,
65
85
  0,
66
86
  0,
67
87
  0
68
- ]), c.clearBufferfv(c.DEPTH, 0, [1]);
69
- let d = s, p = n / i * 2 - 1, m = 1 - r / a * 2, h = new Float32Array([
70
- i / d,
88
+ ]), s.clearBufferfv(s.DEPTH, 0, [1]);
89
+ let d = u, f = t / r * 2 - 1, p = 1 - n / i * 2, m = new Float32Array([
90
+ r / d,
71
91
  0,
72
92
  0,
73
93
  0,
74
94
  0,
75
- a / d,
95
+ i / d,
76
96
  0,
77
97
  0,
78
98
  0,
79
99
  0,
80
100
  1,
81
101
  0,
102
+ -f * r / d,
82
103
  -p * i / d,
83
- -m * a / d,
84
104
  0,
85
105
  1
86
106
  ]);
87
- return t(this.#n, h, e.camera.projection), t(this.#r, this.#n, e.camera.view), l.forEach((t, n) => this.#u(t, n + 1, e, a, o)), c.bindVertexArray(null), {
88
- items: l,
89
- cx: p,
90
- cy: m,
91
- width: i,
92
- height: a
107
+ return o(this.#n, m, e.camera.projection), o(this.#r, this.#n, e.camera.view), c.forEach((t, n) => this.#u(t, n + 1, e, i, a)), s.bindVertexArray(null), {
108
+ items: c,
109
+ cx: f,
110
+ cy: p,
111
+ width: r,
112
+ height: i
93
113
  };
94
114
  }
95
115
  #c({ items: e, cx: t, cy: n, width: r, height: i }) {
96
- let a = this.#t, o = (e, t) => (t * s + e) * 4, l = a[o(16, 16)] ? o(16, 16) : -1, u = 0, d = 0;
97
- if (l < 0) {
116
+ let a = this.#t, o = (e, t) => (t * u + e) * 4, s = a[o(16, 16)] ? o(16, 16) : -1, c = 0, l = 0;
117
+ if (s < 0) {
98
118
  let t = Infinity;
99
- for (let n = -4; n <= c; n++) for (let r = -4; r <= c; r++) {
119
+ for (let n = -4; n <= d; n++) for (let r = -4; r <= d; r++) {
100
120
  let i = r * r + n * n;
101
121
  if (i > 16 || i >= t) continue;
102
- let s = o(16 + r, 16 + n), c = a[s];
103
- c && e[c - 1].node.drawMode !== "triangles" && (l = s, t = i, u = r, d = n);
122
+ let u = o(16 + r, 16 + n), d = a[u];
123
+ d && e[d - 1].node.drawMode !== "triangles" && (s = u, t = i, c = r, l = n);
104
124
  }
105
125
  }
106
- if (l < 0) return null;
107
- let f = new Float32Array(new Uint32Array([a[l + 2]]).buffer)[0], p = [
108
- t + u * 2 / r,
109
- n + d * 2 / i,
126
+ if (s < 0) return null;
127
+ let f = new Float32Array(new Uint32Array([a[s + 2]]).buffer)[0], p = [
128
+ t + c * 2 / r,
129
+ n + l * 2 / i,
110
130
  f * 2 - 1
111
131
  ];
112
132
  return {
113
- item: e[a[l] - 1],
114
- element: a[l + 1],
133
+ item: e[a[s] - 1],
134
+ element: a[s + 1],
115
135
  ndc: p
116
136
  };
117
137
  }
@@ -120,26 +140,39 @@ pickOut = uvec4(uPickId, vPickElement, floatBitsToUint(gl_FragCoord.z), 1u);
120
140
  let e = this.gl;
121
141
  return this.#a = e.createBuffer(), e.bindBuffer(e.PIXEL_PACK_BUFFER, this.#a), e.bufferData(e.PIXEL_PACK_BUFFER, this.#t.byteLength, e.STREAM_READ), e.bindBuffer(e.PIXEL_PACK_BUFFER, null), this.#a;
122
142
  }
123
- #u(e, t, n, i, a) {
124
- let o = this.gl, { node: c, geometry: l, material: d, worldMatrix: f, instances: p } = e, m = c.drawMode, h = m === "points" ? "points" : m === "lines" ? "lines" : "mesh", g = ["PICK"];
125
- p && g.push("INSTANCED"), h === "points" && (d.sizeAttenuation && g.push("SIZE_ATTENUATION"), d.map || g.push("ROUND")), h === "mesh" && e.clip && g.push("CLIP");
126
- let _ = this.programs.ready(`pick-${h}`, g, u[h]);
127
- if (o.useProgram(_.program), _.set("uView", n.camera.view), _.set("uProjection", this.#n), _.set("uViewProjection", this.#r), _.set("uModel", f), _.set("uPickId", t), _.set("uHighlightIndex", -2), e.clip && _.set("uClipPlane", e.clip.plane), h === "lines") {
128
- o.disable(o.CULL_FACE), _.set("uResolution", [s, s]), _.set("uWidth", d.width * a), _.set("uNear", n.camera.near);
129
- let e = this.resources.lines(l);
130
- o.bindVertexArray(e.vao), o.drawArraysInstanced(o.TRIANGLE_STRIP, 0, 4, Math.min(e.count, Math.floor(l.drawnCount / 2)));
131
- } else if (h === "points") {
132
- o.disable(o.CULL_FACE), _.set("uSize", d.sizeAttenuation ? d.size : d.size * a), _.set("uScale", n.camera.projection[5] * i / 2);
133
- let e = this.resources.mesh(l, null, !1);
134
- o.bindVertexArray(e.vao), o.drawArrays(o.POINTS, 0, l.drawnVertices);
135
- } else _.set("uNormalMatrix", this.#i), d.depthTest ? o.enable(o.DEPTH_TEST) : o.disable(o.DEPTH_TEST), d.doubleSided || e.clip?.cap ? o.disable(o.CULL_FACE) : (o.enable(o.CULL_FACE), o.cullFace(o.BACK), o.frontFace(r(f) < 0 ? o.CW : o.CCW)), this.resources.drawTriangles(l, p);
143
+ #u(e, n, r, a, o) {
144
+ let s = this.gl, { node: d, geometry: f, material: m, worldMatrix: h, instances: g } = e, _ = d.drawMode, v = _ === "points" ? "points" : _ === "lines" ? "lines" : "mesh", y = ["PICK"];
145
+ g && y.push("INSTANCED");
146
+ let b = v === "points" && d.drawsSprites;
147
+ v === "points" && (m.sizeAttenuation && y.push("SIZE_ATTENUATION"), m.map || y.push("ROUND"), b && y.push("SPRITES")), v === "mesh" && e.clip && y.push("CLIP");
148
+ let x = i(m);
149
+ x && y.push(...t(x, v === "mesh"));
150
+ let S = this.programs.ready(`pick-${v}`, y, p[v], x?.source);
151
+ if (!S.failed) {
152
+ if (s.useProgram(S.program), S.set("uView", r.camera.view), S.set("uProjection", this.#n), S.set("uViewProjection", this.#r), S.set("uModel", h), S.set("uPickId", n), S.set("uHighlightIndex", -2), e.clip && S.set("uClipPlane", e.clip.plane), x && c(S, m, r.time ?? 0, r.camera.position, (e) => this.resources.texture(e)), v === "lines") {
153
+ s.disable(s.CULL_FACE), S.set("uResolution", [u, u]), S.set("uWidth", m.width * o), S.set("uNear", r.camera.near);
154
+ let e = d.arrowBits ?? 0;
155
+ S.set("uArrows", e), S.set("uArrowSize", d.arrowPixels?.(m.width) * o || 0);
156
+ let t = this.resources.lines(f);
157
+ s.bindVertexArray(t.vao), s.drawArraysInstanced(s.TRIANGLES, 0, e ? 12 : 6, Math.min(t.count, Math.floor(f.drawnCount / 2)));
158
+ } else if (v === "points") {
159
+ if (s.disable(s.CULL_FACE), S.set("uSize", m.sizeAttenuation ? m.size : m.size * o), S.set("uScale", r.camera.projection[5] * a / 2), b) {
160
+ S.set("uResolution", [u, u]);
161
+ let e = this.resources.sprites(f);
162
+ s.bindVertexArray(e.vao), s.drawArraysInstanced(s.TRIANGLE_STRIP, 0, 4, e.count);
163
+ return;
164
+ }
165
+ let e = this.resources.mesh(f, null, !1);
166
+ s.bindVertexArray(e.vao), s.drawArrays(s.POINTS, 0, f.drawnVertices);
167
+ } else S.set("uNormalMatrix", this.#i), m.depthTest ? s.enable(s.DEPTH_TEST) : s.disable(s.DEPTH_TEST), m.doubleSided || e.clip?.cap ? s.disable(s.CULL_FACE) : (s.enable(s.CULL_FACE), s.cullFace(s.BACK), s.frontFace(l(h) < 0 ? s.CW : s.CCW)), this.resources.drawTriangles(f, g);
168
+ }
136
169
  }
137
170
  #d() {
138
171
  if (this.#e) return this.#e;
139
172
  let e = this.gl, t = e.createTexture();
140
- e.activeTexture(e.TEXTURE15), e.bindTexture(e.TEXTURE_2D, t), e.texStorage2D(e.TEXTURE_2D, 1, e.RGBA32UI, s, s);
173
+ e.activeTexture(e.TEXTURE15), e.bindTexture(e.TEXTURE_2D, t), e.texStorage2D(e.TEXTURE_2D, 1, e.RGBA32UI, u, u);
141
174
  let n = e.createRenderbuffer();
142
- e.bindRenderbuffer(e.RENDERBUFFER, n), e.renderbufferStorage(e.RENDERBUFFER, e.DEPTH_COMPONENT24, s, s);
175
+ e.bindRenderbuffer(e.RENDERBUFFER, n), e.renderbufferStorage(e.RENDERBUFFER, e.DEPTH_COMPONENT24, u, u);
143
176
  let r = e.createFramebuffer();
144
177
  return e.bindFramebuffer(e.FRAMEBUFFER, r), e.framebufferTexture2D(e.FRAMEBUFFER, e.COLOR_ATTACHMENT0, e.TEXTURE_2D, t, 0), e.framebufferRenderbuffer(e.FRAMEBUFFER, e.DEPTH_ATTACHMENT, e.RENDERBUFFER, n), e.bindFramebuffer(e.FRAMEBUFFER, null), this.#e = {
145
178
  framebuffer: r,
@@ -148,7 +181,7 @@ pickOut = uvec4(uPickId, vPickElement, floatBitsToUint(gl_FragCoord.z), 1u);
148
181
  }, this.#e;
149
182
  }
150
183
  };
151
- function f(e) {
184
+ function h(e) {
152
185
  let t = /* @__PURE__ */ new Map(), n = (e) => {
153
186
  if (!e) return !0;
154
187
  let r = t.get(e);
@@ -156,6 +189,6 @@ function f(e) {
156
189
  }, r = [...e.opaque, ...e.transparent].filter((e) => n(e.node));
157
190
  return [...r.filter((e) => e.material.depthTest), ...r.filter((e) => !e.material.depthTest)];
158
191
  }
159
- export { d as Picker };
192
+ export { m as Picker };
160
193
 
161
194
  //# sourceMappingURL=picking.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"picking.js","names":["mat4.determinant","LINES_VERTEX","multiply","MESH_VERTEX","determinant","CLIP","POINTS_VERTEX","COMMON","REGION","TOLERANCE","PICK_FRAGMENT","SOURCES","Picker","gl","programs","resources","#target","#pack","#waiting","region","#drawRegion","frame","x","y","width","height","pixelRatio","#pixels","#decode","buffer","#readBuffer","fence","poll","resolve","items","pickable","target","#ensureTarget","s","cx","cy","zoom","#projection","#viewProjection","#draw","item","i","p","at","py","px","best","bestX","bestY","bestD","dy","dx","d","o","id","depth","ndc","mode","node","kind","defines","instances","material","program","worldMatrix","buffers","geometry","#normal","color","framebuffer","seen","ok","n","v"],"sources":[],"mappings":""}
1
+ {"version":3,"file":"picking.js","names":["mat4.determinant","LINES_VERTEX","passDefines","COMMON","POINTS_VERTEX","hookFor","CLIP","multiply","MESH_VERTEX","setHookUniforms","determinant","REGION","TOLERANCE","PICK_FRAGMENT","SOURCES","Picker","gl","programs","resources","#target","#pack","#waiting","region","#drawRegion","frame","x","y","width","height","pixelRatio","#pixels","#decode","buffer","#readBuffer","fence","poll","resolve","items","pickable","target","#ensureTarget","s","cx","cy","zoom","#projection","#viewProjection","#draw","item","i","p","at","py","px","best","bestX","bestY","bestD","dy","dx","d","o","id","depth","ndc","mode","node","kind","defines","instances","sprites","material","hook","program","worldMatrix","t","arrows","buffers","geometry","#normal","color","framebuffer","seen","ok","n","v"],"sources":[],"mappings":""}
package/dist/snapshot.js CHANGED
@@ -1,4 +1,4 @@
1
- import { wt as e } from "./core.js";
1
+ import { kt as e } from "./core.js";
2
2
  // src/snapshot.js
3
3
  function t(t, n, { type: a = "image/png", quality: o, labels: s = !0 } = {}, c = []) {
4
4
  let l = t;
package/dist/tooltip.js CHANGED
@@ -1,4 +1,4 @@
1
- import { At as e, Ot as t } from "./core.js";
1
+ import { Ft as e, Nt as t } from "./core.js";
2
2
  // src/tooltip.js
3
3
  var n = "\n:where(.phoria-tooltip) { position: fixed; left: 0; top: 0; z-index: 1000; pointer-events: none; max-width: 320px;\n padding: 6px 10px; border-radius: 6px; background: rgba(20, 20, 24, 0.92); color: #fff;\n font: 12px/1.45 system-ui, sans-serif; box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3); white-space: pre-line; }\n:where(.phoria-tooltip-title) { font-weight: 600; }\n:where(.phoria-tooltip-rows) { display: grid; grid-template-columns: auto auto; column-gap: 12px; }\n:where(.phoria-tooltip-rows > :nth-child(odd)) { opacity: 0.7; }\n:where(.phoria-tooltip-rows > :nth-child(even)) { text-align: right; font-variant-numeric: tabular-nums; }\n", r = 14, i = class {
4
4
  el;
@@ -17,6 +17,7 @@ import type { CameraOptions } from './scene/cameras.js';
17
17
  import type { ColorInput, Vec3Input } from './types.js';
18
18
  import type { Node } from './scene/Node.js';
19
19
  import type { KeyboardOptions } from './keyboard.js';
20
+ import type { DebugOptions } from './debug.js';
20
21
  export type RenderMode = 'auto' | 'always' | 'manual';
21
22
  export type ViewOptions = {
22
23
  /**
@@ -125,6 +126,11 @@ export type ViewOptions = {
125
126
  * on and no description, one is made from the scene (charts describe themselves)
126
127
  */
127
128
  description?: string | null;
129
+ /**
130
+ * pictures for checking a scene: lights, shadow cameras, bounds,
131
+ * normals, axes, the orbit target and a stats readout. true shows all but normals. Default false
132
+ */
133
+ debug?: boolean | DebugOptions;
128
134
  };
129
135
  export type ViewEvent = 'click' | 'hover';
130
136
  export type ViewHandler = (hit: Hit | null, event: PointerEvent | MouseEvent | null) => void;
@@ -195,6 +201,16 @@ export declare class View {
195
201
  get reducedMotion(): boolean;
196
202
  /** @param {'auto' | boolean} v */
197
203
  set reducedMotion(v: 'auto' | boolean);
204
+ /**
205
+ * Debug drawing, or false: what it shows, as DebugOptions. Assign true for everything but normals,
206
+ * options for some, or false. Its code loads the first time it's turned on.
207
+ *
208
+ * view.debug = { lights: true, shadows: true, bounds: 'hovered', stats: 'bottom-right' }
209
+ * @returns {boolean | DebugOptions} the options in force, every one given, or false
210
+ */
211
+ get debug(): boolean | DebugOptions;
212
+ /** @param {boolean | DebugOptions} v */
213
+ set debug(v: boolean | DebugOptions);
198
214
  /**
199
215
  * What the view shows, for screen readers - the canvas's label - or null. With keyboard on and no
200
216
  * description, one is made from the scene.
@@ -339,6 +355,17 @@ export declare class View {
339
355
  width: number;
340
356
  height: number;
341
357
  } | null;
358
+ /**
359
+ * Everything inside an area of the canvas, nearest first: a box `{ x, y, width, height }` or a
360
+ * lasso of `[x, y]` points, CSS pixels from the canvas's top left. Each point, instance and line
361
+ * segment is its own hit, found by where it is (a mesh by its middle) - hidden behind others or
362
+ * not - so a box dragged over a scatter selects that region of it.
363
+ *
364
+ * const picked = view.pickArea({ x: 40, y: 30, width: 200, height: 120 }).map((h) => h.datum)
365
+ * @param {import('./area.js').PickArea} area
366
+ * @returns {Hit[]}
367
+ */
368
+ pickArea(area: import('./area.js').PickArea): Hit[];
342
369
  /** Every hit under a canvas position, nearest first. @param {number | MouseEvent} x @param {number} [y] @returns {Hit[]} */
343
370
  pickAll(x: number | MouseEvent, y?: number): Hit[];
344
371
  /**
@@ -0,0 +1,18 @@
1
+ import type { Hit } from './Raycaster.js';
2
+ import type { Node } from './scene/Node.js';
3
+ import type { Camera } from './scene/cameras.js';
4
+ export type PickArea = {
5
+ x: number;
6
+ y: number;
7
+ width: number;
8
+ height: number;
9
+ } | [number, number][];
10
+ /**
11
+ * Everything in the scene the camera shows, pickable, whose place falls inside the area - nearest
12
+ * the camera first.
13
+ * @param {Node} scene @param {Camera} camera @param {Float32Array} viewProjection
14
+ * @param {number} width @param {number} height the canvas, CSS pixels
15
+ * @param {PickArea} area
16
+ * @returns {Hit[]}
17
+ */
18
+ export declare function pickArea(scene: Node, camera: Camera, viewProjection: Float32Array, width: number, height: number, area: PickArea): Hit[];
@@ -0,0 +1,74 @@
1
+ import { Box3 } from './value/Box3.js';
2
+ import type { FrameDescription } from './frame.js';
3
+ import type { Hit } from './Raycaster.js';
4
+ import type { Camera } from './scene/cameras.js';
5
+ export type DebugOptions = {
6
+ /**
7
+ * each light: an arrow for directional ones, a small sphere and its range
8
+ * for point lights, a cone for spot lights - in the light's colour
9
+ */
10
+ lights?: boolean;
11
+ /**
12
+ * the box or cone each shadow is drawn from - the usual reason a shadow
13
+ * is cut off or soft
14
+ */
15
+ shadows?: boolean;
16
+ /**
17
+ * world bounding boxes - what culling and view.frame() see
18
+ */
19
+ bounds?: boolean | 'hovered';
20
+ /**
21
+ * short lines along vertex normals (meshes up to 5,000
22
+ * vertices each when all are shown)
23
+ */
24
+ normals?: boolean | 'hovered';
25
+ /**
26
+ * x, y and z at the origin, red, green and blue
27
+ */
28
+ axes?: boolean;
29
+ /**
30
+ * the point the camera orbits round
31
+ */
32
+ camera?: boolean;
33
+ /**
34
+ * a readout of
35
+ * frame time, draw calls, triangles, programs, textures and geometries, in a corner of the canvas
36
+ */
37
+ stats?: boolean | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
38
+ };
39
+ export declare class DebugDraw {
40
+ #private;
41
+ overlay: HTMLElement;
42
+ /** @param {HTMLElement | null} overlay where the stats readout goes - the layer over the canvas */
43
+ constructor(overlay: HTMLElement | null);
44
+ /**
45
+ * Add the helpers to a frame about to be drawn, after everything in it. Returns how many items were
46
+ * added to each list, to take out again once it is drawn.
47
+ * @param {FrameDescription} frame @param {DebugOptions} o
48
+ * @param {{ camera: Camera, hovered: Hit | null, bounds: Box3 }} at
49
+ * @returns {{ opaque: number, transparent: number }}
50
+ */
51
+ add(frame: FrameDescription, o: DebugOptions, at: {
52
+ camera: Camera;
53
+ hovered: Hit | null;
54
+ bounds: Box3;
55
+ }): {
56
+ opaque: number;
57
+ transparent: number;
58
+ };
59
+ /**
60
+ * The corner readout, refreshed a few times a second.
61
+ * @param {DebugOptions['stats']} where
62
+ * @param {{ drawCalls: number, triangles: number, programs: number, textures: number, geometries: number, compiling: number }} info
63
+ * @param {number} frameMs time between drawn frames @param {number} cpuMs time the last frame took to draw
64
+ */
65
+ stats(where: DebugOptions['stats'], info: {
66
+ drawCalls: number;
67
+ triangles: number;
68
+ programs: number;
69
+ textures: number;
70
+ geometries: number;
71
+ compiling: number;
72
+ }, frameMs: number, cpuMs: number): void;
73
+ dispose(): void;
74
+ }
@@ -44,6 +44,10 @@ export type ExplainInfo = {
44
44
  * lines from nodes that describe themselves, such as charts
45
45
  */
46
46
  notes?: string[];
47
+ /**
48
+ * what debug drawing shows, when it is on
49
+ */
50
+ debug?: Record<string, unknown> | null;
47
51
  };
48
52
  /** @param {ExplainInfo} info @returns {string} */
49
53
  export declare function explain(info: ExplainInfo): string;
@@ -272,6 +272,10 @@ export type FrameDescription = {
272
272
  drawn: number;
273
273
  culled: number;
274
274
  };
275
+ /**
276
+ * the View's clock in seconds, for shader hooks that read uTime
277
+ */
278
+ time?: number;
275
279
  /**
276
280
  * set by the View for hover highlighting
277
281
  */
@@ -414,6 +418,7 @@ export type FrameDescription = {
414
418
  * @property {PointShadowFrame | null} pointShadow a point light's shadow, all round it
415
419
  * @property {FrameCap[]} caps section planes' caps to fill, after the opaque pass
416
420
  * @property {{ drawn: number, culled: number }} stats
421
+ * @property {number} [time] the View's clock in seconds, for shader hooks that read uTime
417
422
  * @property {FrameHighlight | null} highlight set by the View for hover highlighting
418
423
  * @property {any[]} overlays HTML elements carried by the nodes drawn (node.overlay)
419
424
  * @property {boolean} interactive some node has a tooltip, highlight or pointer handler
@@ -21,6 +21,19 @@ export type GeometryOptions = {
21
21
  * rgb per vertex, sRGB 0-1 like every colour input
22
22
  */
23
23
  colors?: ArrayLike<number>;
24
+ /**
25
+ * points: one per vertex, a multiple of the material's size
26
+ */
27
+ sizes?: ArrayLike<number>;
28
+ /**
29
+ * points: one per vertex, 0-1, multiplying the material's opacity
30
+ */
31
+ opacities?: ArrayLike<number>;
32
+ /**
33
+ * points: one per vertex, radians anticlockwise on screen - turns
34
+ * each point's texture
35
+ */
36
+ angles?: ArrayLike<number>;
24
37
  /**
25
38
  * vertex indices; triangles for meshes, pairs for lines
26
39
  */
@@ -38,6 +51,9 @@ export declare class Geometry {
38
51
  /** @type {Float32Array | null} */ uvs: Float32Array | null;
39
52
  /** A second UV set, for baked occlusion and light maps. @type {Float32Array | null} */ uvs2: Float32Array | null;
40
53
  /** @type {Float32Array | null} */ colors: Float32Array | null;
54
+ /** Points: each one's size, as a multiple of the material's. @type {Float32Array | null} */ sizes: Float32Array | null;
55
+ /** Points: each one's opacity, 0-1, times the material's. @type {Float32Array | null} */ opacities: Float32Array | null;
56
+ /** Points: each one's turn on screen, in radians anticlockwise. @type {Float32Array | null} */ angles: Float32Array | null;
41
57
  /** @type {Uint16Array | Uint32Array | null} */ indices: Uint16Array | Uint32Array | null;
42
58
  /**
43
59
  * Draw only the first `drawCount` vertices (indices, when indexed), or all of them when null -
@@ -37,6 +37,7 @@ export type RenderMode = import('./View.js').RenderMode;
37
37
  export type CameraPreset = import('./presets.js').CameraPreset;
38
38
  export type TooltipContent = import('./tooltip.js').TooltipContent;
39
39
  export type Hit = import('./Raycaster.js').Hit;
40
+ export type PickArea = import('./area.js').PickArea;
40
41
  export type LabelPlacement = import('./scene/drawables.js').LabelPlacement;
41
42
  export type LabelPlace = import('./scene/drawables.js').LabelPlace;
42
43
  export type InstanceLabels = import('./scene/drawables.js').InstanceLabels;
@@ -49,6 +50,7 @@ export type ImageOptions = import('./snapshot.js').ImageOptions;
49
50
  /** @typedef {import('./presets.js').CameraPreset} CameraPreset */
50
51
  /** @typedef {import('./tooltip.js').TooltipContent} TooltipContent */
51
52
  /** @typedef {import('./Raycaster.js').Hit} Hit */
53
+ /** @typedef {import('./area.js').PickArea} PickArea */
52
54
  /** @typedef {import('./scene/drawables.js').LabelPlacement} LabelPlacement */
53
55
  /** @typedef {import('./scene/drawables.js').LabelPlace} LabelPlace */
54
56
  /** @typedef {import('./scene/drawables.js').InstanceLabels} InstanceLabels */
@@ -3,6 +3,39 @@ import { Texture } from './Texture.js';
3
3
  import type { ColorInput } from '../types.js';
4
4
  import type { TextureOptions } from './Texture.js';
5
5
  export type MapInput = Texture | TextureOptions | string;
6
+ export type ShaderUniform = number | number[] | ColorInput | Texture;
7
+ export type ShaderHooks = {
8
+ /**
9
+ * runs on each vertex before it is placed: change `local` (vec3, the
10
+ * geometry's own space) - and for meshes `n`, its normal; reads `uv` (meshes), `index` (the
11
+ * instance, point or segment), `size` (points: change it too) and `along` (lines: 0 at a
12
+ * segment's start, 1 at its end)
13
+ */
14
+ vertex?: string;
15
+ /**
16
+ * runs on each pixel before lighting: change `base` (vec4: linear
17
+ * colour and alpha) and, on meshes, `emissive` and `normal` (the shading normal, world space - bend it for
18
+ * relief lit per pixel); `discard` cuts the pixel away - shadows and
19
+ * picking too. Reads `vUv`, `vWorldPosition`, `vNormal` (meshes), `pointUv` and `index`
20
+ * (points), `distance` along the line and `index` the segment (lines)
21
+ */
22
+ fragment?: string;
23
+ /**
24
+ * runs after lighting: change `color` (vec3, linear) and `base.a` - glows,
25
+ * scanlines and fades the light doesn't touch
26
+ */
27
+ lit?: string;
28
+ /**
29
+ * values the hooks read, by name; change them on
30
+ * `material.shader.uniforms` and the View redraws
31
+ */
32
+ uniforms?: Record<string, ShaderUniform>;
33
+ /**
34
+ * how far, in world units, the vertex hook can move a vertex: bounds are
35
+ * padded by it for culling, framing and shadows. Without it a vertex hook turns frustum culling off
36
+ */
37
+ bounds?: number;
38
+ };
6
39
  export type MaterialOptions = {
7
40
  name?: string;
8
41
  /**
@@ -207,10 +240,27 @@ export type MaterialOptions = {
207
240
  * Points: sized in the world, so smaller with distance (default true)
208
241
  */
209
242
  sizeAttenuation?: boolean;
243
+ /**
244
+ * GLSL of your own, run at fixed points in the shader - waves,
245
+ * dissolves, glows (default none)
246
+ */
247
+ shader?: ShaderHooks | null;
210
248
  /**
211
249
  * Lines: width in pixels (default 1)
212
250
  */
213
251
  width?: number;
252
+ /**
253
+ * Lines: dashed, with dashes this long in the model's units; 0 is solid (default 0)
254
+ */
255
+ dash?: number;
256
+ /**
257
+ * Lines: the gaps between dashes; 0 is as long as the dashes (default 0)
258
+ */
259
+ gap?: number;
260
+ /**
261
+ * Lines: moves the dashes along the lines - animate it for flow (default 0)
262
+ */
263
+ dashOffset?: number;
214
264
  };
215
265
  /**
216
266
  * The one material: what a surface looks like. Plain objects passed as a `material` become
@@ -240,6 +290,10 @@ export declare class Material {
240
290
  colour: string;
241
291
  emissiveColor: string;
242
292
  alpha: string;
293
+ onBeforeCompile: string;
294
+ vertexShader: string;
295
+ fragmentShader: string;
296
+ uniforms: string;
243
297
  };
244
298
  /** @type {string} */ name: string;
245
299
  /** @type {number} */ opacity: number;
@@ -275,8 +329,21 @@ export declare class Material {
275
329
  /** @type {number} */ size: number;
276
330
  /** @type {boolean} */ sizeAttenuation: boolean;
277
331
  /** @type {number} */ width: number;
332
+ /** Lines: dash length in the model's units; 0 is solid. @type {number} */ dash: number;
333
+ /** Lines: gap length; 0 is the dash's. @type {number} */ gap: number;
334
+ /** Lines: how far the dashes have moved along. @type {number} */ dashOffset: number;
278
335
  /** @param {MaterialOptions} [options] */
279
336
  constructor(options?: MaterialOptions);
337
+ /**
338
+ * GLSL hooks run inside the shader, or null. Change its uniforms in place
339
+ * (`material.shader.uniforms.uAmount = 2`); assign a new object to change the code.
340
+ * @returns {Required<Omit<ShaderHooks, 'bounds'>> & { bounds: number | null } | null}
341
+ */
342
+ get shader(): (Required<Omit<ShaderHooks, 'bounds'>> & {
343
+ bounds: number | null;
344
+ }) | null;
345
+ /** @param {ShaderHooks | null} v */
346
+ set shader(v: ShaderHooks | null);
280
347
  /** Base colour; assign any colour input. @returns {Color} */
281
348
  get color(): Color;
282
349
  /** @param {ColorInput} v */