babylon.quarks-editor 0.18.1 → 0.20.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.
- package/CHANGELOG.md +28 -0
- package/README.md +6 -6
- package/dist/core/assetDragSession.d.ts +4 -0
- package/dist/core/assetDragSession.js +8 -0
- package/dist/core/assetTree.d.ts +39 -0
- package/dist/core/assetTree.js +112 -0
- package/dist/core/assets.d.ts +56 -0
- package/dist/core/assets.js +207 -0
- package/dist/core/binding.d.ts +6 -1
- package/dist/core/binding.js +7 -0
- package/dist/core/collision.d.ts +4 -5
- package/dist/core/collision.js +4 -5
- package/dist/core/colors.d.ts +1 -1
- package/dist/core/defaultEffect.d.ts +4 -5
- package/dist/core/defaultEffect.js +5 -14
- package/dist/core/effectTree.d.ts +1 -1
- package/dist/core/effectTree.js +18 -4
- package/dist/core/emitterShapeWireframe.d.ts +2 -1
- package/dist/core/emitterShapeWireframe.js +2 -1
- package/dist/core/galleryDropMarker.js +1 -1
- package/dist/core/galleryPlayback.d.ts +7 -7
- package/dist/core/galleryPlayback.js +2 -2
- package/dist/core/galleryScene.d.ts +8 -13
- package/dist/core/galleryScene.js +14 -42
- package/dist/core/gallerySelectionMarker.d.ts +1 -0
- package/dist/core/gallerySelectionMarker.js +1 -0
- package/dist/core/history.d.ts +12 -1
- package/dist/core/history.js +16 -3
- package/dist/core/indexedDbProjectStore.d.ts +35 -0
- package/dist/core/indexedDbProjectStore.js +299 -0
- package/dist/core/lineShaders.d.ts +2 -0
- package/dist/core/lineShaders.js +7 -0
- package/dist/core/loadEffect.d.ts +2 -2
- package/dist/core/loadEffect.js +1 -1
- package/dist/core/pickAssetFiles.d.ts +6 -0
- package/dist/core/{pickGalleryFiles.js → pickAssetFiles.js} +13 -8
- package/dist/core/projectStore.d.ts +61 -0
- package/dist/core/projectStore.js +14 -0
- package/dist/core/sceneEntries.d.ts +35 -0
- package/dist/core/sceneEntries.js +62 -0
- package/dist/core/shapes.d.ts +1 -1
- package/dist/core/shapes.js +35 -8
- package/dist/core/systems.d.ts +1 -1
- package/dist/core/systems.js +3 -1
- package/dist/core/timeline.d.ts +1 -2
- package/dist/core/transport.d.ts +12 -0
- package/dist/core/transport.js +36 -0
- package/dist/core/values.d.ts +1 -1
- package/dist/core/viewportPicker.d.ts +4 -4
- package/dist/core/viewportPicker.js +3 -3
- package/dist/index.d.ts +15 -11
- package/dist/index.js +8 -6
- package/dist/react/AssetsPanel.d.ts +22 -0
- package/dist/react/AssetsPanel.js +252 -0
- package/dist/react/ChoiceDialog.d.ts +18 -0
- package/dist/react/ChoiceDialog.js +48 -0
- package/dist/react/ContextMenu.d.ts +16 -0
- package/dist/react/ContextMenu.js +70 -0
- package/dist/react/CurveEditor.d.ts +1 -2
- package/dist/react/CurveEditor.js +14 -3
- package/dist/react/EditorWorkspace.d.ts +21 -0
- package/dist/react/EditorWorkspace.js +63 -0
- package/dist/react/EffectEditor.d.ts +3 -3
- package/dist/react/EffectEditor.js +9 -6
- package/dist/react/EffectEditorHost.d.ts +20 -3
- package/dist/react/EffectEditorHost.js +1426 -558
- package/dist/react/EffectTreeView.d.ts +17 -0
- package/dist/react/EffectTreeView.js +52 -0
- package/dist/react/GradientEditor.d.ts +1 -2
- package/dist/react/GradientEditor.js +8 -1
- package/dist/react/MessageDialog.js +7 -1
- package/dist/react/ModuleSection.js +10 -3
- package/dist/react/ObjectModule.d.ts +5 -2
- package/dist/react/ObjectModule.js +8 -11
- package/dist/react/ProjectsPage.d.ts +9 -0
- package/dist/react/ProjectsPage.js +140 -0
- package/dist/react/ScenePanel.d.ts +26 -0
- package/dist/react/ScenePanel.js +134 -0
- package/dist/react/SubEmittersModule.d.ts +1 -2
- package/dist/react/TimelinePanel.d.ts +11 -4
- package/dist/react/TimelinePanel.js +65 -40
- package/dist/react/TimelineTrackRow.d.ts +1 -1
- package/dist/react/TimelineTrackRow.js +20 -6
- package/dist/react/TransportBar.d.ts +24 -0
- package/dist/react/TransportBar.js +43 -0
- package/dist/react/ValueField.d.ts +1 -2
- package/dist/react/behaviorModules.js +24 -12
- package/dist/react/index.d.ts +31 -17
- package/dist/react/index.js +18 -11
- package/dist/react/inspectorHints.js +1 -2
- package/dist/react/modules.js +55 -25
- package/package.json +68 -68
- package/src/core/assetDragSession.ts +12 -0
- package/src/core/assetTree.ts +148 -0
- package/src/core/assets.ts +262 -0
- package/src/core/binding.ts +10 -2
- package/src/core/collision.ts +4 -5
- package/src/core/colors.ts +1 -1
- package/src/core/defaultEffect.ts +10 -23
- package/src/core/effectTree.ts +22 -7
- package/src/core/emitterShapeWireframe.ts +9 -4
- package/src/core/galleryDropMarker.ts +6 -2
- package/src/core/galleryPlayback.ts +9 -9
- package/src/core/galleryScene.ts +15 -63
- package/src/core/gallerySelectionMarker.ts +6 -1
- package/src/core/history.ts +24 -3
- package/src/core/indexedDbProjectStore.ts +357 -0
- package/src/core/lineShaders.ts +7 -0
- package/src/core/loadEffect.ts +3 -3
- package/src/core/{pickGalleryFiles.ts → pickAssetFiles.ts} +16 -8
- package/src/core/projectStore.ts +81 -0
- package/src/core/sceneEntries.ts +96 -0
- package/src/core/shapes.ts +48 -10
- package/src/core/systems.ts +9 -3
- package/src/core/timeline.ts +8 -8
- package/src/core/transport.ts +53 -0
- package/src/core/values.ts +1 -1
- package/src/core/viewportPicker.ts +5 -5
- package/src/index.ts +29 -18
- package/src/react/AssetsPanel.tsx +559 -0
- package/src/react/ChoiceDialog.tsx +93 -0
- package/src/react/ContextMenu.tsx +107 -0
- package/src/react/CurveEditor.tsx +40 -10
- package/src/react/EditorWorkspace.tsx +95 -0
- package/src/react/EffectEditor.tsx +28 -16
- package/src/react/EffectEditorHost.tsx +2005 -721
- package/src/react/EffectTreeView.tsx +117 -0
- package/src/react/GradientEditor.tsx +21 -4
- package/src/react/InspectorInfoIcon.tsx +6 -2
- package/src/react/MessageDialog.tsx +9 -1
- package/src/react/ModuleSection.tsx +12 -3
- package/src/react/ObjectModule.tsx +41 -16
- package/src/react/ProjectsPage.tsx +306 -0
- package/src/react/PromptDialog.tsx +5 -1
- package/src/react/ScenePanel.tsx +318 -0
- package/src/react/SubEmittersModule.tsx +1 -2
- package/src/react/TimelinePanel.tsx +171 -99
- package/src/react/TimelineTrackRow.tsx +71 -18
- package/src/react/TransportBar.tsx +114 -0
- package/src/react/ValueField.tsx +0 -1
- package/src/react/behaviorModules.tsx +148 -42
- package/src/react/index.ts +47 -33
- package/src/react/inspectorHints.ts +1 -2
- package/src/react/modules.tsx +173 -57
- package/dist/core/galleryDragSession.d.ts +0 -4
- package/dist/core/galleryDragSession.js +0 -8
- package/dist/core/galleryTree.d.ts +0 -49
- package/dist/core/galleryTree.js +0 -138
- package/dist/core/loadEffectGallery.d.ts +0 -36
- package/dist/core/loadEffectGallery.js +0 -97
- package/dist/core/pickGalleryFiles.d.ts +0 -5
- package/dist/react/GalleryPanel.d.ts +0 -20
- package/dist/react/GalleryPanel.js +0 -246
- package/src/core/galleryDragSession.ts +0 -12
- package/src/core/galleryTree.ts +0 -187
- package/src/core/loadEffectGallery.ts +0 -146
- package/src/react/GalleryPanel.tsx +0 -517
package/dist/core/loadEffect.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ParticleEmitter, QuarksLoader, QuarksUtil } from 'babylon.quarks';
|
|
2
1
|
import { TransformNode } from '@babylonjs/core/Meshes/transformNode';
|
|
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. */
|
|
5
5
|
const DEFAULT_EMITTER_NAME = 'particleEmitter';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opens a native folder picker when the host supports it. Returns `null` when the user
|
|
3
|
+
* cancels, or `undefined` when the API is unavailable (caller should fall back to
|
|
4
|
+
* `<input webkitdirectory>`).
|
|
5
|
+
*/
|
|
6
|
+
export declare function pickAssetFolderFiles(): Promise<File[] | null | undefined>;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
/**
|
|
3
|
-
|
|
1
|
+
import { isAssetImportFile, relativePathOf } from './assets';
|
|
2
|
+
/**
|
|
3
|
+
* Recursively collects effect `.json` files and the image files they may reference from a
|
|
4
|
+
* directory handle (File System Access API).
|
|
5
|
+
*/
|
|
6
|
+
async function readAssetFilesFromDirectory(dir, prefix = '') {
|
|
4
7
|
const files = [];
|
|
5
8
|
for await (const [name, entry] of dir.entries()) {
|
|
6
9
|
if (entry.kind === 'file') {
|
|
7
|
-
if (!name
|
|
10
|
+
if (!isAssetImportFile(name))
|
|
8
11
|
continue;
|
|
9
12
|
const file = await entry.getFile();
|
|
10
13
|
Object.defineProperty(file, 'webkitRelativePath', {
|
|
@@ -15,23 +18,25 @@ async function readJsonFilesFromDirectory(dir, prefix = '') {
|
|
|
15
18
|
continue;
|
|
16
19
|
}
|
|
17
20
|
if (entry.kind === 'directory') {
|
|
18
|
-
files.push(...(await
|
|
21
|
+
files.push(...(await readAssetFilesFromDirectory(entry, `${prefix}${name}/`)));
|
|
19
22
|
}
|
|
20
23
|
}
|
|
21
24
|
return files;
|
|
22
25
|
}
|
|
23
26
|
/**
|
|
24
27
|
* Opens a native folder picker when the host supports it. Returns `null` when the user
|
|
25
|
-
* cancels, or when the API is unavailable (caller should fall back to
|
|
28
|
+
* cancels, or `undefined` when the API is unavailable (caller should fall back to
|
|
29
|
+
* `<input webkitdirectory>`).
|
|
26
30
|
*/
|
|
27
|
-
export async function
|
|
31
|
+
export async function pickAssetFolderFiles() {
|
|
28
32
|
const picker = window.showDirectoryPicker;
|
|
29
33
|
if (typeof picker !== 'function') {
|
|
30
34
|
return undefined;
|
|
31
35
|
}
|
|
32
36
|
try {
|
|
33
37
|
const dir = (await picker.call(window, { mode: 'read' }));
|
|
34
|
-
|
|
38
|
+
const files = await readAssetFilesFromDirectory(dir);
|
|
39
|
+
return files.sort((a, b) => relativePathOf(a).localeCompare(relativePathOf(b)));
|
|
35
40
|
}
|
|
36
41
|
catch (err) {
|
|
37
42
|
if (err instanceof DOMException && err.name === 'AbortError') {
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/** One editor project: a library of effect assets plus the scene built from them. */
|
|
2
|
+
export interface ProjectMeta {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
createdAt: number;
|
|
6
|
+
updatedAt: number;
|
|
7
|
+
assetCount: number;
|
|
8
|
+
/** Approximate bytes the project takes in storage (JSON + texture files). */
|
|
9
|
+
sizeBytes: number;
|
|
10
|
+
}
|
|
11
|
+
/** An effect asset as the store keeps it. `json` is a self-contained Quarks effect. */
|
|
12
|
+
export interface StoredAsset {
|
|
13
|
+
/** Relative path inside the project (`Fire/Explosion.json`) — the asset's key. */
|
|
14
|
+
path: string;
|
|
15
|
+
name: string;
|
|
16
|
+
json: unknown;
|
|
17
|
+
missingFiles: string[];
|
|
18
|
+
}
|
|
19
|
+
/** One effect instance in a saved scene. */
|
|
20
|
+
export interface StoredSceneEntry {
|
|
21
|
+
name: string;
|
|
22
|
+
/** Path of the asset it was made from, if any. */
|
|
23
|
+
assetPath: string | null;
|
|
24
|
+
/** The instance's own (possibly edited) effect, root transform included. */
|
|
25
|
+
json: unknown;
|
|
26
|
+
visible: boolean;
|
|
27
|
+
}
|
|
28
|
+
export interface StoredScene {
|
|
29
|
+
entries: StoredSceneEntry[];
|
|
30
|
+
savedAt: number;
|
|
31
|
+
}
|
|
32
|
+
export interface StorageEstimate {
|
|
33
|
+
/** Bytes the whole site uses (all projects), when the browser reports it. */
|
|
34
|
+
usage: number | null;
|
|
35
|
+
/** Bytes the browser lets the site use, when it reports it. */
|
|
36
|
+
quota: number | null;
|
|
37
|
+
/** True when the browser promised not to evict the data under storage pressure. */
|
|
38
|
+
persisted: boolean;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Where projects live. The editor only talks to this interface; `IndexedDbProjectStore` keeps
|
|
42
|
+
* everything in the browser, and an embedding host can supply its own (e.g. on disk).
|
|
43
|
+
*/
|
|
44
|
+
export interface ProjectStore {
|
|
45
|
+
listProjects(): Promise<ProjectMeta[]>;
|
|
46
|
+
getProject(id: string): Promise<ProjectMeta | null>;
|
|
47
|
+
createProject(name: string): Promise<ProjectMeta>;
|
|
48
|
+
renameProject(id: string, name: string): Promise<void>;
|
|
49
|
+
duplicateProject(id: string, name: string): Promise<ProjectMeta>;
|
|
50
|
+
deleteProject(id: string): Promise<void>;
|
|
51
|
+
loadAssets(projectId: string): Promise<StoredAsset[]>;
|
|
52
|
+
/** Adds or replaces assets by path. */
|
|
53
|
+
putAssets(projectId: string, assets: StoredAsset[]): Promise<void>;
|
|
54
|
+
deleteAssets(projectId: string, paths: string[]): Promise<void>;
|
|
55
|
+
loadScene(projectId: string): Promise<StoredScene | null>;
|
|
56
|
+
saveScene(projectId: string, scene: StoredScene): Promise<void>;
|
|
57
|
+
estimate(): Promise<StorageEstimate>;
|
|
58
|
+
/** Asks the browser to keep the data even under storage pressure; resolves to the result. */
|
|
59
|
+
requestPersistence(): Promise<boolean>;
|
|
60
|
+
}
|
|
61
|
+
export declare function formatBytes(bytes: number | null | undefined): string;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function formatBytes(bytes) {
|
|
2
|
+
if (bytes == null)
|
|
3
|
+
return '—';
|
|
4
|
+
if (bytes < 1024)
|
|
5
|
+
return `${bytes} B`;
|
|
6
|
+
const units = ['KB', 'MB', 'GB', 'TB'];
|
|
7
|
+
let value = bytes / 1024;
|
|
8
|
+
let unit = 0;
|
|
9
|
+
while (value >= 1024 && unit < units.length - 1) {
|
|
10
|
+
value /= 1024;
|
|
11
|
+
unit++;
|
|
12
|
+
}
|
|
13
|
+
return `${value < 10 ? value.toFixed(1) : Math.round(value)} ${units[unit]}`;
|
|
14
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { TransformNode } from '@babylonjs/core/Meshes/transformNode';
|
|
2
|
+
import type { Scene } from '@babylonjs/core/scene';
|
|
3
|
+
import type { BatchedRenderer, ParticleSystem } from 'babylon.quarks';
|
|
4
|
+
import type { EffectAsset } from './assets';
|
|
5
|
+
import { EffectBinding } from './binding';
|
|
6
|
+
/**
|
|
7
|
+
* One effect instance placed in the scene. Every instance is live (its systems are registered
|
|
8
|
+
* with the renderer) and independent: dropping the same asset three times gives three entries
|
|
9
|
+
* that are edited separately.
|
|
10
|
+
*/
|
|
11
|
+
export interface SceneEntry {
|
|
12
|
+
name: string;
|
|
13
|
+
/** Path of the asset it came from, or a synthetic `_created/…` path for new effects. */
|
|
14
|
+
path: string;
|
|
15
|
+
/** Asset this instance was made from, if any. */
|
|
16
|
+
assetId: number | null;
|
|
17
|
+
root: TransformNode;
|
|
18
|
+
systems: ParticleSystem[];
|
|
19
|
+
}
|
|
20
|
+
/** `name`, or `name (2)`, `name (3)`… when an entry already uses it. */
|
|
21
|
+
export declare function uniqueEntryName(name: string, existing: SceneEntry[]): string;
|
|
22
|
+
/**
|
|
23
|
+
* Parses an asset into a new scene instance with its systems registered (paused). Returns null
|
|
24
|
+
* when the JSON holds no particle systems.
|
|
25
|
+
*/
|
|
26
|
+
export declare function instantiateSceneEntry(scene: Scene, renderer: BatchedRenderer, asset: EffectAsset, existing: SceneEntry[]): SceneEntry | null;
|
|
27
|
+
/** Builds an edit binding for one scene entry. */
|
|
28
|
+
export declare function bindingFromSceneEntry(entry: SceneEntry): EffectBinding;
|
|
29
|
+
/**
|
|
30
|
+
* Unregisters and disposes one scene entry — including systems the inspector added after the
|
|
31
|
+
* entry was created, which only the live emitter tree knows about.
|
|
32
|
+
*/
|
|
33
|
+
export declare function disposeSceneEntry(entry: SceneEntry, renderer: BatchedRenderer): void;
|
|
34
|
+
/** Number of scene instances per asset id. */
|
|
35
|
+
export declare function countInstancesByAsset(entries: SceneEntry[]): Map<number, number>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { QuarksUtil } from 'babylon.quarks';
|
|
2
|
+
import { EffectBinding } from './binding';
|
|
3
|
+
import { disposeLoadedEffect, parseEffectFromJson } from './loadEffect';
|
|
4
|
+
/** `name`, or `name (2)`, `name (3)`… when an entry already uses it. */
|
|
5
|
+
export function uniqueEntryName(name, existing) {
|
|
6
|
+
const taken = new Set(existing.map((entry) => entry.name));
|
|
7
|
+
if (!taken.has(name)) {
|
|
8
|
+
return name;
|
|
9
|
+
}
|
|
10
|
+
let n = 2;
|
|
11
|
+
while (taken.has(`${name} (${n})`)) {
|
|
12
|
+
n++;
|
|
13
|
+
}
|
|
14
|
+
return `${name} (${n})`;
|
|
15
|
+
}
|
|
16
|
+
function isPlaceholderName(name) {
|
|
17
|
+
return !name || name === 'Effect' || name === 'Object3D' || name === 'Group';
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Parses an asset into a new scene instance with its systems registered (paused). Returns null
|
|
21
|
+
* when the JSON holds no particle systems.
|
|
22
|
+
*/
|
|
23
|
+
export function instantiateSceneEntry(scene, renderer, asset, existing) {
|
|
24
|
+
const { root, systems } = parseEffectFromJson(scene, renderer, asset.json, {
|
|
25
|
+
registerRenderer: true,
|
|
26
|
+
autoplay: false,
|
|
27
|
+
});
|
|
28
|
+
if (systems.length === 0) {
|
|
29
|
+
disposeLoadedEffect(root, systems, renderer, true);
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
const name = uniqueEntryName(isPlaceholderName(root.name) ? asset.name : root.name, existing);
|
|
33
|
+
root.name = name;
|
|
34
|
+
root.parent = null;
|
|
35
|
+
return { name, path: asset.path, assetId: asset.id, root, systems };
|
|
36
|
+
}
|
|
37
|
+
/** Builds an edit binding for one scene entry. */
|
|
38
|
+
export function bindingFromSceneEntry(entry) {
|
|
39
|
+
const main = entry.systems.find((s) => !s.onlyUsedByOther) ?? entry.systems[0];
|
|
40
|
+
return new EffectBinding(main, entry.systems.filter((s) => s !== main), entry.root);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Unregisters and disposes one scene entry — including systems the inspector added after the
|
|
44
|
+
* entry was created, which only the live emitter tree knows about.
|
|
45
|
+
*/
|
|
46
|
+
export function disposeSceneEntry(entry, renderer) {
|
|
47
|
+
const systems = new Set(entry.systems);
|
|
48
|
+
QuarksUtil.runOnAllParticleEmitters(entry.root, (emitter) => {
|
|
49
|
+
systems.add(emitter.system);
|
|
50
|
+
});
|
|
51
|
+
disposeLoadedEffect(entry.root, [...systems], renderer, true);
|
|
52
|
+
}
|
|
53
|
+
/** Number of scene instances per asset id. */
|
|
54
|
+
export function countInstancesByAsset(entries) {
|
|
55
|
+
const counts = new Map();
|
|
56
|
+
for (const entry of entries) {
|
|
57
|
+
if (entry.assetId != null) {
|
|
58
|
+
counts.set(entry.assetId, (counts.get(entry.assetId) ?? 0) + 1);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return counts;
|
|
62
|
+
}
|
package/dist/core/shapes.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import type { Mesh } from '@babylonjs/core/Meshes/mesh';
|
|
1
2
|
import type { EmitterShape } from 'babylon.quarks';
|
|
2
3
|
import { EmitterMode } from 'babylon.quarks';
|
|
3
|
-
import type { Mesh } from '@babylonjs/core/Meshes/mesh';
|
|
4
4
|
export type ShapeType = 'point' | 'sphere' | 'hemisphere' | 'cone' | 'circle' | 'donut' | 'rectangle' | 'grid' | 'mesh_surface';
|
|
5
5
|
export declare const SHAPE_TYPES: Array<{
|
|
6
6
|
value: ShapeType;
|
package/dist/core/shapes.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { CircleEmitter, ConeEmitter, DonutEmitter, GridEmitter, HemisphereEmitter, MeshSurfaceEmitter, PointEmitter, RectangleEmitter, SphereEmitter, } from 'babylon.quarks';
|
|
2
|
-
import { EmitterMode } from 'babylon.quarks';
|
|
1
|
+
import { CircleEmitter, ConeEmitter, DonutEmitter, EmitterMode, GridEmitter, HemisphereEmitter, MeshSurfaceEmitter, PointEmitter, RectangleEmitter, SphereEmitter, } from 'babylon.quarks';
|
|
3
2
|
export const SHAPE_TYPES = [
|
|
4
3
|
{ value: 'cone', label: 'Cone' },
|
|
5
4
|
{ value: 'sphere', label: 'Sphere' },
|
|
@@ -31,7 +30,7 @@ export function getShapeType(shape) {
|
|
|
31
30
|
}
|
|
32
31
|
export function readShapeParams(shape) {
|
|
33
32
|
const anyShape = shape;
|
|
34
|
-
const num = (key, fallback) =>
|
|
33
|
+
const num = (key, fallback) => typeof anyShape[key] === 'number' ? anyShape[key] : fallback;
|
|
35
34
|
return {
|
|
36
35
|
radius: num('radius', DEFAULT_SHAPE_PARAMS.radius),
|
|
37
36
|
mode: num('mode', DEFAULT_SHAPE_PARAMS.mode),
|
|
@@ -65,9 +64,21 @@ export function createShape(type, params) {
|
|
|
65
64
|
case 'point':
|
|
66
65
|
return new PointEmitter();
|
|
67
66
|
case 'sphere':
|
|
68
|
-
return new SphereEmitter({
|
|
67
|
+
return new SphereEmitter({
|
|
68
|
+
radius: params.radius,
|
|
69
|
+
arc: params.arc,
|
|
70
|
+
thickness: params.thickness,
|
|
71
|
+
mode: params.mode,
|
|
72
|
+
spread: params.spread,
|
|
73
|
+
});
|
|
69
74
|
case 'hemisphere':
|
|
70
|
-
return new HemisphereEmitter({
|
|
75
|
+
return new HemisphereEmitter({
|
|
76
|
+
radius: params.radius,
|
|
77
|
+
arc: params.arc,
|
|
78
|
+
thickness: params.thickness,
|
|
79
|
+
mode: params.mode,
|
|
80
|
+
spread: params.spread,
|
|
81
|
+
});
|
|
71
82
|
case 'cone':
|
|
72
83
|
return new ConeEmitter({
|
|
73
84
|
radius: params.radius,
|
|
@@ -78,7 +89,13 @@ export function createShape(type, params) {
|
|
|
78
89
|
spread: params.spread,
|
|
79
90
|
});
|
|
80
91
|
case 'circle':
|
|
81
|
-
return new CircleEmitter({
|
|
92
|
+
return new CircleEmitter({
|
|
93
|
+
radius: params.radius,
|
|
94
|
+
arc: params.arc,
|
|
95
|
+
thickness: params.thickness,
|
|
96
|
+
mode: params.mode,
|
|
97
|
+
spread: params.spread,
|
|
98
|
+
});
|
|
82
99
|
case 'donut':
|
|
83
100
|
return new DonutEmitter({
|
|
84
101
|
radius: params.radius,
|
|
@@ -89,9 +106,19 @@ export function createShape(type, params) {
|
|
|
89
106
|
spread: params.spread,
|
|
90
107
|
});
|
|
91
108
|
case 'rectangle':
|
|
92
|
-
return new RectangleEmitter({
|
|
109
|
+
return new RectangleEmitter({
|
|
110
|
+
width: params.width,
|
|
111
|
+
height: params.height,
|
|
112
|
+
mode: params.mode,
|
|
113
|
+
spread: params.spread,
|
|
114
|
+
});
|
|
93
115
|
case 'grid':
|
|
94
|
-
return new GridEmitter({
|
|
116
|
+
return new GridEmitter({
|
|
117
|
+
width: params.width,
|
|
118
|
+
height: params.height,
|
|
119
|
+
column: params.column,
|
|
120
|
+
row: params.row,
|
|
121
|
+
});
|
|
95
122
|
case 'mesh_surface':
|
|
96
123
|
return new MeshSurfaceEmitter(params.mesh);
|
|
97
124
|
}
|
package/dist/core/systems.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ParticleSystem } from 'babylon.quarks';
|
|
2
1
|
import type { TransformNode } from '@babylonjs/core/Meshes/transformNode';
|
|
2
|
+
import { ParticleSystem } from 'babylon.quarks';
|
|
3
3
|
import type { EffectBinding } from './binding';
|
|
4
4
|
export interface ChildSystemOptions {
|
|
5
5
|
name?: string;
|
package/dist/core/systems.js
CHANGED
|
@@ -15,7 +15,9 @@ export function createChildSystem(parent, options = {}) {
|
|
|
15
15
|
startSize: new IntervalValue(0.05, 0.2),
|
|
16
16
|
startColor: new ConstantColor(new Vector4(1, 0.9, 0.6, 1)),
|
|
17
17
|
emissionOverTime: new ConstantValue(oneShot ? 0 : 30),
|
|
18
|
-
emissionBursts: oneShot
|
|
18
|
+
emissionBursts: oneShot
|
|
19
|
+
? [{ time: 0, count: new ConstantValue(8), cycle: 1, interval: 0.01, probability: 1 }]
|
|
20
|
+
: [],
|
|
19
21
|
shape: new ConeEmitter({ radius: 0.1, angle: Math.PI / 3 }),
|
|
20
22
|
renderMode: RenderMode.BillBoard,
|
|
21
23
|
texture: parent.texture ?? undefined,
|
package/dist/core/timeline.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type { ParticleSystem } from 'babylon.quarks';
|
|
2
|
-
import type { FunctionValueGenerator, ValueGenerator } from 'babylon.quarks';
|
|
3
1
|
import type { TransformNode } from '@babylonjs/core/Meshes/transformNode';
|
|
2
|
+
import type { FunctionValueGenerator, ParticleSystem, ValueGenerator } from 'babylon.quarks';
|
|
4
3
|
import type { EffectBinding } from './binding';
|
|
5
4
|
export type TimelineRow = {
|
|
6
5
|
kind: 'group';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Scene } from '@babylonjs/core/scene';
|
|
2
|
+
import type { BatchedRenderer } from 'babylon.quarks';
|
|
3
|
+
import type { EffectBinding } from './binding';
|
|
4
|
+
import { type PlaybackState } from './playback';
|
|
5
|
+
/**
|
|
6
|
+
* Stage Play/Pause shared by the viewport transport bar and the timeline. Pressing Play on a
|
|
7
|
+
* one-shot focus that already parked at its end replays it from the start.
|
|
8
|
+
*/
|
|
9
|
+
export declare function toggleStagePlayback(binding: EffectBinding | null, scene: Scene, playback: PlaybackState, stagePlaying: boolean, setStagePlaying: (playing: boolean) => void): void;
|
|
10
|
+
/** Restarts the focused effect only; other visible effects keep playing. */
|
|
11
|
+
export declare function restartFocusPlayback(binding: EffectBinding, renderer: BatchedRenderer, scene: Scene, playback: PlaybackState): void;
|
|
12
|
+
export declare const PLAYBACK_SPEEDS: number[];
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { clearFocusFinished, isFocusFinished, setFocusElapsed } from './playback';
|
|
2
|
+
import { playFocus, scrubFocusTo } from './scrub';
|
|
3
|
+
/**
|
|
4
|
+
* Stage Play/Pause shared by the viewport transport bar and the timeline. Pressing Play on a
|
|
5
|
+
* one-shot focus that already parked at its end replays it from the start.
|
|
6
|
+
*/
|
|
7
|
+
export function toggleStagePlayback(binding, scene, playback, stagePlaying, setStagePlaying) {
|
|
8
|
+
if (stagePlaying) {
|
|
9
|
+
playback.stagePlaying = false;
|
|
10
|
+
setStagePlaying(false);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
if (binding) {
|
|
14
|
+
const rootId = binding.root.uniqueId;
|
|
15
|
+
if (isFocusFinished(playback, rootId)) {
|
|
16
|
+
binding.restart();
|
|
17
|
+
clearFocusFinished(playback, rootId);
|
|
18
|
+
setFocusElapsed(playback, rootId, 0);
|
|
19
|
+
scene.render();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
playback.stagePlaying = true;
|
|
23
|
+
setStagePlaying(true);
|
|
24
|
+
}
|
|
25
|
+
/** Restarts the focused effect only; other visible effects keep playing. */
|
|
26
|
+
export function restartFocusPlayback(binding, renderer, scene, playback) {
|
|
27
|
+
const rootId = binding.root.uniqueId;
|
|
28
|
+
scrubFocusTo(binding, renderer, 0);
|
|
29
|
+
setFocusElapsed(playback, rootId, 0);
|
|
30
|
+
clearFocusFinished(playback, rootId);
|
|
31
|
+
if (playback.stagePlaying) {
|
|
32
|
+
playFocus(binding);
|
|
33
|
+
}
|
|
34
|
+
scene.render();
|
|
35
|
+
}
|
|
36
|
+
export const PLAYBACK_SPEEDS = [0.1, 0.25, 0.5, 1, 2, 4];
|
package/dist/core/values.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { PiecewiseBezier } from 'babylon.quarks';
|
|
2
1
|
import type { FunctionValueGenerator, ValueGenerator } from 'babylon.quarks';
|
|
2
|
+
import { PiecewiseBezier } from 'babylon.quarks';
|
|
3
3
|
export type ScalarGenerator = ValueGenerator | FunctionValueGenerator;
|
|
4
4
|
export type ScalarMode = 'constant' | 'random' | 'curve';
|
|
5
5
|
/** Editable snapshot of a scalar value generator. */
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { TransformNode } from '@babylonjs/core/Meshes/transformNode';
|
|
2
2
|
import type { Scene } from '@babylonjs/core/scene';
|
|
3
|
-
import type {
|
|
3
|
+
import type { SceneEntry } from './sceneEntries';
|
|
4
4
|
/**
|
|
5
|
-
* Viewport pick/hover for
|
|
5
|
+
* Viewport pick/hover for visible scene effect roots only (not child emitters).
|
|
6
6
|
* Hover uses the same ground ring as focus selection, in yellow.
|
|
7
7
|
*/
|
|
8
8
|
export declare class GalleryViewportPicker {
|
|
@@ -12,8 +12,8 @@ export declare class GalleryViewportPicker {
|
|
|
12
12
|
private hoveredRoot;
|
|
13
13
|
private syncKey;
|
|
14
14
|
constructor(scene: Scene);
|
|
15
|
-
/** Rebuilds root pick colliders when the
|
|
16
|
-
sync(entries:
|
|
15
|
+
/** Rebuilds root pick colliders when the scene changes. */
|
|
16
|
+
sync(entries: SceneEntry[], soloRoot: TransformNode | null, syncKey: string): void;
|
|
17
17
|
/** Yellow focus-style ring under the hovered effect root. */
|
|
18
18
|
setHover(effectRoot: TransformNode | null): void;
|
|
19
19
|
follow(): void;
|
|
@@ -4,7 +4,7 @@ import { GROUND_RING_RADIUS, GalleryHoverMarker } from './gallerySelectionMarker
|
|
|
4
4
|
/** Invisible pick proxy size — matches the ground ring footprint. */
|
|
5
5
|
const PICK_DIAMETER = GROUND_RING_RADIUS * 2;
|
|
6
6
|
/**
|
|
7
|
-
* Viewport pick/hover for
|
|
7
|
+
* Viewport pick/hover for visible scene effect roots only (not child emitters).
|
|
8
8
|
* Hover uses the same ground ring as focus selection, in yellow.
|
|
9
9
|
*/
|
|
10
10
|
export class GalleryViewportPicker {
|
|
@@ -15,7 +15,7 @@ export class GalleryViewportPicker {
|
|
|
15
15
|
this.scene = scene;
|
|
16
16
|
this.hoverMarker = new GalleryHoverMarker(scene);
|
|
17
17
|
}
|
|
18
|
-
/** Rebuilds root pick colliders when the
|
|
18
|
+
/** Rebuilds root pick colliders when the scene changes. */
|
|
19
19
|
sync(entries, soloRoot, syncKey) {
|
|
20
20
|
if (syncKey === this.syncKey) {
|
|
21
21
|
return;
|
|
@@ -24,7 +24,7 @@ export class GalleryViewportPicker {
|
|
|
24
24
|
this.clearHandles();
|
|
25
25
|
if (entries.length > 0) {
|
|
26
26
|
for (const entry of entries) {
|
|
27
|
-
if (
|
|
27
|
+
if (entry.root.isDisposed() || !entry.root.isEnabled(false)) {
|
|
28
28
|
continue;
|
|
29
29
|
}
|
|
30
30
|
this.createHandle(entry.root);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
export { EffectBinding } from './core/binding';
|
|
2
2
|
export type { EditorListener } from './core/binding';
|
|
3
|
-
export { EffectHistory } from './core/history';
|
|
4
|
-
export { createChildSystem } from './core/systems';
|
|
5
|
-
export { buildEffectTree, collectSystems, serializeEffectTree, serializeEffectForest } from './core/effectTree';
|
|
6
|
-
export type { EffectTreeNode } from './core/effectTree';
|
|
7
|
-
export type { ChildSystemOptions } from './core/systems';
|
|
8
|
-
export { readScalar, buildScalar, sampleCurve, readPieces, buildCurve, splitPieces, mergePieces } from './core/values';
|
|
9
|
-
export type { ScalarGenerator, ScalarMode, ScalarValueState, CurvePiece } from './core/values';
|
|
10
|
-
export { DEFAULT_GRADIENT_STOPS, buildGradient, buildColorOverLife, rgbaToVector4, stopToCss, stopsToCssGradient, hexToRgb, rgbToHex, findBehavior, } from './core/colors';
|
|
11
|
-
export type { GradientStop } from './core/colors';
|
|
12
|
-
export { SHAPE_TYPES, SHAPE_PARAM_KEYS, DEFAULT_SHAPE_PARAMS, getShapeType, readShapeParams, createShape, } from './core/shapes';
|
|
13
|
-
export type { ShapeType, ShapeParams } from './core/shapes';
|
|
14
3
|
export { GroundPlaneResolver, ensureGroundResolver } from './core/collision';
|
|
4
|
+
export { DEFAULT_GRADIENT_STOPS, buildColorOverLife, buildGradient, findBehavior, hexToRgb, rgbToHex, rgbaToVector4, stopToCss, stopsToCssGradient, } from './core/colors';
|
|
5
|
+
export type { GradientStop } from './core/colors';
|
|
6
|
+
export { buildEffectTree, collectSystems, serializeEffectForest, serializeEffectTree } from './core/effectTree';
|
|
7
|
+
export type { EffectTreeNode } from './core/effectTree';
|
|
8
|
+
export { EffectHistory } from './core/history';
|
|
9
|
+
export type { HistoryStacks } from './core/history';
|
|
10
|
+
export { IndexedDbProjectStore } from './core/indexedDbProjectStore';
|
|
15
11
|
export { applyRendererMaterial, getMaterialLabel } from './core/material';
|
|
16
12
|
export type { RendererMaterialPatch } from './core/material';
|
|
13
|
+
export { formatBytes } from './core/projectStore';
|
|
14
|
+
export type { ProjectMeta, ProjectStore, StorageEstimate, StoredAsset, StoredScene, StoredSceneEntry, } from './core/projectStore';
|
|
15
|
+
export { DEFAULT_SHAPE_PARAMS, SHAPE_PARAM_KEYS, SHAPE_TYPES, createShape, getShapeType, readShapeParams, } from './core/shapes';
|
|
16
|
+
export type { ShapeParams, ShapeType } from './core/shapes';
|
|
17
|
+
export { createChildSystem } from './core/systems';
|
|
18
|
+
export type { ChildSystemOptions } from './core/systems';
|
|
17
19
|
export { ensurePortableTextureUrl } from './core/textureExport';
|
|
20
|
+
export { buildCurve, buildScalar, mergePieces, readPieces, readScalar, sampleCurve, splitPieces } from './core/values';
|
|
21
|
+
export type { CurvePiece, ScalarGenerator, ScalarMode, ScalarValueState } from './core/values';
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
export { EffectBinding } from './core/binding';
|
|
2
|
-
export { EffectHistory } from './core/history';
|
|
3
|
-
export { createChildSystem } from './core/systems';
|
|
4
|
-
export { buildEffectTree, collectSystems, serializeEffectTree, serializeEffectForest } from './core/effectTree';
|
|
5
|
-
export { readScalar, buildScalar, sampleCurve, readPieces, buildCurve, splitPieces, mergePieces } from './core/values';
|
|
6
|
-
export { DEFAULT_GRADIENT_STOPS, buildGradient, buildColorOverLife, rgbaToVector4, stopToCss, stopsToCssGradient, hexToRgb, rgbToHex, findBehavior, } from './core/colors';
|
|
7
|
-
export { SHAPE_TYPES, SHAPE_PARAM_KEYS, DEFAULT_SHAPE_PARAMS, getShapeType, readShapeParams, createShape, } from './core/shapes';
|
|
8
2
|
export { GroundPlaneResolver, ensureGroundResolver } from './core/collision';
|
|
3
|
+
export { DEFAULT_GRADIENT_STOPS, buildColorOverLife, buildGradient, findBehavior, hexToRgb, rgbToHex, rgbaToVector4, stopToCss, stopsToCssGradient, } from './core/colors';
|
|
4
|
+
export { buildEffectTree, collectSystems, serializeEffectForest, serializeEffectTree } from './core/effectTree';
|
|
5
|
+
export { EffectHistory } from './core/history';
|
|
6
|
+
export { IndexedDbProjectStore } from './core/indexedDbProjectStore';
|
|
9
7
|
export { applyRendererMaterial, getMaterialLabel } from './core/material';
|
|
8
|
+
export { formatBytes } from './core/projectStore';
|
|
9
|
+
export { DEFAULT_SHAPE_PARAMS, SHAPE_PARAM_KEYS, SHAPE_TYPES, createShape, getShapeType, readShapeParams, } from './core/shapes';
|
|
10
|
+
export { createChildSystem } from './core/systems';
|
|
10
11
|
export { ensurePortableTextureUrl } from './core/textureExport';
|
|
12
|
+
export { buildCurve, buildScalar, mergePieces, readPieces, readScalar, sampleCurve, splitPieces } from './core/values';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type AssetDragPayload } from '../core/assetTree';
|
|
3
|
+
import { type AssetLoadProgress, type EffectAsset } from '../core/assets';
|
|
4
|
+
export interface AssetsPanelProps {
|
|
5
|
+
assets: EffectAsset[];
|
|
6
|
+
/** Scene instance count per asset id. */
|
|
7
|
+
instanceCounts: ReadonlyMap<number, number>;
|
|
8
|
+
loading: AssetLoadProgress | null;
|
|
9
|
+
folderInputRef: React.RefObject<HTMLInputElement | null>;
|
|
10
|
+
onImportFiles: (files: FileList | File[]) => void;
|
|
11
|
+
onImportFolder: () => void;
|
|
12
|
+
/** Adds a new instance of each asset in the payload to the scene. */
|
|
13
|
+
onAddToScene: (payload: AssetDragPayload) => void;
|
|
14
|
+
onDelete: (payload: AssetDragPayload) => void;
|
|
15
|
+
/** Opens the asset on its own to edit it (double-click). Without it, double-click adds to the scene. */
|
|
16
|
+
onOpenPrefab?: (asset: EffectAsset) => void;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Bottom-dock asset browser: folder tree on the left, the open folder's effects as tiles on the
|
|
20
|
+
* right. Drag a tile into the viewport (as many times as needed) or right-click it for actions.
|
|
21
|
+
*/
|
|
22
|
+
export declare function AssetsPanel(props: AssetsPanelProps): React.ReactElement;
|