@aura3d/addons 0.0.0-stage → 5.0.1

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 (54) hide show
  1. package/README.md +11 -2
  2. package/dist/effects/AsciiOutput.d.ts +60 -0
  3. package/dist/effects/AsciiOutput.d.ts.map +1 -0
  4. package/dist/effects/AsciiOutput.js +187 -0
  5. package/dist/effects/AsciiOutput.js.map +1 -0
  6. package/dist/effects/OutlineOutput.d.ts +58 -0
  7. package/dist/effects/OutlineOutput.d.ts.map +1 -0
  8. package/dist/effects/OutlineOutput.js +274 -0
  9. package/dist/effects/OutlineOutput.js.map +1 -0
  10. package/dist/effects/StereoOutputs.d.ts +106 -0
  11. package/dist/effects/StereoOutputs.d.ts.map +1 -0
  12. package/dist/effects/StereoOutputs.js +315 -0
  13. package/dist/effects/StereoOutputs.js.map +1 -0
  14. package/dist/generators/EnvironmentGenerators.d.ts +100 -0
  15. package/dist/generators/EnvironmentGenerators.d.ts.map +1 -0
  16. package/dist/generators/EnvironmentGenerators.js +477 -0
  17. package/dist/generators/EnvironmentGenerators.js.map +1 -0
  18. package/dist/index.d.ts +14 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +14 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/objects/MarchingCubes.d.ts +80 -0
  23. package/dist/objects/MarchingCubes.d.ts.map +1 -0
  24. package/dist/objects/MarchingCubes.js +289 -0
  25. package/dist/objects/MarchingCubes.js.map +1 -0
  26. package/dist/objects/SpecializedObjects.d.ts +150 -0
  27. package/dist/objects/SpecializedObjects.d.ts.map +1 -0
  28. package/dist/objects/SpecializedObjects.js +544 -0
  29. package/dist/objects/SpecializedObjects.js.map +1 -0
  30. package/dist/renderers/SpatialDOMRenderer.d.ts +76 -0
  31. package/dist/renderers/SpatialDOMRenderer.d.ts.map +1 -0
  32. package/dist/renderers/SpatialDOMRenderer.js +279 -0
  33. package/dist/renderers/SpatialDOMRenderer.js.map +1 -0
  34. package/dist/renderers/VectorRenderer.d.ts +98 -0
  35. package/dist/renderers/VectorRenderer.d.ts.map +1 -0
  36. package/dist/renderers/VectorRenderer.js +253 -0
  37. package/dist/renderers/VectorRenderer.js.map +1 -0
  38. package/dist/tools/Fracture.d.ts +87 -0
  39. package/dist/tools/Fracture.d.ts.map +1 -0
  40. package/dist/tools/Fracture.js +422 -0
  41. package/dist/tools/Fracture.js.map +1 -0
  42. package/dist/tools/Sculptor.d.ts +84 -0
  43. package/dist/tools/Sculptor.d.ts.map +1 -0
  44. package/dist/tools/Sculptor.js +207 -0
  45. package/dist/tools/Sculptor.js.map +1 -0
  46. package/dist/tools/TubePainter.d.ts +47 -0
  47. package/dist/tools/TubePainter.d.ts.map +1 -0
  48. package/dist/tools/TubePainter.js +170 -0
  49. package/dist/tools/TubePainter.js.map +1 -0
  50. package/dist/utils/WorkerPool.d.ts +93 -0
  51. package/dist/utils/WorkerPool.d.ts.map +1 -0
  52. package/dist/utils/WorkerPool.js +360 -0
  53. package/dist/utils/WorkerPool.js.map +1 -0
  54. package/package.json +114 -3
