babylon.quarks-editor 0.27.0 → 0.29.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 (149) hide show
  1. package/CHANGELOG.md +92 -0
  2. package/README.md +31 -4
  3. package/dist/core/assetTree.d.ts +5 -0
  4. package/dist/core/assets.d.ts +7 -0
  5. package/dist/core/assets.js +11 -7
  6. package/dist/core/binding.d.ts +9 -0
  7. package/dist/core/binding.js +8 -0
  8. package/dist/core/collision.d.ts +4 -0
  9. package/dist/core/collision.js +4 -0
  10. package/dist/core/colors.d.ts +16 -0
  11. package/dist/core/colors.js +8 -0
  12. package/dist/core/defaultEffect.d.ts +3 -2
  13. package/dist/core/defaultEffect.js +3 -1
  14. package/dist/core/editorSession.d.ts +229 -0
  15. package/dist/core/editorSession.js +983 -0
  16. package/dist/core/effectTree.d.ts +5 -0
  17. package/dist/core/emitterShapeWireframe.js +11 -4
  18. package/dist/core/galleryPlayback.js +2 -1
  19. package/dist/core/history.d.ts +6 -0
  20. package/dist/core/history.js +4 -0
  21. package/dist/core/indexedDbProjectStore.d.ts +13 -0
  22. package/dist/core/indexedDbProjectStore.js +13 -0
  23. package/dist/core/loadEffect.js +2 -0
  24. package/dist/core/material.d.ts +12 -2
  25. package/dist/core/playback.d.ts +6 -0
  26. package/dist/core/projectStore.d.ts +26 -0
  27. package/dist/core/projectStore.js +1 -0
  28. package/dist/core/sceneEntries.d.ts +3 -0
  29. package/dist/core/scrub.d.ts +1 -1
  30. package/dist/core/scrub.js +2 -2
  31. package/dist/core/shapes.d.ts +31 -1
  32. package/dist/core/shapes.js +18 -3
  33. package/dist/core/systems.d.ts +2 -0
  34. package/dist/core/systems.js +5 -1
  35. package/dist/core/timeline.d.ts +23 -3
  36. package/dist/core/values.d.ts +42 -8
  37. package/dist/core/values.js +52 -9
  38. package/dist/index.d.ts +5 -1
  39. package/dist/react/AssetsPanel.d.ts +7 -0
  40. package/dist/react/AxesCurveField.d.ts +21 -0
  41. package/dist/react/AxesCurveField.js +25 -0
  42. package/dist/react/ChoiceDialog.d.ts +7 -0
  43. package/dist/react/ContextMenu.d.ts +10 -1
  44. package/dist/react/EditorWorkspace.d.ts +7 -0
  45. package/dist/react/EditorWorkspace.js +6 -3
  46. package/dist/react/EffectEditor.d.ts +8 -2
  47. package/dist/react/EffectEditorHost.d.ts +32 -28
  48. package/dist/react/EffectEditorHost.js +241 -1665
  49. package/dist/react/EffectTreeView.d.ts +3 -0
  50. package/dist/react/LifeCurveField.d.ts +18 -0
  51. package/dist/react/LifeCurveField.js +26 -0
  52. package/dist/react/MessageDialog.d.ts +5 -0
  53. package/dist/react/ModuleSection.d.ts +10 -0
  54. package/dist/react/ModuleSection.js +4 -1
  55. package/dist/react/ProjectsPage.d.ts +6 -0
  56. package/dist/react/ProjectsPage.js +27 -11
  57. package/dist/react/PromptDialog.d.ts +7 -0
  58. package/dist/react/ScenePanel.d.ts +7 -0
  59. package/dist/react/ScenePanel.js +7 -5
  60. package/dist/react/TextureSelect.d.ts +5 -5
  61. package/dist/react/TimelinePanel.d.ts +13 -0
  62. package/dist/react/TimelinePanel.js +10 -6
  63. package/dist/react/TimelineTrackRow.d.ts +10 -0
  64. package/dist/react/TransportBar.d.ts +9 -0
  65. package/dist/react/ValueField.d.ts +33 -5
  66. package/dist/react/ValueField.js +34 -9
  67. package/dist/react/behaviorModules.d.ts +16 -5
  68. package/dist/react/behaviorModules.js +28 -9
  69. package/dist/react/fields.d.ts +8 -0
  70. package/dist/react/fields.js +3 -0
  71. package/dist/react/host/BottomDocks.d.ts +36 -0
  72. package/dist/react/host/BottomDocks.js +63 -0
  73. package/dist/react/host/HostDialogs.d.ts +50 -0
  74. package/dist/react/host/HostDialogs.js +113 -0
  75. package/dist/react/host/InspectorSidebar.d.ts +32 -0
  76. package/dist/react/host/InspectorSidebar.js +59 -0
  77. package/dist/react/host/SceneSidebar.d.ts +53 -0
  78. package/dist/react/host/SceneSidebar.js +62 -0
  79. package/dist/react/host/ViewportPane.d.ts +56 -0
  80. package/dist/react/host/ViewportPane.js +91 -0
  81. package/dist/react/host/hostHooks.d.ts +44 -0
  82. package/dist/react/host/hostHooks.js +164 -0
  83. package/dist/react/host/hostStyles.d.ts +21 -0
  84. package/dist/react/host/hostStyles.js +57 -0
  85. package/dist/react/index.d.ts +3 -1
  86. package/dist/react/inspectorHints.js +1 -1
  87. package/dist/react/modules.d.ts +17 -6
  88. package/dist/react/modules.js +77 -34
  89. package/dist/react/theme.d.ts +15 -0
  90. package/dist/react/theme.js +15 -0
  91. package/dist/session.d.ts +9 -0
  92. package/dist/session.js +8 -0
  93. package/package.json +17 -11
  94. package/src/core/assetTree.ts +14 -1
  95. package/src/core/assets.ts +25 -10
  96. package/src/core/binding.ts +9 -0
  97. package/src/core/collision.ts +4 -0
  98. package/src/core/colors.ts +21 -1
  99. package/src/core/defaultEffect.ts +6 -3
  100. package/src/core/editorSession.ts +1299 -0
  101. package/src/core/effectTree.ts +5 -0
  102. package/src/core/emitterShapeWireframe.ts +12 -4
  103. package/src/core/galleryPlayback.ts +2 -1
  104. package/src/core/history.ts +6 -0
  105. package/src/core/indexedDbProjectStore.ts +13 -0
  106. package/src/core/loadEffect.ts +3 -1
  107. package/src/core/material.ts +14 -4
  108. package/src/core/playback.ts +6 -0
  109. package/src/core/projectStore.ts +26 -0
  110. package/src/core/sceneEntries.ts +3 -0
  111. package/src/core/scrub.ts +2 -2
  112. package/src/core/shapes.ts +48 -3
  113. package/src/core/systems.ts +11 -2
  114. package/src/core/timeline.ts +40 -15
  115. package/src/core/values.ts +91 -14
  116. package/src/index.ts +5 -1
  117. package/src/react/AssetsPanel.tsx +7 -0
  118. package/src/react/AxesCurveField.tsx +72 -0
  119. package/src/react/ChoiceDialog.tsx +7 -0
  120. package/src/react/ContextMenu.tsx +10 -1
  121. package/src/react/EditorWorkspace.tsx +11 -5
  122. package/src/react/EffectEditor.tsx +7 -1
  123. package/src/react/EffectEditorHost.tsx +406 -2340
  124. package/src/react/EffectTreeView.tsx +3 -0
  125. package/src/react/LifeCurveField.tsx +57 -0
  126. package/src/react/MessageDialog.tsx +5 -0
  127. package/src/react/ModuleSection.tsx +11 -0
  128. package/src/react/ProjectsPage.tsx +38 -11
  129. package/src/react/PromptDialog.tsx +7 -0
  130. package/src/react/ScenePanel.tsx +14 -5
  131. package/src/react/TextureSelect.tsx +6 -6
  132. package/src/react/TimelinePanel.tsx +23 -6
  133. package/src/react/TimelineTrackRow.tsx +10 -0
  134. package/src/react/TransportBar.tsx +9 -0
  135. package/src/react/ValueField.tsx +99 -15
  136. package/src/react/behaviorModules.tsx +58 -42
  137. package/src/react/fields.tsx +12 -1
  138. package/src/react/host/BottomDocks.tsx +178 -0
  139. package/src/react/host/HostDialogs.tsx +228 -0
  140. package/src/react/host/InspectorSidebar.tsx +147 -0
  141. package/src/react/host/SceneSidebar.tsx +227 -0
  142. package/src/react/host/ViewportPane.tsx +300 -0
  143. package/src/react/host/hostHooks.ts +198 -0
  144. package/src/react/host/hostStyles.ts +65 -0
  145. package/src/react/index.ts +3 -1
  146. package/src/react/inspectorHints.ts +1 -1
  147. package/src/react/modules.tsx +192 -71
  148. package/src/react/theme.ts +15 -0
  149. package/src/session.ts +17 -0
