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":"particles.js","names":["OPTIONS","HINTS","mat4.invert","fail","Vec3","checkOptions","invert","describe","checkNumber","Color","Material","Geometry","Node","Points","SPAWN_OPTIONS","OPTIONS$1","HINTS$1","DEFAULT_MATERIAL","tmpMat","Emitter","options","max","given","material","geometry","#max","#velocities","#ages","#lifetimes","#palette","#setBehaviour","#setSpawn","dt","known","key","#alive","#palettes","colorStops","v","s","spawn","saved","#saveSpawn","color","#spawnPalette","#paletteFor","origin","i","count","#spawn","#upload","#due","p","ages","lives","g","keep","hook","particle","#particle","age","#remove","i3","k","#paint","#followedOrigin","from","#lastOrigin","born","n","rest","t","o","randomInBall","dv","a","b","head","min","last","c","stops","x","f","world","owner","lifetimeRange","checkFollow","id","#paletteIds","range","list","r","y","z","OPTIONS","HINTS","Body"],"sources":["../src/particles/Emitter.js","../src/particles/Body.js"],"sourcesContent":["// A particle system as one Points node: particles are born at the emitter's origin, move under\n// velocity, gravity and drag, fade through a list of colours and die. The simulation runs in the\n// emitter's own update handler, so it follows the scene clock (frame-rate independent) and its\n// arrays go to the GPU as one buffer refill per frame.\nimport * as mat4 from '../math/mat4.js'\nimport { Geometry } from '../geometry/Geometry.js'\nimport { Material } from '../material/Material.js'\nimport { Points } from '../scene/drawables.js'\nimport { Node } from '../scene/Node.js'\nimport { Color } from '../value/Color.js'\nimport { Vec3 } from '../value/Vec3.js'\nimport { checkNumber, describe, fail } from '../validate.js'\n\n/** @import { MaterialOptions } from '../material/Material.js' */\n/** @import { NodeOptions } from '../scene/Node.js' */\n/** @import { ColorInput, Vec3Input } from '../types.js' */\n\n/**\n * A live particle, as passed to `onParticle`. Change `position` or `velocity` and the changes stick.\n * @typedef {object} Particle\n * @property {Vec3} position in the emitter's space\n * @property {Vec3} velocity units per second\n * @property {number} age seconds since birth\n * @property {number} lifetime seconds it lives for\n */\n\n/**\n * How particles are born. Each can also be passed to burst() for that burst alone.\n * @typedef {object} SpawnOptions\n * @property {Vec3Input} [origin] where particles start, in the emitter's space (default [0, 0, 0])\n * @property {number} [originSpread] start anywhere within this radius of the origin (default 0)\n * @property {Vec3Input} [velocity] starting velocity, units per second (default [0, 1, 0])\n * @property {number | Vec3Input} [velocitySpread] random extra velocity: a number spreads evenly in every\n * direction (a sphere - explosions), [x, y, z] spreads up to ± that much per axis (default 0)\n * @property {number | [number, number]} [lifetime] seconds, or a [min, max] range (default 1)\n * @property {ColorInput | ColorInput[]} [color] a colour, or colours each particle fades through over its\n * life - end on black to fade out with additive blending (default white)\n */\n\n/**\n * @typedef {NodeOptions & SpawnOptions & {\n * max?: number,\n * rate?: number,\n * gravity?: Vec3Input,\n * drag?: number,\n * follow?: Node | null,\n * material?: Material | MaterialOptions,\n * frustumCulled?: boolean,\n * renderOrder?: number,\n * onParticle?: ((p: Particle) => boolean | void) | null,\n * }} EmitterOptions\n * max: most live particles at once (default 1000). rate: particles born per second (default 100).\n * gravity: acceleration, e.g. [0, -9.8, 0] (default none). drag: share of velocity lost per second\n * (default 0). follow: a node whose position becomes the origin each frame - a trail that stays\n * behind it. onParticle: called for every live particle each frame after it moves; return false to\n * remove it (a particle hitting something).\n */\n\nconst SPAWN_OPTIONS = ['origin', 'originSpread', 'velocity', 'velocitySpread', 'lifetime', 'color']\nconst OPTIONS = [...Points.OPTIONS, ...SPAWN_OPTIONS,\n 'max', 'rate', 'gravity', 'drag', 'follow', 'onParticle']\nconst HINTS = {\n count: \"use 'max' for the most particles alive at once, 'rate' for how many are born per second\",\n lifetimeRnd: \"give 'lifetime' a [min, max] range\",\n velocityRnd: \"use 'velocitySpread'\",\n positionRnd: \"use 'originSpread'\",\n}\n// the look most particle effects want: glowing dots that don't hide each other\n/** @type {MaterialOptions} */\nconst DEFAULT_MATERIAL = { size: 0.1, vertexColors: true, blending: 'additive', depthWrite: false }\n\nconst tmpMat = new Float32Array(16)\n\n/**\n * Particles drawn as points. Add it to a scene and it runs itself.\n *\n * const sparks = scene.add(new Emitter({ rate: 200, velocitySpread: 2, gravity: [0, -9.8, 0], color: ['#ffd27f', '#000'] }))\n * sparks.burst(500, { velocitySpread: 8 })\n */\nexport class Emitter extends Points {\n static type = 'emitter'\n static get OPTIONS() { return OPTIONS }\n static get HINTS() { return { ...Points.HINTS, ...HINTS } }\n\n /** Particles born per second; 0 stops new ones (existing particles live out their lives). */\n rate = 100\n /** Acceleration on every particle, units per second². */\n gravity = new Vec3()\n /** Share of velocity lost per second. */\n drag = 0\n /** Where particles start, in the emitter's space. */\n origin = new Vec3()\n originSpread = 0\n velocity = new Vec3(0, 1, 0)\n /** @type {number | Vec3} */ velocitySpread = 0\n /** @type {[number, number]} */ lifetime = [1, 1]\n /** A node whose position becomes the origin each frame. @type {Node | null} */\n follow = null\n /** @type {((p: Particle) => boolean | void) | null} */\n onParticle = null\n\n #max\n #alive = 0\n #due = 0\n /** @type {Float32Array} */ #velocities\n /** @type {Float32Array} */ #ages\n /** @type {Float32Array} */ #lifetimes\n // Colour stop lists, sRGB like geometry colours. Palette 0 is the emitter's own colour; bursts with\n // their own colours add more, and each particle remembers which one it fades through.\n /** @type {number[][][]} */ #palettes = [[[1, 1, 1]]]\n /** @type {Map<string, number>} */ #paletteIds = new Map()\n /** @type {Uint8Array} */ #palette\n #spawnPalette = 0\n /** Last frame's origin, so a moving source leaves an even trail rather than clumps. */\n #lastOrigin = /** @type {Vec3 | null} */ (null)\n #particle = /** @type {Particle} */ ({ position: new Vec3(), velocity: new Vec3(), age: 0, lifetime: 0 })\n\n /** @param {EmitterOptions} [options] */\n constructor(options = {}) {\n if (/** @type {any} */ (options)?.geometry !== undefined) fail('Emitter', \"an Emitter makes its own geometry - size it with 'max'\")\n const max = options?.max ?? 1000\n if (!Number.isInteger(max) || max < 1) fail('Emitter', `'max' must be a whole number of 1 or more, got ${describe(max)}`)\n const given = options?.material\n if (given !== undefined && !(given instanceof Material) && (!given || typeof given !== 'object' || Array.isArray(given))) {\n fail('Emitter', `'material' is a Material or material options such as { size: 0.2 }, got ${describe(given)}`)\n }\n const material = given instanceof Material ? given : { ...DEFAULT_MATERIAL, ...given }\n const geometry = new Geometry({ positions: new Float32Array(max * 3), colors: new Float32Array(max * 3) })\n geometry.drawCount = 0\n super({ ...options, geometry, material })\n this.#max = max\n this.#velocities = new Float32Array(max * 3)\n this.#ages = new Float32Array(max)\n this.#lifetimes = new Float32Array(max)\n this.#palette = new Uint8Array(max)\n // particles spread anywhere, and stale slots would make bounds meaningless\n if (options.frustumCulled === undefined) this.frustumCulled = false\n\n this.#setBehaviour(options)\n this.#setSpawn(options)\n this.onUpdate((_, dt) => this.step(dt))\n }\n\n /**\n * Change several settings at once - a preset, say. Vectors are copied into the emitter's own,\n * so anything bound to `emitter.gravity` and friends stays bound. Live particles carry on.\n * @param {SpawnOptions & { rate?: number, gravity?: Vec3Input, drag?: number, follow?: Node | null,\n * onParticle?: ((p: Particle) => boolean | void) | null }} options\n * @returns {this}\n */\n set(options) {\n const known = [...SPAWN_OPTIONS, 'rate', 'gravity', 'drag', 'follow', 'onParticle']\n for (const key of Object.keys(options ?? {})) {\n if (!known.includes(key)) fail('Emitter', `set() doesn't take '${key}' - it takes ${known.join(', ')}`)\n }\n this.#setBehaviour(options)\n this.#setSpawn(options)\n return this\n }\n\n /** Most live particles at once. */\n get max() { return this.#max }\n\n /** Particles alive now. */\n get count() { return this.#alive }\n\n /** The colours particles fade through - one or several. @param {ColorInput | ColorInput[]} v */\n set color(v) { this.#palettes[0] = colorStops(v) }\n /** @returns {[number, number, number][]} */\n get color() { return /** @type {[number, number, number][]} */ (this.#palettes[0].map((s) => [...s])) }\n\n /**\n * Birth `count` particles at once - explosions, splashes. Options override the emitter's own\n * spawn settings for this burst only.\n * @param {number} count\n * @param {SpawnOptions} [options]\n * @returns {this}\n */\n burst(count, options = {}) {\n for (const key of Object.keys(options)) {\n if (!SPAWN_OPTIONS.includes(key)) fail('Emitter', `burst() doesn't take '${key}' - it takes ${SPAWN_OPTIONS.join(', ')}`)\n }\n const { color, ...spawn } = options\n const saved = this.#saveSpawn()\n this.#setSpawn(spawn)\n if (color !== undefined) this.#spawnPalette = this.#paletteFor(colorStops(color))\n const origin = this.origin.clone()\n for (let i = 0; i < count; i++) this.#spawn(origin, origin, 0)\n this.#setSpawn(saved)\n this.#spawnPalette = 0\n this.#upload()\n return this\n }\n\n /** Remove every particle. @returns {this} */\n clear() {\n this.#alive = 0\n this.#due = 0\n this.#upload()\n return this\n }\n\n /**\n * Advance the simulation by dt seconds. The emitter's update handler calls this each frame; call\n * it yourself only for an emitter outside a running scene.\n * @param {number} dt\n */\n step(dt) {\n if (dt <= 0) return\n const p = this.geometry.positions\n const v = this.#velocities, ages = this.#ages, lives = this.#lifetimes\n const g = this.gravity, keep = Math.exp(-this.drag * dt)\n const hook = this.onParticle, particle = this.#particle\n\n for (let i = 0; i < this.#alive;) {\n const age = ages[i] += dt\n if (age >= lives[i]) {\n this.#remove(i)\n continue\n }\n const i3 = i * 3\n for (let k = 0; k < 3; k++) {\n v[i3 + k] = (v[i3 + k] + g[k] * dt) * keep\n p[i3 + k] += v[i3 + k] * dt\n }\n this.#paint(i, age / lives[i])\n if (hook) {\n particle.position.set(p[i3], p[i3 + 1], p[i3 + 2])\n particle.velocity.set(v[i3], v[i3 + 1], v[i3 + 2])\n particle.age = age\n particle.lifetime = lives[i]\n if (hook(particle) === false) {\n this.#remove(i)\n continue\n }\n p.set(particle.position, i3)\n v.set(particle.velocity, i3)\n }\n i++\n }\n\n // new particles, spread along the path the origin took this frame\n const origin = this.#followedOrigin()\n const from = this.#lastOrigin ?? origin\n this.#due += this.rate * dt\n const born = Math.floor(this.#due)\n this.#due -= born\n for (let n = 0; n < born; n++) this.#spawn(from, origin, (n + Math.random()) / born * dt)\n this.#lastOrigin = origin.clone()\n this.#upload()\n }\n\n /** @internal @returns {Record<string, unknown>} */\n cloneOptions() {\n // an emitter makes its own geometry, so neither form carries it\n const rest = super.cloneOptions()\n delete rest.geometry\n return { ...rest, ...this.#saveSpawn(), max: this.#max, rate: this.rate, gravity: this.gravity.clone(),\n drag: this.drag, follow: this.follow, onParticle: this.onParticle, color: this.color }\n }\n\n /** @internal @returns {Record<string, unknown>} */\n jsonOptions() {\n const rest = super.jsonOptions()\n delete rest.geometry\n return { ...rest, max: this.#max, rate: this.rate, gravity: this.gravity.toArray(), drag: this.drag,\n origin: this.origin.toArray(), originSpread: this.originSpread, velocity: this.velocity.toArray(),\n velocitySpread: typeof this.velocitySpread === 'number' ? this.velocitySpread : this.velocitySpread.toArray(),\n lifetime: [...this.lifetime], color: this.#palettes[0].map((s) => new Color().setSRGB(s[0], s[1], s[2]).toJSON()) }\n }\n\n // birth one particle somewhere between a and b, already `head` seconds into its life\n /** @param {Vec3} a @param {Vec3} b @param {number} head */\n #spawn(a, b, head) {\n if (this.#alive >= this.#max) return\n const i = this.#alive++, i3 = i * 3\n const p = this.geometry.positions, v = this.#velocities\n const t = Math.random()\n const o = randomInBall(this.originSpread)\n const s = this.velocitySpread\n const dv = typeof s === 'number' ? randomInBall(s) : [(Math.random() * 2 - 1) * s[0], (Math.random() * 2 - 1) * s[1], (Math.random() * 2 - 1) * s[2]]\n for (let k = 0; k < 3; k++) {\n v[i3 + k] = this.velocity[k] + dv[k]\n p[i3 + k] = a[k] + (b[k] - a[k]) * t + o[k] + v[i3 + k] * head\n }\n const [min, max] = this.lifetime\n this.#lifetimes[i] = min + Math.random() * (max - min)\n this.#ages[i] = head\n this.#palette[i] = this.#spawnPalette\n this.#paint(i, head / this.#lifetimes[i])\n }\n\n // move the last particle into slot i\n /** @param {number} i */\n #remove(i) {\n const last = --this.#alive\n if (i === last) return\n const p = this.geometry.positions, c = /** @type {Float32Array} */ (this.geometry.colors), v = this.#velocities\n p.copyWithin(i * 3, last * 3, last * 3 + 3)\n c.copyWithin(i * 3, last * 3, last * 3 + 3)\n v.copyWithin(i * 3, last * 3, last * 3 + 3)\n this.#ages[i] = this.#ages[last]\n this.#lifetimes[i] = this.#lifetimes[last]\n this.#palette[i] = this.#palette[last]\n }\n\n /** @param {number} i @param {number} t 0-1 through its life */\n #paint(i, t) {\n const c = /** @type {Float32Array} */ (this.geometry.colors), stops = this.#palettes[this.#palette[i]]\n if (stops.length === 1) {\n c.set(stops[0], i * 3)\n return\n }\n const x = Math.min(Math.max(t, 0), 1) * (stops.length - 1)\n const s = Math.min(Math.floor(x), stops.length - 2), f = x - s\n const a = stops[s], b = stops[s + 1]\n c[i * 3] = a[0] + (b[0] - a[0]) * f\n c[i * 3 + 1] = a[1] + (b[1] - a[1]) * f\n c[i * 3 + 2] = a[2] + (b[2] - a[2]) * f\n }\n\n #upload() {\n this.geometry.drawCount = this.#alive\n this.geometry.needsUpdate()\n }\n\n /** The followed node's position in the emitter's space, or the origin. @returns {Vec3} */\n #followedOrigin() {\n if (!this.follow) return this.origin\n const world = this.follow.worldPosition\n if (!mat4.invert(tmpMat, this.worldMatrix)) return this.origin\n return this.origin.copy(world).applyMatrix4(tmpMat)\n }\n\n /** @param {SpawnOptions} o */\n #setSpawn(o) {\n const owner = 'Emitter'\n // vectors are copied in place, so references to them stay live\n if (o.origin !== undefined) this.origin.copy(o.origin, owner)\n if (o.originSpread !== undefined) this.originSpread = Math.max(0, checkNumber(owner, 'originSpread', o.originSpread))\n if (o.velocity !== undefined) this.velocity.copy(o.velocity, owner)\n if (o.velocitySpread !== undefined) {\n const s = o.velocitySpread\n if (typeof s === 'number') this.velocitySpread = checkNumber(owner, 'velocitySpread', s)\n else if (this.velocitySpread instanceof Vec3) this.velocitySpread.copy(s, owner)\n else this.velocitySpread = Vec3.fromInput(s, owner)\n }\n if (o.lifetime !== undefined) this.lifetime = lifetimeRange(o.lifetime)\n if (o.color !== undefined) this.#palettes[0] = colorStops(o.color)\n }\n\n /** @param {{ rate?: number, gravity?: Vec3Input, drag?: number, follow?: Node | null, onParticle?: ((p: Particle) => boolean | void) | null }} o */\n #setBehaviour(o) {\n const owner = 'Emitter'\n if (o.rate !== undefined) this.rate = Math.max(0, checkNumber(owner, 'rate', o.rate))\n if (o.gravity !== undefined) this.gravity.copy(o.gravity, owner)\n if (o.drag !== undefined) this.drag = Math.max(0, checkNumber(owner, 'drag', o.drag))\n if (o.follow !== undefined) this.follow = checkFollow(o.follow)\n if (o.onParticle !== undefined) this.onParticle = o.onParticle\n }\n\n /** The spawn settings other than colour, as options. @returns {SpawnOptions} */\n #saveSpawn() {\n return { origin: this.origin.clone(), originSpread: this.originSpread, velocity: this.velocity.clone(),\n velocitySpread: typeof this.velocitySpread === 'number' ? this.velocitySpread : this.velocitySpread.clone(),\n lifetime: /** @type {[number, number]} */ ([...this.lifetime]) }\n }\n\n // The palette id for a burst's colours, reused when the same colours come again. Ids are a byte\n // per particle, so past 255 distinct burst palettes the oldest is overwritten.\n /** @param {number[][]} stops @returns {number} */\n #paletteFor(stops) {\n const key = stops.flat().join()\n let id = this.#paletteIds.get(key)\n if (id === undefined) {\n id = this.#palettes.length < 256 ? this.#palettes.length : 1 + (this.#paletteIds.size % 255)\n for (const [k, v] of this.#paletteIds) if (v === id) this.#paletteIds.delete(k)\n this.#paletteIds.set(key, id)\n }\n this.#palettes[id] = stops\n return id\n }\n}\n\n/** @param {unknown} v @returns {Node | null} */\nfunction checkFollow(v) {\n if (v === null || v instanceof Node) return /** @type {Node | null} */ (v)\n return fail('Emitter', `'follow' must be a node, got ${describe(v)}`)\n}\n\n/** @param {number | [number, number]} v @returns {[number, number]} */\nfunction lifetimeRange(v) {\n const range = typeof v === 'number' ? [v, v] : v\n if (!Array.isArray(range) || range.length !== 2 || !range.every((n) => typeof n === 'number' && n > 0)) {\n fail('Emitter', `'lifetime' must be seconds above 0, or a [min, max] range, got ${describe(v)}`)\n }\n return [Math.min(range[0], range[1]), Math.max(range[0], range[1])]\n}\n\n/** @param {ColorInput | ColorInput[]} v @returns {number[][]} */\nfunction colorStops(v) {\n // a single [r, g, b] array is one colour, not three\n const list = Array.isArray(v) && !(v.length === 3 && v.every((n) => typeof n === 'number')) ? v : [v]\n if (!list.length) fail('Emitter', \"'color' needs at least one colour\")\n return list.map((c) => Color.fromInput(/** @type {ColorInput} */ (c), 'Emitter').toSRGB())\n}\n\n/** A random point within a ball of radius r, evenly spread. @param {number} r @returns {number[]} */\nfunction randomInBall(r) {\n if (!r) return [0, 0, 0]\n let x, y, z\n do {\n x = Math.random() * 2 - 1\n y = Math.random() * 2 - 1\n z = Math.random() * 2 - 1\n } while (x * x + y * y + z * z > 1)\n return [x * r, y * r, z * r]\n}\n","// Simple physics as a behaviour rather than a base class: any node moves with velocity, gravity\n// and drag once its body's update is attached - node.onUpdate(body.update).\nimport { Vec3 } from '../value/Vec3.js'\nimport { checkNumber, checkOptions } from '../validate.js'\n\n/** @import { Node } from '../scene/Node.js' */\n/** @import { Vec3Input } from '../types.js' */\n\n/**\n * @typedef {object} BodyOptions\n * @property {Vec3Input} [velocity] units per second (default [0, 0, 0])\n * @property {Vec3Input} [gravity] acceleration, e.g. [0, -9.8, 0] (default none)\n * @property {number} [drag] share of velocity lost per second (default 0)\n */\n\n/**\n * Velocity, gravity and drag for a node.\n *\n * const body = new Body({ velocity: [0, 20, 0], gravity: [0, -9.8, 0] })\n * rocket.onUpdate(body.update)\n */\nconst OPTIONS = ['velocity', 'gravity', 'drag']\nconst HINTS = {\n impulse: 'call body.impulse([x, y, z]) after creating it',\n mass: 'a Body has no mass - impulse() changes velocity directly',\n}\n\nexport class Body {\n /** The options it takes, and hints for names people reach for instead. */\n static OPTIONS = OPTIONS\n static HINTS = HINTS\n velocity = new Vec3()\n gravity = new Vec3()\n drag = 0\n\n /** @param {BodyOptions} [options] */\n constructor(options = {}) {\n checkOptions('Body', options, OPTIONS, HINTS)\n if (options.velocity !== undefined) this.velocity.copy(options.velocity, 'Body')\n if (options.gravity !== undefined) this.gravity.copy(options.gravity, 'Body')\n if (options.drag !== undefined) this.drag = Math.max(0, checkNumber('Body', 'drag', options.drag))\n }\n\n /** Add to the velocity at once - a kick. @param {Vec3Input} v @returns {this} */\n impulse(v) {\n this.velocity.add(Vec3.fromInput(v, 'Body'))\n return this\n }\n\n /**\n * Move a node one step. Bound, so it can be handed straight to node.onUpdate().\n * @param {Node} node\n * @param {number} dt seconds\n */\n update = (node, dt) => {\n const v = this.velocity, keep = Math.exp(-this.drag * dt)\n for (let k = 0; k < 3; k++) {\n v[k] = (v[k] + this.gravity[k] * dt) * keep\n node.position[k] += v[k] * dt\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"particles.js","names":["OPTIONS","HINTS","mat4.invert","checkNumber","invert","fail","checkOptions","describe","Vec3","Color","Points","Node","Geometry","Material","SPAWN_OPTIONS","BEHAVIOUR_OPTIONS","OPTIONS$1","HINTS$1","DEFAULT_MATERIAL","tmpMat","Emitter","options","max","given","material","geometry","#max","#velocities","#ages","#lifetimes","#palette","#setBehaviour","#setSpawn","dt","known","key","#alive","#palettes","colorStops","v","s","#setLooks","#size","#sizeSpread","#opacity","#spin","spawn","saved","#saveSpawn","color","#spawnPalette","#paletteFor","origin","i","count","#spawn","#upload","#due","p","ages","lives","g","keep","hook","particle","#particle","angles","spins","#spins","age","#remove","i3","k","#paint","#followedOrigin","from","#lastOrigin","born","n","rest","#looks","t","o","randomInBall","dv","a","b","head","min","#sizeFactors","last","c","sizes","along","opacities","stops","x","f","world","owner","lifetimeRange","checkFollow","lookStops","spinRange","plain","unstop","id","#paletteIds","range","list","name","most","r","y","z","OPTIONS","HINTS","Body"],"sources":["../src/particles/Emitter.js","../src/particles/Body.js"],"sourcesContent":["// A particle system as one Points node: particles are born at the emitter's origin, move under\n// velocity, gravity and drag, fade through a list of colours and die. The simulation runs in the\n// emitter's own update handler, so it follows the scene clock (frame-rate independent) and its\n// arrays go to the GPU as one buffer refill per frame. Particles that grow, fade or turn give the\n// geometry per-point sizes, opacities or angles, and are drawn as squares.\nimport * as mat4 from '../math/mat4.js'\nimport { Geometry } from '../geometry/Geometry.js'\nimport { Material } from '../material/Material.js'\nimport { Points } from '../scene/drawables.js'\nimport { Node } from '../scene/Node.js'\nimport { Color } from '../value/Color.js'\nimport { Vec3 } from '../value/Vec3.js'\nimport { checkNumber, describe, fail } from '../validate.js'\n\n/** @import { MaterialOptions } from '../material/Material.js' */\n/** @import { NodeOptions } from '../scene/Node.js' */\n/** @import { ColorInput, Vec3Input } from '../types.js' */\n\n/**\n * A live particle, as passed to `onParticle`. Change `position` or `velocity` and the changes stick.\n * @typedef {object} Particle\n * @property {Vec3} position in the emitter's space\n * @property {Vec3} velocity units per second\n * @property {number} age seconds since birth\n * @property {number} lifetime seconds it lives for\n */\n\n/**\n * How particles are born. Each can also be passed to burst() for that burst alone.\n * @typedef {object} SpawnOptions\n * @property {Vec3Input} [origin] where particles start, in the emitter's space (default [0, 0, 0])\n * @property {number} [originSpread] start anywhere within this radius of the origin (default 0)\n * @property {Vec3Input} [velocity] starting velocity, units per second (default [0, 1, 0])\n * @property {number | Vec3Input} [velocitySpread] random extra velocity: a number spreads evenly in every\n * direction (a sphere - explosions), [x, y, z] spreads up to ± that much per axis (default 0)\n * @property {number | [number, number]} [lifetime] seconds, or a [min, max] range (default 1)\n * @property {ColorInput | ColorInput[]} [color] a colour, or colours each particle fades through over its\n * life - end on black to fade out with additive blending (default white)\n */\n\n/**\n * @typedef {NodeOptions & SpawnOptions & {\n * max?: number,\n * rate?: number,\n * gravity?: Vec3Input,\n * drag?: number,\n * follow?: Node | null,\n * material?: Material | MaterialOptions,\n * frustumCulled?: boolean,\n * renderOrder?: number,\n * onParticle?: ((p: Particle) => boolean | void) | null,\n * sort?: boolean,\n * } & LookOptions} EmitterOptions\n * max: most live particles at once (default 1000). rate: particles born per second (default 100).\n * gravity: acceleration, e.g. [0, -9.8, 0] (default none). drag: share of velocity lost per second\n * (default 0). follow: a node whose position becomes the origin each frame - a trail that stays\n * behind it. onParticle: called for every live particle each frame after it moves; return false to\n * remove it (a particle hitting something). sort: draw furthest first, for soft particles with\n * blending 'normal' (smoke) that would otherwise layer wrongly.\n */\n\n/**\n * How particles look over their lives, beyond colour.\n * @typedef {object} LookOptions\n * @property {number | number[]} [size] a multiple of the material's size, or several each particle goes\n * through over its life - [0.5, 3] for smoke that billows out (default 1)\n * @property {number} [sizeSpread] each particle's size varies by up to this share either way, 0-1 (default 0)\n * @property {number | number[]} [opacity] 0-1, or several each particle goes through - [0, 1, 0] fades in\n * and out (default 1)\n * @property {number | [number, number]} [spin] radians per second: a number turns each particle up to that\n * fast either way, [min, max] a range. Spinning particles start at a random angle (default 0)\n */\n\nconst SPAWN_OPTIONS = ['origin', 'originSpread', 'velocity', 'velocitySpread', 'lifetime', 'color']\nconst LOOK_OPTIONS = ['size', 'sizeSpread', 'opacity', 'spin']\nconst BEHAVIOUR_OPTIONS = ['rate', 'gravity', 'drag', 'follow', 'onParticle', ...LOOK_OPTIONS]\nconst OPTIONS = [...Points.OPTIONS, ...SPAWN_OPTIONS, 'max', ...BEHAVIOUR_OPTIONS]\nconst HINTS = {\n count: \"use 'max' for the most particles alive at once, 'rate' for how many are born per second\",\n lifetimeRnd: \"give 'lifetime' a [min, max] range\",\n velocityRnd: \"use 'velocitySpread'\",\n positionRnd: \"use 'originSpread'\",\n}\n// the look most particle effects want: glowing dots that don't hide each other\n/** @type {MaterialOptions} */\nconst DEFAULT_MATERIAL = { size: 0.1, vertexColors: true, blending: 'additive', depthWrite: false }\n\nconst tmpMat = new Float32Array(16)\n\n/**\n * Particles drawn as points. Add it to a scene and it runs itself.\n *\n * const sparks = scene.add(new Emitter({ rate: 200, velocitySpread: 2, gravity: [0, -9.8, 0], color: ['#ffd27f', '#000'] }))\n * sparks.burst(500, { velocitySpread: 8 })\n */\nexport class Emitter extends Points {\n static type = 'emitter'\n static get OPTIONS() { return OPTIONS }\n static get HINTS() { return { ...Points.HINTS, ...HINTS } }\n\n /** Particles born per second; 0 stops new ones (existing particles live out their lives). */\n rate = 100\n /** Acceleration on every particle, units per second². */\n gravity = new Vec3()\n /** Share of velocity lost per second. */\n drag = 0\n /** Where particles start, in the emitter's space. */\n origin = new Vec3()\n originSpread = 0\n velocity = new Vec3(0, 1, 0)\n /** @type {number | Vec3} */ velocitySpread = 0\n /** @type {[number, number]} */ lifetime = [1, 1]\n /** A node whose position becomes the origin each frame. @type {Node | null} */\n follow = null\n /** @type {((p: Particle) => boolean | void) | null} */\n onParticle = null\n\n #max\n #alive = 0\n #due = 0\n /** @type {Float32Array} */ #velocities\n /** @type {Float32Array} */ #ages\n /** @type {Float32Array} */ #lifetimes\n // Colour stop lists, sRGB like geometry colours. Palette 0 is the emitter's own colour; bursts with\n // their own colours add more, and each particle remembers which one it fades through.\n /** @type {number[][][]} */ #palettes = [[[1, 1, 1]]]\n /** @type {Map<string, number>} */ #paletteIds = new Map()\n /** @type {Uint8Array} */ #palette\n #spawnPalette = 0\n // the looks: stops over each particle's life, and each one's own size factor and turn rate\n /** @type {number[]} */ #size = [1]\n #sizeSpread = 0\n /** @type {number[]} */ #opacity = [1]\n /** @type {[number, number]} */ #spin = [0, 0]\n /** @type {Float32Array | null} */ #sizeFactors = null\n /** @type {Float32Array | null} */ #spins = null\n /** Last frame's origin, so a moving source leaves an even trail rather than clumps. */\n #lastOrigin = /** @type {Vec3 | null} */ (null)\n #particle = /** @type {Particle} */ ({ position: new Vec3(), velocity: new Vec3(), age: 0, lifetime: 0 })\n\n /** @param {EmitterOptions} [options] */\n constructor(options = {}) {\n if (/** @type {any} */ (options)?.geometry !== undefined) fail('Emitter', \"an Emitter makes its own geometry - size it with 'max'\")\n const max = options?.max ?? 1000\n if (!Number.isInteger(max) || max < 1) fail('Emitter', `'max' must be a whole number of 1 or more, got ${describe(max)}`)\n const given = options?.material\n if (given !== undefined && !(given instanceof Material) && (!given || typeof given !== 'object' || Array.isArray(given))) {\n fail('Emitter', `'material' is a Material or material options such as { size: 0.2 }, got ${describe(given)}`)\n }\n const material = given instanceof Material ? given : { ...DEFAULT_MATERIAL, ...given }\n const geometry = new Geometry({ positions: new Float32Array(max * 3), colors: new Float32Array(max * 3) })\n geometry.drawCount = 0\n super({ ...options, geometry, material })\n this.#max = max\n this.#velocities = new Float32Array(max * 3)\n this.#ages = new Float32Array(max)\n this.#lifetimes = new Float32Array(max)\n this.#palette = new Uint8Array(max)\n // particles spread anywhere, and stale slots would make bounds meaningless\n if (options.frustumCulled === undefined) this.frustumCulled = false\n\n this.#setBehaviour(options)\n this.#setSpawn(options)\n this.onUpdate((_, dt) => this.step(dt))\n }\n\n /**\n * Change several settings at once - a preset, say. Vectors are copied into the emitter's own,\n * so anything bound to `emitter.gravity` and friends stays bound. Live particles carry on.\n * @param {SpawnOptions & LookOptions & { rate?: number, gravity?: Vec3Input, drag?: number, follow?: Node | null,\n * onParticle?: ((p: Particle) => boolean | void) | null }} options\n * @returns {this}\n */\n set(options) {\n const known = [...SPAWN_OPTIONS, ...BEHAVIOUR_OPTIONS]\n for (const key of Object.keys(options ?? {})) {\n if (!known.includes(key)) fail('Emitter', `set() doesn't take '${key}' - it takes ${known.join(', ')}`)\n }\n this.#setBehaviour(options)\n this.#setSpawn(options)\n return this\n }\n\n /** Most live particles at once. */\n get max() { return this.#max }\n\n /** Particles alive now. */\n get count() { return this.#alive }\n\n /** The colours particles fade through - one or several. @param {ColorInput | ColorInput[]} v */\n set color(v) { this.#palettes[0] = colorStops(v) }\n /** @returns {[number, number, number][]} */\n get color() { return /** @type {[number, number, number][]} */ (this.#palettes[0].map((s) => [...s])) }\n\n /** Each particle's size over its life, as multiples of the material's. @param {number | number[]} v */\n set size(v) { this.#setLooks({ size: v }) }\n /** @returns {number[]} */\n get size() { return [...this.#size] }\n /** How much each particle's size varies, 0-1 either way. @param {number} v */\n set sizeSpread(v) { this.#setLooks({ sizeSpread: v }) }\n get sizeSpread() { return this.#sizeSpread }\n /** Each particle's opacity over its life. @param {number | number[]} v */\n set opacity(v) { this.#setLooks({ opacity: v }) }\n /** @returns {number[]} */\n get opacity() { return [...this.#opacity] }\n /** How fast particles turn, radians per second, as a [min, max] range. @param {number | [number, number]} v */\n set spin(v) { this.#setLooks({ spin: v }) }\n /** @returns {[number, number]} */\n get spin() { return [...this.#spin] }\n\n /**\n * Birth `count` particles at once - explosions, splashes. Options override the emitter's own\n * spawn settings for this burst only.\n * @param {number} count\n * @param {SpawnOptions} [options]\n * @returns {this}\n */\n burst(count, options = {}) {\n for (const key of Object.keys(options)) {\n if (!SPAWN_OPTIONS.includes(key)) fail('Emitter', `burst() doesn't take '${key}' - it takes ${SPAWN_OPTIONS.join(', ')}`)\n }\n const { color, ...spawn } = options\n const saved = this.#saveSpawn()\n this.#setSpawn(spawn)\n if (color !== undefined) this.#spawnPalette = this.#paletteFor(colorStops(color))\n const origin = this.origin.clone()\n for (let i = 0; i < count; i++) this.#spawn(origin, origin, 0)\n this.#setSpawn(saved)\n this.#spawnPalette = 0\n this.#upload()\n return this\n }\n\n /** Remove every particle. @returns {this} */\n clear() {\n this.#alive = 0\n this.#due = 0\n this.#upload()\n return this\n }\n\n /**\n * Advance the simulation by dt seconds. The emitter's update handler calls this each frame; call\n * it yourself only for an emitter outside a running scene.\n * @param {number} dt\n */\n step(dt) {\n if (dt <= 0) return\n const p = this.geometry.positions\n const v = this.#velocities, ages = this.#ages, lives = this.#lifetimes\n const g = this.gravity, keep = Math.exp(-this.drag * dt)\n const hook = this.onParticle, particle = this.#particle\n const angles = this.geometry.angles, spins = this.#spins\n\n for (let i = 0; i < this.#alive;) {\n const age = ages[i] += dt\n if (age >= lives[i]) {\n this.#remove(i)\n continue\n }\n const i3 = i * 3\n for (let k = 0; k < 3; k++) {\n v[i3 + k] = (v[i3 + k] + g[k] * dt) * keep\n p[i3 + k] += v[i3 + k] * dt\n }\n if (angles && spins) angles[i] += spins[i] * dt\n this.#paint(i, age / lives[i])\n if (hook) {\n particle.position.set(p[i3], p[i3 + 1], p[i3 + 2])\n particle.velocity.set(v[i3], v[i3 + 1], v[i3 + 2])\n particle.age = age\n particle.lifetime = lives[i]\n if (hook(particle) === false) {\n this.#remove(i)\n continue\n }\n p.set(particle.position, i3)\n v.set(particle.velocity, i3)\n }\n i++\n }\n\n // new particles, spread along the path the origin took this frame\n const origin = this.#followedOrigin()\n const from = this.#lastOrigin ?? origin\n this.#due += this.rate * dt\n const born = Math.floor(this.#due)\n this.#due -= born\n for (let n = 0; n < born; n++) this.#spawn(from, origin, (n + Math.random()) / born * dt)\n this.#lastOrigin = origin.clone()\n this.#upload()\n }\n\n /** @internal @returns {Record<string, unknown>} */\n cloneOptions() {\n // an emitter makes its own geometry, so neither form carries it\n const rest = super.cloneOptions()\n delete rest.geometry\n return { ...rest, ...this.#saveSpawn(), max: this.#max, rate: this.rate, gravity: this.gravity.clone(),\n drag: this.drag, follow: this.follow, onParticle: this.onParticle, color: this.color, ...this.#looks() }\n }\n\n /** @internal @returns {Record<string, unknown>} */\n jsonOptions() {\n const rest = super.jsonOptions()\n delete rest.geometry\n return { ...rest, max: this.#max, rate: this.rate, gravity: this.gravity.toArray(), drag: this.drag,\n origin: this.origin.toArray(), originSpread: this.originSpread, velocity: this.velocity.toArray(),\n velocitySpread: typeof this.velocitySpread === 'number' ? this.velocitySpread : this.velocitySpread.toArray(),\n lifetime: [...this.lifetime], color: this.#palettes[0].map((s) => new Color().setSRGB(s[0], s[1], s[2]).toJSON()),\n ...this.#looks() }\n }\n\n // birth one particle somewhere between a and b, already `head` seconds into its life\n /** @param {Vec3} a @param {Vec3} b @param {number} head */\n #spawn(a, b, head) {\n if (this.#alive >= this.#max) return\n const i = this.#alive++, i3 = i * 3\n const p = this.geometry.positions, v = this.#velocities\n const t = Math.random()\n const o = randomInBall(this.originSpread)\n const s = this.velocitySpread\n const dv = typeof s === 'number' ? randomInBall(s) : [(Math.random() * 2 - 1) * s[0], (Math.random() * 2 - 1) * s[1], (Math.random() * 2 - 1) * s[2]]\n for (let k = 0; k < 3; k++) {\n v[i3 + k] = this.velocity[k] + dv[k]\n p[i3 + k] = a[k] + (b[k] - a[k]) * t + o[k] + v[i3 + k] * head\n }\n const [min, max] = this.lifetime\n this.#lifetimes[i] = min + Math.random() * (max - min)\n this.#ages[i] = head\n this.#palette[i] = this.#spawnPalette\n if (this.#sizeFactors) this.#sizeFactors[i] = 1 + (Math.random() * 2 - 1) * this.#sizeSpread\n const angles = this.geometry.angles, spins = this.#spins\n if (angles && spins) {\n spins[i] = this.#spin[0] + Math.random() * (this.#spin[1] - this.#spin[0])\n angles[i] = Math.random() * Math.PI * 2 + spins[i] * head\n }\n this.#paint(i, head / this.#lifetimes[i])\n }\n\n // move the last particle into slot i\n /** @param {number} i */\n #remove(i) {\n const last = --this.#alive\n if (i === last) return\n const p = this.geometry.positions, c = /** @type {Float32Array} */ (this.geometry.colors), v = this.#velocities\n p.copyWithin(i * 3, last * 3, last * 3 + 3)\n c.copyWithin(i * 3, last * 3, last * 3 + 3)\n v.copyWithin(i * 3, last * 3, last * 3 + 3)\n this.#ages[i] = this.#ages[last]\n this.#lifetimes[i] = this.#lifetimes[last]\n this.#palette[i] = this.#palette[last]\n for (const a of [this.geometry.sizes, this.geometry.opacities, this.geometry.angles, this.#sizeFactors, this.#spins]) {\n if (a) a[i] = a[last]\n }\n }\n\n /** @param {number} i @param {number} t 0-1 through its life */\n #paint(i, t) {\n const { sizes, opacities } = this.geometry\n if (sizes) sizes[i] = along(this.#size, t) * (this.#sizeFactors ? this.#sizeFactors[i] : 1)\n if (opacities) opacities[i] = along(this.#opacity, t)\n const c = /** @type {Float32Array} */ (this.geometry.colors), stops = this.#palettes[this.#palette[i]]\n if (stops.length === 1) {\n c.set(stops[0], i * 3)\n return\n }\n const x = Math.min(Math.max(t, 0), 1) * (stops.length - 1)\n const s = Math.min(Math.floor(x), stops.length - 2), f = x - s\n const a = stops[s], b = stops[s + 1]\n c[i * 3] = a[0] + (b[0] - a[0]) * f\n c[i * 3 + 1] = a[1] + (b[1] - a[1]) * f\n c[i * 3 + 2] = a[2] + (b[2] - a[2]) * f\n }\n\n #upload() {\n this.geometry.drawCount = this.#alive\n this.geometry.needsUpdate()\n }\n\n /** The followed node's position in the emitter's space, or the origin. @returns {Vec3} */\n #followedOrigin() {\n if (!this.follow) return this.origin\n const world = this.follow.worldPosition\n if (!mat4.invert(tmpMat, this.worldMatrix)) return this.origin\n return this.origin.copy(world).applyMatrix4(tmpMat)\n }\n\n /** @param {SpawnOptions} o */\n #setSpawn(o) {\n const owner = 'Emitter'\n // vectors are copied in place, so references to them stay live\n if (o.origin !== undefined) this.origin.copy(o.origin, owner)\n if (o.originSpread !== undefined) this.originSpread = Math.max(0, checkNumber(owner, 'originSpread', o.originSpread))\n if (o.velocity !== undefined) this.velocity.copy(o.velocity, owner)\n if (o.velocitySpread !== undefined) {\n const s = o.velocitySpread\n if (typeof s === 'number') this.velocitySpread = checkNumber(owner, 'velocitySpread', s)\n else if (this.velocitySpread instanceof Vec3) this.velocitySpread.copy(s, owner)\n else this.velocitySpread = Vec3.fromInput(s, owner)\n }\n if (o.lifetime !== undefined) this.lifetime = lifetimeRange(o.lifetime)\n if (o.color !== undefined) this.#palettes[0] = colorStops(o.color)\n }\n\n /** @param {LookOptions & { rate?: number, gravity?: Vec3Input, drag?: number, follow?: Node | null, onParticle?: ((p: Particle) => boolean | void) | null }} o */\n #setBehaviour(o) {\n this.#setLooks(o)\n const owner = 'Emitter'\n if (o.rate !== undefined) this.rate = Math.max(0, checkNumber(owner, 'rate', o.rate))\n if (o.gravity !== undefined) this.gravity.copy(o.gravity, owner)\n if (o.drag !== undefined) this.drag = Math.max(0, checkNumber(owner, 'drag', o.drag))\n if (o.follow !== undefined) this.follow = checkFollow(o.follow)\n if (o.onParticle !== undefined) this.onParticle = o.onParticle\n }\n\n // Sizes, opacities and angles go on the geometry when first wanted (the points are then drawn as\n // squares) and stay; live particles get the plain values until they are next painted.\n /** @param {LookOptions} o */\n #setLooks(o) {\n const owner = 'Emitter', g = this.geometry, max = this.#max\n if (o.size !== undefined) this.#size = lookStops('size', o.size, Infinity)\n if (o.sizeSpread !== undefined) this.#sizeSpread = Math.min(Math.max(checkNumber(owner, 'sizeSpread', o.sizeSpread), 0), 1)\n if (o.opacity !== undefined) this.#opacity = lookStops('opacity', o.opacity, 1)\n if (o.spin !== undefined) this.#spin = spinRange(o.spin)\n const plain = (/** @type {number[]} */ stops) => stops.length === 1 && stops[0] === 1\n if (!g.sizes && (!plain(this.#size) || this.#sizeSpread)) g.sizes = new Float32Array(max).fill(1)\n if (!this.#sizeFactors && this.#sizeSpread) this.#sizeFactors = new Float32Array(max).fill(1)\n if (!g.opacities && !plain(this.#opacity)) g.opacities = new Float32Array(max).fill(1)\n if (!g.angles && (this.#spin[0] || this.#spin[1])) {\n g.angles = new Float32Array(max)\n this.#spins = new Float32Array(max)\n }\n }\n\n /** The looks, as options. @returns {LookOptions} */\n #looks() {\n return { size: unstop(this.#size), sizeSpread: this.#sizeSpread, opacity: unstop(this.#opacity),\n spin: /** @type {[number, number]} */ ([...this.#spin]) }\n }\n\n /** The spawn settings other than colour, as options. @returns {SpawnOptions} */\n #saveSpawn() {\n return { origin: this.origin.clone(), originSpread: this.originSpread, velocity: this.velocity.clone(),\n velocitySpread: typeof this.velocitySpread === 'number' ? this.velocitySpread : this.velocitySpread.clone(),\n lifetime: /** @type {[number, number]} */ ([...this.lifetime]) }\n }\n\n // The palette id for a burst's colours, reused when the same colours come again. Ids are a byte\n // per particle, so past 255 distinct burst palettes the oldest is overwritten.\n /** @param {number[][]} stops @returns {number} */\n #paletteFor(stops) {\n const key = stops.flat().join()\n let id = this.#paletteIds.get(key)\n if (id === undefined) {\n id = this.#palettes.length < 256 ? this.#palettes.length : 1 + (this.#paletteIds.size % 255)\n for (const [k, v] of this.#paletteIds) if (v === id) this.#paletteIds.delete(k)\n this.#paletteIds.set(key, id)\n }\n this.#palettes[id] = stops\n return id\n }\n}\n\n/** @param {unknown} v @returns {Node | null} */\nfunction checkFollow(v) {\n if (v === null || v instanceof Node) return /** @type {Node | null} */ (v)\n return fail('Emitter', `'follow' must be a node, got ${describe(v)}`)\n}\n\n/** @param {number | [number, number]} v @returns {[number, number]} */\nfunction lifetimeRange(v) {\n const range = typeof v === 'number' ? [v, v] : v\n if (!Array.isArray(range) || range.length !== 2 || !range.every((n) => typeof n === 'number' && n > 0)) {\n fail('Emitter', `'lifetime' must be seconds above 0, or a [min, max] range, got ${describe(v)}`)\n }\n return [Math.min(range[0], range[1]), Math.max(range[0], range[1])]\n}\n\n/** @param {string} name @param {number | number[]} v @param {number} most @returns {number[]} */\nfunction lookStops(name, v, most) {\n const list = Array.isArray(v) ? v : [v]\n if (!list.length || !list.every((n) => typeof n === 'number' && Number.isFinite(n) && n >= 0)) {\n fail('Emitter', `'${name}' must be a number of 0 or more, or a list of them a particle goes through, got ${describe(v)}`)\n }\n return list.map((n) => Math.min(n, most))\n}\n\n/** One stop as a number, several as a list. @param {number[]} stops @returns {number | number[]} */\nfunction unstop(stops) {\n return stops.length === 1 ? stops[0] : [...stops]\n}\n\n/** The value t (0-1) of the way through evenly spaced stops. @param {number[]} stops @param {number} t */\nfunction along(stops, t) {\n if (stops.length === 1) return stops[0]\n const x = Math.min(Math.max(t, 0), 1) * (stops.length - 1)\n const s = Math.min(Math.floor(x), stops.length - 2)\n return stops[s] + (stops[s + 1] - stops[s]) * (x - s)\n}\n\n/** @param {number | [number, number]} v @returns {[number, number]} */\nfunction spinRange(v) {\n const range = typeof v === 'number' ? [-Math.abs(v), Math.abs(v)] : v\n if (!Array.isArray(range) || range.length !== 2 || !range.every((n) => typeof n === 'number' && Number.isFinite(n))) {\n fail('Emitter', `'spin' must be radians per second, or a [min, max] range, got ${describe(v)}`)\n }\n return [Math.min(range[0], range[1]), Math.max(range[0], range[1])]\n}\n\n/** @param {ColorInput | ColorInput[]} v @returns {number[][]} */\nfunction colorStops(v) {\n // a single [r, g, b] array is one colour, not three\n const list = Array.isArray(v) && !(v.length === 3 && v.every((n) => typeof n === 'number')) ? v : [v]\n if (!list.length) fail('Emitter', \"'color' needs at least one colour\")\n return list.map((c) => Color.fromInput(/** @type {ColorInput} */ (c), 'Emitter').toSRGB())\n}\n\n/** A random point within a ball of radius r, evenly spread. @param {number} r @returns {number[]} */\nfunction randomInBall(r) {\n if (!r) return [0, 0, 0]\n let x, y, z\n do {\n x = Math.random() * 2 - 1\n y = Math.random() * 2 - 1\n z = Math.random() * 2 - 1\n } while (x * x + y * y + z * z > 1)\n return [x * r, y * r, z * r]\n}\n","// Simple physics as a behaviour rather than a base class: any node moves with velocity, gravity\n// and drag once its body's update is attached - node.onUpdate(body.update).\nimport { Vec3 } from '../value/Vec3.js'\nimport { checkNumber, checkOptions } from '../validate.js'\n\n/** @import { Node } from '../scene/Node.js' */\n/** @import { Vec3Input } from '../types.js' */\n\n/**\n * @typedef {object} BodyOptions\n * @property {Vec3Input} [velocity] units per second (default [0, 0, 0])\n * @property {Vec3Input} [gravity] acceleration, e.g. [0, -9.8, 0] (default none)\n * @property {number} [drag] share of velocity lost per second (default 0)\n */\n\n/**\n * Velocity, gravity and drag for a node.\n *\n * const body = new Body({ velocity: [0, 20, 0], gravity: [0, -9.8, 0] })\n * rocket.onUpdate(body.update)\n */\nconst OPTIONS = ['velocity', 'gravity', 'drag']\nconst HINTS = {\n impulse: 'call body.impulse([x, y, z]) after creating it',\n mass: 'a Body has no mass - impulse() changes velocity directly',\n}\n\nexport class Body {\n /** The options it takes, and hints for names people reach for instead. */\n static OPTIONS = OPTIONS\n static HINTS = HINTS\n velocity = new Vec3()\n gravity = new Vec3()\n drag = 0\n\n /** @param {BodyOptions} [options] */\n constructor(options = {}) {\n checkOptions('Body', options, OPTIONS, HINTS)\n if (options.velocity !== undefined) this.velocity.copy(options.velocity, 'Body')\n if (options.gravity !== undefined) this.gravity.copy(options.gravity, 'Body')\n if (options.drag !== undefined) this.drag = Math.max(0, checkNumber('Body', 'drag', options.drag))\n }\n\n /** Add to the velocity at once - a kick. @param {Vec3Input} v @returns {this} */\n impulse(v) {\n this.velocity.add(Vec3.fromInput(v, 'Body'))\n return this\n }\n\n /**\n * Move a node one step. Bound, so it can be handed straight to node.onUpdate().\n * @param {Node} node\n * @param {number} dt seconds\n */\n update = (node, dt) => {\n const v = this.velocity, keep = Math.exp(-this.drag * dt)\n for (let k = 0; k < 3; k++) {\n v[k] = (v[k] + this.gravity[k] * dt) * keep\n node.position[k] += v[k] * dt\n }\n }\n}\n"],"mappings":"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"}
package/dist/passes.js CHANGED
@@ -1,6 +1,6 @@
1
- import { A as e, C as t, E as n, Ft as r, It as i, Nt as a, O as o, S as s, T as c, bt as l, vt as u, w as d, x as f, yt as p } from "./core.js";
1
+ import { Bt as e, C as t, Ct as n, D as r, E as i, N as a, O as o, Rt as s, S as c, T as l, Tt as u, Vt as d, j as f, k as p, w as m, wt as h, x as g } from "./core.js";
2
2
  // src/irradiance.js
