babylon.quarks-editor 0.27.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.
- package/CHANGELOG.md +71 -0
- package/README.md +31 -4
- package/dist/core/assetTree.d.ts +5 -0
- package/dist/core/assets.d.ts +7 -0
- package/dist/core/assets.js +11 -7
- package/dist/core/binding.d.ts +9 -0
- package/dist/core/binding.js +8 -0
- package/dist/core/collision.d.ts +4 -0
- package/dist/core/collision.js +4 -0
- package/dist/core/colors.d.ts +16 -0
- package/dist/core/colors.js +8 -0
- package/dist/core/defaultEffect.d.ts +3 -2
- package/dist/core/defaultEffect.js +1 -1
- package/dist/core/editorSession.d.ts +229 -0
- package/dist/core/editorSession.js +983 -0
- package/dist/core/effectTree.d.ts +5 -0
- package/dist/core/galleryPlayback.js +2 -1
- package/dist/core/history.d.ts +6 -0
- package/dist/core/history.js +4 -0
- package/dist/core/indexedDbProjectStore.d.ts +13 -0
- package/dist/core/indexedDbProjectStore.js +13 -0
- package/dist/core/material.d.ts +12 -2
- package/dist/core/playback.d.ts +6 -0
- package/dist/core/projectStore.d.ts +26 -0
- package/dist/core/projectStore.js +1 -0
- package/dist/core/sceneEntries.d.ts +3 -0
- package/dist/core/scrub.d.ts +1 -1
- package/dist/core/scrub.js +2 -2
- package/dist/core/shapes.d.ts +24 -0
- package/dist/core/shapes.js +5 -0
- package/dist/core/systems.d.ts +2 -0
- package/dist/core/systems.js +3 -1
- package/dist/core/timeline.d.ts +23 -3
- package/dist/core/values.d.ts +21 -4
- package/dist/core/values.js +5 -0
- package/dist/index.d.ts +5 -1
- package/dist/react/AssetsPanel.d.ts +7 -0
- package/dist/react/ChoiceDialog.d.ts +7 -0
- package/dist/react/ContextMenu.d.ts +10 -1
- package/dist/react/EditorWorkspace.d.ts +7 -0
- package/dist/react/EditorWorkspace.js +6 -3
- package/dist/react/EffectEditor.d.ts +8 -2
- package/dist/react/EffectEditorHost.d.ts +32 -28
- package/dist/react/EffectEditorHost.js +241 -1665
- package/dist/react/EffectTreeView.d.ts +3 -0
- package/dist/react/MessageDialog.d.ts +5 -0
- package/dist/react/ModuleSection.d.ts +10 -0
- package/dist/react/ModuleSection.js +4 -1
- package/dist/react/ProjectsPage.d.ts +6 -0
- package/dist/react/ProjectsPage.js +27 -11
- package/dist/react/PromptDialog.d.ts +7 -0
- package/dist/react/ScenePanel.d.ts +7 -0
- package/dist/react/ScenePanel.js +7 -5
- package/dist/react/TextureSelect.d.ts +5 -5
- package/dist/react/TimelinePanel.d.ts +13 -0
- package/dist/react/TimelinePanel.js +10 -6
- package/dist/react/TimelineTrackRow.d.ts +10 -0
- package/dist/react/TransportBar.d.ts +9 -0
- package/dist/react/ValueField.d.ts +32 -5
- package/dist/react/ValueField.js +20 -9
- package/dist/react/behaviorModules.d.ts +16 -5
- package/dist/react/behaviorModules.js +16 -1
- package/dist/react/fields.d.ts +8 -0
- package/dist/react/fields.js +3 -0
- package/dist/react/host/BottomDocks.d.ts +36 -0
- package/dist/react/host/BottomDocks.js +63 -0
- package/dist/react/host/HostDialogs.d.ts +50 -0
- package/dist/react/host/HostDialogs.js +113 -0
- package/dist/react/host/InspectorSidebar.d.ts +32 -0
- package/dist/react/host/InspectorSidebar.js +59 -0
- package/dist/react/host/SceneSidebar.d.ts +53 -0
- package/dist/react/host/SceneSidebar.js +62 -0
- package/dist/react/host/ViewportPane.d.ts +56 -0
- package/dist/react/host/ViewportPane.js +91 -0
- package/dist/react/host/hostHooks.d.ts +44 -0
- package/dist/react/host/hostHooks.js +164 -0
- package/dist/react/host/hostStyles.d.ts +21 -0
- package/dist/react/host/hostStyles.js +57 -0
- package/dist/react/index.d.ts +3 -1
- package/dist/react/modules.d.ts +17 -6
- package/dist/react/modules.js +38 -20
- package/dist/react/theme.d.ts +15 -0
- package/dist/react/theme.js +15 -0
- package/dist/session.d.ts +9 -0
- package/dist/session.js +8 -0
- package/package.json +17 -11
- package/src/core/assetTree.ts +14 -1
- package/src/core/assets.ts +25 -10
- package/src/core/binding.ts +9 -0
- package/src/core/collision.ts +4 -0
- package/src/core/colors.ts +21 -1
- package/src/core/defaultEffect.ts +4 -3
- package/src/core/editorSession.ts +1299 -0
- package/src/core/effectTree.ts +5 -0
- package/src/core/galleryPlayback.ts +2 -1
- package/src/core/history.ts +6 -0
- package/src/core/indexedDbProjectStore.ts +13 -0
- package/src/core/loadEffect.ts +1 -1
- package/src/core/material.ts +14 -4
- package/src/core/playback.ts +6 -0
- package/src/core/projectStore.ts +26 -0
- package/src/core/sceneEntries.ts +3 -0
- package/src/core/scrub.ts +2 -2
- package/src/core/shapes.ts +28 -1
- package/src/core/systems.ts +9 -2
- package/src/core/timeline.ts +40 -15
- package/src/core/values.ts +20 -4
- package/src/index.ts +5 -1
- package/src/react/AssetsPanel.tsx +7 -0
- package/src/react/ChoiceDialog.tsx +7 -0
- package/src/react/ContextMenu.tsx +10 -1
- package/src/react/EditorWorkspace.tsx +11 -5
- package/src/react/EffectEditor.tsx +7 -1
- package/src/react/EffectEditorHost.tsx +406 -2340
- package/src/react/EffectTreeView.tsx +3 -0
- package/src/react/MessageDialog.tsx +5 -0
- package/src/react/ModuleSection.tsx +11 -0
- package/src/react/ProjectsPage.tsx +38 -11
- package/src/react/PromptDialog.tsx +7 -0
- package/src/react/ScenePanel.tsx +14 -5
- package/src/react/TextureSelect.tsx +6 -6
- package/src/react/TimelinePanel.tsx +23 -6
- package/src/react/TimelineTrackRow.tsx +10 -0
- package/src/react/TransportBar.tsx +9 -0
- package/src/react/ValueField.tsx +58 -14
- package/src/react/behaviorModules.tsx +34 -32
- package/src/react/fields.tsx +12 -1
- package/src/react/host/BottomDocks.tsx +178 -0
- package/src/react/host/HostDialogs.tsx +228 -0
- package/src/react/host/InspectorSidebar.tsx +147 -0
- package/src/react/host/SceneSidebar.tsx +227 -0
- package/src/react/host/ViewportPane.tsx +300 -0
- package/src/react/host/hostHooks.ts +198 -0
- package/src/react/host/hostStyles.ts +65 -0
- package/src/react/index.ts +3 -1
- package/src/react/modules.tsx +70 -44
- package/src/react/theme.ts +15 -0
- package/src/session.ts +17 -0
package/dist/react/theme.js
CHANGED
|
@@ -1,15 +1,27 @@
|
|
|
1
|
+
/** The editor's colours and font, for hosts that style their own panels to match. */
|
|
1
2
|
export const theme = {
|
|
3
|
+
/** Background of the side panels and docks. */
|
|
2
4
|
panelBg: 'linear-gradient(180deg, rgba(12, 18, 38, 0.92), rgba(6, 10, 22, 0.94))',
|
|
5
|
+
/** Background of an inspector module or a framed list. */
|
|
3
6
|
sectionBg: 'rgba(10, 16, 34, 0.6)',
|
|
7
|
+
/** Borders and separators. */
|
|
4
8
|
border: '#2a365f',
|
|
9
|
+
/** The border of a selected or switched-on control. */
|
|
5
10
|
borderActive: '#4f6fbe',
|
|
11
|
+
/** Body text. */
|
|
6
12
|
text: '#eef3ff',
|
|
13
|
+
/** Labels and secondary text. */
|
|
7
14
|
textDim: '#b7c6ea',
|
|
15
|
+
/** Highlights, such as a Save button with something to save. */
|
|
8
16
|
accent: '#9eb9ff',
|
|
17
|
+
/** Background of inputs. */
|
|
9
18
|
inputBg: 'rgba(7, 11, 22, 0.82)',
|
|
19
|
+
/** The line of curve editors. */
|
|
10
20
|
curveStroke: '#78a5ff',
|
|
21
|
+
/** The editor's font stack. */
|
|
11
22
|
font: 'Inter, Segoe UI, Roboto, sans-serif',
|
|
12
23
|
};
|
|
24
|
+
/** Style of a text or number input. */
|
|
13
25
|
export const inputStyle = {
|
|
14
26
|
background: theme.inputBg,
|
|
15
27
|
border: `1px solid ${theme.border}`,
|
|
@@ -22,6 +34,7 @@ export const inputStyle = {
|
|
|
22
34
|
boxSizing: 'border-box',
|
|
23
35
|
fontFamily: theme.font,
|
|
24
36
|
};
|
|
37
|
+
/** Style of an inspector row: a label column, then the controls. */
|
|
25
38
|
export const rowStyle = {
|
|
26
39
|
display: 'grid',
|
|
27
40
|
gridTemplateColumns: '110px 1fr',
|
|
@@ -29,6 +42,7 @@ export const rowStyle = {
|
|
|
29
42
|
gap: 8,
|
|
30
43
|
marginTop: 6,
|
|
31
44
|
};
|
|
45
|
+
/** Style of an inspector row's label. */
|
|
32
46
|
export const labelStyle = {
|
|
33
47
|
color: theme.textDim,
|
|
34
48
|
fontSize: 12,
|
|
@@ -36,6 +50,7 @@ export const labelStyle = {
|
|
|
36
50
|
overflow: 'hidden',
|
|
37
51
|
textOverflow: 'ellipsis',
|
|
38
52
|
};
|
|
53
|
+
/** Style of a button. */
|
|
39
54
|
export const buttonStyle = {
|
|
40
55
|
border: `1px solid #4563ad`,
|
|
41
56
|
background: 'rgba(28, 48, 98, 0.55)',
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The editor's headless session: scene, camera, gizmos, history, scene entries, assets, prefab
|
|
3
|
+
* mode and project save/load, without React. It builds the whole viewport (grid, gizmos, markers),
|
|
4
|
+
* so it has its own entry point and the UI-free `babylon.quarks-editor` entry stays light.
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
7
|
+
*/
|
|
8
|
+
export { createEditorSession } from './core/editorSession.js';
|
|
9
|
+
export type { EditorFrameStats, EditorProject, EditorSession, EditorSessionOptions, EditorSessionView, EffectEditorHostHandle, PrefabInfo, } from './core/editorSession.js';
|
package/dist/session.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The editor's headless session: scene, camera, gizmos, history, scene entries, assets, prefab
|
|
3
|
+
* mode and project save/load, without React. It builds the whole viewport (grid, gizmos, markers),
|
|
4
|
+
* so it has its own entry point and the UI-free `babylon.quarks-editor` entry stays light.
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
7
|
+
*/
|
|
8
|
+
export { createEditorSession } from './core/editorSession.js';
|
package/package.json
CHANGED
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "babylon.quarks-editor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.0",
|
|
4
4
|
"description": "Shuriken-style effect editor for babylon.quarks: headless edit-model core + React module-stack UI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
|
-
"
|
|
11
|
-
"
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./session": {
|
|
14
|
+
"types": "./dist/session.d.ts",
|
|
15
|
+
"import": "./dist/session.js"
|
|
12
16
|
},
|
|
13
17
|
"./react": {
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
}
|
|
18
|
+
"types": "./dist/react/index.d.ts",
|
|
19
|
+
"import": "./dist/react/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./package.json": "./package.json"
|
|
17
22
|
},
|
|
18
23
|
"files": [
|
|
19
24
|
"dist",
|
|
@@ -25,7 +30,8 @@
|
|
|
25
30
|
"scripts": {
|
|
26
31
|
"clean": "rimraf dist",
|
|
27
32
|
"build": "npm run clean && tsc -p tsconfig.json",
|
|
28
|
-
"test": "jest"
|
|
33
|
+
"test": "jest",
|
|
34
|
+
"docs:check": "typedoc --out node_modules/.cache/typedoc-check"
|
|
29
35
|
},
|
|
30
36
|
"repository": {
|
|
31
37
|
"type": "git",
|
|
@@ -51,11 +57,11 @@
|
|
|
51
57
|
},
|
|
52
58
|
"homepage": "https://github.com/Soullnik/babylon.quarks-standalone#readme",
|
|
53
59
|
"peerDependencies": {
|
|
54
|
-
"@babylonjs/core": "
|
|
55
|
-
"@babylonjs/materials": "
|
|
60
|
+
"@babylonjs/core": "^9.0.0",
|
|
61
|
+
"@babylonjs/materials": "^9.0.0",
|
|
56
62
|
"@heroicons/react": ">=2.0.0",
|
|
57
63
|
"@tanstack/react-virtual": ">=3.0.0",
|
|
58
|
-
"babylon.quarks": "~0.
|
|
64
|
+
"babylon.quarks": "~0.28.0",
|
|
59
65
|
"react": ">=18",
|
|
60
66
|
"react-dom": ">=18"
|
|
61
67
|
},
|
|
@@ -69,6 +75,6 @@
|
|
|
69
75
|
"react": "^19.0.0",
|
|
70
76
|
"react-dom": "^19.0.0",
|
|
71
77
|
"rimraf": "^6.0.0",
|
|
72
|
-
"typescript": "^
|
|
78
|
+
"typescript": "^6.0.3"
|
|
73
79
|
}
|
|
74
80
|
}
|
package/src/core/assetTree.ts
CHANGED
|
@@ -85,7 +85,20 @@ export function searchAssets(assets: EffectAsset[], query: string): EffectAsset[
|
|
|
85
85
|
|
|
86
86
|
export const ASSET_DRAG_MIME = 'application/x-quarks-asset-drop';
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
/** What is dragged out of the Assets panel: one asset, or every asset under a folder. */
|
|
89
|
+
export type AssetDragPayload =
|
|
90
|
+
| {
|
|
91
|
+
/** One asset. */
|
|
92
|
+
kind: 'asset';
|
|
93
|
+
/** The asset's id. */
|
|
94
|
+
id: number;
|
|
95
|
+
}
|
|
96
|
+
| {
|
|
97
|
+
/** A folder. */
|
|
98
|
+
kind: 'folder';
|
|
99
|
+
/** The folder's path; every asset under it is dragged. */
|
|
100
|
+
path: string;
|
|
101
|
+
};
|
|
89
102
|
|
|
90
103
|
export function writeAssetDrag(dataTransfer: DataTransfer, payload: AssetDragPayload): void {
|
|
91
104
|
dataTransfer.setData(ASSET_DRAG_MIME, JSON.stringify(payload));
|
package/src/core/assets.ts
CHANGED
|
@@ -7,7 +7,9 @@ import {QuarksLoader} from 'babylon.quarks';
|
|
|
7
7
|
* fresh, independent copy (see `instantiateSceneEntry`).
|
|
8
8
|
*/
|
|
9
9
|
export interface EffectAsset {
|
|
10
|
+
/** The asset's id in this session; scene entries made from it keep it. */
|
|
10
11
|
id: number;
|
|
12
|
+
/** The asset's display name, its file name without `.json`. */
|
|
11
13
|
name: string;
|
|
12
14
|
/** Relative path inside the imported folder (preserves subfolders). */
|
|
13
15
|
path: string;
|
|
@@ -17,10 +19,15 @@ export interface EffectAsset {
|
|
|
17
19
|
missingFiles: string[];
|
|
18
20
|
}
|
|
19
21
|
|
|
22
|
+
/** How far an asset import got. */
|
|
20
23
|
export interface AssetLoadProgress {
|
|
24
|
+
/** JSON files read so far. */
|
|
21
25
|
done: number;
|
|
26
|
+
/** JSON files in the import. */
|
|
22
27
|
total: number;
|
|
28
|
+
/** Effects found so far. */
|
|
23
29
|
loaded: number;
|
|
30
|
+
/** Files that could not be read. */
|
|
24
31
|
failed: number;
|
|
25
32
|
}
|
|
26
33
|
|
|
@@ -75,6 +82,13 @@ function readAsDataUrl(file: File): Promise<string> {
|
|
|
75
82
|
});
|
|
76
83
|
}
|
|
77
84
|
|
|
85
|
+
/** An object with fields of unknown types, as an imported file's JSON may hold anywhere. */
|
|
86
|
+
type JSONRecord = {[key: string]: unknown};
|
|
87
|
+
|
|
88
|
+
function isRecord(value: unknown): value is JSONRecord {
|
|
89
|
+
return value !== null && typeof value === 'object';
|
|
90
|
+
}
|
|
91
|
+
|
|
78
92
|
/** True for a Quarks effect envelope with at least one particle emitter. */
|
|
79
93
|
export function isEffectJson(json: unknown): boolean {
|
|
80
94
|
const object = (json as {object?: unknown} | null)?.object;
|
|
@@ -166,7 +180,7 @@ function createFileResolver(effectPath: string, index: ImportedFileIndex, missin
|
|
|
166
180
|
* {@link QuarksLoader.resolveLinkedResources}, so the effect loads (and later exports) as a
|
|
167
181
|
* self-contained effect. Returns the references that could not be found among the files.
|
|
168
182
|
*/
|
|
169
|
-
async function inlineExternalFiles(json:
|
|
183
|
+
async function inlineExternalFiles(json: unknown, effectPath: string, index: ImportedFileIndex): Promise<string[]> {
|
|
170
184
|
const missing: string[] = [];
|
|
171
185
|
// baseUrl is the effect file path so resolveRelativePath can walk from its folder; the
|
|
172
186
|
// File resolver ignores HTTP join and looks up files by path/name instead.
|
|
@@ -263,22 +277,23 @@ export function assetIconUrl(asset: EffectAsset): string | null {
|
|
|
263
277
|
if (cached !== undefined) {
|
|
264
278
|
return cached;
|
|
265
279
|
}
|
|
266
|
-
const json = asset.json
|
|
267
|
-
const findMaterial = (node:
|
|
268
|
-
if (!node
|
|
269
|
-
if (node.type === 'ParticleEmitter' && node.ps
|
|
280
|
+
const json = isRecord(asset.json) ? asset.json : {};
|
|
281
|
+
const findMaterial = (node: unknown): unknown => {
|
|
282
|
+
if (!isRecord(node)) return undefined;
|
|
283
|
+
if (node.type === 'ParticleEmitter' && isRecord(node.ps) && node.ps.material) return node.ps.material;
|
|
270
284
|
for (const child of Array.isArray(node.children) ? node.children : []) {
|
|
271
285
|
const found = findMaterial(child);
|
|
272
286
|
if (found) return found;
|
|
273
287
|
}
|
|
274
288
|
return undefined;
|
|
275
289
|
};
|
|
276
|
-
const byUuid = (list: unknown, uuid: unknown) =>
|
|
277
|
-
(Array.isArray(list) ? list : []).find((item:
|
|
290
|
+
const byUuid = (list: unknown, uuid: unknown): JSONRecord | undefined =>
|
|
291
|
+
(Array.isArray(list) ? list : []).find((item: unknown) => isRecord(item) && item.uuid === uuid);
|
|
278
292
|
|
|
279
|
-
const material = byUuid(json
|
|
280
|
-
const texture = byUuid(json
|
|
281
|
-
const
|
|
293
|
+
const material = byUuid(json.materials, findMaterial(json.object));
|
|
294
|
+
const texture = byUuid(json.textures, material?.map ?? material?.texture);
|
|
295
|
+
const firstImage: unknown = Array.isArray(json.images) ? json.images[0] : undefined;
|
|
296
|
+
const image = byUuid(json.images, texture?.image) ?? (isRecord(firstImage) ? firstImage : undefined);
|
|
282
297
|
const url = typeof image?.url === 'string' && image.url ? image.url : null;
|
|
283
298
|
iconCache.set(asset, url);
|
|
284
299
|
return url;
|
package/src/core/binding.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type {ParticleSystem} from 'babylon.quarks';
|
|
|
3
3
|
import type {EffectTreeNode} from './effectTree.js';
|
|
4
4
|
import {buildEffectTree, serializeEffectTree} from './effectTree.js';
|
|
5
5
|
|
|
6
|
+
/** Called after every change made through an {@link EffectBinding}. */
|
|
6
7
|
export type EditorListener = () => void;
|
|
7
8
|
|
|
8
9
|
/**
|
|
@@ -32,6 +33,7 @@ export class EffectBinding {
|
|
|
32
33
|
return buildEffectTree(this.root);
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
/** Calls `listener` after every change; returns the call that stops it. */
|
|
35
37
|
subscribe(listener: EditorListener): () => void {
|
|
36
38
|
this.listeners.add(listener);
|
|
37
39
|
return () => this.listeners.delete(listener);
|
|
@@ -42,17 +44,23 @@ export class EffectBinding {
|
|
|
42
44
|
return this.revision;
|
|
43
45
|
}
|
|
44
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Changes the main system through `mutate`: takes an undo snapshot first, then notifies
|
|
49
|
+
* subscribers. Every inspector edit goes through here.
|
|
50
|
+
*/
|
|
45
51
|
apply(mutate: (system: ParticleSystem) => void): void {
|
|
46
52
|
this.onBeforeChange?.();
|
|
47
53
|
mutate(this.system);
|
|
48
54
|
this.notify();
|
|
49
55
|
}
|
|
50
56
|
|
|
57
|
+
/** Adds a system to the effect's sub systems. */
|
|
51
58
|
addSubSystem(sub: ParticleSystem): void {
|
|
52
59
|
this.subSystems.push(sub);
|
|
53
60
|
this.notify();
|
|
54
61
|
}
|
|
55
62
|
|
|
63
|
+
/** Removes a system from the effect's sub systems; a system it does not have is ignored. */
|
|
56
64
|
removeSubSystem(sub: ParticleSystem): void {
|
|
57
65
|
const index = this.subSystems.indexOf(sub);
|
|
58
66
|
if (index >= 0) {
|
|
@@ -92,6 +100,7 @@ export class EffectBinding {
|
|
|
92
100
|
}
|
|
93
101
|
}
|
|
94
102
|
|
|
103
|
+
/** Restarts every system of the effect from time 0. */
|
|
95
104
|
restart(): void {
|
|
96
105
|
// onlyUsedByOther systems skip self-emission in update(); play() only unpauses sim.
|
|
97
106
|
for (const system of [this.system, ...this.subSystems]) {
|
package/src/core/collision.ts
CHANGED
|
@@ -8,8 +8,12 @@ import {Vector3, getPhysicsResolver, setPhysicsResolver} from 'babylon.quarks';
|
|
|
8
8
|
* which is exactly what ApplyCollision.update reflects the velocity against.
|
|
9
9
|
*/
|
|
10
10
|
export class GroundPlaneResolver {
|
|
11
|
+
/** @param y - Height of the ground plane. */
|
|
11
12
|
constructor(public y = 0) {}
|
|
12
13
|
|
|
14
|
+
/**
|
|
15
|
+
* True when `position` is at or below the plane, with the plane's normal written to `normal`.
|
|
16
|
+
*/
|
|
13
17
|
resolve(position: Vector3, normal: Vector3): boolean {
|
|
14
18
|
if (position.y <= this.y) {
|
|
15
19
|
normal.set(0, 1, 0);
|
package/src/core/colors.ts
CHANGED
|
@@ -3,18 +3,25 @@ import {ColorOverLife, Gradient, Vector3, Vector4} from 'babylon.quarks';
|
|
|
3
3
|
|
|
4
4
|
/** One RGBA stop of the editable gradient (all channels 0..1, pos 0..1). */
|
|
5
5
|
export interface GradientStop {
|
|
6
|
+
/** Where the stop sits over the particle's life, 0..1. */
|
|
6
7
|
pos: number;
|
|
8
|
+
/** Red, 0..1. */
|
|
7
9
|
r: number;
|
|
10
|
+
/** Green, 0..1. */
|
|
8
11
|
g: number;
|
|
12
|
+
/** Blue, 0..1. */
|
|
9
13
|
b: number;
|
|
14
|
+
/** Alpha, 0..1. */
|
|
10
15
|
a: number;
|
|
11
16
|
}
|
|
12
17
|
|
|
18
|
+
/** The gradient a new Color over Lifetime starts with: warm yellow fading out through red. */
|
|
13
19
|
export const DEFAULT_GRADIENT_STOPS: GradientStop[] = [
|
|
14
20
|
{pos: 0, r: 1, g: 0.85, b: 0.4, a: 1},
|
|
15
21
|
{pos: 1, r: 1, g: 0.25, b: 0.05, a: 0},
|
|
16
22
|
];
|
|
17
23
|
|
|
24
|
+
/** A quarks gradient through `stops`, sorted by position; colour and alpha share the stops. */
|
|
18
25
|
export function buildGradient(stops: GradientStop[]): Gradient {
|
|
19
26
|
const sorted = [...stops].sort((a, b) => a.pos - b.pos);
|
|
20
27
|
return new Gradient(
|
|
@@ -23,26 +30,38 @@ export function buildGradient(stops: GradientStop[]): Gradient {
|
|
|
23
30
|
);
|
|
24
31
|
}
|
|
25
32
|
|
|
33
|
+
/** A Color over Lifetime behavior through `stops`. */
|
|
26
34
|
export function buildColorOverLife(stops: GradientStop[]): ColorOverLife {
|
|
27
35
|
return new ColorOverLife(buildGradient(stops));
|
|
28
36
|
}
|
|
29
37
|
|
|
38
|
+
/** An RGBA colour as a quarks `Vector4`. */
|
|
30
39
|
export function rgbaToVector4(stop: {r: number; g: number; b: number; a: number}): Vector4 {
|
|
31
40
|
return new Vector4(stop.r, stop.g, stop.b, stop.a);
|
|
32
41
|
}
|
|
33
42
|
|
|
43
|
+
/** A stop's colour as CSS `rgba()`. */
|
|
34
44
|
export function stopToCss(stop: GradientStop): string {
|
|
35
45
|
const c = (v: number) => Math.round(v * 255);
|
|
36
46
|
return `rgba(${c(stop.r)}, ${c(stop.g)}, ${c(stop.b)}, ${stop.a.toFixed(2)})`;
|
|
37
47
|
}
|
|
38
48
|
|
|
49
|
+
/** The stops as a left-to-right CSS `linear-gradient`, for a gradient preview. */
|
|
39
50
|
export function stopsToCssGradient(stops: GradientStop[]): string {
|
|
40
51
|
const sorted = [...stops].sort((a, b) => a.pos - b.pos);
|
|
41
52
|
const parts = sorted.map((s) => `${stopToCss(s)} ${(s.pos * 100).toFixed(1)}%`);
|
|
42
53
|
return `linear-gradient(90deg, ${parts.join(', ')})`;
|
|
43
54
|
}
|
|
44
55
|
|
|
45
|
-
|
|
56
|
+
/** A `#rrggbb` colour as channels 0..1. */
|
|
57
|
+
export function hexToRgb(hex: string): {
|
|
58
|
+
/** Red, 0..1. */
|
|
59
|
+
r: number;
|
|
60
|
+
/** Green, 0..1. */
|
|
61
|
+
g: number;
|
|
62
|
+
/** Blue, 0..1. */
|
|
63
|
+
b: number;
|
|
64
|
+
} {
|
|
46
65
|
const value = hex.replace('#', '');
|
|
47
66
|
return {
|
|
48
67
|
r: parseInt(value.slice(0, 2), 16) / 255,
|
|
@@ -51,6 +70,7 @@ export function hexToRgb(hex: string): {r: number; g: number; b: number} {
|
|
|
51
70
|
};
|
|
52
71
|
}
|
|
53
72
|
|
|
73
|
+
/** Channels 0..1, clamped, as a `#rrggbb` colour. */
|
|
54
74
|
export function rgbToHex(r: number, g: number, b: number): string {
|
|
55
75
|
const c = (v: number) =>
|
|
56
76
|
Math.round(Math.min(1, Math.max(0, v)) * 255)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type {Texture} from '@babylonjs/core/Materials/Textures/texture.js';
|
|
1
2
|
import {TransformNode} from '@babylonjs/core/Meshes/transformNode.js';
|
|
2
3
|
import type {Scene} from '@babylonjs/core/scene.js';
|
|
3
4
|
import {
|
|
@@ -14,14 +15,14 @@ import {DEFAULT_GRADIENT_STOPS, buildGradient} from './colors.js';
|
|
|
14
15
|
import {uniqueEntryName, type SceneEntry} from './sceneEntries.js';
|
|
15
16
|
|
|
16
17
|
export interface CreateSceneDefaultOptions {
|
|
17
|
-
resolveTexture?: (url: string, scene: Scene) =>
|
|
18
|
+
resolveTexture?: (url: string, scene: Scene) => Texture | null | undefined;
|
|
18
19
|
textureUrl?: string;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
/** Creates a simple cone burst preset for new editor effects. */
|
|
22
23
|
export function createDefaultEffect(
|
|
23
24
|
scene: Scene,
|
|
24
|
-
resolveTexture?: (url: string, scene: Scene) =>
|
|
25
|
+
resolveTexture?: (url: string, scene: Scene) => Texture | null | undefined,
|
|
25
26
|
textureUrl?: string
|
|
26
27
|
): ParticleSystem {
|
|
27
28
|
const system = new ParticleSystem({
|
|
@@ -35,7 +36,7 @@ export function createDefaultEffect(
|
|
|
35
36
|
emissionOverTime: new ConstantValue(80),
|
|
36
37
|
shape: new ConeEmitter({radius: 0.3, angle: 0.25}),
|
|
37
38
|
renderMode: RenderMode.BillBoard,
|
|
38
|
-
texture:
|
|
39
|
+
texture: textureUrl && resolveTexture ? resolveTexture(textureUrl, scene) : undefined,
|
|
39
40
|
transparent: true,
|
|
40
41
|
blendMode: 1,
|
|
41
42
|
});
|