babylon.quarks-editor 0.26.0 → 0.28.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 (211) hide show
  1. package/CHANGELOG.md +134 -0
  2. package/README.md +69 -9
  3. package/dist/core/assetDragSession.d.ts +1 -1
  4. package/dist/core/assetTree.d.ts +6 -1
  5. package/dist/core/assets.d.ts +7 -0
  6. package/dist/core/assets.js +11 -7
  7. package/dist/core/binding.d.ts +11 -2
  8. package/dist/core/binding.js +9 -1
  9. package/dist/core/collision.d.ts +4 -0
  10. package/dist/core/collision.js +4 -0
  11. package/dist/core/colors.d.ts +16 -0
  12. package/dist/core/colors.js +8 -0
  13. package/dist/core/defaultEffect.d.ts +5 -4
  14. package/dist/core/defaultEffect.js +4 -4
  15. package/dist/core/editorScene.d.ts +1 -1
  16. package/dist/core/editorSession.d.ts +229 -0
  17. package/dist/core/editorSession.js +983 -0
  18. package/dist/core/effectTree.d.ts +6 -1
  19. package/dist/core/effectTree.js +13 -88
  20. package/dist/core/emitterShapeWireframe.d.ts +2 -2
  21. package/dist/core/emitterShapeWireframe.js +45 -13
  22. package/dist/core/galleryDropMarker.d.ts +2 -2
  23. package/dist/core/galleryDropMarker.js +6 -6
  24. package/dist/core/galleryLayout.d.ts +3 -3
  25. package/dist/core/galleryLayout.js +1 -1
  26. package/dist/core/galleryPlayback.d.ts +1 -1
  27. package/dist/core/galleryPlayback.js +2 -1
  28. package/dist/core/galleryScene.d.ts +4 -4
  29. package/dist/core/galleryScene.js +3 -3
  30. package/dist/core/gallerySelectionMarker.d.ts +4 -4
  31. package/dist/core/gallerySelectionMarker.js +5 -5
  32. package/dist/core/groups.d.ts +1 -1
  33. package/dist/core/groups.js +1 -1
  34. package/dist/core/history.d.ts +7 -1
  35. package/dist/core/history.js +4 -0
  36. package/dist/core/indexedDbProjectStore.d.ts +14 -1
  37. package/dist/core/indexedDbProjectStore.js +13 -0
  38. package/dist/core/lineShaders.d.ts +2 -2
  39. package/dist/core/lineShaders.js +2 -2
  40. package/dist/core/loadEffect.d.ts +2 -2
  41. package/dist/core/loadEffect.js +1 -1
  42. package/dist/core/material.d.ts +12 -2
  43. package/dist/core/pickAssetFiles.js +1 -1
  44. package/dist/core/playback.d.ts +6 -0
  45. package/dist/core/projectStore.d.ts +26 -0
  46. package/dist/core/projectStore.js +1 -0
  47. package/dist/core/sceneEntries.d.ts +7 -4
  48. package/dist/core/sceneEntries.js +2 -2
  49. package/dist/core/scrub.d.ts +2 -2
  50. package/dist/core/scrub.js +2 -2
  51. package/dist/core/shapes.d.ts +25 -1
  52. package/dist/core/shapes.js +5 -0
  53. package/dist/core/systems.d.ts +4 -2
  54. package/dist/core/systems.js +3 -1
  55. package/dist/core/textureExport.d.ts +1 -1
  56. package/dist/core/timeline.d.ts +25 -5
  57. package/dist/core/transport.d.ts +3 -3
  58. package/dist/core/transport.js +2 -2
  59. package/dist/core/values.d.ts +21 -4
  60. package/dist/core/values.js +5 -0
  61. package/dist/core/viewportPicker.d.ts +4 -4
  62. package/dist/core/viewportPicker.js +8 -7
  63. package/dist/index.d.ts +25 -21
  64. package/dist/index.js +12 -12
  65. package/dist/react/AssetsPanel.d.ts +9 -2
  66. package/dist/react/AssetsPanel.js +6 -6
  67. package/dist/react/ChoiceDialog.d.ts +7 -0
  68. package/dist/react/ChoiceDialog.js +1 -1
  69. package/dist/react/ContextMenu.d.ts +10 -1
  70. package/dist/react/ContextMenu.js +1 -1
  71. package/dist/react/CurveEditor.d.ts +1 -1
  72. package/dist/react/CurveEditor.js +2 -2
  73. package/dist/react/EditorWorkspace.d.ts +9 -2
  74. package/dist/react/EditorWorkspace.js +10 -7
  75. package/dist/react/EffectEditor.d.ts +11 -5
  76. package/dist/react/EffectEditor.js +7 -7
  77. package/dist/react/EffectEditorHost.d.ts +33 -29
  78. package/dist/react/EffectEditorHost.js +247 -1674
  79. package/dist/react/EffectTreeView.d.ts +4 -1
  80. package/dist/react/EffectTreeView.js +3 -3
  81. package/dist/react/GradientEditor.d.ts +1 -1
  82. package/dist/react/GradientEditor.js +3 -3
  83. package/dist/react/InspectorInfoIcon.js +3 -3
  84. package/dist/react/InspectorLabel.js +3 -3
  85. package/dist/react/MessageDialog.d.ts +5 -0
  86. package/dist/react/MessageDialog.js +1 -1
  87. package/dist/react/ModuleSection.d.ts +10 -0
  88. package/dist/react/ModuleSection.js +8 -5
  89. package/dist/react/ObjectModule.d.ts +1 -1
  90. package/dist/react/ObjectModule.js +4 -4
  91. package/dist/react/ProjectsPage.d.ts +7 -1
  92. package/dist/react/ProjectsPage.js +33 -17
  93. package/dist/react/PromptDialog.d.ts +7 -0
  94. package/dist/react/PromptDialog.js +1 -1
  95. package/dist/react/ScenePanel.d.ts +9 -2
  96. package/dist/react/ScenePanel.js +12 -10
  97. package/dist/react/SubEmittersModule.d.ts +1 -1
  98. package/dist/react/SubEmittersModule.js +5 -5
  99. package/dist/react/TextureSelect.d.ts +24 -0
  100. package/dist/react/TextureSelect.js +74 -0
  101. package/dist/react/TextureUVField.d.ts +14 -0
  102. package/dist/react/TextureUVField.js +23 -0
  103. package/dist/react/TimelinePanel.d.ts +20 -7
  104. package/dist/react/TimelinePanel.js +21 -15
  105. package/dist/react/TimelineTrackRow.d.ts +13 -3
  106. package/dist/react/TimelineTrackRow.js +6 -6
  107. package/dist/react/TransportBar.d.ts +13 -4
  108. package/dist/react/TransportBar.js +3 -3
  109. package/dist/react/ValueField.d.ts +32 -5
  110. package/dist/react/ValueField.js +22 -11
  111. package/dist/react/behaviorModules.d.ts +19 -6
  112. package/dist/react/behaviorModules.js +68 -17
  113. package/dist/react/fields.d.ts +9 -0
  114. package/dist/react/fields.js +6 -3
  115. package/dist/react/host/BottomDocks.d.ts +36 -0
  116. package/dist/react/host/BottomDocks.js +63 -0
  117. package/dist/react/host/HostDialogs.d.ts +50 -0
  118. package/dist/react/host/HostDialogs.js +113 -0
  119. package/dist/react/host/InspectorSidebar.d.ts +32 -0
  120. package/dist/react/host/InspectorSidebar.js +59 -0
  121. package/dist/react/host/SceneSidebar.d.ts +53 -0
  122. package/dist/react/host/SceneSidebar.js +62 -0
  123. package/dist/react/host/ViewportPane.d.ts +56 -0
  124. package/dist/react/host/ViewportPane.js +91 -0
  125. package/dist/react/host/hostHooks.d.ts +44 -0
  126. package/dist/react/host/hostHooks.js +164 -0
  127. package/dist/react/host/hostStyles.d.ts +21 -0
  128. package/dist/react/host/hostStyles.js +57 -0
  129. package/dist/react/index.d.ts +42 -38
  130. package/dist/react/index.js +25 -23
  131. package/dist/react/inspectorHints.js +31 -0
  132. package/dist/react/modules.d.ts +19 -6
  133. package/dist/react/modules.js +134 -117
  134. package/dist/react/theme.d.ts +15 -0
  135. package/dist/react/theme.js +15 -0
  136. package/dist/session.d.ts +9 -0
  137. package/dist/session.js +8 -0
  138. package/package.json +18 -11
  139. package/src/core/assetDragSession.ts +1 -1
  140. package/src/core/assetTree.ts +15 -2
  141. package/src/core/assets.ts +25 -10
  142. package/src/core/binding.ts +12 -3
  143. package/src/core/collision.ts +4 -0
  144. package/src/core/colors.ts +21 -1
  145. package/src/core/defaultEffect.ts +8 -7
  146. package/src/core/editorScene.ts +1 -1
  147. package/src/core/editorSession.ts +1299 -0
  148. package/src/core/effectTree.ts +18 -101
  149. package/src/core/emitterShapeWireframe.ts +48 -16
  150. package/src/core/galleryDropMarker.ts +8 -8
  151. package/src/core/galleryLayout.ts +4 -4
  152. package/src/core/galleryPlayback.ts +3 -2
  153. package/src/core/galleryScene.ts +5 -5
  154. package/src/core/gallerySelectionMarker.ts +7 -7
  155. package/src/core/groups.ts +1 -1
  156. package/src/core/history.ts +7 -1
  157. package/src/core/indexedDbProjectStore.ts +14 -1
  158. package/src/core/lineShaders.ts +2 -2
  159. package/src/core/loadEffect.ts +4 -4
  160. package/src/core/material.ts +14 -4
  161. package/src/core/pickAssetFiles.ts +2 -2
  162. package/src/core/playback.ts +6 -0
  163. package/src/core/projectStore.ts +26 -0
  164. package/src/core/sceneEntries.ts +8 -5
  165. package/src/core/scrub.ts +3 -3
  166. package/src/core/shapes.ts +29 -2
  167. package/src/core/systems.ts +11 -4
  168. package/src/core/textureExport.ts +1 -1
  169. package/src/core/timeline.ts +43 -18
  170. package/src/core/transport.ts +4 -4
  171. package/src/core/values.ts +20 -4
  172. package/src/core/viewportPicker.ts +12 -11
  173. package/src/index.ts +25 -21
  174. package/src/react/AssetsPanel.tsx +13 -6
  175. package/src/react/ChoiceDialog.tsx +8 -1
  176. package/src/react/ContextMenu.tsx +11 -2
  177. package/src/react/CurveEditor.tsx +2 -2
  178. package/src/react/EditorWorkspace.tsx +16 -10
  179. package/src/react/EffectEditor.tsx +17 -9
  180. package/src/react/EffectEditorHost.tsx +415 -2354
  181. package/src/react/EffectTreeView.tsx +7 -4
  182. package/src/react/GradientEditor.tsx +3 -3
  183. package/src/react/InspectorInfoIcon.tsx +3 -3
  184. package/src/react/InspectorLabel.tsx +3 -3
  185. package/src/react/MessageDialog.tsx +6 -1
  186. package/src/react/ModuleSection.tsx +15 -4
  187. package/src/react/ObjectModule.tsx +5 -5
  188. package/src/react/ProjectsPage.tsx +44 -17
  189. package/src/react/PromptDialog.tsx +8 -1
  190. package/src/react/ScenePanel.tsx +21 -12
  191. package/src/react/SubEmittersModule.tsx +6 -6
  192. package/src/react/TextureSelect.tsx +118 -0
  193. package/src/react/TextureUVField.tsx +62 -0
  194. package/src/react/TimelinePanel.tsx +40 -21
  195. package/src/react/TimelineTrackRow.tsx +19 -9
  196. package/src/react/TransportBar.tsx +16 -7
  197. package/src/react/ValueField.tsx +60 -16
  198. package/src/react/behaviorModules.tsx +240 -55
  199. package/src/react/fields.tsx +22 -4
  200. package/src/react/host/BottomDocks.tsx +178 -0
  201. package/src/react/host/HostDialogs.tsx +228 -0
  202. package/src/react/host/InspectorSidebar.tsx +147 -0
  203. package/src/react/host/SceneSidebar.tsx +227 -0
  204. package/src/react/host/ViewportPane.tsx +300 -0
  205. package/src/react/host/hostHooks.ts +198 -0
  206. package/src/react/host/hostStyles.ts +65 -0
  207. package/src/react/index.ts +43 -38
  208. package/src/react/inspectorHints.ts +40 -0
  209. package/src/react/modules.tsx +440 -170
  210. package/src/react/theme.ts +15 -0
  211. package/src/session.ts +17 -0