3
- function m({ data: e, width: t, height: n }) {
3
+ function _({ data: e, width: t, height: n }) {
4
4
  let r = /* @__PURE__ */ new Float64Array(27), i = Math.max(1, Math.floor(t / 128)), a = Math.max(1, Math.floor(n / 64)), o = 2 * Math.PI * i / t, s = Math.PI * a / n;
5
5
  for (let c = 0; c < n; c += a) {
6
6
  let l = (c + .5 * a) / n * Math.PI, u = Math.sin(l), d = Math.cos(l), f = o * s * u;
@@ -47,7 +47,7 @@ function m({ data: e, width: t, height: n }) {
47
47
  return u;
48
48
  }
49
49
  // src/webgl/passes.js
50
- var h = `precision highp float;
50
+ var v = `precision highp float;
51
51
  in vec2 vUv;
52
52
  uniform mat4 uInverseViewProjection;
53
53
  uniform vec3 uCameraPosition;
@@ -56,9 +56,9 @@ uniform float uLod;
56
56
  uniform float uIntensity;
57
57
  uniform int uHdr;
58
58
  out vec4 fragColor;
59
- ${d}
60
- ${c}
61
- ${e}
59
+ ${r}
60
+ ${o}
61
+ ${a}
62
62
  void main() {
63
63
  vec4 far = uInverseViewProjection * vec4(vUv * 2.0 - 1.0, 1.0, 1.0);
64
64
  vec3 d = normalize(far.xyz / far.w - uCameraPosition);
@@ -66,14 +66,14 @@ vec3 c = textureLod(uBackground, equirectUv(d), uLod).rgb * uIntensity;
66
66
  if (uHdr == 1) c = toneMap(c);
67
67
  fragColor = vec4(linearToSrgb(c), 1.0);
68
68
  }
69
- `, g = `precision highp float;
69
+ `, y = `precision highp float;
70
70
  in vec2 vUv;
71
71
  uniform sampler2D uSource;
72
72
  uniform float uRoughness;
73
73
  uniform float uSourceTexels;
74
74
  uniform float uCopyLod;
75
75
  out vec4 fragColor;
76
- ${c}
76
+ ${o}
77
77
  const float PI = 3.14159265359;
78
78
  const uint SAMPLES = 192u;
79
79
  vec2 hammersley(uint i) {
@@ -114,74 +114,74 @@ weight += NoL;
114
114
  }
115
115
  fragColor = vec4(sum / max(weight, 1e-4), 1.0);
116
116
  }
117
- `, _ = `precision highp float;
117
+ `, b = `precision highp float;
118
118
  in vec3 vWorldPosition;
119
119
  uniform vec4 uMask;
120
120
  out vec4 fragColor;
121
- ${s}
121
+ ${l}
122
122
  void main() {
123
123
  clipAway(vWorldPosition);
124
124
  fragColor = uMask;
125
125
  }
126
- `, v = 16, y = `uniform sampler2D uSource;
126
+ `, x = 16, S = `uniform sampler2D uSource;
127
127
  uniform vec2 uStep;
128
128
  uniform int uRadius;
129
129
  vec4 widened(vec2 uv) {
130
130
  vec4 best = vec4(0.0);
131
131
  float nearest = 1e9;
132
- for (int i = -${v}; i <= ${v}; i++) {
132
+ for (int i = -${x}; i <= ${x}; i++) {
133
133
  float d = abs(float(i));
134
134
  if (d > float(uRadius)) continue;
135
135
  vec4 s = texture(uSource, uv + uStep * float(i));
136
- if (s.a > 0.0 && d <= s.a * ${v}.0 && d < nearest) {
136
+ if (s.a > 0.0 && d <= s.a * ${x}.0 && d < nearest) {
137
137
  best = s;
138
138
  nearest = d;
139
139
  }
140
140
  }
141
141
  return best;
142
142
  }
143
- `, b = `precision highp float;
143
+ `, C = `precision highp float;
144
144
  in vec2 vUv;
145
145
  out vec4 fragColor;
146
- ${y}
146
+ ${S}
147
147
  void main() {
148
148
  fragColor = widened(vUv);
149
149
  }
150
- `, x = `precision highp float;
150
+ `, w = `precision highp float;
151
151
  in vec2 vUv;
152
152
  uniform sampler2D uMask;
153
153
  out vec4 fragColor;
154
- ${d}
155
- ${y}
154
+ ${r}
155
+ ${S}
156
156
  void main() {
157
157
  if (texture(uMask, vUv).a > 0.0) discard;
158
158
  vec4 ring = widened(vUv);
159
159
  if (ring.a <= 0.0) discard;
160
160
  fragColor = vec4(linearToSrgb(ring.rgb), 1.0);
161
161
  }
162
- `, S = `precision highp float;
162
+ `, T = `precision highp float;
163
163
  in vec3 vWorldPosition;
164
164
  uniform vec4 uMask;
165
165
  layout(location = 0) out vec4 fragColor;
166
166
  layout(location = 1) out vec4 fragCoverage;
167
- ${s}
167
+ ${l}
168
168
  void main() {
169
169
  clipAway(vWorldPosition);
170
170
  fragColor = uMask;
171
171
  fragCoverage = vec4(1.0);
172
172
  }
173
- `, C = `uniform sampler2D uMask;
173
+ `, E = `uniform sampler2D uMask;
174
174
  uniform sampler2D uCoverage;
175
175
  uniform vec2 uTexel;
176
176
  uniform int uRadius;
177
177
  vec4 masked(vec2 uv, float coverage) {
178
178
  vec4 m = texture(uMask, uv) / coverage;
179
- return vec4(m.rgb, m.a * ${v}.0);
179
+ return vec4(m.rgb, m.a * ${x}.0);
180
180
  }
181
- `, w = `precision highp float;
181
+ `, D = `precision highp float;
182
182
  in vec2 vUv;
183
183
  out vec4 fragColor;
184
- ${C}
184
+ ${E}
185
185
  void main() {
186
186
  float nearest = 1e9;
187
187
  int at = 0;
@@ -200,12 +200,12 @@ found = true;
200
200
  }
201
201
  fragColor = found ? vec4(float(at + 32) / 255.0, 0.0, 0.0, 1.0) : vec4(0.0);
202
202
  }
203
- `, T = `precision highp float;
203
+ `, O = `precision highp float;
204
204
  in vec2 vUv;
205
205
  uniform sampler2D uNearest;
206
206
  out vec4 fragColor;
207
- ${d}
208
- ${C}
207
+ ${r}
208
+ ${E}
209
209
  void main() {
210
210
  float here = texture(uCoverage, vUv).r;
211
211
  if (here >= 1.0) discard;
@@ -229,7 +229,13 @@ float alpha = clamp(ring.a - nearest + 0.5, 0.0, 1.0) * (1.0 - here);
229
229
  if (alpha <= 0.0) discard;
230
230
  fragColor = vec4(linearToSrgb(ring.rgb) * alpha, alpha);
231
231
  }
232
- `, E = [o, _], D = [n, b], O = [n, x], k = [o, S], A = [n, w], j = [n, T], M = [o, t], N = [n, h], P = [n, g], F = 512, I = 6, L = class {
232
+ `, k = [f, b], A = [p, C], j = [p, w], M = [f, T], N = [p, D], P = [p, O], F = [f, i], I = [p, v], L = [p, y], R = 512, z = 6, B = class {
233
+ time = 0;
234
+ cameraPosition = [
235
+ 0,
236
+ 0,
237
+ 0
238
+ ];
233
239
  #e = /* @__PURE__ */ new WeakMap();
234
240
  #t = null;
235
241
  #n = null;
@@ -253,33 +259,33 @@ fragColor = vec4(linearToSrgb(ring.rgb) * alpha, alpha);
253
259
  });
254
260
  let r = e.image, i = this.#d(r.width, r.height, Math.floor(Math.log2(Math.max(r.width, r.height))) + 1);
255
261
  n.texSubImage2D(n.TEXTURE_2D, 0, 0, 0, r.width, r.height, n.RGBA, n.FLOAT, r.data), n.generateMipmap(n.TEXTURE_2D);
256
- let a = this.#d(F, F / 2, I), o = this.programs.ready("prefilter", [], P);
262
+ let a = this.#d(R, R / 2, z), o = this.programs.ready("prefilter", [], L);
257
263
  n.useProgram(o.program), o.set("uSource", i), o.set("uSourceTexels", r.width * r.height);
258
264
  let s = n.createFramebuffer();
259
265
  n.bindFramebuffer(n.FRAMEBUFFER, s), n.disable(n.DEPTH_TEST), n.disable(n.BLEND), n.disable(n.CULL_FACE);
260
- for (let e = 0; e < I; e++) {
261
- let t = F >> e;
266
+ for (let e = 0; e < z; e++) {
267
+ let t = R >> e;
262
268
  n.framebufferTexture2D(n.FRAMEBUFFER, n.COLOR_ATTACHMENT0, n.TEXTURE_2D, a, e), n.viewport(0, 0, t, t / 2), o.set("uRoughness", e / 5), o.set("uCopyLod", Math.max(0, Math.log2(r.width / t))), this.programs.drawFullscreen();
263
269
  }
264
270
  return n.bindFramebuffer(n.FRAMEBUFFER, null), n.deleteFramebuffer(s), n.enable(n.DEPTH_TEST), t = {
265
271
  source: i,
266
272
  prefiltered: a,
267
273
  maxLod: 5,
268
- sh: m(r),
274
+ sh: _(r),
269
275
  version: e.version
270
276
  }, this.#e.set(e, t), t;
271
277
  }
272
- background(e, t, n) {
273
- let i = this.gl, o = t.isHDR, s = o ? this.environment(t) : null;
274
- if (o && !s) return;
275
- let c = this.programs.ready("background", [], N);
278
+ background(t, n, r) {
279
+ let i = this.gl, a = n.isHDR, o = a ? this.environment(n) : null;
280
+ if (a && !o) return;
281
+ let c = this.programs.ready("background", [], I);
276
282
  i.useProgram(c.program);
277
- let l = Math.min(Math.max(e.backgroundBlur, 0), 1);
278
- s ? (c.set("uBackground", l > 0 ? s.prefiltered : s.source), c.set("uLod", l * s.maxLod), c.set("uIntensity", e.environment === t ? e.environmentIntensity : 1)) : (c.set("uBackground", this.resources.texture(t)), c.set("uLod", l * 6), c.set("uIntensity", 1)), c.set("uHdr", +!!o), c.set("uExposure", n.exposure), c.set("uToneMapping", n.toneMapping), c.set("uInverseViewProjection", r(a(), e.camera.viewProjection)), c.set("uCameraPosition", e.camera.position), i.disable(i.DEPTH_TEST), i.disable(i.BLEND), i.disable(i.CULL_FACE), i.depthMask(!1), this.programs.drawFullscreen(), i.depthMask(!0), i.enable(i.DEPTH_TEST);
283
+ let l = Math.min(Math.max(t.backgroundBlur, 0), 1);
284
+ o ? (c.set("uBackground", l > 0 ? o.prefiltered : o.source), c.set("uLod", l * o.maxLod), c.set("uIntensity", t.environment === n ? t.environmentIntensity : 1)) : (c.set("uBackground", this.resources.texture(n)), c.set("uLod", l * 6), c.set("uIntensity", 1)), c.set("uHdr", +!!a), c.set("uExposure", r.exposure), c.set("uToneMapping", r.toneMapping), c.set("uInverseViewProjection", e(s(), t.camera.viewProjection)), c.set("uCameraPosition", t.camera.position), i.disable(i.DEPTH_TEST), i.disable(i.BLEND), i.disable(i.CULL_FACE), i.depthMask(!1), this.programs.drawFullscreen(), i.depthMask(!0), i.enable(i.DEPTH_TEST);
279
285
  }
280
286
  shadowMap(e) {
281
287
  let t = this.gl;
282
- this.#t ??= this.#l(p), t.bindFramebuffer(t.FRAMEBUFFER, this.#t.framebuffer), t.viewport(0, 0, p, p), this.#a(), this.#s(e.casters, e.viewProjection), this.#o();
288
+ this.#t ??= this.#l(h), t.bindFramebuffer(t.FRAMEBUFFER, this.#t.framebuffer), t.viewport(0, 0, h, h), this.#a(), this.#s(e.casters, e.viewProjection), this.#o();
283
289
  let n = new Float32Array([
284
290
  .5,
285
291
  0,
@@ -303,20 +309,20 @@ fragColor = vec4(linearToSrgb(ring.rgb) * alpha, alpha);
303
309
  sampler: "uShadowMap",
304
310
  texture: this.#t.texture,
305
311
  uniforms: {
306
- uShadowMatrix: i(/* @__PURE__ */ new Float32Array(16), n, e.viewProjection),
307
- uShadowTexel: 1 / p,
312
+ uShadowMatrix: d(/* @__PURE__ */ new Float32Array(16), n, e.viewProjection),
313
+ uShadowTexel: 1 / h,
308
314
  uShadowBias: e.texelSize * 1.5
309
315
  }
310
316
  };
311
317
  }
312
318
  spotShadowMaps(e) {
313
319
  let t = this.gl;
314
- this.#n ??= this.#l(l), t.bindFramebuffer(t.FRAMEBUFFER, this.#n.framebuffer), t.viewport(0, 0, l, l), this.#a();
315
- let n = /* @__PURE__ */ new Float32Array(64), r = /* @__PURE__ */ new Float32Array(16), a = /* @__PURE__ */ new Float32Array(4), o = l / 2;
316
- for (let s of e) {
317
- let e = s.tile % 2, c = Math.floor(s.tile / 2);
318
- t.viewport(e * o, c * o, o, o), this.#s(s.casters, s.viewProjection);
319
- let l = new Float32Array([
320
+ this.#n ??= this.#l(u), t.bindFramebuffer(t.FRAMEBUFFER, this.#n.framebuffer), t.viewport(0, 0, u, u), this.#a();
321
+ let n = /* @__PURE__ */ new Float32Array(64), r = /* @__PURE__ */ new Float32Array(16), i = /* @__PURE__ */ new Float32Array(4), a = u / 2;
322
+ for (let o of e) {
323
+ let e = o.tile % 2, s = Math.floor(o.tile / 2);
324
+ t.viewport(e * a, s * a, a, a), this.#s(o.casters, o.viewProjection);
325
+ let c = new Float32Array([
320
326
  .25,
321
327
  0,
322
328
  0,
@@ -330,16 +336,16 @@ fragColor = vec4(linearToSrgb(ring.rgb) * alpha, alpha);
330
336
  .5,
331
337
  0,
332
338
  e * .5 + .25,
333
- c * .5 + .25,
339
+ s * .5 + .25,
334
340
  .5,
335
341
  1
336
342
  ]);
337
- n.set(i(/* @__PURE__ */ new Float32Array(16), l, s.viewProjection), s.tile * 16), r.set([
343
+ n.set(d(/* @__PURE__ */ new Float32Array(16), c, o.viewProjection), o.tile * 16), r.set([
338
344
  e * .5,
339
- c * .5,
345
+ s * .5,
340
346
  e * .5 + .5,
341
- c * .5 + .5
342
- ], s.tile * 4), a[s.tile] = s.texelAngle * 1.5;
347
+ s * .5 + .5
348
+ ], o.tile * 4), i[o.tile] = o.texelAngle * 1.5;
343
349
  }
344
350
  return this.#o(), {
345
351
  define: "SPOT_SHADOWS",
@@ -348,15 +354,15 @@ fragColor = vec4(linearToSrgb(ring.rgb) * alpha, alpha);
348
354
  uniforms: {
349
355
  uSpotShadowMatrix: n,
350
356
  uSpotShadowTile: r,
351
- uSpotShadowBias: a,
352
- uSpotShadowTexel: 1 / l
357
+ uSpotShadowBias: i,
358
+ uSpotShadowTexel: 1 / u
353
359
  }
354
360
  };
355
361
  }
356
362
  pointShadowMap(e) {
357
363
  let t = this.gl;
358
- return this.#r ??= this.#u(u), e.faces.forEach((e, n) => {
359
- t.bindFramebuffer(t.FRAMEBUFFER, this.#r.framebuffers[n]), t.viewport(0, 0, u, u), this.#a(), this.#s(e.casters, e.viewProjection);
364
+ return this.#r ??= this.#u(n), e.faces.forEach((e, r) => {
365
+ t.bindFramebuffer(t.FRAMEBUFFER, this.#r.framebuffers[r]), t.viewport(0, 0, n, n), this.#a(), this.#s(e.casters, e.viewProjection);
360
366
  }), this.#o(), {
361
367
  define: "POINT_SHADOW",
362
368
  sampler: "uPointShadowMap",
@@ -376,68 +382,74 @@ fragColor = vec4(linearToSrgb(ring.rgb) * alpha, alpha);
376
382
  let e = this.gl;
377
383
  e.disable(e.POLYGON_OFFSET_FILL), e.bindFramebuffer(e.FRAMEBUFFER, null);
378
384
  }
379
- #s(e, t) {
380
- let n = this.gl;
381
- for (let r of e) {
382
- let e = r.instances, i = e ? ["INSTANCED"] : [];
383
- r.clip && i.push("CLIP");
384
- let a = r.material, o = a.alphaTest > 0, s = o && !!a.map && a.triplanar > 0;
385
- o && i.push("ALPHA_TEST", ...a.map ? ["MAP"] : [], ...s ? ["TRIPLANAR"] : []), o && a.map?.uvSet === 1 && r.geometry.uvs2 && i.push("UV2", "uMapSet vUv2");
386
- let c = this.programs.ready("depth", i, M);
387
- if (n.useProgram(c.program), c.set("uViewProjection", t), c.set("uModel", r.worldMatrix), r.clip && c.set("uClipPlane", r.clip.plane), o) {
388
- c.set("uOpacity", a.opacity), c.set("uAlphaTest", a.alphaTest);
389
- let e = a.map;
390
- e && (c.set("uMapUv", f(e)), c.set("uMap", this.resources.texture(e))), s && c.set("uTriplanar", a.triplanar);
385
+ #s(e, n) {
386
+ let r = this.gl;
387
+ for (let i of e) {
388
+ let e = i.instances, a = e ? ["INSTANCED"] : [];
389
+ i.clip && a.push("CLIP");
390
+ let o = i.material, s = o.alphaTest > 0, l = s && !!o.map && o.triplanar > 0;
391
+ s && a.push("ALPHA_TEST", ...o.map ? ["MAP"] : [], ...l ? ["TRIPLANAR"] : []), s && o.map?.uvSet === 1 && i.geometry.uvs2 && a.push("UV2", "uMapSet vUv2");
392
+ let u = c(o);
393
+ u && a.push(...t(u, !0));
394
+ let d = this.programs.ready("depth", a, F, u?.source);
395
+ if (!d.failed) {
396
+ if (r.useProgram(d.program), u && m(d, o, this.time, this.cameraPosition, (e) => this.resources.texture(e)), d.set("uViewProjection", n), d.set("uModel", i.worldMatrix), i.clip && d.set("uClipPlane", i.clip.plane), s) {
397
+ d.set("uOpacity", o.opacity), d.set("uAlphaTest", o.alphaTest);
398
+ let e = o.map;
399
+ e && (d.set("uMapUv", g(e)), d.set("uMap", this.resources.texture(e))), l && d.set("uTriplanar", o.triplanar);
400
+ }
401
+ this.resources.drawTriangles(i.geometry, e);
391
402
  }
392
- this.resources.drawTriangles(r.geometry, e);
393
403
  }
394
404
  }
395
- outlineMask(e, t, n, r, i, a) {
396
- let o = this.gl, s = this.#c(n, r, a);
397
- o.bindFramebuffer(o.FRAMEBUFFER, s.msaa ? s.msaa.framebuffer : s.mask.framebuffer), s.msaa && o.drawBuffers([o.COLOR_ATTACHMENT0, o.COLOR_ATTACHMENT1]), o.viewport(0, 0, n, r), o.clearColor(0, 0, 0, 0), o.clear(o.COLOR_BUFFER_BIT), o.disable(o.DEPTH_TEST), o.disable(o.BLEND), o.disable(o.CULL_FACE), o.depthMask(!1);
398
- let c = 1;
399
- for (let n of e) {
400
- let e = Math.min(v, Math.max(1, Math.round(n.width * i)));
401
- c = Math.max(c, e);
402
- for (let r of n.items) {
403
- let i = r.instances ? ["INSTANCED"] : [];
404
- r.clip && i.push("CLIP");
405
- let s = a ? this.programs.ready("outline-cover", i, k) : this.programs.ready("outline-mask", i, E);
406
- o.useProgram(s.program), s.set("uViewProjection", t.camera.viewProjection), s.set("uModel", r.worldMatrix), s.set("uMask", [
407
- n.color[0],
408
- n.color[1],
409
- n.color[2],
410
- e / v
411
- ]), r.clip && s.set("uClipPlane", r.clip.plane), this.resources.drawTriangles(r.geometry, r.instances);
405
+ outlineMask(e, n, r, i, a, o) {
406
+ let s = this.gl, l = this.#c(r, i, o);
407
+ s.bindFramebuffer(s.FRAMEBUFFER, l.msaa ? l.msaa.framebuffer : l.mask.framebuffer), l.msaa && s.drawBuffers([s.COLOR_ATTACHMENT0, s.COLOR_ATTACHMENT1]), s.viewport(0, 0, r, i), s.clearColor(0, 0, 0, 0), s.clear(s.COLOR_BUFFER_BIT), s.disable(s.DEPTH_TEST), s.disable(s.BLEND), s.disable(s.CULL_FACE), s.depthMask(!1);
408
+ let u = 1;
409
+ for (let r of e) {
410
+ let e = Math.min(x, Math.max(1, Math.round(r.width * a)));
411
+ u = Math.max(u, e);
412
+ for (let i of r.items) {
413
+ let a = i.instances ? ["INSTANCED"] : [];
414
+ i.clip && a.push("CLIP");
415
+ let l = c(i.material);
416
+ l && a.push(...t(l, !1));
417
+ let u = o ? this.programs.ready("outline-cover", a, M, l?.source) : this.programs.ready("outline-mask", a, k, l?.source);
418
+ u.failed || (s.useProgram(u.program), l && m(u, i.material, n.time ?? 0, n.camera.position, (e) => this.resources.texture(e)), u.set("uViewProjection", n.camera.viewProjection), u.set("uModel", i.worldMatrix), u.set("uMask", [
419
+ r.color[0],
420
+ r.color[1],
421
+ r.color[2],
422
+ e / x
423
+ ]), i.clip && u.set("uClipPlane", i.clip.plane), this.resources.drawTriangles(i.geometry, i.instances));
412
424
  }
413
425
  }
414
- if (s.msaa && s.coverage) {
415
- o.bindFramebuffer(o.READ_FRAMEBUFFER, s.msaa.framebuffer);
416
- for (let [e, t] of [[o.COLOR_ATTACHMENT0, s.mask], [o.COLOR_ATTACHMENT1, s.coverage]]) o.readBuffer(e), o.bindFramebuffer(o.DRAW_FRAMEBUFFER, t.framebuffer), o.blitFramebuffer(0, 0, n, r, 0, 0, n, r, o.COLOR_BUFFER_BIT, o.NEAREST);
426
+ if (l.msaa && l.coverage) {
427
+ s.bindFramebuffer(s.READ_FRAMEBUFFER, l.msaa.framebuffer);
428
+ for (let [e, t] of [[s.COLOR_ATTACHMENT0, l.mask], [s.COLOR_ATTACHMENT1, l.coverage]]) s.readBuffer(e), s.bindFramebuffer(s.DRAW_FRAMEBUFFER, t.framebuffer), s.blitFramebuffer(0, 0, r, i, 0, 0, r, i, s.COLOR_BUFFER_BIT, s.NEAREST);
417
429
  }
418
- if (o.bindFramebuffer(o.FRAMEBUFFER, s.wide.framebuffer), o.clear(o.COLOR_BUFFER_BIT), s.coverage) {
419
- let e = this.programs.ready("outline-smooth-widen", [], A);
420
- o.useProgram(e.program), e.set("uMask", s.mask.texture), e.set("uCoverage", s.coverage.texture), e.set("uTexel", [1 / n, 1 / r]), e.set("uRadius", c);
430
+ if (s.bindFramebuffer(s.FRAMEBUFFER, l.wide.framebuffer), s.clear(s.COLOR_BUFFER_BIT), l.coverage) {
431
+ let e = this.programs.ready("outline-smooth-widen", [], N);
432
+ s.useProgram(e.program), e.set("uMask", l.mask.texture), e.set("uCoverage", l.coverage.texture), e.set("uTexel", [1 / r, 1 / i]), e.set("uRadius", u);
421
433
  } else {
422
- let e = this.programs.ready("outline-widen", [], D);
423
- o.useProgram(e.program), e.set("uSource", s.mask.texture), e.set("uStep", [1 / n, 0]), e.set("uRadius", c);
434
+ let e = this.programs.ready("outline-widen", [], A);
435
+ s.useProgram(e.program), e.set("uSource", l.mask.texture), e.set("uStep", [1 / r, 0]), e.set("uRadius", u);
424
436
  }
425
- return this.programs.drawFullscreen(), o.depthMask(!0), o.enable(o.DEPTH_TEST), o.bindFramebuffer(o.FRAMEBUFFER, null), {
426
- mask: s.mask.texture,
427
- wide: s.wide.texture,
428
- coverage: s.coverage?.texture ?? null,
429
- radius: c,
430
- width: n,
431
- height: r
437
+ return this.programs.drawFullscreen(), s.depthMask(!0), s.enable(s.DEPTH_TEST), s.bindFramebuffer(s.FRAMEBUFFER, null), {
438
+ mask: l.mask.texture,
439
+ wide: l.wide.texture,
440
+ coverage: l.coverage?.texture ?? null,
441
+ radius: u,
442
+ width: r,
443
+ height: i
432
444
  };
433
445
  }
434
446
  outlineComposite(e) {
435
447
  let t = this.gl;
436
448
  if (e.coverage) {
437
- let n = this.programs.ready("outline-smooth-composite", [], j);
449
+ let n = this.programs.ready("outline-smooth-composite", [], P);
438
450
  t.useProgram(n.program), n.set("uNearest", e.wide), n.set("uMask", e.mask), n.set("uCoverage", e.coverage), n.set("uTexel", [1 / e.width, 1 / e.height]), n.set("uRadius", e.radius);
439
451
  } else {
440
- let n = this.programs.ready("outline-composite", [], O);
452
+ let n = this.programs.ready("outline-composite", [], j);
441
453
  t.useProgram(n.program), n.set("uSource", e.wide), n.set("uMask", e.mask), n.set("uStep", [0, 1 / e.height]), n.set("uRadius", e.radius);
442
454
  }
443
455
  t.disable(t.DEPTH_TEST), t.disable(t.CULL_FACE), t.depthMask(!1), t.enable(t.BLEND), t.blendFunc(t.ONE, t.ONE_MINUS_SRC_ALPHA), this.programs.drawFullscreen(), t.depthMask(!0), t.enable(t.DEPTH_TEST);
@@ -519,6 +531,6 @@ fragColor = vec4(linearToSrgb(ring.rgb) * alpha, alpha);
519
531
  return r.activeTexture(r.TEXTURE15), r.bindTexture(r.TEXTURE_2D, i), r.texStorage2D(r.TEXTURE_2D, n, r.RGBA16F, e, t), r.texParameteri(r.TEXTURE_2D, r.TEXTURE_WRAP_S, r.REPEAT), r.texParameteri(r.TEXTURE_2D, r.TEXTURE_WRAP_T, r.CLAMP_TO_EDGE), r.texParameteri(r.TEXTURE_2D, r.TEXTURE_MIN_FILTER, r.LINEAR_MIPMAP_LINEAR), r.texParameteri(r.TEXTURE_2D, r.TEXTURE_MAG_FILTER, r.LINEAR), r.pixelStorei(r.UNPACK_FLIP_Y_WEBGL, !1), i;
520
532
  }
521
533
  };
522
- export { L as Passes };
534
+ export { B as Passes };
523
535
 
524
536
  //# sourceMappingURL=passes.js.map