@@ -1,24 +1,44 @@
1
1
  import type { TransformNode } from '@babylonjs/core/Meshes/transformNode.js';
2
2
  import type { FunctionValueGenerator, ParticleSystem, ValueGenerator } from 'babylon.quarks';
3
3
  import type { EffectBinding } from './binding.js';
4
- export type TimelineRow = {
4
+ /** A timeline row for a group node: a heading over the tracks of the systems under it. */
5
+ export interface TimelineGroupRow {
6
+ /** Tells group rows from track rows. */
5
7
  kind: 'group';
8
+ /** The group's node. */
6
9
  node: TransformNode;
10
+ /** The group's name. */
7
11
  name: string;
12
+ /** How deep the group sits in the effect tree; the root's children are 0. */
8
13
  depth: number;
9
- } | {
14
+ }
15
+ /** A timeline row for one particle system: a bar from its start delay over its duration. */
16
+ export interface TimelineSystemRow {
17
+ /** Tells group rows from track rows. */
10
18
  kind: 'track';
19
+ /** The system's emitter node. */
11
20
  node: TransformNode;
21
+ /** The system. */
12
22
  system: ParticleSystem;
23
+ /** The emitter's name. */
13
24
  name: string;
25
+ /** How deep the emitter sits in the effect tree. */
14
26
  depth: number;
27
+ /** True for the effect's main system. */
15
28
  isRoot: boolean;
29
+ /** False for the one system an effect cannot lose. */
16
30
  removable: boolean;
31
+ /** Where the bar starts: the start delay, its lower end when random, in seconds. */
17
32
  startOffset: number;
33
+ /** True when the start delay is a curve and the bar's start is only indicative. */
18
34
  startOffsetVariable: boolean;
35
+ /** The bar's length, the system's duration in seconds. */
19
36
  duration: number;
37
+ /** True when the system loops; its bar runs to the end of the ruler. */
20
38
  looping: boolean;
21
- };
39
+ }
40
+ /** One row of the focused effect's timeline. */
41
+ export type TimelineRow = TimelineGroupRow | TimelineSystemRow;
22
42
  /**
23
43
  * Reads a representative start-delay value for display without invoking the generator's
24
44
  * stateful `genValue` (IntervalValue caches its sampled slot on first call and returns NaN
@@ -1,28 +1,62 @@
1
- import type { FunctionValueGenerator, ValueGenerator } from 'babylon.quarks';
2
- import { PiecewiseBezier } from 'babylon.quarks';
1
+ import type { FunctionValueGenerator, RotationGenerator, ValueGenerator, Vector3Generator } from 'babylon.quarks';
2
+ import { PiecewiseBezier, RandomBetweenCurves } from 'babylon.quarks';
3
+ /** A generator of one number: a constant or random value, or a curve over the particle's life. */
3
4
  export type ScalarGenerator = ValueGenerator | FunctionValueGenerator;