@@ -1,12 +1,17 @@
1
- import { TransformNode } from '@babylonjs/core/Meshes/transformNode';
1
+ import { TransformNode } from '@babylonjs/core/Meshes/transformNode.js';
2
2
  import type { ParticleSystem } from 'babylon.quarks';
3
3
  /** A node in the editor's effect hierarchy: a Group (organizational) or a ParticleEmitter. */
4
+ /** One node of an effect's tree: a group, or an emitter with its system. */
4
5
  export interface EffectTreeNode {
6
+ /** The node. */
5
7
  node: TransformNode;
8
+ /** The node's name, or a readable stand-in when it has none. */
6
9
  name: string;
7
10
  /** Present when the node is a ParticleEmitter. */
8
11
  system?: ParticleSystem;
12
+ /** How deep the node sits; the root is 0. */
9
13
  depth: number;
14
+ /** The node's child groups and emitters. */
10
15
  children: EffectTreeNode[];
11
16
  }
12
17
  /** Walks a loaded root TransformNode into the editor tree (groups + emitters). */
@@ -1,7 +1,6 @@
1
- import { Matrix, Quaternion } from '@babylonjs/core/Maths/math.vector';
2
- import { TransformNode } from '@babylonjs/core/Meshes/transformNode';
3
- import { ParticleEmitter } from 'babylon.quarks';
4
- import { isEditorSceneNode } from './editorScene';
1
+ import { TransformNode } from '@babylonjs/core/Meshes/transformNode.js';
2
+ import { ParticleEmitter, serializeEffect } from 'babylon.quarks';
3
+ import { isEditorSceneNode } from './editorScene.js';
5
4
  /** Walks a loaded root TransformNode into the editor tree (groups + emitters). */