package/README.md CHANGED
@@ -1,3 +1,12 @@
1
- # Temporary Holding Version
1
+ # @aura3d/addons
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Optional Aura3D modules for spatial DOM and vector output, alternative visual outputs, procedural objects and generators, editable geometry tools, and bounded worker tasks. Each module is independently authored for Aura3D. These entrypoints are separate from the engine root, and can be imported individually, for example:
4
+
5
+ ```ts
6
+ import { WorkerPool } from "@aura3d/addons/utils/WorkerPool";
7
+ import { fractureConvex } from "@aura3d/addons/tools/Fracture";
8
+ ```
9
+
10
+ Install the package alongside compatible Aura3D packages. Output effects use `@aura3d/rendering`; that package is declared as a dependency. DOM renderers require browser DOM objects when constructed. Each module documents its own input limits, ownership, teardown, and unsupported content. Generated geometry and outputs remain caller-owned unless the API explicitly transfers ownership.
11
+
12
+ The package provides source implementations; browser and hardware support claims require the applicable runtime evidence.
@@ -0,0 +1,60 @@
1
+ import type { CameraLike, RenderItem, RenderSource, Renderer } from "@aura3d/rendering";
2
+ import type { Scene } from "@aura3d/scene";
3
+ export interface AsciiOutputOptions {
4
+ /** Character columns and rows, not device pixels. */
5
+ readonly width: number;
6
+ readonly height: number;
7
+ /** Characters ordered from darkest to brightest. Unicode code points are allowed. */
8
+ readonly characters?: string;
9
+ readonly invert?: boolean;
10
+ /** Render resolution per character dimension, from 1 to 16 (default 2). */
11
+ readonly sampleScale?: number;
12
+ /** Supplied by and still owned by the caller. Child nodes and attributes are restored on disposal. */
13
+ readonly element?: HTMLElement;
14
+ readonly accessibleLabel?: string;
15
+ }
16
+ export interface AsciiPixels {
17
+ readonly width: number;
18
+ readonly height: number;
19
+ readonly pixels: Uint8Array | Uint8ClampedArray;
20
+ readonly origin?: "top-left" | "bottom-left";
21
+ }
22
+ export interface AsciiOutputFrame {
23
+ readonly width: number;
24
+ readonly height: number;
25
+ readonly text: string;
26
+ }
27
+ export type AsciiSource = RenderSource | Iterable<RenderItem> | Scene;
28
+ /**
29
+ * A bounded luminance-to-text output. Pixels are interpreted as unpremultiplied
30
+ * sRGB and composited over black before area averaging. Characters are inserted
31
+ * as text, never HTML. Rendering captures a private target and requires exclusive
32
+ * renderer access until the returned promise settles. Captured bytes use the
33
+ * same straight-alpha interpretation; a renderer with transparent compositing
34
+ * may have associated RGB, so use an opaque clear for consistent luminance.
35
+ * The target and optional DOM mutations are released by dispose().
36
+ */
37
+ export declare class AsciiOutput {
38
+ private width;
39
+ private height;
40
+ private readonly characters;
41
+ private readonly invert;
42
+ private readonly scale;
43
+ private readonly element?;
44
+ private readonly originalElement?;
45
+ private target;
46
+ private targetOwner;
47
+ private busy;
48
+ private disposed;
49
+ constructor(options: AsciiOutputOptions);
50
+ setSize(width: number, height: number): void;
51
+ /** Convert caller-supplied pixels. The same conversion is used by render(). */
52
+ renderPixels(input: AsciiPixels): AsciiOutputFrame;
53
+ render(renderer: Pick<Renderer, "device" | "render" | "renderAsync">, source: AsciiSource, camera?: CameraLike): Promise<AsciiOutputFrame>;
54
+ dispose(): void;
55
+ private ensureTarget;
56
+ private releaseTarget;
57
+ private validateCaptureSize;
58
+ private assertUsable;
59
+ }
60
+ //# sourceMappingURL=AsciiOutput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AsciiOutput.d.ts","sourceRoot":"","sources":["../../src/effects/AsciiOutput.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAgB,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACtG,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAE3C,MAAM,WAAW,kBAAkB;IACjC,qDAAqD;IACrD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,qFAAqF;IACrF,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,2EAA2E;IAC3E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,sGAAsG;IACtG,QAAQ,CAAC,OAAO,CAAC,EAAE,WAAW,CAAC;IAC/B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;CACnC;AACD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,iBAAiB,CAAC;IAChD,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,aAAa,CAAC;CAC9C;AACD,MAAM,WAAW,gBAAgB;IAAG,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CAAE;AAC7G,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,QAAQ,CAAC,UAAU,CAAC,GAAG,KAAK,CAAC;AAKtE;;;;;;;;GAQG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAoB;IAC/C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAU;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAc;IACvC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAkH;IACnJ,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,WAAW,CAAoE;IACvF,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,QAAQ,CAAS;gBAEb,OAAO,EAAE,kBAAkB;IAwBvC,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAW5C,+EAA+E;IAC/E,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,gBAAgB;IAsC5C,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,GAAG,aAAa,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA2BhJ,OAAO,IAAI,IAAI;IAef,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,mBAAmB;IAC3B,OAAO,CAAC,YAAY;CACrB"}
@@ -0,0 +1,187 @@
1
+ const MAX_CELLS = 20_000;
2
+ const MAX_PIXELS = 4_194_304;
3
+ /**
4
+ * A bounded luminance-to-text output. Pixels are interpreted as unpremultiplied
5
+ * sRGB and composited over black before area averaging. Characters are inserted
6
+ * as text, never HTML. Rendering captures a private target and requires exclusive
7
+ * renderer access until the returned promise settles. Captured bytes use the
8
+ * same straight-alpha interpretation; a renderer with transparent compositing
9
+ * may have associated RGB, so use an opaque clear for consistent luminance.
10
+ * The target and optional DOM mutations are released by dispose().
11
+ */
12
+ export class AsciiOutput {
13
+ width;
14
+ height;
15
+ characters;
16
+ invert;
17
+ scale;
18
+ element;
19
+ originalElement;
20
+ target = null;
21
+ targetOwner = null;
22
+ busy = false;
23
+ disposed = false;
24
+ constructor(options) {
25
+ validateCells(options.width, options.height);
26
+ this.width = options.width;
27
+ this.height = options.height;
28
+ this.characters = [...(options.characters ?? " .:-=+*#%@")];
29
+ if (this.characters.length < 2 || this.characters.length > 128 || this.characters.some((char) => /[\u0000-\u001f\u007f]/u.test(char)))
30
+ throw new RangeError("ASCII character ramp must contain 2 to 128 printable characters");
31
+ this.invert = options.invert ?? false;
32
+ this.scale = options.sampleScale ?? 2;
33
+ if (!Number.isInteger(this.scale) || this.scale < 1 || this.scale > 16)
34
+ throw new RangeError("sampleScale must be an integer in [1, 16]");
35
+ this.validateCaptureSize();
36
+ this.element = options.element;
37
+ if (this.element) {
38
+ this.originalElement = {
39
+ children: Array.from(this.element.childNodes),
40
+ role: this.element.getAttribute("role"),
41
+ label: this.element.getAttribute("aria-label"),
42
+ whiteSpace: [this.element.style.getPropertyValue("white-space"), this.element.style.getPropertyPriority("white-space")]
43
+ };
44
+ this.element.style.whiteSpace = "pre";
45
+ this.element.setAttribute("role", "img");
46
+ this.element.setAttribute("aria-label", options.accessibleLabel ?? "ASCII rendering");
47
+ }
48
+ }
49
+ setSize(width, height) {
50
+ this.assertUsable();
51
+ if (this.busy)
52
+ throw new Error("Cannot resize ASCII output during render");
53
+ validateCells(width, height);
54
+ if (width * height * this.scale * this.scale > MAX_PIXELS)
55
+ throw new RangeError(`ASCII capture may not exceed ${MAX_PIXELS} pixels`);
56
+ if (width === this.width && height === this.height)
57
+ return;
58
+ this.width = width;
59
+ this.height = height;
60
+ this.releaseTarget();
61
+ }
62
+ /** Convert caller-supplied pixels. The same conversion is used by render(). */
63
+ renderPixels(input) {
64
+ this.assertUsable();
65
+ validatePixels(input);
66
+ // When a very narrow image is expanded into a wide character grid (or
67
+ // vice versa), samples are intentionally reused. Bound this work as well
68
+ // as the two independent input/output allocation sizes.
69
+ if (Math.max(input.width, this.width) * Math.max(input.height, this.height) > MAX_PIXELS * 2)
70
+ throw new RangeError("ASCII sampling work exceeds the pixel budget");
71
+ const rows = [];
72
+ for (let row = 0; row < this.height; row += 1) {
73
+ let line = "";
74
+ const y0 = row * input.height / this.height;
75
+ const y1 = (row + 1) * input.height / this.height;
76
+ for (let col = 0; col < this.width; col += 1) {
77
+ const x0 = col * input.width / this.width;
78
+ const x1 = (col + 1) * input.width / this.width;
79
+ let sum = 0;
80
+ let area = 0;
81
+ // A boundary may split an input pixel between cells. Weight by the
82
+ // actual overlap rather than assigning the whole pixel to one cell.
83
+ for (let y = Math.floor(y0); y < Math.min(input.height, Math.ceil(y1)); y += 1)
84
+ for (let x = Math.floor(x0); x < Math.min(input.width, Math.ceil(x1)); x += 1) {
85
+ const sy = input.origin === "bottom-left" ? input.height - y - 1 : y;
86
+ const offset = (sy * input.width + x) * 4;
87
+ const alpha = input.pixels[offset + 3] / 255;
88
+ const weight = (Math.min(x + 1, x1) - Math.max(x, x0)) * (Math.min(y + 1, y1) - Math.max(y, y0));
89
+ sum += (0.2126 * input.pixels[offset] + 0.7152 * input.pixels[offset + 1] + 0.0722 * input.pixels[offset + 2]) * alpha * weight;
90
+ area += weight;
91
+ }
92
+ let brightness = Math.max(0, Math.min(1, sum / (area * 255)));
93
+ if (this.invert)
94
+ brightness = 1 - brightness;
95
+ line += this.characters[Math.min(this.characters.length - 1, Math.floor(brightness * this.characters.length))];
96
+ }
97
+ rows.push(line);
98
+ }
99
+ const text = rows.join("\n");
100
+ if (this.element)
101
+ this.element.textContent = text;
102
+ return { width: this.width, height: this.height, text };
103
+ }
104
+ async render(renderer, source, camera) {
105
+ this.assertUsable();
106
+ if (this.busy)
107
+ throw new Error("An ASCII render is already in progress");
108
+ const device = renderer.device;
109
+ if (!device.getRenderTarget)
110
+ throw new Error("ASCII output requires getRenderTarget for safe state restoration");
111
+ const previous = device.getRenderTarget();
112
+ this.busy = true;
113
+ try {
114
+ const target = this.ensureTarget(renderer);
115
+ const input = { source, camera, target };
116
+ if (device.kind === "webgpu")
117
+ await renderer.renderAsync(input);
118
+ else
119
+ renderer.render(input);
120
+ device.setRenderTarget(target);
121
+ const pixels = device.kind === "webgpu" && device.readPixelsAsync
122
+ ? await device.readPixelsAsync(0, 0, target.width, target.height)
123
+ : device.readPixels(0, 0, target.width, target.height);
124
+ if (this.disposed)
125
+ throw new Error("ASCII output was disposed during render");
126
+ return this.renderPixels({ width: target.width, height: target.height, pixels, origin: device.kind === "webgl2" ? "bottom-left" : "top-left" });
127
+ }
128
+ finally {
129
+ try {
130
+ device.setRenderTarget(previous);
131
+ }
132
+ finally {
133
+ this.busy = false;
134
+ if (this.disposed)
135
+ this.releaseTarget();
136
+ }
137
+ }
138
+ }
139
+ dispose() {
140
+ if (this.disposed)
141
+ return;
142
+ this.disposed = true;
143
+ if (!this.busy)
144
+ this.releaseTarget();
145
+ if (this.element && this.originalElement) {
146
+ this.element.replaceChildren();
147
+ for (const child of this.originalElement.children)
148
+ this.element.appendChild(child);
149
+ restoreAttribute(this.element, "role", this.originalElement.role);
150
+ restoreAttribute(this.element, "aria-label", this.originalElement.label);
151
+ const [whiteSpace, priority] = this.originalElement.whiteSpace;
152
+ if (whiteSpace)
153
+ this.element.style.setProperty("white-space", whiteSpace, priority);
154
+ else
155
+ this.element.style.removeProperty("white-space");
156
+ }
157
+ }
158
+ ensureTarget(renderer) {
159
+ if (this.target && (this.target.disposed || this.targetOwner !== renderer))
160
+ this.releaseTarget();
161
+ if (!this.target) {
162
+ this.target = renderer.device.createRenderTarget({ width: this.width * this.scale, height: this.height * this.scale, format: "rgba8", depth: true, label: "a3d.ascii" });
163
+ this.targetOwner = renderer;
164
+ }
165
+ return this.target;
166
+ }
167
+ releaseTarget() { this.target?.dispose(); this.target = null; this.targetOwner = null; }
168
+ validateCaptureSize() { if (this.width * this.height * this.scale * this.scale > MAX_PIXELS)
169
+ throw new RangeError(`ASCII capture may not exceed ${MAX_PIXELS} pixels`); }
170
+ assertUsable() { if (this.disposed)
171
+ throw new Error("ASCII output is disposed"); }
172
+ }
173
+ function validateCells(width, height) {
174
+ if (!Number.isSafeInteger(width) || !Number.isSafeInteger(height) || width <= 0 || height <= 0 || width * height > MAX_CELLS)
175
+ throw new RangeError(`ASCII output dimensions must be positive and at most ${MAX_CELLS} cells`);
176
+ }
177
+ function validatePixels(input) {
178
+ if (!(input.pixels instanceof Uint8Array || input.pixels instanceof Uint8ClampedArray) || !Number.isSafeInteger(input.width) || !Number.isSafeInteger(input.height) || input.width <= 0 || input.height <= 0 || input.width * input.height > MAX_PIXELS || input.pixels.length !== input.width * input.height * 4)
179
+ throw new RangeError("ASCII pixels must be a bounded, complete RGBA8 image");
180
+ if (input.origin !== undefined && input.origin !== "top-left" && input.origin !== "bottom-left")
181
+ throw new RangeError("Invalid pixel origin");
182
+ }
183
+ function restoreAttribute(element, name, value) { if (value === null)
184
+ element.removeAttribute(name);
185
+ else
186
+ element.setAttribute(name, value); }
187
+ //# sourceMappingURL=AsciiOutput.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AsciiOutput.js","sourceRoot":"","sources":["../../src/effects/AsciiOutput.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ import { type CameraLike, type RenderItem, type Renderer } from "@aura3d/rendering";
2
+ export interface OutlineOutputOptions {
3
+ /** Normal extrusion distance in the object's local units. */
4
+ readonly width?: number;
5
+ readonly color?: readonly [number, number, number] | readonly [number, number, number, number];
6
+ readonly opacity?: number;
7
+ /**
8
+ * The default rejects alpha cutouts, alpha-to-coverage, textured or vertex/
9
+ * instance alpha on blended materials, unknown shaders or variants, custom
10
+ * instance attributes, and scissor/stencil/color-write masks. An untextured
11
+ * hull cannot reproduce these silhouettes. "geometry" explicitly opts into
12
+ * that approximation (including unknown shader deformation and masks).
13
+ * Uniform base-color alpha of blended materials is multiplied by outline
14
+ * opacity in either mode. Opaque materials ignore their base-color alpha.
15
+ */
16
+ readonly alphaMode?: "strict" | "geometry";
17
+ readonly maxVertices?: number;
18
+ readonly maxIndices?: number;
19
+ readonly maxItems?: number;
20
+ }
21
+ /**
22
+ * Inverted-hull, object-space outlines for triangle RenderItems. Each call to
23
+ * items() snapshots positions and normals, expanding them in local space and
24
+ * making a front-face-culled unlit shell. Submit both the original items and
25
+ * the shells (or call render()). Width changes with object transform and
26
+ * perspective; this is not a constant-pixel screen-space outline.
27
+ *
28
+ * Static and instanced geometry is supported. Skinning and morphing are
29
+ * rejected because CPU snapshots cannot follow GPU deformations. The caller
30
+ * owns the source geometry/materials; the output owns its generated buffers
31
+ * and materials, replacing them on the next items() call or dispose().
32
+ */
33
+ export declare class OutlineOutput {
34
+ private readonly width;
35
+ private readonly color;
36
+ private readonly opacity;
37
+ private readonly alphaMode;
38
+ private readonly maxVertices;
39
+ private readonly maxIndices;
40
+ private readonly maxItems;
41
+ private owned;
42
+ private readonly retiring;
43
+ private readonly disposalInFlight;
44
+ private disposed;
45
+ private building;
46
+ constructor(options?: OutlineOutputOptions);
47
+ /** Returned shell resources stay live until the next items() call or disposal. */
48
+ items(source: Iterable<RenderItem>): readonly RenderItem[];
49
+ private buildItems;
50
+ /** Render the source and its shells in a single ordinary renderer frame. */
51
+ render(renderer: Pick<Renderer, "render">, source: Iterable<RenderItem>, camera?: CameraLike): ReturnType<Renderer["render"]>;
52
+ dispose(): void;
53
+ private retireOwned;
54
+ private cleanupRetired;
55
+ private cleanupAfterFailure;
56
+ private assertAlive;
57
+ }
58
+ //# sourceMappingURL=OutlineOutput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OutlineOutput.d.ts","sourceRoot":"","sources":["../../src/effects/OutlineOutput.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,UAAU,EAAyB,KAAK,UAAU,EAAE,KAAK,QAAQ,EACvE,MAAM,mBAAmB,CAAC;AAE3B,MAAM,WAAW,oBAAoB;IACnC,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/F,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;;;;OAQG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC3C,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAcD;;;;;;;;;;;GAWG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA4C;IAClE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwB;IAClD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsD;IAC/E,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAsD;IACvF,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAS;gBAEb,OAAO,GAAE,oBAAyB;IAkB9C,kFAAkF;IAClF,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,SAAS,UAAU,EAAE;IAW1D,OAAO,CAAC,UAAU;IAqFlB,4EAA4E;IAC5E,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAU7H,OAAO,IAAI,IAAI;IASf,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,cAAc;IAkBtB,OAAO,CAAC,mBAAmB;IAQ3B,OAAO,CAAC,WAAW;CACpB"}
@@ -0,0 +1,274 @@
1
+ import { DEFAULT_INSTANCED_PBR_SHADER_NAME, DEFAULT_INSTANCED_UNLIT_SHADER_NAME, DEFAULT_NORMAL_MAPPED_PBR_SHADER_NAME, DEFAULT_PBR_SHADER_NAME, DEFAULT_TEXTURED_PBR_SHADER_NAME, DEFAULT_TEXTURED_UNLIT_SHADER_NAME, DEFAULT_UNLIT_SHADER_NAME, Geometry, IndexBuffer, UnlitMaterial, VertexBuffer, VertexFormat, isTextureBinding } from "@aura3d/rendering";
2
+ // These built-in shaders expose the standard base color/alpha contract. An
3
+ // arbitrary shader may discard fragments or deform vertices without declaring
4
+ // either through the material's ordinary alpha parameters.
5
+ const KNOWN_OUTLINE_SHADERS = new Set([
6
+ DEFAULT_UNLIT_SHADER_NAME, DEFAULT_PBR_SHADER_NAME,
7
+ DEFAULT_INSTANCED_UNLIT_SHADER_NAME, DEFAULT_INSTANCED_PBR_SHADER_NAME,
8
+ DEFAULT_TEXTURED_UNLIT_SHADER_NAME, DEFAULT_TEXTURED_PBR_SHADER_NAME,
9
+ DEFAULT_NORMAL_MAPPED_PBR_SHADER_NAME
10
+ ]);
11
+ /**
12
+ * Inverted-hull, object-space outlines for triangle RenderItems. Each call to
13
+ * items() snapshots positions and normals, expanding them in local space and
14
+ * making a front-face-culled unlit shell. Submit both the original items and
15
+ * the shells (or call render()). Width changes with object transform and
16
+ * perspective; this is not a constant-pixel screen-space outline.
17
+ *
18
+ * Static and instanced geometry is supported. Skinning and morphing are
19
+ * rejected because CPU snapshots cannot follow GPU deformations. The caller
20
+ * owns the source geometry/materials; the output owns its generated buffers
21
+ * and materials, replacing them on the next items() call or dispose().
22
+ */
23
+ export class OutlineOutput {
24
+ width;
25
+ color;
26
+ opacity;
27
+ alphaMode;
28
+ maxVertices;
29
+ maxIndices;
30
+ maxItems;
31
+ owned = [];
32
+ retiring = new Set();
33
+ disposalInFlight = new Set();
34
+ disposed = false;
35
+ building = false;
36
+ constructor(options = {}) {
37
+ this.width = options.width ?? 0.03;
38
+ this.opacity = options.opacity ?? 1;
39
+ this.alphaMode = options.alphaMode ?? "strict";
40
+ this.maxVertices = options.maxVertices ?? 1_000_000;
41
+ this.maxIndices = options.maxIndices ?? 3_000_000;
42
+ this.maxItems = options.maxItems ?? 16_384;
43
+ const color = options.color ?? [0, 0, 0, 1];
44
+ if (!Number.isFinite(this.width) || this.width < 0)
45
+ throw new RangeError("Outline width must be finite and non-negative");
46
+ if (!Number.isFinite(this.opacity) || this.opacity < 0 || this.opacity > 1)
47
+ throw new RangeError("Outline opacity must be in [0, 1]");
48
+ if (color.length < 3 || color.length > 4 || color.some((value) => !Number.isFinite(value) || value < 0 || value > 1))
49
+ throw new RangeError("Outline color must contain three or four values in [0, 1]");
50
+ if (this.alphaMode !== "strict" && this.alphaMode !== "geometry")
51
+ throw new RangeError("Invalid outline alphaMode");
52
+ if (!Number.isSafeInteger(this.maxVertices) || this.maxVertices < 1 || this.maxVertices > 1_000_000)
53
+ throw new RangeError("Outline maxVertices must be in [1, 1000000]");
54
+ if (!Number.isSafeInteger(this.maxIndices) || this.maxIndices < 1 || this.maxIndices > 3_000_000)
55
+ throw new RangeError("Outline maxIndices must be in [1, 3000000]");
56
+ if (!Number.isSafeInteger(this.maxItems) || this.maxItems < 1 || this.maxItems > 16_384)
57
+ throw new RangeError("Outline maxItems must be in [1, 16384]");
58
+ this.color = [color[0], color[1], color[2], color[3] ?? 1];
59
+ }
60
+ /** Returned shell resources stay live until the next items() call or disposal. */
61
+ items(source) {
62
+ this.assertAlive();
63
+ if (this.building)
64
+ throw new Error("Outline output is already building items");
65
+ this.building = true;
66
+ try {
67
+ return this.buildItems(source);
68
+ }
69
+ finally {
70
+ this.building = false;
71
+ }
72
+ }
73
+ buildItems(source) {
74
+ this.cleanupRetired();
75
+ const allocated = [];
76
+ const result = [];
77
+ let count = 0;
78
+ let indices = 0;
79
+ let itemCount = 0;
80
+ try {
81
+ for (const item of source) {
82
+ this.assertAlive();
83
+ itemCount += 1;
84
+ if (itemCount > this.maxItems)
85
+ throw new RangeError("Outline item budget exceeded");
86
+ const alpha = materialAlpha(item, this.alphaMode) * this.opacity * this.color[3];
87
+ if (alpha === 0 || this.width === 0)
88
+ continue;
89
+ const geometry = item.geometry;
90
+ if (geometry.topology !== "triangles")
91
+ throw new Error("Object-space outlines require triangle geometry");
92
+ if (item.skinning || (item.morphTargets?.length ?? 0) > 0 || (item.morphWeights?.length ?? 0) > 0 || (geometry.morphTargets?.length ?? 0) > 0)
93
+ throw new Error("Object-space outlines do not support skinned or morphed geometry");
94
+ const vertices = geometry.vertexBuffer;
95
+ if (!vertices.format.hasAttribute("normal"))
96
+ throw new Error("Object-space outlines require vertex normals");
97
+ const normalSlot = vertices.format.getAttribute("normal").bufferSlot;
98
+ if (vertices.format.streams.find((stream) => stream.slot === normalSlot)?.stepMode !== "vertex") {
99
+ throw new Error("Object-space outlines require per-vertex normals");
100
+ }
101
+ const positions = geometry.getAttributeBuffer("position");
102
+ const normals = geometry.getAttributeBuffer("normal");
103
+ count += vertices.vertexCount;
104
+ if (count > this.maxVertices)
105
+ throw new RangeError("Outline vertex budget exceeded");
106
+ indices += geometry.indexBuffer?.count ?? 0;
107
+ if (indices > this.maxIndices)
108
+ throw new RangeError("Outline index budget exceeded");
109
+ const expanded = new VertexBuffer(VertexFormat.P3, vertices.vertexCount);
110
+ let shell;
111
+ let material;
112
+ try {
113
+ for (let index = 0; index < vertices.vertexCount; index += 1) {
114
+ const position = positions.getDecodedAttribute(index, "position");
115
+ const normal = normals.getDecodedAttribute(index, "normal");
116
+ const length = Math.hypot(normal[0], normal[1], normal[2]);
117
+ if (!Number.isFinite(length) || length < 1e-12)
118
+ throw new Error(`Outline vertex ${index} has a zero or non-finite normal`);
119
+ expanded.setAttribute(index, "position", [position[0] + this.width * normal[0] / length, position[1] + this.width * normal[1] / length, position[2] + this.width * normal[2] / length]);
120
+ }
121
+ shell = new Geometry(expanded, geometry.indexBuffer ? new IndexBuffer(geometry.indexBuffer.data, vertices.vertexCount) : null);
122
+ shell.drawRange = geometry.drawRange;
123
+ shell.groups = geometry.groups;
124
+ material = new UnlitMaterial({ color: [this.color[0], this.color[1], this.color[2], alpha], renderState: { cullMode: "front", depthTest: true, depthWrite: alpha >= 1, blend: alpha < 1 } });
125
+ allocated.push({ geometry: shell, material });
126
+ }
127
+ catch (error) {
128
+ this.retiring.add(shell ?? expanded);
129
+ if (material)
130
+ this.retiring.add(material);
131
+ throw this.cleanupAfterFailure(error);
132
+ }
133
+ result.push({
134
+ geometry: shell,
135
+ material,
136
+ label: item.label ? `${item.label}:outline` : "outline",
137
+ drawRange: item.drawRange,
138
+ modelMatrix: item.modelMatrix,
139
+ geometryMatrix: item.geometryMatrix,
140
+ modelViewProjectionMatrix: item.modelViewProjectionMatrix,
141
+ instanceTransforms: item.instanceTransforms,
142
+ renderOrder: item.renderOrder,
143
+ layerMask: item.layerMask,
144
+ frustumCulled: item.frustumCulled,
145
+ castShadow: false,
146
+ receiveShadow: false,
147
+ includeInAutoFrame: false
148
+ });
149
+ }
150
+ this.assertAlive();
151
+ }
152
+ catch (error) {
153
+ this.retireOwned(allocated);
154
+ throw this.cleanupAfterFailure(error);
155
+ }
156
+ this.retireOwned(this.owned);
157
+ this.owned = [];
158
+ try {
159
+ this.cleanupRetired();
160
+ this.assertAlive();
161
+ }
162
+ catch (error) {
163
+ this.retireOwned(allocated);
164
+ throw this.cleanupAfterFailure(error);
165
+ }
166
+ this.owned = allocated;
167
+ return result;
168
+ }
169
+ /** Render the source and its shells in a single ordinary renderer frame. */
170
+ render(renderer, source, camera) {
171
+ this.assertAlive();
172
+ const originals = [];
173
+ for (const item of source) {
174
+ if (originals.length >= this.maxItems)
175
+ throw new RangeError("Outline item budget exceeded");
176
+ originals.push(item);
177
+ }
178
+ return renderer.render([...originals, ...this.items(originals)], camera);
179
+ }
180
+ dispose() {
181
+ if (!this.disposed) {
182
+ this.disposed = true;
183
+ this.retireOwned(this.owned);
184
+ this.owned = [];
185
+ }
186
+ this.cleanupRetired();
187
+ }
188
+ retireOwned(items) {
189
+ for (const item of items) {
190
+ this.retiring.add(item.geometry);
191
+ this.retiring.add(item.material);
192
+ }
193
+ }
194
+ cleanupRetired() {
195
+ const errors = [];
196
+ for (const resource of [...this.retiring]) {
197
+ if (this.disposalInFlight.has(resource))
198
+ continue;
199
+ this.disposalInFlight.add(resource);
200
+ try {
201
+ if (!resource.disposed)
202
+ resource.dispose();
203
+ if (!resource.disposed)
204
+ throw new Error("Outline resource disposal did not complete");
205
+ }
206
+ catch (error) {
207
+ errors.push(error);
208
+ }
209
+ finally {
210
+ if (resource.disposed)
211
+ this.retiring.delete(resource);
212
+ this.disposalInFlight.delete(resource);
213
+ }
214
+ }
215
+ if (errors.length)
216
+ throw new AggregateError(errors, "Outline resource cleanup failed");
217
+ }
218
+ cleanupAfterFailure(error) {
219
+ try {
220
+ this.cleanupRetired();
221
+ return error;
222
+ }
223
+ catch (cleanup) {
224
+ return new AggregateError([error, cleanup], "Outline operation and cleanup failed");
225
+ }
226
+ }
227
+ assertAlive() { if (this.disposed)
228
+ throw new Error("Outline output is disposed"); }
229
+ }
230
+ function materialAlpha(item, mode) {
231
+ if (mode === "strict" && item.instanceAttributes?.length) {
232
+ throw new Error("Outline cannot reproduce source scissor, stencil, color-write masks or custom instance attributes; use alphaMode: geometry for an approximate silhouette");
233
+ }
234
+ const material = item.material;
235
+ if (!material)
236
+ return 1;
237
+ // Package consumers may provide a MaterialInstance from another copy of the
238
+ // rendering package, where a local instanceof check would not recognize it.
239
+ const base = "baseMaterial" in material ? material.baseMaterial : material;
240
+ if (base.disposed)
241
+ throw new Error("Cannot outline a disposed material");
242
+ if (mode === "strict" && (!KNOWN_OUTLINE_SHADERS.has(base.shaderKey) || base.shaderVariant !== undefined)) {
243
+ throw new Error("Outline cannot determine custom shader alpha or deformation; use alphaMode: geometry for an approximate silhouette");
244
+ }
245
+ const state = base.renderState;
246
+ if (mode === "strict" && (state.scissor || state.stencil || state.colorWrite.some((enabled) => !enabled))) {
247
+ throw new Error("Outline cannot reproduce source scissor, stencil, color-write masks or custom instance attributes; use alphaMode: geometry for an approximate silhouette");
248
+ }
249
+ const cutoff = material.getParameter("u_alphaCutoff");
250
+ if (typeof cutoff === "number" && (!Number.isFinite(cutoff) || cutoff < 0 || cutoff > 1))
251
+ throw new RangeError("Source material alpha cutoff must be in [0, 1]");
252
+ // Named blendMode has precedence over the legacy blend boolean.
253
+ const isBlend = state.blendMode === undefined ? state.blend : state.blendMode !== "opaque";
254
+ const binding = material.getParameter("u_baseColorTexture");
255
+ // Some generated shader paths sample a bound map regardless of the legacy
256
+ // texture-enabled uniform, so a present map must be treated conservatively.
257
+ const texturedAlpha = isBlend && isTextureBinding(binding) && binding.texture !== null;
258
+ const format = item.geometry.vertexBuffer.format;
259
+ const vertexAlpha = isBlend && base.requiredAttributes.includes("a_color") && format.hasAttribute("color") && format.getAttribute("color").components === 4;
260
+ const instanceAlpha = isBlend && item.instanceTransforms !== undefined && item.instanceColors !== undefined;
261
+ if (mode === "strict" && ((typeof cutoff === "number" && cutoff > 0) || state.alphaToCoverage || texturedAlpha || vertexAlpha || instanceAlpha)) {
262
+ throw new Error("Outline alpha cutouts, coverage, or per-pixel/instance alpha require alphaMode: geometry for an approximate silhouette");
263
+ }
264
+ const factor = material.getParameter("u_baseColor");
265
+ if (!isBlend || !(Array.isArray(factor) || ArrayBuffer.isView(factor)))
266
+ return 1;
267
+ if (factor.length !== 4)
268
+ throw new RangeError("Source material base color must contain four channels");
269
+ const alpha = factor[3];
270
+ if (!Number.isFinite(alpha) || alpha < 0 || alpha > 1)
271
+ throw new RangeError("Source material alpha must be in [0, 1]");
272
+ return alpha;
273
+ }
274
+ //# sourceMappingURL=OutlineOutput.js.map