4
- export type ScalarMode = 'constant' | 'random' | 'curve';
5
+ /** Any generator a value field can be pointed at; ones that are not scalar read as the default. */
6
+ export type AnyGenerator = ScalarGenerator | Vector3Generator | RotationGenerator;
7
+ /**
8
+ * How a value field produces its number: Unity's Constant, Random Between Two Constants, Curve or
9
+ * Random Between Two Curves.
10
+ */
11
+ export type ScalarMode = 'constant' | 'random' | 'curve' | 'curves';
5
12
  /** Editable snapshot of a scalar value generator. */
6
13
  export interface ScalarValueState {
14
+ /** Which of the fields below is the value. */
7
15
  mode: ScalarMode;
8
16
  /** Constant value (mode: constant). */
9
17
  value: number;
10
- /** Random range (mode: random). */
18
+ /** Lower end of the random range (mode: random). */
11
19
  min: number;
20
+ /** Upper end of the random range (mode: random). */
12
21
  max: number;
13
- /** Cubic bezier control values over normalized lifetime (mode: curve). */
22
+ /** Cubic bezier control values over normalized lifetime (mode: curve; the first curve of curves). */
14
23
  curve: [number, number, number, number];
24
+ /** The second curve's control values (mode: curves); the first curve's when left out. */
25
+ curve2?: [number, number, number, number];
15
26
  }
16
- export declare function readScalar(generator: ScalarGenerator | undefined): ScalarValueState;
27
+ /**
28
+ * What a value field shows for `generator`: its constant, its range or its first curve segment.
29
+ * Anything else, including a missing generator, reads as the constant 1.
30
+ */
31
+ export declare function readScalar(generator: AnyGenerator | undefined): ScalarValueState;
32
+ /** The generator a value field's state describes; a curve state builds a one-segment curve. */
33
+ export declare function buildScalar(state: ScalarValueState & {
34
+ mode: 'curve';
35
+ }): PiecewiseBezier;
17
36
  export declare function buildScalar(state: ScalarValueState): ScalarGenerator;
18
37
  /** One segment of a piecewise bezier curve: start x (0..1) + four control values. */
19
38
  export interface CurvePiece {
39
+ /** Where the segment starts, 0..1 over the particle's life. */
20
40
  start: number;
41
+ /** The segment's four bezier control values. */
21
42
  p: [number, number, number, number];
22
43
  }
23
- /** Reads pieces from a PiecewiseBezier (or synthesizes one segment for other generators). */
24
- export declare function readPieces(generator: ScalarGenerator | undefined): CurvePiece[];
44
+ /**
45
+ * Reads pieces from a PiecewiseBezier, or from the first curve of a RandomBetweenCurves (the
46
+ * second with `second`). A constant reads as a flat curve, a random range as a flat curve at its
47
+ * lower end (its upper with `second`), and other generators as one default segment.
48
+ */
49
+ export declare function readPieces(generator: AnyGenerator | undefined, second?: boolean): CurvePiece[];
50
+ /** A piecewise bezier from segments, sorted by their start. */
25
51
  export declare function buildCurve(pieces: CurvePiece[]): PiecewiseBezier;