6
5
  export function buildEffectTree(root, depth = 0) {
7
6
  const system = root instanceof ParticleEmitter ? root.system : undefined;
@@ -31,100 +30,26 @@ export function collectSystems(tree) {
31
30
  walk(tree);
32
31
  return out;
33
32
  }
34
- function localMatrix(node) {
35
- const rotation = node.rotationQuaternion ?? Quaternion.FromEulerVector(node.rotation);
36
- return Matrix.Compose(node.scaling, rotation, node.position).asArray().slice();
37
- }
38
- function serializeNode(tree, meta, fallbackUuid) {
39
- // Emitter nodes must serialize with the ParticleEmitter's own `.uuid` — that is exactly
40
- // what EmitSubParticleSystem.toJSON writes as its target reference, so the two match and
41
- // QuarksLoader.linkReferences can re-resolve sub-emitters on reimport.
42
- const uuid = tree.system
43
- ? tree.node.uuid
44
- : (tree.node._quarksUUID ?? fallbackUuid);
45
- const children = tree.children.map((child, i) => serializeNode(child, meta, `${fallbackUuid}-${i}`));
46
- const base = {
47
- uuid,
48
- name: tree.name,
49
- layers: 1,
50
- matrix: localMatrix(tree.node),
51
- children: children.length > 0 ? children : undefined,
52
- };
53
- if (tree.system) {
54
- return {
55
- ...base,
56
- type: 'ParticleEmitter',
57
- ps: tree.system.toJSON(meta, { useUrlForImage: true }),
58
- };
59
- }
60
- return { ...base, type: 'Group' };
33
+ /** Writes the effect under `roots`, leaving out the editor's own helper nodes. */
34
+ function serialize(roots, name) {
35
+ const json = serializeEffect(roots, {
36
+ name,
37
+ generator: 'babylon.quarks-editor',
38
+ filter: (node) => !isEditorSceneNode(node),
39
+ });
40
+ return JSON.stringify(json, null, 2);
61
41
  }
62
42
  /**
63
43
  * Serializes an effect tree into the Quarks JSON envelope understood by QuarksLoader,
64
44
  * preserving the group/emitter hierarchy (not flattened).
65
45
  */
66
46
  export function serializeEffectTree(root, name = 'effect') {
67
- const meta = { textures: {}, materials: {}, geometries: {} };
68
- const object = serializeNode(buildEffectTree(root), meta, `${name}-node`);
69
- return finishEnvelope(object, meta);
47
+ return serialize(root, name);
70
48
  }
71
49
  /**
72
50
  * Serializes several effect roots as children of a synthetic group — used when an effect
73
51
  * spans multiple top-level trees (e.g. a demo running several loaded effects at once).
74
52
  */
75
53
  export function serializeEffectForest(roots, name = 'effect') {
76
- if (roots.length === 1) {
77
- return serializeEffectTree(roots[0], name);
78
- }
79
- const meta = { textures: {}, materials: {}, geometries: {} };
80
- const children = roots.map((root, i) => serializeNode(buildEffectTree(root), meta, `${name}-node-${i}`));
81
- const object = {
82
- uuid: `${name}-root`,
83
- name,
84
- type: 'Group',
85
- layers: 1,
86
- matrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
87
- children,
88
- };
89
- return finishEnvelope(object, meta);
90
- }
91
- /** Flattens accumulated meta and wraps the serialized object into the Quarks envelope. */
92
- function finishEnvelope(object, meta) {
93
- // toJSON stashes live Texture instances and material records in meta; flatten them into
94
- // the serializable images/textures/materials arrays QuarksLoader expects.
95
- const images = [];
96
- const textures = [];
97
- for (const [uuid, tex] of Object.entries(meta.textures)) {
98
- const url = tex?.url ?? tex?.name;
99
- const invertY = typeof tex?.invertY === 'boolean' ? tex.invertY : undefined;
100
- const isEnvAtlas = tex?.name === 'quarksEnvAtlas' || (typeof tex?.name === 'string' && tex.name.endsWith('_envAtlas'));
101
- if (url) {
102
- const imageUuid = `${uuid}-image`;
103
- images.push({ uuid: imageUuid, url });
104
- const entry = {
105
- uuid,
106
- image: imageUuid,
107
- };
108
- if (invertY === false || isEnvAtlas) {
109
- entry.invertY = false;
110
- entry.noMipmap = true;
111
- }
112
- textures.push(entry);
113
- }
114
- else {
115
- textures.push({ uuid });
116
- }
117
- }
118
- const materials = Object.values(meta.materials).map((material) => {
119
- const { sourceMaterial: _sourceMaterial, ...serializable } = material;
120
- return serializable;
121
- });
122
- return JSON.stringify({
123
- metadata: { version: 4.5, type: 'Object3D', generator: 'babylon.quarks-editor' },
124
- geometries: Object.values(meta.geometries),
125
- materials,
126
- textures,
127
- images,
128
- object,
129
- }, null, 2);
54
+ return serialize(roots.length === 1 ? roots[0] : roots, name);
130
55
  }
@@ -1,6 +1,6 @@
1
- import type { Scene } from '@babylonjs/core/scene';
1
+ import type { Scene } from '@babylonjs/core/scene.js';
2
2
  import type { ParticleSystem } from 'babylon.quarks';
3
- import './lineShaders';
3
+ import './lineShaders.js';
4
4
  /**
5
5
  * Optional wireframe overlays for every emitter shape in the current effect.
6
6
  * Geometry follows quarks.core spawn math (local XY spawn plane, +Z emit axis).
@@ -1,10 +1,10 @@
1
- import { Color3 } from '@babylonjs/core/Maths/math.color';
2
- import { Vector3 } from '@babylonjs/core/Maths/math.vector';
3
- import { MeshBuilder } from '@babylonjs/core/Meshes/meshBuilder';
4
- import { TransformNode } from '@babylonjs/core/Meshes/transformNode';
5
- import { EDITOR_SCENE_NODE_METADATA } from './editorScene';
6
- import './lineShaders';
7
- import { getShapeType, readShapeParams } from './shapes';
1
+ import { Color3 } from '@babylonjs/core/Maths/math.color.js';
2
+ import { Vector3 } from '@babylonjs/core/Maths/math.vector.js';
3
+ import { MeshBuilder } from '@babylonjs/core/Meshes/meshBuilder.js';
4
+ import { TransformNode } from '@babylonjs/core/Meshes/transformNode.js';
5
+ import { EDITOR_SCENE_NODE_METADATA } from './editorScene.js';
6
+ import './lineShaders.js';
7
+ import { getShapeType, readShapeParams } from './shapes.js';
8
8
  const LINE_COLOR = new Color3(0.45, 0.82, 1);
9
9
  const DIRECTION_COLOR = new Color3(0.75, 0.92, 1);
10
10
  /**
@@ -204,6 +204,38 @@ function buildEmitterWireframe(system, scene) {
204
204
  }
205
205
  break;
206
206
  }
207
+ case 'box': {
208
+ // Spawn anywhere in the box (volume), on its faces (shell) or along its edges;
209
+ // velocity straight +Z, like Unity's Box shape.
210
+ const halfW = params.width / 2;
211
+ const halfH = params.height / 2;
212
+ const halfD = params.depth / 2;
213
+ lines('wf-box-front', xyRect(params.width, params.height, halfD));
214
+ lines('wf-box-back', xyRect(params.width, params.height, -halfD));
215
+ const corners = [
216
+ [-halfW, -halfH],
217
+ [halfW, -halfH],
218
+ [halfW, halfH],
219
+ [-halfW, halfH],
220
+ ];
221
+ corners.forEach(([x, y], i) => {
222
+ lines(`wf-box-side-${i}`, [new Vector3(x, y, -halfD), new Vector3(x, y, halfD)]);
223
+ });
224
+ const ray = Math.max(halfD * 2, 0.25);
225
+ lines('wf-box-dir', [new Vector3(0, 0, halfD), new Vector3(0, 0, halfD + ray)], DIRECTION_COLOR);
226
+ break;
227
+ }
228
+ case 'edge': {
229
+ // Spawn on a line along X of half-length `radius`; velocity straight +Y.
230
+ const radius = Math.max(params.radius, 0.001);
231
+ lines('wf-edge', [new Vector3(-radius, 0, 0), new Vector3(radius, 0, 0)]);
232
+ const ray = Math.max(radius * 0.5, 0.25);
233
+ for (let i = 0; i <= 4; i++) {
234
+ const x = -radius + (i / 4) * radius * 2;
235
+ lines(`wf-edge-ray-${i}`, [new Vector3(x, 0, 0), new Vector3(x, ray, 0)], DIRECTION_COLOR);
236
+ }
237
+ break;
238
+ }
207
239
  case 'mesh_surface': {
208
240
  const source = params.mesh;
209
241
  if (source && !source.isDisposed()) {
@@ -305,15 +337,15 @@ function xyArc(radius, arc, segments = 48, z = 0) {
305
337
  function arcPoint(radius, theta, z = 0) {
306
338
  return new Vector3(Math.cos(theta) * radius, Math.sin(theta) * radius, z);
307
339
  }
308
- function xyRect(width, height) {
340
+ function xyRect(width, height, z = 0) {
309
341
  const halfW = width / 2;
310
342
  const halfH = height / 2;
311
343
  return [
312
- new Vector3(-halfW, -halfH, 0),
313
- new Vector3(halfW, -halfH, 0),
314
- new Vector3(halfW, halfH, 0),
315
- new Vector3(-halfW, halfH, 0),
316
- new Vector3(-halfW, -halfH, 0),
344
+ new Vector3(-halfW, -halfH, z),
345
+ new Vector3(halfW, -halfH, z),
346
+ new Vector3(halfW, halfH, z),
347
+ new Vector3(-halfW, halfH, z),
348
+ new Vector3(-halfW, -halfH, z),
317
349
  ];
318
350
  }
319
351
  function clampArc(arc) {
@@ -1,5 +1,5 @@
1
- import { Vector3 } from '@babylonjs/core/Maths/math.vector';
2
- import type { Scene } from '@babylonjs/core/scene';
1
+ import { Vector3 } from '@babylonjs/core/Maths/math.vector.js';
2
+ import type { Scene } from '@babylonjs/core/scene.js';
3
3
  /** Ground decal shown while dragging gallery items over the viewport. */
4
4
  export declare class GalleryDropMarker {
5
5
  private readonly root;
@@ -1,9 +1,9 @@
1
- import { StandardMaterial } from '@babylonjs/core/Materials/standardMaterial';
2
- import { Color3 } from '@babylonjs/core/Maths/math.color';
3
- import { Mesh } from '@babylonjs/core/Meshes/mesh';
4
- import { MeshBuilder } from '@babylonjs/core/Meshes/meshBuilder';
5
- import { TransformNode } from '@babylonjs/core/Meshes/transformNode';
6
- import { DEFAULT_GALLERY_SPACING, gallerySpan } from './galleryLayout';
1
+ import { StandardMaterial } from '@babylonjs/core/Materials/standardMaterial.js';
2
+ import { Color3 } from '@babylonjs/core/Maths/math.color.js';
3
+ import { Mesh } from '@babylonjs/core/Meshes/mesh.js';
4
+ import { MeshBuilder } from '@babylonjs/core/Meshes/meshBuilder.js';
5
+ import { TransformNode } from '@babylonjs/core/Meshes/transformNode.js';
6
+ import { DEFAULT_GALLERY_SPACING, gallerySpan } from './galleryLayout.js';
7
7
  /** Ground decal shown while dragging gallery items over the viewport. */
8
8
  export class GalleryDropMarker {
9
9
  constructor(scene) {
@@ -1,6 +1,6 @@
1
- import type { ArcRotateCamera } from '@babylonjs/core/Cameras/arcRotateCamera';
2
- import type { Mesh } from '@babylonjs/core/Meshes/mesh';
3
- import type { TransformNode } from '@babylonjs/core/Meshes/transformNode';
1
+ import type { ArcRotateCamera } from '@babylonjs/core/Cameras/arcRotateCamera.js';
2
+ import type { Mesh } from '@babylonjs/core/Meshes/mesh.js';
3
+ import type { TransformNode } from '@babylonjs/core/Meshes/transformNode.js';
4
4
  /** Default gap between neighbouring effects in a folder gallery import. */
5
5
  export declare const DEFAULT_GALLERY_SPACING = 4;
6
6
  /** Square-grid column count for `count` items. */
@@ -1,4 +1,4 @@
1
- import { Vector3 } from '@babylonjs/core/Maths/math.vector';
1
+ import { Vector3 } from '@babylonjs/core/Maths/math.vector.js';
2
2
  /** Default gap between neighbouring effects in a folder gallery import. */
3
3
  export const DEFAULT_GALLERY_SPACING = 4;
4
4
  /** Square-grid column count for `count` items. */
@@ -1,4 +1,4 @@
1
- import type { SceneEntry } from './sceneEntries';
1
+ import type { SceneEntry } from './sceneEntries.js';
2
2
  /** Freezes every scene entry; unpauses the visible ones (the preview set) when transport is playing. */
3
3
  export declare function applyGalleryPlayback(entries: SceneEntry[], previewRootIds: ReadonlySet<number>, transportPlaying: boolean, focusFinishedRootIds?: ReadonlySet<number>): void;
4
4
  /** Whether any preview effect on stage should keep simulating. */
@@ -20,7 +20,8 @@ export function applyGalleryPlayback(entries, previewRootIds, transportPlaying,
20
20
  continue;
21
21
  }
22
22
  for (const system of entry.systems) {
23
- system.play();
23
+ // Undoes the pause above; a finished effect stays finished.
24
+ system.resume();
24
25
  }
25
26
  }
26
27
  }
@@ -1,8 +1,8 @@
1
- import '@babylonjs/core/Culling/ray';
2
- import { Vector3 } from '@babylonjs/core/Maths/math.vector';
3
- import type { Scene } from '@babylonjs/core/scene';
1
+ import '@babylonjs/core/Culling/ray.js';
2
+ import { Vector3 } from '@babylonjs/core/Maths/math.vector.js';
3
+ import type { Scene } from '@babylonjs/core/scene.js';
4
4
  import type { BatchedRenderer } from 'babylon.quarks';
5
- import type { SceneEntry } from './sceneEntries';
5
+ import type { SceneEntry } from './sceneEntries.js';
6
6
  /** Clears live particles and freezes simulation for one scene entry. */
7
7
  export declare function clearSceneEntrySimulation(entry: SceneEntry, renderer: BatchedRenderer): void;
8
8
  /**
@@ -1,6 +1,6 @@
1
- import '@babylonjs/core/Culling/ray';
2
- import { Vector3 } from '@babylonjs/core/Maths/math.vector';
3
- import { DEFAULT_GALLERY_SPACING, galleryGridPosition } from './galleryLayout';
1
+ import '@babylonjs/core/Culling/ray.js';
2
+ import { Vector3 } from '@babylonjs/core/Maths/math.vector.js';
3
+ import { DEFAULT_GALLERY_SPACING, galleryGridPosition } from './galleryLayout.js';
4
4
  /** Clears live particles and freezes simulation for one scene entry. */
5
5
  export function clearSceneEntrySimulation(entry, renderer) {
6
6
  for (const system of entry.systems) {
@@ -1,7 +1,7 @@
1
- import { Color3 } from '@babylonjs/core/Maths/math.color';
2
- import { TransformNode } from '@babylonjs/core/Meshes/transformNode';
3
- import type { Scene } from '@babylonjs/core/scene';
4
- import './lineShaders';
1
+ import { Color3 } from '@babylonjs/core/Maths/math.color.js';
2
+ import { TransformNode } from '@babylonjs/core/Meshes/transformNode.js';
3
+ import type { Scene } from '@babylonjs/core/scene.js';
4
+ import './lineShaders.js';
5
5
  export declare const GROUND_RING_RADIUS = 1.2;
6
6
  /** Flat ground ring that tracks a transform on the XZ plane. */
7
7
  declare class GroundRingMarker {
@@ -1,8 +1,8 @@
1
- import { Color3 } from '@babylonjs/core/Maths/math.color';
2
- import { Vector3 } from '@babylonjs/core/Maths/math.vector';
3
- import { MeshBuilder } from '@babylonjs/core/Meshes/meshBuilder';
4
- import { TransformNode } from '@babylonjs/core/Meshes/transformNode';
5
- import './lineShaders';
1
+ import { Color3 } from '@babylonjs/core/Maths/math.color.js';
2
+ import { Vector3 } from '@babylonjs/core/Maths/math.vector.js';
3
+ import { MeshBuilder } from '@babylonjs/core/Meshes/meshBuilder.js';
4
+ import { TransformNode } from '@babylonjs/core/Meshes/transformNode.js';
5
+ import './lineShaders.js';
6
6
  export const GROUND_RING_RADIUS = 1.2;
7
7
  const RING_SEGMENTS = 64;
8
8
  const RING_Y = 0.03;
@@ -1,4 +1,4 @@
1
- import { TransformNode } from '@babylonjs/core/Meshes/transformNode';
1
+ import { TransformNode } from '@babylonjs/core/Meshes/transformNode.js';
2
2
  /**
3
3
  * Groups the given nodes under a new organizational TransformNode. Each node's world
4
4
  * transform is preserved via `setParent` — grouping never moves anything visually, it only
@@ -1,4 +1,4 @@
1
- import { TransformNode } from '@babylonjs/core/Meshes/transformNode';
1
+ import { TransformNode } from '@babylonjs/core/Meshes/transformNode.js';
2
2
  /**
3
3
  * Groups the given nodes under a new organizational TransformNode. Each node's world
4
4
  * transform is preserved via `setParent` — grouping never moves anything visually, it only
@@ -1,7 +1,9 @@
1
- import type { EffectBinding } from './binding';
1
+ import type { EffectBinding } from './binding.js';
2
2
  /** Undo/redo stacks of one effect, kept by the host while another effect is focused. */
3
3
  export interface HistoryStacks {
4
+ /** Snapshots to undo to, oldest first. */
4
5
  undo: string[];
6
+ /** Snapshots to redo to, most recent undo last. */
5
7
  redo: string[];
6
8
  }
7
9
  /**
@@ -18,11 +20,15 @@ export declare class EffectHistory {
18
20
  private binding;
19
21
  /** Points the history at the active binding and hooks snapshot capture. */
20
22
  attach(binding: EffectBinding): void;
23
+ /** There is a change to undo. */
21
24
  get canUndo(): boolean;
25
+ /** There is an undone change to redo. */
22
26
  get canRedo(): boolean;
27
+ /** Forgets every snapshot. */
23
28
  clear(): void;
24
29
  /** The current stacks, to hand back to `restoreStacks` when this effect is focused again. */
25
30
  saveStacks(): HistoryStacks;
31
+ /** Puts back stacks from `saveStacks`; undefined starts empty. */
26
32
  restoreStacks(stacks: HistoryStacks | undefined): void;
27
33
  /**
28
34
  * Pushes an undo snapshot. A burst of edits — typing a number, dragging a curve handle or a
@@ -19,12 +19,15 @@ export class EffectHistory {
19
19
  this.binding = binding;
20
20
  binding.onBeforeChange = () => this.capture();
21
21
  }
22
+ /** There is a change to undo. */
22
23
  get canUndo() {
23
24
  return this.undoStack.length > 0;
24
25
  }
26
+ /** There is an undone change to redo. */
25
27
  get canRedo() {
26
28
  return this.redoStack.length > 0;
27
29
  }
30
+ /** Forgets every snapshot. */
28
31
  clear() {
29
32
  this.undoStack = [];
30
33
  this.redoStack = [];
@@ -34,6 +37,7 @@ export class EffectHistory {
34
37
  saveStacks() {
35
38
  return { undo: this.undoStack, redo: this.redoStack };
36
39
  }
40
+ /** Puts back stacks from `saveStacks`; undefined starts empty. */
37
41
  restoreStacks(stacks) {
38
42
  this.undoStack = stacks?.undo ?? [];
39
43
  this.redoStack = stacks?.redo ?? [];
@@ -1,4 +1,4 @@
1
- import type { ProjectMeta, ProjectStore, StorageEstimate, StoredAsset, StoredScene } from './projectStore';
1
+ import type { ProjectMeta, ProjectStore, StorageEstimate, StoredAsset, StoredScene } from './projectStore.js';
2
2
  /**
3
3
  * Projects in the browser's IndexedDB. Texture data URIs are pulled out of the effect JSON into
4
4
  * a per-project file store keyed by content hash — 50 effects sharing a texture store it once —
@@ -11,18 +11,31 @@ export declare class IndexedDbProjectStore implements ProjectStore {
11
11
  /** `${projectId}/${hash}` → data URI, so every effect sharing a file gets the same string. */
12
12
  private readonly dataUrlByFile;
13
13
  private db;
14
+ /** {@inheritDoc ProjectStore.listProjects} */
14
15
  listProjects(): Promise<ProjectMeta[]>;
16
+ /** {@inheritDoc ProjectStore.getProject} */
15
17
  getProject(id: string): Promise<ProjectMeta | null>;
18
+ /** {@inheritDoc ProjectStore.createProject} */
16
19
  createProject(name: string): Promise<ProjectMeta>;
20
+ /** {@inheritDoc ProjectStore.renameProject} */
17
21
  renameProject(id: string, name: string): Promise<void>;
22
+ /** {@inheritDoc ProjectStore.duplicateProject} */
18
23
  duplicateProject(id: string, name: string): Promise<ProjectMeta>;
24
+ /** {@inheritDoc ProjectStore.deleteProject} */
19
25
  deleteProject(id: string): Promise<void>;
26
+ /** {@inheritDoc ProjectStore.loadAssets} */
20
27
  loadAssets(projectId: string): Promise<StoredAsset[]>;
28
+ /** {@inheritDoc ProjectStore.putAssets} */
21
29
  putAssets(projectId: string, assets: StoredAsset[]): Promise<void>;
30
+ /** {@inheritDoc ProjectStore.deleteAssets} */
22
31
  deleteAssets(projectId: string, paths: string[]): Promise<void>;
32
+ /** {@inheritDoc ProjectStore.loadScene} */
23
33
  loadScene(projectId: string): Promise<StoredScene | null>;
34
+ /** {@inheritDoc ProjectStore.saveScene} */
24
35
  saveScene(projectId: string, scene: StoredScene): Promise<void>;
36
+ /** {@inheritDoc ProjectStore.estimate} */
25
37
  estimate(): Promise<StorageEstimate>;
38
+ /** {@inheritDoc ProjectStore.requestPersistence} */
26
39
  requestPersistence(): Promise<boolean>;
27
40
  /** Clones `json` with each texture data URI replaced by a `quarks-file:<hash>` reference. */
28
41
  private pack;
@@ -84,15 +84,18 @@ export class IndexedDbProjectStore {
84
84
  return this.dbPromise;
85
85
  }
86
86
  // ---- projects -------------------------------------------------------------------------
87
+ /** {@inheritDoc ProjectStore.listProjects} */
87
88
  async listProjects() {
88
89
  const db = await this.db();
89
90
  const all = await request(db.transaction('projects').objectStore('projects').getAll());
90
91
  return all.sort((a, b) => b.updatedAt - a.updatedAt);
91
92
  }
93
+ /** {@inheritDoc ProjectStore.getProject} */
92
94
  async getProject(id) {
93
95
  const db = await this.db();
94
96
  return ((await request(db.transaction('projects').objectStore('projects').get(id))) ?? null);
95
97
  }
98
+ /** {@inheritDoc ProjectStore.createProject} */
96
99
  async createProject(name) {
97
100
  const now = Date.now();
98
101
  const meta = { id: newId(), name, createdAt: now, updatedAt: now, assetCount: 0, sizeBytes: 0 };
@@ -102,6 +105,7 @@ export class IndexedDbProjectStore {
102
105
  await done(tx);
103
106
  return meta;
104
107
  }
108
+ /** {@inheritDoc ProjectStore.renameProject} */
105
109
  async renameProject(id, name) {
106
110
  const meta = await this.getProject(id);
107
111
  if (!meta)
@@ -111,6 +115,7 @@ export class IndexedDbProjectStore {
111
115
  tx.objectStore('projects').put({ ...meta, name, updatedAt: Date.now() });
112
116
  await done(tx);
113
117
  }
118
+ /** {@inheritDoc ProjectStore.duplicateProject} */
114
119
  async duplicateProject(id, name) {
115
120
  const source = await this.getProject(id);
116
121
  if (!source)
@@ -135,6 +140,7 @@ export class IndexedDbProjectStore {
135
140
  await done(tx);
136
141
  return meta;
137
142
  }
143
+ /** {@inheritDoc ProjectStore.deleteProject} */
138
144
  async deleteProject(id) {
139
145
  const db = await this.db();
140
146
  const tx = db.transaction(['projects', 'assets', 'files', 'scenes'], 'readwrite');
@@ -152,6 +158,7 @@ export class IndexedDbProjectStore {
152
158
  }
153
159
  }
154
160
  // ---- assets ---------------------------------------------------------------------------
161
+ /** {@inheritDoc ProjectStore.loadAssets} */
155
162
  async loadAssets(projectId) {
156
163
  const db = await this.db();
157
164
  const records = await request(db.transaction('assets').objectStore('assets').index('projectId').getAll(projectId));
@@ -162,6 +169,7 @@ export class IndexedDbProjectStore {
162
169
  missingFiles,
163
170
  })));
164
171
  }
172
+ /** {@inheritDoc ProjectStore.putAssets} */
165
173
  async putAssets(projectId, assets) {
166
174
  const files = new Map();
167
175
  const packed = await Promise.all(assets.map(async (asset) => ({ ...asset, projectId, json: await this.pack(asset.json, files) })));
@@ -173,6 +181,7 @@ export class IndexedDbProjectStore {
173
181
  await done(tx);
174
182
  await this.refresh(projectId);
175
183
  }
184
+ /** {@inheritDoc ProjectStore.deleteAssets} */
176
185
  async deleteAssets(projectId, paths) {
177
186
  const db = await this.db();
178
187
  const tx = db.transaction('assets', 'readwrite');
@@ -182,6 +191,7 @@ export class IndexedDbProjectStore {
182
191
  await this.refresh(projectId);
183
192
  }
184
193
  // ---- scene ----------------------------------------------------------------------------
194
+ /** {@inheritDoc ProjectStore.loadScene} */
185
195
  async loadScene(projectId) {
186
196
  const db = await this.db();
187
197
  const record = await request(db.transaction('scenes').objectStore('scenes').get(projectId));
@@ -190,6 +200,7 @@ export class IndexedDbProjectStore {
190
200
  const entries = await Promise.all(record.entries.map(async (entry) => ({ ...entry, json: await this.unpack(projectId, entry.json) })));
191
201
  return { entries, savedAt: record.savedAt };
192
202
  }
203
+ /** {@inheritDoc ProjectStore.saveScene} */
193
204
  async saveScene(projectId, scene) {
194
205
  const files = new Map();
195
206
  const entries = await Promise.all(scene.entries.map(async (entry) => ({ ...entry, json: await this.pack(entry.json, files) })));
@@ -201,12 +212,14 @@ export class IndexedDbProjectStore {
201
212
  await this.refresh(projectId);
202
213
  }
203
214
  // ---- storage ----------------------------------------------------------------------------
215
+ /** {@inheritDoc ProjectStore.estimate} */
204
216
  async estimate() {
205
217
  const storage = typeof navigator !== 'undefined' ? navigator.storage : undefined;
206
218
  const estimate = storage?.estimate ? await storage.estimate() : undefined;
207
219
  const persisted = storage?.persisted ? await storage.persisted() : false;
208
220
  return { usage: estimate?.usage ?? null, quota: estimate?.quota ?? null, persisted };
209
221
  }
222
+ /** {@inheritDoc ProjectStore.requestPersistence} */
210
223
  async requestPersistence() {
211
224
  const storage = typeof navigator !== 'undefined' ? navigator.storage : undefined;
212
225
  return storage?.persist ? storage.persist() : false;
@@ -1,2 +1,2 @@
1
- import '@babylonjs/core/Shaders/color.fragment';
2
- import '@babylonjs/core/Shaders/color.vertex';
1
+ import '@babylonjs/core/Shaders/color.fragment.js';
2
+ import '@babylonjs/core/Shaders/color.vertex.js';
@@ -3,5 +3,5 @@
3
3
  // Babylon never looks, it falls back to fetching `src/Shaders/color.fragment.fx`, gets the
4
4
  // page's HTML back and the selection rings never render. Registering the shader up front, from
5
5
  // the same module graph as the rest of the editor, makes the lookup succeed either way.
6
- import '@babylonjs/core/Shaders/color.fragment';
7
- import '@babylonjs/core/Shaders/color.vertex';
6
+ import '@babylonjs/core/Shaders/color.fragment.js';
7
+ import '@babylonjs/core/Shaders/color.vertex.js';
@@ -1,5 +1,5 @@
1
- import { TransformNode } from '@babylonjs/core/Meshes/transformNode';
2
- import type { Scene } from '@babylonjs/core/scene';
1
+ import { TransformNode } from '@babylonjs/core/Meshes/transformNode.js';
2
+ import type { Scene } from '@babylonjs/core/scene.js';
3
3
  import type { BatchedRenderer, ParticleSystem } from 'babylon.quarks';
4
4
  export interface LoadEffectOptions {
5
5
  /** Registers systems with the batched renderer (default true). */
@@ -1,4 +1,4 @@
1
- import { TransformNode } from '@babylonjs/core/Meshes/transformNode';
1
+ import { TransformNode } from '@babylonjs/core/Meshes/transformNode.js';
2
2
  import { ParticleEmitter, QuarksLoader, QuarksUtil } from 'babylon.quarks';
3
3
  /** `ParticleEmitter`'s constructor default (see babylon.quarks/src/ParticleEmitter.ts) — the
4
4
  * name every emitter gets when the source JSON didn't give it one of its own. */
@@ -1,13 +1,23 @@
1
+ import type { BaseTexture } from '@babylonjs/core/Materials/Textures/baseTexture.js';
2
+ import type { Texture } from '@babylonjs/core/Materials/Textures/texture.js';
1
3
  import type { ParticleSystem } from 'babylon.quarks';
4
+ /** Material settings the Renderer module changes; fields left out keep their value. */
2
5
  export interface RendererMaterialPatch {
6
+ /** The blend mode, see `ParticleSystem.blending`. */
3
7
  blendMode?: number;
8
+ /** Draws with alpha blending. */
4
9
  transparent?: boolean;
10
+ /** Tests against the depth buffer. */
5
11
  depthTest?: boolean;
12
+ /** Writes to the depth buffer. */
6
13
  depthWrite?: boolean;
14
+ /** Discards pixels whose alpha is below this. */
7
15
  alphaTest?: number;
8
- texture?: unknown;
16
+ /** The main texture, or null for none. */
17
+ texture?: Texture | null;
9
18
  /** A 3×2 cube-face atlas mesh particles reflect, or null for none. */
10
- reflectionAtlas?: unknown;
19
+ reflectionAtlas?: BaseTexture | null;
20
+ /** How strongly mesh particles reflect the atlas, 0..1. */
11
21
  reflectionLevel?: number;
12
22
  }
13
23
  /** Applies renderer material settings and keeps imported QuarksMaterial JSON in sync for export. */
@@ -1,4 +1,4 @@
1
- import { isAssetImportFile, relativePathOf } from './assets';
1
+ import { isAssetImportFile, relativePathOf } from './assets.js';
2
2
  /**
3
3
  * Recursively collects effect `.json` files and the image files they may reference from a
4
4
  * directory handle (File System Access API).
@@ -2,11 +2,17 @@
2
2
  export interface PlaybackState {
3
3
  /** When true, all in-scene preview effects simulate. */
4
4
  stagePlaying: boolean;
5
+ /** Root `uniqueId` of the focused effect, whose time the timeline shows. */
5
6
  focusRootId: number | null;
7
+ /** Each effect's timeline time, in seconds, by root `uniqueId`. */
6
8
  focusElapsedByRoot: Map<number, number>;
9
+ /** Effects parked at their end, by root `uniqueId`; Play restarts them. */
7
10
  focusFinishedByRoot: Set<number>;
11
+ /** Playback speed, 1 for real time. */
8
12
  speed: number;
13
+ /** While paused: advance one frame on the next render. */
9
14
  stepQueued: boolean;
15
+ /** The timeline is being scrubbed; the render loop does not simulate. */
10
16
  scrubbing: boolean;
11
17
  }
12
18
  /** Default playback state for a new editor session. */