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.
- package/CHANGELOG.md +134 -0
- package/README.md +69 -9
- package/dist/core/assetDragSession.d.ts +1 -1
- package/dist/core/assetTree.d.ts +6 -1
- package/dist/core/assets.d.ts +7 -0
- package/dist/core/assets.js +11 -7
- package/dist/core/binding.d.ts +11 -2
- package/dist/core/binding.js +9 -1
- 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 +5 -4
- package/dist/core/defaultEffect.js +4 -4
- package/dist/core/editorScene.d.ts +1 -1
- package/dist/core/editorSession.d.ts +229 -0
- package/dist/core/editorSession.js +983 -0
- package/dist/core/effectTree.d.ts +6 -1
- package/dist/core/effectTree.js +13 -88
- package/dist/core/emitterShapeWireframe.d.ts +2 -2
- package/dist/core/emitterShapeWireframe.js +45 -13
- package/dist/core/galleryDropMarker.d.ts +2 -2
- package/dist/core/galleryDropMarker.js +6 -6
- package/dist/core/galleryLayout.d.ts +3 -3
- package/dist/core/galleryLayout.js +1 -1
- package/dist/core/galleryPlayback.d.ts +1 -1
- package/dist/core/galleryPlayback.js +2 -1
- package/dist/core/galleryScene.d.ts +4 -4
- package/dist/core/galleryScene.js +3 -3
- package/dist/core/gallerySelectionMarker.d.ts +4 -4
- package/dist/core/gallerySelectionMarker.js +5 -5
- package/dist/core/groups.d.ts +1 -1
- package/dist/core/groups.js +1 -1
- package/dist/core/history.d.ts +7 -1
- package/dist/core/history.js +4 -0
- package/dist/core/indexedDbProjectStore.d.ts +14 -1
- package/dist/core/indexedDbProjectStore.js +13 -0
- package/dist/core/lineShaders.d.ts +2 -2
- package/dist/core/lineShaders.js +2 -2
- package/dist/core/loadEffect.d.ts +2 -2
- package/dist/core/loadEffect.js +1 -1
- package/dist/core/material.d.ts +12 -2
- package/dist/core/pickAssetFiles.js +1 -1
- 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 +7 -4
- package/dist/core/sceneEntries.js +2 -2
- package/dist/core/scrub.d.ts +2 -2
- package/dist/core/scrub.js +2 -2
- package/dist/core/shapes.d.ts +25 -1
- package/dist/core/shapes.js +5 -0
- package/dist/core/systems.d.ts +4 -2
- package/dist/core/systems.js +3 -1
- package/dist/core/textureExport.d.ts +1 -1
- package/dist/core/timeline.d.ts +25 -5
- package/dist/core/transport.d.ts +3 -3
- package/dist/core/transport.js +2 -2
- package/dist/core/values.d.ts +21 -4
- package/dist/core/values.js +5 -0
- package/dist/core/viewportPicker.d.ts +4 -4
- package/dist/core/viewportPicker.js +8 -7
- package/dist/index.d.ts +25 -21
- package/dist/index.js +12 -12
- package/dist/react/AssetsPanel.d.ts +9 -2
- package/dist/react/AssetsPanel.js +6 -6
- package/dist/react/ChoiceDialog.d.ts +7 -0
- package/dist/react/ChoiceDialog.js +1 -1
- package/dist/react/ContextMenu.d.ts +10 -1
- package/dist/react/ContextMenu.js +1 -1
- package/dist/react/CurveEditor.d.ts +1 -1
- package/dist/react/CurveEditor.js +2 -2
- package/dist/react/EditorWorkspace.d.ts +9 -2
- package/dist/react/EditorWorkspace.js +10 -7
- package/dist/react/EffectEditor.d.ts +11 -5
- package/dist/react/EffectEditor.js +7 -7
- package/dist/react/EffectEditorHost.d.ts +33 -29
- package/dist/react/EffectEditorHost.js +247 -1674
- package/dist/react/EffectTreeView.d.ts +4 -1
- package/dist/react/EffectTreeView.js +3 -3
- package/dist/react/GradientEditor.d.ts +1 -1
- package/dist/react/GradientEditor.js +3 -3
- package/dist/react/InspectorInfoIcon.js +3 -3
- package/dist/react/InspectorLabel.js +3 -3
- package/dist/react/MessageDialog.d.ts +5 -0
- package/dist/react/MessageDialog.js +1 -1
- package/dist/react/ModuleSection.d.ts +10 -0
- package/dist/react/ModuleSection.js +8 -5
- package/dist/react/ObjectModule.d.ts +1 -1
- package/dist/react/ObjectModule.js +4 -4
- package/dist/react/ProjectsPage.d.ts +7 -1
- package/dist/react/ProjectsPage.js +33 -17
- package/dist/react/PromptDialog.d.ts +7 -0
- package/dist/react/PromptDialog.js +1 -1
- package/dist/react/ScenePanel.d.ts +9 -2
- package/dist/react/ScenePanel.js +12 -10
- package/dist/react/SubEmittersModule.d.ts +1 -1
- package/dist/react/SubEmittersModule.js +5 -5
- package/dist/react/TextureSelect.d.ts +24 -0
- package/dist/react/TextureSelect.js +74 -0
- package/dist/react/TextureUVField.d.ts +14 -0
- package/dist/react/TextureUVField.js +23 -0
- package/dist/react/TimelinePanel.d.ts +20 -7
- package/dist/react/TimelinePanel.js +21 -15
- package/dist/react/TimelineTrackRow.d.ts +13 -3
- package/dist/react/TimelineTrackRow.js +6 -6
- package/dist/react/TransportBar.d.ts +13 -4
- package/dist/react/TransportBar.js +3 -3
- package/dist/react/ValueField.d.ts +32 -5
- package/dist/react/ValueField.js +22 -11
- package/dist/react/behaviorModules.d.ts +19 -6
- package/dist/react/behaviorModules.js +68 -17
- package/dist/react/fields.d.ts +9 -0
- package/dist/react/fields.js +6 -3
- 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 +42 -38
- package/dist/react/index.js +25 -23
- package/dist/react/inspectorHints.js +31 -0
- package/dist/react/modules.d.ts +19 -6
- package/dist/react/modules.js +134 -117
- 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 +18 -11
- package/src/core/assetDragSession.ts +1 -1
- package/src/core/assetTree.ts +15 -2
- package/src/core/assets.ts +25 -10
- package/src/core/binding.ts +12 -3
- package/src/core/collision.ts +4 -0
- package/src/core/colors.ts +21 -1
- package/src/core/defaultEffect.ts +8 -7
- package/src/core/editorScene.ts +1 -1
- package/src/core/editorSession.ts +1299 -0
- package/src/core/effectTree.ts +18 -101
- package/src/core/emitterShapeWireframe.ts +48 -16
- package/src/core/galleryDropMarker.ts +8 -8
- package/src/core/galleryLayout.ts +4 -4
- package/src/core/galleryPlayback.ts +3 -2
- package/src/core/galleryScene.ts +5 -5
- package/src/core/gallerySelectionMarker.ts +7 -7
- package/src/core/groups.ts +1 -1
- package/src/core/history.ts +7 -1
- package/src/core/indexedDbProjectStore.ts +14 -1
- package/src/core/lineShaders.ts +2 -2
- package/src/core/loadEffect.ts +4 -4
- package/src/core/material.ts +14 -4
- package/src/core/pickAssetFiles.ts +2 -2
- package/src/core/playback.ts +6 -0
- package/src/core/projectStore.ts +26 -0
- package/src/core/sceneEntries.ts +8 -5
- package/src/core/scrub.ts +3 -3
- package/src/core/shapes.ts +29 -2
- package/src/core/systems.ts +11 -4
- package/src/core/textureExport.ts +1 -1
- package/src/core/timeline.ts +43 -18
- package/src/core/transport.ts +4 -4
- package/src/core/values.ts +20 -4
- package/src/core/viewportPicker.ts +12 -11
- package/src/index.ts +25 -21
- package/src/react/AssetsPanel.tsx +13 -6
- package/src/react/ChoiceDialog.tsx +8 -1
- package/src/react/ContextMenu.tsx +11 -2
- package/src/react/CurveEditor.tsx +2 -2
- package/src/react/EditorWorkspace.tsx +16 -10
- package/src/react/EffectEditor.tsx +17 -9
- package/src/react/EffectEditorHost.tsx +415 -2354
- package/src/react/EffectTreeView.tsx +7 -4
- package/src/react/GradientEditor.tsx +3 -3
- package/src/react/InspectorInfoIcon.tsx +3 -3
- package/src/react/InspectorLabel.tsx +3 -3
- package/src/react/MessageDialog.tsx +6 -1
- package/src/react/ModuleSection.tsx +15 -4
- package/src/react/ObjectModule.tsx +5 -5
- package/src/react/ProjectsPage.tsx +44 -17
- package/src/react/PromptDialog.tsx +8 -1
- package/src/react/ScenePanel.tsx +21 -12
- package/src/react/SubEmittersModule.tsx +6 -6
- package/src/react/TextureSelect.tsx +118 -0
- package/src/react/TextureUVField.tsx +62 -0
- package/src/react/TimelinePanel.tsx +40 -21
- package/src/react/TimelineTrackRow.tsx +19 -9
- package/src/react/TransportBar.tsx +16 -7
- package/src/react/ValueField.tsx +60 -16
- package/src/react/behaviorModules.tsx +240 -55
- package/src/react/fields.tsx +22 -4
- 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 +43 -38
- package/src/react/inspectorHints.ts +40 -0
- package/src/react/modules.tsx +440 -170
- package/src/react/theme.ts +15 -0
- package/src/session.ts +17 -0
package/dist/react/modules.js
CHANGED
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { Texture } from '@babylonjs/core/Materials/Textures/texture';
|
|
3
|
-
import { Mesh } from '@babylonjs/core/Meshes/mesh';
|
|
2
|
+
import { Texture } from '@babylonjs/core/Materials/Textures/texture.js';
|
|
3
|
+
import { Mesh } from '@babylonjs/core/Meshes/mesh.js';
|
|
4
4
|
import { PlusIcon, XMarkIcon } from '@heroicons/react/24/solid';
|
|
5
|
-
import { AxisAngleGenerator, BoxEmitFrom, ColorOverLife, ColorRange, ConstantColor, ConstantValue, EulerGenerator, Gradient, IntervalValue, RandomColor, RandomColorBetweenGradient, RandomQuatGenerator, RenderMode, SizeOverLife, Vector3, Vector3Function, Vector4, } from 'babylon.quarks';
|
|
6
|
-
import React, { useMemo
|
|
7
|
-
import { DEFAULT_GRADIENT_STOPS, buildGradient, findBehavior, hexToRgb, readGradientStops, rgbToHex, } from '../core/colors';
|
|
8
|
-
import { applyRendererMaterial, getMaterialLabel } from '../core/material';
|
|
9
|
-
import { DEFAULT_SHAPE_PARAMS, SHAPE_PARAM_KEYS, SHAPE_TYPES, createShape, getShapeType, readShapeParams, } from '../core/shapes';
|
|
10
|
-
import { buildCurve, buildScalar, readPieces, readScalar } from '../core/values';
|
|
11
|
-
import { CurveEditor } from './CurveEditor';
|
|
12
|
-
import { GradientEditor } from './GradientEditor';
|
|
13
|
-
import { InspectorInfoIcon } from './InspectorInfoIcon';
|
|
14
|
-
import { ModuleSection } from './ModuleSection';
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
5
|
+
import { AxisAngleGenerator, BoxEmitFrom, ColorOverLife, ColorRange, ConstantColor, ConstantValue, EulerGenerator, Gradient, IntervalValue, RandomColor, RandomColorBetweenGradient, RandomQuatGenerator, RenderMode, SizeOverLife, Vector2, Vector3, Vector3Function, Vector4, normalizeTextureUV, } from 'babylon.quarks';
|
|
6
|
+
import React, { useMemo } from 'react';
|
|
7
|
+
import { DEFAULT_GRADIENT_STOPS, buildGradient, findBehavior, hexToRgb, readGradientStops, rgbToHex, } from '../core/colors.js';
|
|
8
|
+
import { applyRendererMaterial, getMaterialLabel } from '../core/material.js';
|
|
9
|
+
import { DEFAULT_SHAPE_PARAMS, SHAPE_PARAM_KEYS, SHAPE_TYPES, createShape, getShapeType, readShapeParams, } from '../core/shapes.js';
|
|
10
|
+
import { buildCurve, buildScalar, readPieces, readScalar } from '../core/values.js';
|
|
11
|
+
import { CurveEditor } from './CurveEditor.js';
|
|
12
|
+
import { GradientEditor } from './GradientEditor.js';
|
|
13
|
+
import { InspectorInfoIcon } from './InspectorInfoIcon.js';
|
|
14
|
+
import { ModuleSection } from './ModuleSection.js';
|
|
15
|
+
import { TextureSelect } from './TextureSelect.js';
|
|
16
|
+
import { TextureUVField } from './TextureUVField.js';
|
|
17
|
+
import { ValueField } from './ValueField.js';
|
|
18
|
+
import { CheckboxField, NumberField, Row, SelectField } from './fields.js';
|
|
19
|
+
import { iconStyle } from './icons.js';
|
|
20
|
+
import { theme } from './theme.js';
|
|
20
21
|
const MESH_QUAD_POSITIONS = new Float32Array([-0.5, -0.5, 0, 0.5, -0.5, 0, 0.5, 0.5, 0, -0.5, 0.5, 0]);
|
|
21
22
|
const MESH_QUAD_INDICES = new Uint32Array([0, 1, 2, 0, 2, 3]);
|
|
22
23
|
const MESH_QUAD_UVS = new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]);
|
|
@@ -61,6 +62,29 @@ const PARAM_LABELS = {
|
|
|
61
62
|
depth: 'Depth',
|
|
62
63
|
speed: 'Speed (lengths/s)',
|
|
63
64
|
};
|
|
65
|
+
/** Uppercase group title inside a module, as the Renderer's "Material" heading. */
|
|
66
|
+
function SubHeader(props) {
|
|
67
|
+
return (_jsxs("div", { style: {
|
|
68
|
+
marginTop: 10,
|
|
69
|
+
marginBottom: 2,
|
|
70
|
+
fontSize: 11,
|
|
71
|
+
fontWeight: 600,
|
|
72
|
+
color: '#7c8db5',
|
|
73
|
+
textTransform: 'uppercase',
|
|
74
|
+
letterSpacing: 0.4,
|
|
75
|
+
display: 'flex',
|
|
76
|
+
alignItems: 'center',
|
|
77
|
+
gap: 5,
|
|
78
|
+
}, children: [props.title, _jsx(InspectorInfoIcon, { content: props.hint, size: 12 })] }));
|
|
79
|
+
}
|
|
80
|
+
/** Three number inputs editing a Vector3 by value. */
|
|
81
|
+
function AxisField(props) {
|
|
82
|
+
return (_jsx("div", { style: { display: 'flex', gap: 6 }, children: ['x', 'y', 'z'].map((axis) => (_jsx(NumberField, { value: props.value[axis], step: 0.1, onChange: (v) => {
|
|
83
|
+
const next = props.value.clone();
|
|
84
|
+
next[axis] = v;
|
|
85
|
+
props.onChange(next);
|
|
86
|
+
} }, axis))) }));
|
|
87
|
+
}
|
|
64
88
|
function ColorInput(props) {
|
|
65
89
|
const { value, onChange } = props;
|
|
66
90
|
return (_jsxs("div", { style: { display: 'flex', gap: 6, alignItems: 'center' }, children: [_jsx("input", { type: "color", className: "qe-hover", value: rgbToHex(value.x, value.y, value.z), onChange: (e) => {
|
|
@@ -75,17 +99,19 @@ function ColorInput(props) {
|
|
|
75
99
|
cursor: 'pointer',
|
|
76
100
|
} }), _jsx(NumberField, { value: value.w, min: 0, step: 0.05, onChange: (a) => onChange(new Vector4(value.x, value.y, value.z, Math.min(1, a))) })] }));
|
|
77
101
|
}
|
|
102
|
+
/** The Main module (Unity's main module): duration, looping, start delay, life, speed, size, rotation and colour. */
|
|
78
103
|
export function MainModule({ binding }) {
|
|
79
104
|
const system = binding.system;
|
|
80
105
|
const startColor = system.startColor;
|
|
81
106
|
const colorMode = startColor instanceof RandomColor ? 'random' : startColor instanceof ColorRange ? 'range' : 'constant';
|
|
82
|
-
const
|
|
83
|
-
const colorA =
|
|
84
|
-
?
|
|
107
|
+
const twoColor = startColor instanceof RandomColor || startColor instanceof ColorRange ? startColor : null;
|
|
108
|
+
const colorA = twoColor
|
|
109
|
+
? twoColor.a
|
|
85
110
|
: startColor instanceof ConstantColor
|
|
86
111
|
? startColor.color
|
|
87
112
|
: new Vector4(1, 1, 1, 1);
|
|
88
|
-
const colorB =
|
|
113
|
+
const colorB = twoColor ? twoColor.b : new Vector4(1, 0.5, 0.2, 1);
|
|
114
|
+
const twoColors = twoColor !== null;
|
|
89
115
|
/** The two-colour generator of the current mode. */
|
|
90
116
|
const makeTwoColors = (a, b) => colorMode === 'range' ? new ColorRange(a, b) : new RandomColor(a, b);
|
|
91
117
|
const rotation = system.startRotation;
|
|
@@ -111,34 +137,36 @@ export function MainModule({ binding }) {
|
|
|
111
137
|
const cur = s.startRotation;
|
|
112
138
|
s.startRotation = new EulerGenerator(axis === 'x' ? new ConstantValue(v) : cur.angleX, axis === 'y' ? new ConstantValue(v) : cur.angleY, axis === 'z' ? new ConstantValue(v) : cur.angleZ);
|
|
113
139
|
});
|
|
114
|
-
const
|
|
115
|
-
const size3D = startSize.type === 'vec3function';
|
|
140
|
+
const startSize3D = system.startSize instanceof Vector3Function ? system.startSize : null;
|
|
116
141
|
const colorGenMode = startColor instanceof RandomColorBetweenGradient
|
|
117
142
|
? 'gradient2'
|
|
118
143
|
: startColor instanceof Gradient
|
|
119
144
|
? 'gradient'
|
|
120
145
|
: colorMode;
|
|
121
|
-
return (_jsxs(ModuleSection, { title: "Main", children: [_jsx(Row, { label: "Duration", children: _jsx(NumberField, { value: system.duration, min: 0.05, onChange: (v) => binding.apply((s) => (s.duration = v)) }) }), _jsx(Row, { label: "Looping", children: _jsx(CheckboxField, { value: system.looping, onChange: (v) => binding.apply((s) => (s.looping = v)) }) }), _jsx(Row, { label: "World space", children: _jsx(CheckboxField, { value: system.worldSpace, onChange: (v) => binding.apply((s) => (s.worldSpace = v)) }) }),
|
|
146
|
+
return (_jsxs(ModuleSection, { title: "Main", children: [_jsx(Row, { label: "Duration", children: _jsx(NumberField, { value: system.duration, min: 0.05, onChange: (v) => binding.apply((s) => (s.duration = v)) }) }), _jsx(Row, { label: "Looping", children: _jsx(CheckboxField, { value: system.looping, onChange: (v) => binding.apply((s) => (s.looping = v)) }) }), _jsx(Row, { label: "World space", children: _jsx(CheckboxField, { value: system.worldSpace, onChange: (v) => binding.apply((s) => (s.worldSpace = v)) }) }), system.worldSpace && (_jsx(Row, { label: "Scaling mode", children: _jsx(SelectField, { value: system.scalingMode, options: [
|
|
147
|
+
{ value: 'shape', label: 'Shape (sizes and shape only)' },
|
|
148
|
+
{ value: 'hierarchy', label: 'Hierarchy (motion scales too)' },
|
|
149
|
+
], onChange: (mode) => binding.apply((s) => (s.scalingMode = mode)) }) })), _jsx(Row, { label: "Prewarm", children: _jsx(CheckboxField, { value: system.prewarm, onChange: (v) => binding.apply((s) => (s.prewarm = v)) }) }), system.prewarm && (_jsx(Row, { label: "Warm-up time", children: _jsx(NumberField, { value: system.prewarmTime, step: 0.1, min: 0, onChange: (v) => binding.apply((s) => (s.prewarmTime = Math.max(0, v))) }) })), _jsx(Row, { label: "Playback speed", children: _jsx(NumberField, { value: system.playbackSpeed, step: 0.1, min: 0, onChange: (v) => binding.apply((s) => (s.playbackSpeed = Math.max(0, v))) }) }), _jsxs(Row, { label: "Fixed seed", children: [_jsx(CheckboxField, { value: system.seed !== undefined, onChange: (v) => binding.apply((s) => (s.seed = v ? (s.seed ?? 1) : undefined)) }), system.seed !== undefined && (_jsx(NumberField, { value: system.seed, step: 1, min: 0, onChange: (v) => binding.apply((s) => (s.seed = Math.max(0, Math.floor(v)))) }))] }), _jsx(Row, { label: "Off screen", children: _jsx(SelectField, { value: system.cullingMode, options: [
|
|
122
150
|
{ value: 'always', label: 'Always simulate' },
|
|
123
151
|
{ value: 'pause', label: 'Pause' },
|
|
124
152
|
{ value: 'pauseAndCatchUp', label: 'Pause and catch up' },
|
|
125
153
|
], onChange: (mode) => binding.apply((s) => (s.cullingMode = mode)) }) }), _jsx(ValueField, { label: "Start delay", generator: system.startDelay, min: 0, curveMax: 5, onChange: (g) => binding.apply((s) => {
|
|
126
154
|
s.startDelay = g;
|
|
127
155
|
s.restart();
|
|
128
|
-
}) }), _jsx(ValueField, { label: "Start lifetime", generator: system.startLife, min: 0.01, curveMax: 10, onChange: (g) => binding.apply((s) => (s.startLife = g)) }), _jsx(ValueField, { label: "Start speed", generator: system.startSpeed, min: 0, curveMax: 20, onChange: (g) => binding.apply((s) => (s.startSpeed = g)) }), _jsx(Row, { label: "Start size", children: _jsx(SelectField, { value:
|
|
156
|
+
}) }), _jsx(ValueField, { label: "Start lifetime", generator: system.startLife, min: 0.01, curveMax: 10, onChange: (g) => binding.apply((s) => (s.startLife = g)) }), _jsx(ValueField, { label: "Start speed", generator: system.startSpeed, min: 0, curveMax: 20, onChange: (g) => binding.apply((s) => (s.startSpeed = g)) }), _jsx(Row, { label: "Start size", children: _jsx(SelectField, { value: startSize3D ? '3d' : 'uniform', options: [
|
|
129
157
|
{ value: 'uniform', label: 'Uniform' },
|
|
130
158
|
{ value: '3d', label: '3D (per axis)' },
|
|
131
159
|
], onChange: (mode) => binding.apply((s) => {
|
|
132
160
|
const cur = s.startSize;
|
|
133
|
-
if (mode === '3d') {
|
|
161
|
+
if (mode === '3d' && cur.type !== 'vec3function') {
|
|
134
162
|
// Seed all three axes from the current uniform generator.
|
|
135
163
|
s.startSize = new Vector3Function(cur.clone(), cur.clone(), cur.clone());
|
|
136
164
|
}
|
|
137
|
-
else {
|
|
165
|
+
else if (mode !== '3d' && cur instanceof Vector3Function) {
|
|
138
166
|
// Collapse back to the X-axis generator.
|
|
139
|
-
s.startSize =
|
|
167
|
+
s.startSize = cur.x;
|
|
140
168
|
}
|
|
141
|
-
}) }) }), !
|
|
169
|
+
}) }) }), !startSize3D && (_jsx(ValueField, { label: "", generator: system.startSize, min: 0, curveMax: 5, onChange: (g) => binding.apply((s) => (s.startSize = g)) })), startSize3D && (_jsx(_Fragment, { children: ['x', 'y', 'z'].map((axis) => (_jsx(ValueField, { label: axis.toUpperCase(), generator: startSize3D[axis], min: 0, curveMax: 5, onChange: (g) => binding.apply((s) => {
|
|
142
170
|
const cur = s.startSize;
|
|
143
171
|
s.startSize = new Vector3Function(axis === 'x' ? g : cur.x, axis === 'y' ? g : cur.y, axis === 'z' ? g : cur.z);
|
|
144
172
|
}) }, axis))) })), _jsx(Row, { label: "Start color", children: _jsx(SelectField, { value: colorGenMode, options: [
|
|
@@ -194,6 +222,7 @@ export function MainModule({ binding }) {
|
|
|
194
222
|
s.startRotation = new AxisAngleGenerator(next, axisAngle.angle);
|
|
195
223
|
}) }, axis))) }) }), _jsx(ValueField, { label: " Angle (rad)", generator: axisAngle.angle, curveMax: Math.PI * 2, onChange: (g) => binding.apply((s) => (s.startRotation = new AxisAngleGenerator(axisAngle.axis.clone(), g))) })] })), rotationMode === 'angle' && (_jsx(Row, { label: "", children: _jsx(NumberField, { value: rotationState.value, step: 0.1, onChange: (v) => binding.apply((s) => (s.startRotation = new ConstantValue(v))) }) })), rotationMode === 'random' && (_jsx(Row, { label: "", children: _jsxs("div", { style: { display: 'flex', gap: 6 }, children: [_jsx(NumberField, { value: rotationState.min, step: 0.1, onChange: (v) => binding.apply((s) => (s.startRotation = new IntervalValue(v, rotationState.max))) }), _jsx(NumberField, { value: rotationState.max, step: 0.1, onChange: (v) => binding.apply((s) => (s.startRotation = new IntervalValue(rotationState.min, v))) })] }) }))] }));
|
|
196
224
|
}
|
|
225
|
+
/** The Emission module (Unity's Emission): rate over time and distance, and bursts. */
|
|
197
226
|
export function EmissionModule({ binding }) {
|
|
198
227
|
const system = binding.system;
|
|
199
228
|
const bursts = system.emissionBursts ?? [];
|
|
@@ -228,6 +257,7 @@ export function EmissionModule({ binding }) {
|
|
|
228
257
|
// for them would edit a value the shape silently ignores, just like the old "default to
|
|
229
258
|
// first texture" bug: a control that looks live but does nothing.
|
|
230
259
|
const SHAPES_WITHOUT_MODE_SPREAD = new Set(['point', 'grid', 'box', 'mesh_surface']);
|
|
260
|
+
/** The Shape module (Unity's Shape): the emitter shape and its settings. */
|
|
231
261
|
export function ShapeModule({ binding }) {
|
|
232
262
|
const system = binding.system;
|
|
233
263
|
const type = getShapeType(system.emitterShape);
|
|
@@ -253,12 +283,11 @@ export function ShapeModule({ binding }) {
|
|
|
253
283
|
{ value: 3, label: 'Burst spread' },
|
|
254
284
|
], onChange: (mode) => binding.apply((s) => (s.emitterShape = createShape(type, { ...params, mode }))) }) }), _jsx(Row, { label: "Spread", children: _jsx(NumberField, { value: params.spread, min: 0, step: 0.05, onChange: (spread) => binding.apply((s) => (s.emitterShape = createShape(type, { ...params, spread }))) }) })] })), _jsx(Row, { label: "Align to direction", children: _jsx(CheckboxField, { value: binding.system.alignToDirection, onChange: (v) => binding.apply((s) => (s.alignToDirection = v)) }) })] }));
|
|
255
285
|
}
|
|
286
|
+
/** The Size over Lifetime module (Unity's Size over Lifetime): size by a curve over the particle's life. */
|
|
256
287
|
export function SizeOverLifeModule({ binding }) {
|
|
257
288
|
const system = binding.system;
|
|
258
289
|
const behavior = findBehavior(system.behaviors, 'SizeOverLife');
|
|
259
|
-
const curve = behavior
|
|
260
|
-
? readScalar(behavior.size).curve
|
|
261
|
-
: [0.2, 1, 1, 0.4];
|
|
290
|
+
const curve = behavior ? readScalar(behavior.size).curve : [0.2, 1, 1, 0.4];
|
|
262
291
|
return (_jsx(ModuleSection, { title: "Size over Lifetime", enabled: !!behavior, onToggle: (enabled) => binding.apply((s) => {
|
|
263
292
|
removeBehavior(s, 'SizeOverLife');
|
|
264
293
|
if (enabled) {
|
|
@@ -269,6 +298,7 @@ export function SizeOverLifeModule({ binding }) {
|
|
|
269
298
|
s.addBehavior(new SizeOverLife(buildCurve(pieces)));
|
|
270
299
|
}) }) }) }));
|
|
271
300
|
}
|
|
301
|
+
/** The Color over Lifetime module (Unity's Color over Lifetime): a gradient over the particle's life. */
|
|
272
302
|
export function ColorOverLifeModule({ binding }) {
|
|
273
303
|
const system = binding.system;
|
|
274
304
|
const behavior = findBehavior(system.behaviors, 'ColorOverLife');
|
|
@@ -349,11 +379,16 @@ function MeshPreview({ positions, indices, size = 96, }) {
|
|
|
349
379
|
}, [positions, indices, size]);
|
|
350
380
|
return (_jsx("canvas", { ref: canvasRef, width: size, height: size, style: { width: size, height: size, border: '1px solid #2b3761', borderRadius: 6, background: '#0c1330' } }));
|
|
351
381
|
}
|
|
382
|
+
/** The Renderer module (Unity's Renderer): render mode, material, textures, sorting and mesh settings. */
|
|
352
383
|
export function RendererModule({ binding, textureOptions, geometryOptions, resolveTexture, resolveGeometry, }) {
|
|
353
384
|
const system = binding.system;
|
|
354
|
-
const currentTextureUrl = system.texture?.url ?? '';
|
|
355
|
-
const [promptingTextureUrl, setPromptingTextureUrl] = useState(false);
|
|
356
385
|
const materialLabel = getMaterialLabel(system.material);
|
|
386
|
+
const isMesh = system.renderMode === RenderMode.Mesh;
|
|
387
|
+
const gradientMap = system.gradientMap;
|
|
388
|
+
const erosion = system.erosion;
|
|
389
|
+
const distortion = system.uvDistortion;
|
|
390
|
+
const patchErosion = (patch) => binding.apply((s) => (s.erosion = { ...s.erosion, ...patch }));
|
|
391
|
+
const patchDistortion = (patch) => binding.apply((s) => (s.uvDistortion = { ...s.uvDistortion, ...patch }));
|
|
357
392
|
const loadTexture = useMemo(() => {
|
|
358
393
|
return (url) => {
|
|
359
394
|
if (resolveTexture) {
|
|
@@ -386,99 +421,76 @@ export function RendererModule({ binding, textureOptions, geometryOptions, resol
|
|
|
386
421
|
['dieWithParticle', 'Die with particle'],
|
|
387
422
|
].map(([key, label]) => (_jsx(Row, { label: label, children: _jsx(CheckboxField, { value: !!system.rendererEmitterSettings[key], onChange: (v) => binding.apply((s) => {
|
|
388
423
|
s.rendererEmitterSettings[key] = v;
|
|
389
|
-
}) }) }, key))), _jsx(Row, { label: "
|
|
390
|
-
s.rendererEmitterSettings.
|
|
391
|
-
}) }) })
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
424
|
+
}) }) }, key))), _jsx(Row, { label: "Tessellation", children: _jsx(NumberField, { value: system.rendererEmitterSettings.tessellation ?? 0, min: 0, max: 16, step: 1, onChange: (v) => binding.apply((s) => {
|
|
425
|
+
s.rendererEmitterSettings.tessellation = Math.round(v);
|
|
426
|
+
}) }) }), _jsx(Row, { label: "Curve tension", children: _jsx(NumberField, { value: system.rendererEmitterSettings.curveTension ?? 0, min: 0, max: 1, step: 0.05, onChange: (v) => binding.apply((s) => {
|
|
427
|
+
s.rendererEmitterSettings.curveTension = v;
|
|
428
|
+
}) }) }), _jsx(Row, { label: "Texture mode", children: _jsx(SelectField, { value: system.rendererEmitterSettings.textureMode ?? 'stretch', options: [
|
|
429
|
+
{ value: 'stretch', label: 'Stretch (once, by distance)' },
|
|
430
|
+
{ value: 'distributePerSegment', label: 'Distribute per segment' },
|
|
431
|
+
{ value: 'tile', label: 'Tile (per world unit)' },
|
|
432
|
+
{ value: 'repeatPerSegment', label: 'Repeat per segment' },
|
|
433
|
+
], onChange: (mode) => binding.apply((s) => {
|
|
434
|
+
s.rendererEmitterSettings.textureMode =
|
|
435
|
+
mode === 'stretch' ? undefined : mode;
|
|
436
|
+
}) }) }), _jsx(Row, { label: "Texture scale", children: _jsx(NumberField, { value: system.rendererEmitterSettings.textureScale ?? 1, min: 0.01, step: 0.1, onChange: (v) => binding.apply((s) => {
|
|
437
|
+
s.rendererEmitterSettings.textureScale = v === 1 ? undefined : v;
|
|
438
|
+
}) }) }), _jsx(Row, { label: "Min vertex distance", children: _jsx(NumberField, { value: system.rendererEmitterSettings.minVertexDistance ?? 0, min: 0, step: 0.05, onChange: (v) => binding.apply((s) => {
|
|
439
|
+
s.rendererEmitterSettings.minVertexDistance =
|
|
440
|
+
v > 0 ? v : undefined;
|
|
441
|
+
}) }) }), _jsx(Row, { label: "Trail time", children: _jsx(NumberField, { value: system.rendererEmitterSettings.lifetime ?? 0, min: 0, step: 0.1, onChange: (v) => binding.apply((s) => {
|
|
442
|
+
s.rendererEmitterSettings.lifetime = v > 0 ? v : undefined;
|
|
443
|
+
}) }) }), _jsx(Row, { label: "Swept trail", children: _jsx(CheckboxField, { value: !!system.rendererEmitterSettings.sideAxis, onChange: (v) => binding.apply((s) => {
|
|
444
|
+
s.rendererEmitterSettings.sideAxis = v
|
|
445
|
+
? new Vector3(0, 1, 0)
|
|
446
|
+
: undefined;
|
|
447
|
+
}) }) }), system.rendererEmitterSettings.sideAxis && (_jsx(Row, { label: "Side axis", children: _jsx(AxisField, { value: system.rendererEmitterSettings.sideAxis, onChange: (axis) => binding.apply((s) => {
|
|
448
|
+
// The axis's length scales the width: keep it a unit vector.
|
|
449
|
+
s.rendererEmitterSettings.sideAxis =
|
|
450
|
+
axis.length() > 0 ? axis.normalize() : axis;
|
|
451
|
+
}) }) }))] })), system.renderMode === RenderMode.StretchedBillBoard && (_jsxs(_Fragment, { children: [_jsx(Row, { label: "Speed factor", children: _jsx(NumberField, { value: system.rendererEmitterSettings.speedFactor ?? 0, step: 0.1, onChange: (v) => binding.apply((s) => (s.rendererEmitterSettings.speedFactor = v)) }) }), _jsx(Row, { label: "Length factor", children: _jsx(NumberField, { value: system.rendererEmitterSettings.lengthFactor ?? 2, step: 0.1, onChange: (v) => binding.apply((s) => (s.rendererEmitterSettings.lengthFactor = v)) }) })] })), _jsx(SubHeader, { title: "Material", hint: "Shader and blending settings applied to the particle sprite or mesh." }), materialLabel ? (_jsx(Row, { label: "Source", children: _jsx("div", { style: { fontSize: 12, color: theme.textDim, lineHeight: 1.45 }, children: materialLabel }) })) : null, _jsx(Row, { label: "Blend mode", children: _jsx(SelectField, { value: system.blending, options: [
|
|
403
452
|
{ value: 1, label: 'Additive' },
|
|
404
453
|
{ value: 2, label: 'Alpha blend' },
|
|
405
454
|
{ value: 3, label: 'Subtract' },
|
|
406
455
|
{ value: 4, label: 'Multiply' },
|
|
407
|
-
], onChange: (blend) => patchMaterial({ blendMode: blend }) }) }), _jsx(Row, { label: "Transparent", children: _jsx(CheckboxField, { value: !!system.getRendererSettings().materialTransparent, onChange: (v) => patchMaterial({ transparent: v }) }) }),
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
}
|
|
423
|
-
if (url === '__none') {
|
|
424
|
-
patchMaterial({ texture: null });
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
if (url === '__url') {
|
|
428
|
-
setPromptingTextureUrl(true);
|
|
429
|
-
return;
|
|
430
|
-
}
|
|
431
|
-
if (url === '__file') {
|
|
432
|
-
const input = document.createElement('input');
|
|
433
|
-
input.type = 'file';
|
|
434
|
-
input.accept = 'image/*';
|
|
435
|
-
input.onchange = () => {
|
|
436
|
-
const file = input.files?.[0];
|
|
437
|
-
if (!file) {
|
|
438
|
-
return;
|
|
439
|
-
}
|
|
440
|
-
const reader = new FileReader();
|
|
441
|
-
reader.onload = () => {
|
|
442
|
-
const texture = loadTexture(reader.result);
|
|
443
|
-
if (texture) {
|
|
444
|
-
patchMaterial({ texture });
|
|
445
|
-
}
|
|
446
|
-
};
|
|
447
|
-
reader.readAsDataURL(file);
|
|
448
|
-
};
|
|
449
|
-
input.click();
|
|
450
|
-
return;
|
|
451
|
-
}
|
|
452
|
-
const texture = loadTexture(url);
|
|
453
|
-
if (texture) {
|
|
454
|
-
patchMaterial({ texture });
|
|
455
|
-
}
|
|
456
|
-
} }), promptingTextureUrl && (_jsx(PromptDialog, { title: "Texture URL", defaultValue: currentTextureUrl, placeholder: "https://\u2026", onSubmit: (url) => {
|
|
457
|
-
const texture = loadTexture(url);
|
|
458
|
-
if (texture) {
|
|
459
|
-
patchMaterial({ texture });
|
|
460
|
-
}
|
|
461
|
-
setPromptingTextureUrl(false);
|
|
462
|
-
}, onCancel: () => setPromptingTextureUrl(false) })), currentTextureUrl && (_jsx("img", { src: currentTextureUrl, alt: "texture preview", style: {
|
|
463
|
-
width: 64,
|
|
464
|
-
height: 64,
|
|
465
|
-
marginTop: 6,
|
|
466
|
-
objectFit: 'contain',
|
|
467
|
-
borderRadius: 6,
|
|
468
|
-
border: '1px solid #2b3761',
|
|
469
|
-
background: 'repeating-conic-gradient(#2a3252 0% 25%, #171d33 0% 50%) 0 0 / 12px 12px',
|
|
470
|
-
} }))] }), _jsx(Row, { label: "Alpha test", children: _jsx(NumberField, { value: system.getRendererSettings().materialAlphaTest ?? 0, min: 0, step: 0.05, onChange: (v) => patchMaterial({ alphaTest: Math.min(1, v) }) }) }), _jsx(Row, { label: "Depth write", children: _jsx(CheckboxField, { value: system.getRendererSettings().materialDepthWrite, onChange: (v) => patchMaterial({ depthWrite: v }) }) }), _jsx(Row, { label: "Depth test", children: _jsx(CheckboxField, { value: system.getRendererSettings().materialDepthTest, onChange: (v) => patchMaterial({ depthTest: v }) }) }), system.renderMode === RenderMode.Mesh &&
|
|
456
|
+
], onChange: (blend) => patchMaterial({ blendMode: blend }) }) }), _jsx(Row, { label: "Transparent", children: _jsx(CheckboxField, { value: !!system.getRendererSettings().materialTransparent, onChange: (v) => patchMaterial({ transparent: v }) }) }), _jsx(Row, { label: "Texture", children: _jsx(TextureSelect, { value: system.texture, textureOptions: textureOptions, loadTexture: loadTexture, onChange: (texture) => patchMaterial({ texture }), preview: true }) }), _jsx(TextureUVField, { labelPrefix: "Map ", value: system.mapUV ?? normalizeTextureUV(undefined), onChange: (uv) => binding.apply((s) => (s.mapUV = uv)) }), !isMesh && (_jsx(Row, { label: "Flip U/V", children: _jsx("div", { style: { display: 'flex', gap: 6 }, children: ['x', 'y'].map((axis) => (_jsx(NumberField, { value: system.flip?.[axis] ?? 0, min: 0, max: 1, step: 0.1, onChange: (v) => binding.apply((s) => {
|
|
457
|
+
const next = s.flip?.clone() ?? new Vector2(0, 0);
|
|
458
|
+
next[axis] = Math.min(1, Math.max(0, v));
|
|
459
|
+
s.flip = next.x === 0 && next.y === 0 ? undefined : next;
|
|
460
|
+
}) }, axis))) }) })), _jsx(Row, { label: "Alpha test", children: _jsx(NumberField, { value: system.getRendererSettings().materialAlphaTest ?? 0, min: 0, step: 0.05, onChange: (v) => patchMaterial({ alphaTest: Math.min(1, v) }) }) }), _jsx(Row, { label: "Depth write", children: _jsx(CheckboxField, { value: system.getRendererSettings().materialDepthWrite, onChange: (v) => patchMaterial({ depthWrite: v }) }) }), _jsx(Row, { label: "Depth test", children: _jsx(CheckboxField, { value: system.getRendererSettings().materialDepthTest, onChange: (v) => patchMaterial({ depthTest: v }) }) }), _jsx(SubHeader, { title: "Gradient map", hint: "Recolours the map through a colour ramp: one channel of the map, or its luminance, picks a colour along the ramp." }), _jsx(Row, { label: "Ramp", children: _jsx(TextureSelect, { value: gradientMap?.ramp, textureOptions: textureOptions, loadTexture: loadTexture, promptTitle: "Ramp texture URL", onChange: (ramp) => binding.apply((s) => (s.gradientMap = ramp ? { ...s.gradientMap, ramp } : undefined)) }) }), gradientMap && (_jsx(Row, { label: "Ramp channel", children: _jsx(SelectField, { value: gradientMap.channel, options: [
|
|
461
|
+
{ value: 'r', label: 'Red' },
|
|
462
|
+
{ value: 'g', label: 'Green' },
|
|
463
|
+
{ value: 'b', label: 'Blue' },
|
|
464
|
+
{ value: 'a', label: 'Alpha (ramp alpha replaces)' },
|
|
465
|
+
{ value: 'luminance', label: 'Luminance' },
|
|
466
|
+
], onChange: (channel) => binding.apply((s) => (s.gradientMap = { ...s.gradientMap, channel })) }) })), _jsx(SubHeader, { title: "Erosion", hint: "Alpha decides how much of the shape is left instead of how transparent it is, so a fading alpha dissolves or wipes the shape away." }), _jsx(Row, { label: "Erosion", children: _jsx(CheckboxField, { value: !!erosion, onChange: (v) => binding.apply((s) => (s.erosion = v ? {} : undefined)) }) }), erosion && (_jsxs(_Fragment, { children: [_jsx(Row, { label: "Erosion mask", children: _jsx(TextureSelect, { value: erosion.mask, textureOptions: textureOptions, loadTexture: loadTexture, noneLabel: "(map alpha)", promptTitle: "Erosion mask URL", onChange: (mask) => patchErosion({ mask }) }) }), erosion.mask && (_jsx(TextureUVField, { labelPrefix: "Mask ", value: erosion.maskUV, onChange: (maskUV) => patchErosion({ maskUV }) })), _jsx(Row, { label: "Softness", children: _jsx(NumberField, { value: erosion.softness, min: 0.0001, step: 0.01, onChange: (softness) => patchErosion({ softness }) }) }), _jsx(Row, { label: "Edge width", children: _jsx(NumberField, { value: erosion.edgeWidth, min: 0, step: 0.01, onChange: (edgeWidth) => patchErosion({ edgeWidth }) }) }), (erosion.edgeWidth > 0 || erosion.edgeColor.some((c) => c !== 1)) && (_jsx(Row, { label: "Edge color", children: _jsx("div", { style: { display: 'flex', gap: 6 }, children: [0, 1, 2].map((channel) => (_jsx(NumberField, { value: erosion.edgeColor[channel], min: 0, step: 0.05, onChange: (v) => {
|
|
467
|
+
const edgeColor = [...erosion.edgeColor];
|
|
468
|
+
edgeColor[channel] = v;
|
|
469
|
+
patchErosion({ edgeColor });
|
|
470
|
+
} }, channel))) }) }))] })), _jsx(SubHeader, { title: "UV distortion", hint: "A scrolling noise texture pushes the map's UV about, for heat shimmer and licking, breathing shapes." }), _jsx(Row, { label: "Noise texture", children: _jsx(TextureSelect, { value: distortion?.map, textureOptions: textureOptions, loadTexture: loadTexture, promptTitle: "Distortion noise URL", onChange: (map) => binding.apply((s) => (s.uvDistortion = map ? { ...s.uvDistortion, map } : undefined)) }) }), distortion && (_jsxs(_Fragment, { children: [_jsx(Row, { label: "Distortion strength", children: _jsx(NumberField, { value: distortion.strength, min: 0, step: 0.01, onChange: (strength) => patchDistortion({ strength }) }) }), _jsx(TextureUVField, { labelPrefix: "Noise ", value: distortion.mapUV, onChange: (mapUV) => patchDistortion({ mapUV }) })] })), system.renderMode === RenderMode.Mesh &&
|
|
471
471
|
(() => {
|
|
472
472
|
const preset = detectGeometryPreset(system.instancingGeometry);
|
|
473
473
|
const vertexCount = system.instancingGeometry.length / 3;
|
|
474
474
|
const triangleCount = (system.getRendererSettings().instancingIndices?.length ?? 0) / 3;
|
|
475
475
|
const hostGeometry = geometryOptions ?? [];
|
|
476
476
|
const applyGeometry = (g) => binding.apply((s) => {
|
|
477
|
+
// The previous mesh's sets go first, so the positions setter gives
|
|
478
|
+
// this mesh the library's own fallback UVs and normals; then every set
|
|
479
|
+
// this mesh has. An unindexed mesh draws its vertices in sequence.
|
|
477
480
|
const settings = s.getRendererSettings();
|
|
478
|
-
settings.
|
|
479
|
-
settings.
|
|
480
|
-
settings.instancingNormals =
|
|
481
|
+
settings.instancingUVs = undefined;
|
|
482
|
+
settings.instancingUVs2 = undefined;
|
|
483
|
+
settings.instancingNormals = undefined;
|
|
481
484
|
s.instancingGeometry = new Float32Array(g.positions);
|
|
485
|
+
s.instancingIndices = g.indices
|
|
486
|
+
? new Uint32Array(g.indices)
|
|
487
|
+
: Uint32Array.from({ length: g.positions.length / 3 }, (_, i) => i);
|
|
488
|
+
if (g.uvs)
|
|
489
|
+
s.instancingUVs = new Float32Array(g.uvs);
|
|
490
|
+
if (g.uvs2)
|
|
491
|
+
s.instancingUVs2 = new Float32Array(g.uvs2);
|
|
492
|
+
if (g.normals)
|
|
493
|
+
settings.instancingNormals = new Float32Array(g.normals);
|
|
482
494
|
});
|
|
483
495
|
return (_jsx(Row, { label: "Geometry", children: _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 6 }, children: [_jsx(SelectField, { value: preset, options: [
|
|
484
496
|
...(preset === 'custom' ? [{ value: 'custom', label: 'Custom (imported)' }] : []),
|
|
@@ -509,6 +521,7 @@ export function RendererModule({ binding, textureOptions, geometryOptions, resol
|
|
|
509
521
|
}
|
|
510
522
|
binding.apply((s) => {
|
|
511
523
|
const settings = s.getRendererSettings();
|
|
524
|
+
settings.instancingUVs2 = undefined;
|
|
512
525
|
if (next === 'cube') {
|
|
513
526
|
settings.instancingIndices = MESH_CUBE_INDICES;
|
|
514
527
|
settings.instancingUVs = undefined;
|
|
@@ -521,7 +534,7 @@ export function RendererModule({ binding, textureOptions, geometryOptions, resol
|
|
|
521
534
|
s.instancingGeometry = MESH_QUAD_POSITIONS;
|
|
522
535
|
}
|
|
523
536
|
});
|
|
524
|
-
} }), _jsx(MeshPreview, { positions: system.instancingGeometry, indices: system.getRendererSettings().instancingIndices }), preset === 'custom' && (_jsxs("span", { style: { fontSize: 10.5, color: '#9eb9ff' }, children: [vertexCount, " verts / ", triangleCount, " tris \u2014 picking a preset replaces this mesh"] }))] }) }));
|
|
537
|
+
} }), _jsx(MeshPreview, { positions: system.instancingGeometry, indices: system.getRendererSettings().instancingIndices }), preset === 'custom' && (_jsxs("span", { style: { fontSize: 10.5, color: '#9eb9ff' }, children: [vertexCount, " verts / ", triangleCount, " tris", system.getRendererSettings().instancingUVs2 ? ' / 2 UV sets' : '', " \u2014 picking a preset replaces this mesh"] }))] }) }));
|
|
525
538
|
})(), system.renderMode === RenderMode.Mesh && (_jsx(Row, { label: "Lighting", children: _jsx(SelectField, { value: system.lighting, options: [
|
|
526
539
|
{ value: 'fixed', label: 'Fixed light' },
|
|
527
540
|
{ value: 'scene', label: 'Scene light' },
|
|
@@ -548,7 +561,11 @@ export function RendererModule({ binding, textureOptions, geometryOptions, resol
|
|
|
548
561
|
}) }, axis))) }) })), system.renderMode === RenderMode.Mesh && (_jsxs(_Fragment, { children: [_jsx(Row, { label: "Env map atlas", children: _jsx(SelectField, { value: system.getRendererSettings().reflectionAtlas?.url ?? '', options: [
|
|
549
562
|
{ value: '', label: 'None' },
|
|
550
563
|
...(textureOptions ?? []).map((option) => ({ value: option.url, label: option.label })),
|
|
551
|
-
], onChange: (url) => patchMaterial({ reflectionAtlas: url ? loadTexture(url) : null }) }) }), _jsx(Row, { label: "Reflection level", children: _jsx(NumberField, { value: system.getRendererSettings().reflectionLevel ?? 1, min: 0, step: 0.1, onChange: (v) => patchMaterial({ reflectionLevel: Math.max(0, v) }) }) })] })), _jsx(Row, { label: "
|
|
564
|
+
], onChange: (url) => patchMaterial({ reflectionAtlas: url ? loadTexture(url) : null }) }) }), _jsx(Row, { label: "Reflection level", children: _jsx(NumberField, { value: system.getRendererSettings().reflectionLevel ?? 1, min: 0, step: 0.1, onChange: (v) => patchMaterial({ reflectionLevel: Math.max(0, v) }) }) })] })), isMesh && (_jsx(Row, { label: "Render face", children: _jsx(SelectField, { value: system.renderFace, options: [
|
|
565
|
+
{ value: 'both', label: 'Both' },
|
|
566
|
+
{ value: 'front', label: 'Front' },
|
|
567
|
+
{ value: 'back', label: 'Back' },
|
|
568
|
+
], onChange: (face) => binding.apply((s) => (s.renderFace = face)) }) })), _jsx(Row, { label: "Sort mode", children: _jsx(SelectField, { value: system.sortMode, options: [
|
|
552
569
|
{ value: 'none', label: 'None' },
|
|
553
570
|
{ value: 'distance', label: 'By distance' },
|
|
554
571
|
{ value: 'oldestInFront', label: 'Oldest in front' },
|
package/dist/react/theme.d.ts
CHANGED
|
@@ -1,19 +1,34 @@
|
|
|
1
1
|
import type { CSSProperties } from 'react';
|
|
2
|
+
/** The editor's colours and font, for hosts that style their own panels to match. */
|
|
2
3
|
export declare const theme: {
|
|
4
|
+
/** Background of the side panels and docks. */
|
|
3
5
|
panelBg: string;
|
|
6
|
+
/** Background of an inspector module or a framed list. */
|
|
4
7
|
sectionBg: string;
|
|
8
|
+
/** Borders and separators. */
|
|
5
9
|
border: string;
|
|
10
|
+
/** The border of a selected or switched-on control. */
|
|
6
11
|
borderActive: string;
|
|
12
|
+
/** Body text. */
|
|
7
13
|
text: string;
|
|
14
|
+
/** Labels and secondary text. */
|
|
8
15
|
textDim: string;
|
|
16
|
+
/** Highlights, such as a Save button with something to save. */
|
|
9
17
|
accent: string;
|
|
18
|
+
/** Background of inputs. */
|
|
10
19
|
inputBg: string;
|
|
20
|
+
/** The line of curve editors. */
|
|
11
21
|
curveStroke: string;
|
|
22
|
+
/** The editor's font stack. */
|
|
12
23
|
font: string;
|
|
13
24
|
};
|
|
25
|
+
/** Style of a text or number input. */
|
|
14
26
|
export declare const inputStyle: CSSProperties;
|
|
27
|
+
/** Style of an inspector row: a label column, then the controls. */
|
|
15
28
|
export declare const rowStyle: CSSProperties;
|
|
29
|
+
/** Style of an inspector row's label. */
|
|
16
30
|
export declare const labelStyle: CSSProperties;
|
|
31
|
+
/** Style of a button. */
|
|
17
32
|
export declare const buttonStyle: CSSProperties;
|
|
18
33
|
/** Visually hides a file input while keeping it activatable via an associated <label>. */
|
|
19
34
|
export declare const hiddenFileInputStyle: CSSProperties;
|
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": "
|
|
64
|
+
"babylon.quarks": "~0.28.0",
|
|
59
65
|
"react": ">=18",
|
|
60
66
|
"react-dom": ">=18"
|
|
61
67
|
},
|
|
@@ -65,9 +71,10 @@
|
|
|
65
71
|
"@tanstack/react-virtual": "^3.14.6",
|
|
66
72
|
"@types/react": "^19.0.0",
|
|
67
73
|
"@types/react-dom": "^19.0.0",
|
|
74
|
+
"fake-indexeddb": "^6.2.6",
|
|
68
75
|
"react": "^19.0.0",
|
|
69
76
|
"react-dom": "^19.0.0",
|
|
70
77
|
"rimraf": "^6.0.0",
|
|
71
|
-
"typescript": "^
|
|
78
|
+
"typescript": "^6.0.3"
|
|
72
79
|
}
|
|
73
80
|
}
|
package/src/core/assetTree.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {EffectAsset} from './assets';
|
|
1
|
+
import type {EffectAsset} from './assets.js';
|
|
2
2
|
|
|
3
3
|
export interface AssetTreeFolder {
|
|
4
4
|
kind: 'folder';
|
|
@@ -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));
|