52
+ /**
53
+ * `generator` as one curve (`'curve'`) or two (`'curves'`, Unity's Random Between Two Curves),
54
+ * keeping every segment: one curve becomes both bounds of two, and two keep their first as one.
55
+ * A constant is a flat curve, and a random range flat curves at its ends.
56
+ */
57
+ export declare function withCurves(generator: AnyGenerator | undefined, mode: 'curve'): PiecewiseBezier;
58
+ export declare function withCurves(generator: AnyGenerator | undefined, mode: 'curves'): RandomBetweenCurves;
59
+ export declare function withCurves(generator: AnyGenerator | undefined, mode: 'curve' | 'curves'): PiecewiseBezier | RandomBetweenCurves;
26
60
  /** Splits the piece containing x into two exact halves using Bezier.split. */
27
61
  export declare function splitPieces(pieces: CurvePiece[], x: number): CurvePiece[];
28
62
  /** Removes the boundary at pieces[index].start by merging with the previous piece. */
@@ -1,6 +1,17 @@
1
- import { Bezier, ConstantValue, IntervalValue, PiecewiseBezier } from 'babylon.quarks';
1
+ import { Bezier, ConstantValue, IntervalValue, PiecewiseBezier, RandomBetweenCurves } from 'babylon.quarks';
2
+ /**
3
+ * What a value field shows for `generator`: its constant, its range or its first curve segment.
4
+ * Anything else, including a missing generator, reads as the constant 1.
5
+ */
2
6
  export function readScalar(generator) {
3
- const state = { mode: 'constant', value: 1, min: 0.5, max: 1.5, curve: [0, 1, 1, 0] };
7
+ const state = {
8
+ mode: 'constant',
9
+ value: 1,
10
+ min: 0.5,
11
+ max: 1.5,
12
+ curve: [0, 1, 1, 0],
13
+ curve2: [0, 1, 1, 0],
14
+ };
4
15
  if (generator instanceof ConstantValue) {
5
16
  state.mode = 'constant';
6
17
  state.value = generator.value;
@@ -15,14 +26,23 @@ export function readScalar(generator) {
15
26
  }
16
27
  else if (generator instanceof PiecewiseBezier) {
17
28
  state.mode = 'curve';
18
- const first = generator.functions[0]?.[0];
19
- if (first) {
20
- state.curve = [first.p[0], first.p[1], first.p[2], first.p[3]];
21
- state.value = first.p[0];
22
- }
29
+ state.curve = firstSegment(generator) ?? state.curve;
30
+ state.curve2 = state.curve;
31
+ state.value = state.curve[0];
32
+ }
33
+ else if (generator instanceof RandomBetweenCurves) {
34
+ state.mode = 'curves';
35
+ state.curve = firstSegment(generator.curve1) ?? state.curve;
36
+ state.curve2 = firstSegment(generator.curve2) ?? state.curve;
37
+ state.value = state.curve[0];
23
38
  }
24
39
  return state;
25
40
  }
41
+ /** The control values of a curve's first segment, if it has one. */
42
+ function firstSegment(curve) {
43
+ const first = curve.functions[0]?.[0];
44
+ return first ? [first.p[0], first.p[1], first.p[2], first.p[3]] : undefined;
45
+ }
26
46
  export function buildScalar(state) {
27
47
  switch (state.mode) {
28
48
  case 'constant':
@@ -31,10 +51,24 @@ export function buildScalar(state) {
31
51
  return new IntervalValue(Math.min(state.min, state.max), Math.max(state.min, state.max));
32
52
  case 'curve':
33
53
  return new PiecewiseBezier([[new Bezier(...state.curve), 0]]);
54
+ case 'curves':
55
+ return new RandomBetweenCurves(new PiecewiseBezier([[new Bezier(...state.curve), 0]]), new PiecewiseBezier([[new Bezier(...(state.curve2 ?? state.curve)), 0]]));
34
56
  }
35
57
  }
36
- /** Reads pieces from a PiecewiseBezier (or synthesizes one segment for other generators). */
37
- export function readPieces(generator) {
58
+ /**
59
+ * Reads pieces from a PiecewiseBezier, or from the first curve of a RandomBetweenCurves (the
60
+ * second with `second`). A constant reads as a flat curve, a random range as a flat curve at its
61
+ * lower end (its upper with `second`), and other generators as one default segment.
62
+ */
63
+ export function readPieces(generator, second = false) {
64
+ if (generator instanceof RandomBetweenCurves) {
65
+ return readPieces(second ? generator.curve2 : generator.curve1);
66
+ }
67
+ // A constant is a flat curve, and a random range two flat curves at its ends.
68
+ if (generator instanceof ConstantValue || generator instanceof IntervalValue) {
69
+ const value = generator instanceof ConstantValue ? generator.value : second ? generator.b : generator.a;
70
+ return [{ start: 0, p: [value, value, value, value] }];
71
+ }
38
72
  if (generator instanceof PiecewiseBezier && generator.functions.length > 0) {
39
73
  return generator.functions.map(([bezier, start]) => ({
40
74
  start,
@@ -44,10 +78,19 @@ export function readPieces(generator) {
44
78
  const fallback = readScalar(generator);
45
79
  return [{ start: 0, p: fallback.curve }];
46
80
  }
81
+ /** A piecewise bezier from segments, sorted by their start. */
47
82
  export function buildCurve(pieces) {
48
83
  const sorted = [...pieces].sort((a, b) => a.start - b.start);
49
84
  return new PiecewiseBezier(sorted.map((piece) => [new Bezier(...piece.p), piece.start]));
50
85
  }
86
+ export function withCurves(generator, mode) {
87
+ const first = buildCurve(readPieces(generator));
88
+ if (mode === 'curve') {
89
+ return first;
90
+ }
91
+ const second = generator instanceof RandomBetweenCurves || generator instanceof IntervalValue;
92
+ return new RandomBetweenCurves(first, buildCurve(readPieces(generator, second)));
93
+ }
51
94
  /** Splits the piece containing x into two exact halves using Bezier.split. */
52
95
  export function splitPieces(pieces, x) {
53
96
  const sorted = [...pieces].sort((a, b) => a.start - b.start);
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ export type { AssetLoadProgress, EffectAsset } from './core/assets.js';
2
+ export type { AssetDragPayload } from './core/assetTree.js';
1
3
  export { EffectBinding } from './core/binding.js';
2
4
  export type { EditorListener } from './core/binding.js';
3
5
  export { GroundPlaneResolver, ensureGroundResolver } from './core/collision.js';
@@ -12,10 +14,12 @@ export { applyRendererMaterial, getMaterialLabel } from './core/material.js';
12
14
  export type { RendererMaterialPatch } from './core/material.js';
13
15
  export { formatBytes } from './core/projectStore.js';
14
16
  export type { ProjectMeta, ProjectStore, StorageEstimate, StoredAsset, StoredScene, StoredSceneEntry, } from './core/projectStore.js';
17
+ export type { SceneEntry } from './core/sceneEntries.js';
15
18
  export { DEFAULT_SHAPE_PARAMS, SHAPE_PARAM_KEYS, SHAPE_TYPES, createShape, getShapeType, readShapeParams, } from './core/shapes.js';
16
19
  export type { ShapeParams, ShapeType } from './core/shapes.js';
17
20
  export { createChildSystem } from './core/systems.js';
18
21
  export type { ChildSystemOptions } from './core/systems.js';
19
22
  export { ensurePortableTextureUrl } from './core/textureExport.js';
23
+ export type { TimelineGroupRow, TimelineRow, TimelineSystemRow } from './core/timeline.js';
20
24
  export { buildCurve, buildScalar, mergePieces, readPieces, readScalar, sampleCurve, splitPieces } from './core/values.js';
21
- export type { CurvePiece, ScalarGenerator, ScalarMode, ScalarValueState } from './core/values.js';
25
+ export type { AnyGenerator, CurvePiece, ScalarGenerator, ScalarMode, ScalarValueState } from './core/values.js';
@@ -1,16 +1,23 @@
1
1
  import React from 'react';
2
2
  import { type AssetDragPayload } from '../core/assetTree.js';
3
3
  import { type AssetLoadProgress, type EffectAsset } from '../core/assets.js';
4
+ /** Props of {@link AssetsPanel}. */
4
5
  export interface AssetsPanelProps {
6
+ /** The assets, shown as a folder tree by their paths. */
5
7
  assets: EffectAsset[];
6
8
  /** Scene instance count per asset id. */
7
9
  instanceCounts: ReadonlyMap<number, number>;
10
+ /** Import progress while importing; the import buttons are disabled meanwhile. */
8
11
  loading: AssetLoadProgress | null;
12
+ /** The hidden folder input, clicked by hosts whose browser has no directory picker. */
9
13
  folderInputRef: React.RefObject<HTMLInputElement | null>;
14
+ /** Imports picked files. */
10
15
  onImportFiles: (files: FileList | File[]) => void;
16
+ /** Opens a folder to import. */
11
17
  onImportFolder: () => void;
12
18
  /** Adds a new instance of each asset in the payload to the scene. */
13
19
  onAddToScene: (payload: AssetDragPayload) => void;
20
+ /** Deletes the assets in the payload. */
14
21
  onDelete: (payload: AssetDragPayload) => void;
15
22
  /** Opens the asset on its own to edit it (double-click). Without it, double-click adds to the scene. */
16
23
  onOpenPrefab?: (asset: EffectAsset) => void;
@@ -0,0 +1,21 @@
1
+ import { PiecewiseBezier, RandomBetweenCurves, Vector3Function } from 'babylon.quarks';
2
+ import React from 'react';
3
+ import { AnyGenerator } from '../core/values.js';
4
+ /** One curve or two, for one axis or for all of them. */
5
+ type AxisCurve = PiecewiseBezier | RandomBetweenCurves;
6
+ /** Props of an {@link AxesCurveField}. */
7
+ export interface AxesCurveFieldProps {
8
+ /** The generator shown: one curve (or two) for every axis, or a `Vector3Function` per axis. */
9
+ generator: AnyGenerator | undefined;
10
+ /** Top of the curve editors' value range. */
11
+ maxValue?: number;
12
+ /** Receives the edited generator. */
13
+ onChange: (generator: AxisCurve | Vector3Function) => void;
14
+ }
15
+ /**
16
+ * A curve over life with Unity's Separate Axes switch: off, one curve (or two) for all axes; on, a
17
+ * {@link Vector3Function} with its own for X, Y and Z. Switching on gives every axis the shared
18
+ * curve; switching off keeps the X one.
19
+ */
20
+ export declare function AxesCurveField(props: AxesCurveFieldProps): React.ReactElement;
21
+ export {};
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { IntervalValue, RandomBetweenCurves, Vector3Function } from 'babylon.quarks';
3
+ import { withCurves } from '../core/values.js';
4
+ import { CheckboxField, Row } from './fields.js';
5
+ import { LifeCurveField } from './LifeCurveField.js';
6
+ const captionStyle = { fontSize: 10.5, opacity: 0.6, marginTop: 6 };
7
+ /**
8
+ * The curve or curves `generator` holds, as a fresh copy: a constant as a flat curve, a random
9
+ * range as two flat ones.
10
+ */
11
+ function curveOf(generator) {
12
+ const two = generator instanceof RandomBetweenCurves || generator instanceof IntervalValue;
13
+ return withCurves(generator, two ? 'curves' : 'curve');
14
+ }
15
+ /**
16
+ * A curve over life with Unity's Separate Axes switch: off, one curve (or two) for all axes; on, a
17
+ * {@link Vector3Function} with its own for X, Y and Z. Switching on gives every axis the shared
18
+ * curve; switching off keeps the X one.
19
+ */
20
+ export function AxesCurveField(props) {
21
+ const { generator } = props;
22
+ const axes = generator instanceof Vector3Function ? generator : undefined;
23
+ const toggle = (on) => props.onChange(on ? new Vector3Function(curveOf(generator), curveOf(generator), curveOf(generator)) : curveOf(axes?.x));
24
+ return (_jsxs(_Fragment, { children: [_jsx(Row, { label: "Separate axes", hint: "A curve for each of X, Y and Z, as Unity's Separate Axes.", children: _jsx(CheckboxField, { value: axes !== undefined, onChange: toggle }) }), axes === undefined ? (_jsx(LifeCurveField, { generator: generator, maxValue: props.maxValue, onChange: props.onChange })) : (['x', 'y', 'z'].map((axis) => (_jsxs("div", { children: [_jsx("div", { style: captionStyle, children: axis.toUpperCase() }), _jsx(LifeCurveField, { generator: axes[axis], maxValue: props.maxValue, onChange: (curve) => props.onChange(new Vector3Function(axis === 'x' ? curve : axes.x, axis === 'y' ? curve : axes.y, axis === 'z' ? curve : axes.z)) })] }, axis))))] }));
25
+ }
@@ -1,13 +1,20 @@
1
1
  import React from 'react';
2
+ /** One button of a {@link ChoiceDialog}. */
2
3
  export interface ChoiceDialogAction {
4
+ /** The button's text. */
3
5
  label: string;
6
+ /** Runs when the button is pressed. */
4
7
  onSelect: () => void;
5
8
  /** Accent button — also what Enter picks. */
6
9
  primary?: boolean;
10
+ /** Shows the button in red, for a destructive choice. */
7
11
  danger?: boolean;
8
12
  }
13
+ /** Props of {@link ChoiceDialog}. */
9
14
  export interface ChoiceDialogProps {
15
+ /** The question. */
10
16
  title: string;
17
+ /** What the choice means. */
11
18
  message: React.ReactNode;
12
19
  /** Shown left to right; put the cancel action first. */
13
20
  actions: ChoiceDialogAction[];
@@ -1,15 +1,24 @@
1
1
  import React from 'react';
2
+ /** One entry of a {@link ContextMenu}. */
2
3
  export interface ContextMenuItem {
4
+ /** The entry's text. */
3
5
  label: string;
6
+ /** Runs when the entry is picked; the menu closes first. */
4
7
  onSelect: () => void;
8
+ /** Shows the entry in red, for a destructive action. */
5
9
  danger?: boolean;
10
+ /** Shows the entry greyed out and unpickable. */
6
11
  disabled?: boolean;
7
12
  }
13
+ /** Props of {@link ContextMenu}. */
8
14
  export interface ContextMenuProps {
9
- /** Client coordinates of the right-click. */
15
+ /** Client X of the right-click. */
10
16
  x: number;
17
+ /** Client Y of the right-click. */
11
18
  y: number;
19
+ /** The entries, top to bottom. */
12
20
  items: ContextMenuItem[];
21
+ /** Closes the menu: the host stops rendering it. */
13
22
  onClose: () => void;
14
23
  }
15
24
  /** Right-click menu at the pointer; closes on outside click, Escape, scroll or window blur. */
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import type { ProjectStore } from '../core/projectStore.js';
3
3
  import { type EffectEditorHostProps } from './EffectEditorHost.js';
4
+ /** Props of {@link EditorWorkspace}: the editor's, plus where projects live. */
4
5
  export interface EditorWorkspaceProps extends Omit<EffectEditorHostProps, 'project' | 'onExitProject' | 'effectJson'> {
5
6
  /** Where projects are kept; defaults to the browser's IndexedDB. */
6
7
  store?: ProjectStore;
@@ -13,9 +14,15 @@ export interface EditorWorkspaceProps extends Omit<EffectEditorHostProps, 'proje
13
14
  export declare function EditorWorkspace(props: EditorWorkspaceProps): React.ReactElement;
14
15
  /** Imperative entry point, like QuarksEffectEditor.Show, for the project-based workspace. */
15
16
  export declare const QuarksEditorWorkspace: {
17
+ /**
18
+ * Mounts an {@link EditorWorkspace} into `hostElement`.
19
+ *
20
+ * @returns A handle whose `dispose()` unmounts it.
21
+ */
16
22
  Show(options: EditorWorkspaceProps & {
17
23
  hostElement: HTMLElement;
18
24
  }): {
25
+ /** Unmounts the workspace. */
19
26
  dispose: () => void;
20
27
  };
21
28
  };
@@ -12,10 +12,8 @@ export function EditorWorkspace(props) {
12
12
  const [project, setProject] = useState(null);
13
13
  const [resolving, setResolving] = useState(Boolean(projectId));
14
14
  useEffect(() => {
15
- if (!projectId) {
16
- setResolving(false);
15
+ if (!projectId)
17
16
  return;
18
- }
19
17
  let cancelled = false;
20
18
  void store.getProject(projectId).then((meta) => {
21
19
  if (cancelled)
@@ -54,6 +52,11 @@ export function EditorWorkspace(props) {
54
52
  }
55
53
  /** Imperative entry point, like QuarksEffectEditor.Show, for the project-based workspace. */
56
54
  export const QuarksEditorWorkspace = {
55
+ /**
56
+ * Mounts an {@link EditorWorkspace} into `hostElement`.
57
+ *
58
+ * @returns A handle whose `dispose()` unmounts it.
59
+ */
57
60
  Show(options) {
58
61
  const { hostElement, ...props } = options;
59
62
  const root = createRoot(hostElement);
@@ -1,19 +1,25 @@
1
+ import type { Texture } from '@babylonjs/core/Materials/Textures/texture.js';
1
2
  import type { TransformNode } from '@babylonjs/core/Meshes/transformNode.js';
2
3
  import type { ParticleSystem } from 'babylon.quarks';
3
4
  import React from 'react';
4
5
  import { EffectBinding } from '../core/binding.js';
5
6
  import type { GeometryData, GeometryOption, TextureOption } from './modules.js';
7
+ /** Props of {@link EffectEditor}. */
6
8
  export interface EffectEditorProps {
9
+ /** The effect the modules edit. */
7
10
  binding: EffectBinding;
8
11
  /** Currently selected system, lifted up so the timeline panel and this module stack agree. */
9
12
  selectedSystem: ParticleSystem;
13
+ /** The node the Object module edits the transform of. */
10
14
  gizmoTargetNode: TransformNode;
15
+ /** The Object module's title for that node. */
11
16
  gizmoTargetLabel: string;
12
17
  /** Texture presets offered in the Renderer module (host supplies the loader). */
13
18
  textureOptions?: TextureOption[];
14
19
  /** Mesh presets offered for the Mesh render mode (host supplies the geometry buffers). */
15
20
  geometryOptions?: GeometryOption[];
16
- resolveTexture?: (url: string) => unknown;
21
+ /** Host texture loader for picked URLs; a plain `Texture` is made without it. */
22
+ resolveTexture?: (url: string) => Texture | null | undefined;
17
23
  /** Host loader for "Load from file…" in the Mesh render mode (e.g. a GLB importer). */
18
24
  resolveGeometry?: (file: File) => Promise<GeometryData>;
19
25
  }
@@ -22,4 +28,4 @@ export interface EffectEditorProps {
22
28
  * Embed it anywhere React runs — the standalone examples page or a BabylonJS Editor plugin.
23
29
  * Module order mirrors Unity's Particle System inspector.
24
30
  */
25
- export declare const EffectEditor: React.NamedExoticComponent<EffectEditorProps>;
31
+ export declare const EffectEditor: React.MemoExoticComponent<(props: EffectEditorProps) => React.JSX.Element>;
@@ -1,67 +1,71 @@
1
- import '@babylonjs/core/Culling/ray.js';
2
- import { Scene } from '@babylonjs/core/scene.js';
3
- import { BatchedRenderer } from 'babylon.quarks';
1
+ import type { AbstractEngine } from '@babylonjs/core/Engines/abstractEngine.js';
2
+ import type { Texture } from '@babylonjs/core/Materials/Textures/texture.js';
3
+ import type { Scene } from '@babylonjs/core/scene.js';
4
4
  import React from 'react';
5
- import { EffectBinding } from '../core/binding.js';
6
- import { EffectHistory } from '../core/history.js';
7
- import type { ProjectStore } from '../core/projectStore.js';
5
+ import { type EditorProject, type EffectEditorHostHandle } from '../core/editorSession.js';
8
6
  import type { GeometryData, GeometryOption, TextureOption } from './modules.js';
9
- export interface EffectEditorHostHandle {
10
- binding: EffectBinding;
11
- history: EffectHistory;
12
- scene: Scene;
13
- renderer: BatchedRenderer;
14
- /** Replaces the scene with this effect (it is also added to Assets). */
15
- importJson: (json: unknown) => void;
16
- /** Imports effect files (and the texture/geometry files they reference) into Assets. */
17
- importAssets: (files: FileList | File[]) => Promise<void>;
18
- /** @deprecated Renamed to `importAssets`. */
19
- importGallery: (files: FileList | File[]) => Promise<void>;
20
- exportJson: () => string;
21
- }
7
+ export type { EditorProject, EffectEditorHostHandle } from '../core/editorSession.js';
8
+ /** Props of {@link EffectEditorHost}. */
22
9
  export interface EffectEditorHostProps {
23
10
  /** Quarks JSON export to open; a default effect is created when omitted. */
24
11
  effectJson?: unknown;
25
12
  /** When set, a Save button appears and receives the serialized effect. */
26
13
  onSave?: (json: string) => void;
27
- /** Called once the engine/scene/binding are live (and after every import). */
14
+ /** Called once the engine, scene and binding are live, and again whenever the focused effect changes. */
28
15
  onReady?: (handle: EffectEditorHostHandle) => void;
16
+ /** Inspector header shown while the focused effect has no name. */
29
17
  title?: string;
18
+ /** Texture presets offered in the Renderer module; the first is the default effect's texture. */
30
19
  textureOptions?: TextureOption[];
31
20
  /** Mesh presets for the Mesh render mode (e.g. asset meshes from a host editor). */
32
21
  geometryOptions?: GeometryOption[];
33
- resolveTexture?: (url: string, scene: Scene) => unknown;
22
+ /** Host texture loader for texture URLs picked in the editor. */
23
+ resolveTexture?: (url: string, scene: Scene) => Texture | null | undefined;
34
24
  /** Host loader for the Mesh "Load from file…" option (e.g. GLB via @babylonjs/loaders). */
35
25
  resolveGeometry?: (file: File, scene: Scene) => Promise<GeometryData>;
36
26
  /** Optional link rendered in the viewport's top-left corner (e.g. back to a host gallery page). */
37
27
  backLink?: {
28
+ /** Where the link goes. */
38
29
  href: string;
30
+ /** The link's text; a leading `←` is dropped, an arrow icon is drawn instead. */
39
31
  label: string;
40
32
  };
41
33
  /**
42
34
  * Project to edit: its assets load from and save to `store`, and the scene is saved with
43
35
  * Save / Ctrl+S. Without it the editor works in memory only.
44
36
  */
45
- project?: {
46
- store: ProjectStore;
47
- id: string;
48
- name: string;
49
- };
37
+ project?: EditorProject;
50
38
  /** Shows a "← Projects" button; called once unsaved scene changes are dealt with. */
51
39
  onExitProject?: () => void;
40
+ /**
41
+ * Builds the engine the editor draws with, on the canvas it is given, for example a
42
+ * `WebGPUEngine` after `await engine.initAsync()`. It may return a promise. It must build a new
43
+ * engine on every call: the editor disposes the engine when it unmounts, and React may mount
44
+ * it more than once (StrictMode). Defaults to a WebGL `Engine` with antialiasing.
45
+ */
46
+ createEngine?: (canvas: HTMLCanvasElement) => AbstractEngine | Promise<AbstractEngine>;
52
47
  }
53
48
  /**
54
49
  * Self-contained effect editor: live Babylon preview, a Scene list of the effects placed in it,
55
50
  * an inspector, and a bottom dock with the timeline and the Assets browser. Assets are effect
56
51
  * files; dragging one into the viewport instantiates an independent copy, as many times as needed.
57
- * Designed for embedding — see QuarksEffectEditor.Show for the NME-style entry point.
52
+ * Designed for embedding — see {@link QuarksEffectEditor}.Show for the NME-style entry point.
53
+ *
54
+ * The scene, Assets, prefab mode and undo live in an {@link EditorSession}; this component is its
55
+ * React view.
58
56
  */
59
- export declare function EffectEditorHost(props: EffectEditorHostProps): React.JSX.Element;
57
+ export declare function EffectEditorHost(props: EffectEditorHostProps): React.ReactElement;
60
58
  /** NME-style imperative entry point for hosts like BabylonJS Editor windows. */
61
59
  export declare const QuarksEffectEditor: {
60
+ /**
61
+ * Mounts an {@link EffectEditorHost} into `hostElement`.
62
+ *
63
+ * @returns A handle whose `dispose()` unmounts the editor and frees its engine.
64
+ */
62
65
  Show(options: EffectEditorHostProps & {
63
66
  hostElement: HTMLElement;
64
67
  }): {
68
+ /** Unmounts the editor. */
65
69
  dispose: () => void;
66
70
  };
67
71
  };