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
|
@@ -12,8 +12,8 @@ import {
|
|
|
12
12
|
EulerGenerator,
|
|
13
13
|
ForceOverLife,
|
|
14
14
|
FrameOverLife,
|
|
15
|
-
getPhysicsResolver,
|
|
16
15
|
Gradient,
|
|
16
|
+
GravityForce,
|
|
17
17
|
InheritVelocity,
|
|
18
18
|
IntervalValue,
|
|
19
19
|
LimitSpeedOverLife,
|
|
@@ -33,21 +33,17 @@ import {
|
|
|
33
33
|
WidthOverLength,
|
|
34
34
|
} from 'babylon.quarks';
|
|
35
35
|
import React from 'react';
|
|
36
|
-
import {EffectBinding} from '../core/binding';
|
|
37
|
-
import {buildGradient, DEFAULT_GRADIENT_STOPS, findBehavior, readGradientStops} from '../core/colors';
|
|
38
|
-
import {buildCurve, buildScalar, readPieces, readScalar} from '../core/values';
|
|
39
|
-
import {CurveEditor} from './CurveEditor';
|
|
40
|
-
import {CheckboxField, NumberField, Row, SelectField} from './fields';
|
|
41
|
-
import {GradientEditor} from './GradientEditor';
|
|
42
|
-
import {iconStyle} from './icons';
|
|
43
|
-
import type {TextureOption} from './modules';
|
|
44
|
-
import {ModuleSection} from './ModuleSection';
|
|
45
|
-
import {theme} from './theme';
|
|
46
|
-
import {ValueField} from './ValueField';
|
|
47
|
-
|
|
48
|
-
interface ModuleProps {
|
|
49
|
-
binding: EffectBinding;
|
|
50
|
-
}
|
|
36
|
+
import {EffectBinding} from '../core/binding.js';
|
|
37
|
+
import {buildGradient, DEFAULT_GRADIENT_STOPS, findBehavior, readGradientStops} from '../core/colors.js';
|
|
38
|
+
import {buildCurve, buildScalar, readPieces, readScalar} from '../core/values.js';
|
|
39
|
+
import {CurveEditor} from './CurveEditor.js';
|
|
40
|
+
import {CheckboxField, NumberField, Row, SelectField} from './fields.js';
|
|
41
|
+
import {GradientEditor} from './GradientEditor.js';
|
|
42
|
+
import {iconStyle} from './icons.js';
|
|
43
|
+
import type {TextureOption} from './modules.js';
|
|
44
|
+
import {ModuleSection, type ModuleProps} from './ModuleSection.js';
|
|
45
|
+
import {theme} from './theme.js';
|
|
46
|
+
import {ValueField} from './ValueField.js';
|
|
51
47
|
|
|
52
48
|
function removeBehaviorOfType(behaviors: Behavior[], type: string): void {
|
|
53
49
|
for (let i = behaviors.length - 1; i >= 0; i--) {
|
|
@@ -85,6 +81,7 @@ function BehaviorModule<T extends Behavior>(props: {
|
|
|
85
81
|
);
|
|
86
82
|
}
|
|
87
83
|
|
|
84
|
+
/** The Speed over Lifetime module (Unity's Speed Modifier): scales speed by a curve over the particle's life. */
|
|
88
85
|
export function SpeedOverLifeModule({binding}: ModuleProps) {
|
|
89
86
|
return (
|
|
90
87
|
<BehaviorModule<SpeedOverLife>
|
|
@@ -92,9 +89,7 @@ export function SpeedOverLifeModule({binding}: ModuleProps) {
|
|
|
92
89
|
title="Speed over Lifetime"
|
|
93
90
|
type="SpeedOverLife"
|
|
94
91
|
create={() =>
|
|
95
|
-
new SpeedOverLife(
|
|
96
|
-
buildScalar({mode: 'curve', value: 1, min: 0, max: 1, curve: [1, 0.75, 0.5, 0]}) as never
|
|
97
|
-
)
|
|
92
|
+
new SpeedOverLife(buildScalar({mode: 'curve', value: 1, min: 0, max: 1, curve: [1, 0.75, 0.5, 0]}))
|
|
98
93
|
}
|
|
99
94
|
>
|
|
100
95
|
{(behavior) => (
|
|
@@ -102,7 +97,7 @@ export function SpeedOverLifeModule({binding}: ModuleProps) {
|
|
|
102
97
|
<CurveEditor
|
|
103
98
|
pieces={readPieces(behavior.speed)}
|
|
104
99
|
maxValue={2}
|
|
105
|
-
onChange={(pieces) => binding.apply(() => (behavior.speed = buildCurve(pieces)
|
|
100
|
+
onChange={(pieces) => binding.apply(() => (behavior.speed = buildCurve(pieces)))}
|
|
106
101
|
/>
|
|
107
102
|
</div>
|
|
108
103
|
)}
|
|
@@ -110,6 +105,7 @@ export function SpeedOverLifeModule({binding}: ModuleProps) {
|
|
|
110
105
|
);
|
|
111
106
|
}
|
|
112
107
|
|
|
108
|
+
/** The Limit Speed over Lifetime module (Unity's Limit Velocity over Lifetime): caps speed by a curve, with dampening. */
|
|
113
109
|
export function LimitSpeedOverLifeModule({binding}: ModuleProps) {
|
|
114
110
|
return (
|
|
115
111
|
<BehaviorModule<LimitSpeedOverLife>
|
|
@@ -117,10 +113,7 @@ export function LimitSpeedOverLifeModule({binding}: ModuleProps) {
|
|
|
117
113
|
title="Limit Speed over Lifetime"
|
|
118
114
|
type="LimitSpeedOverLife"
|
|
119
115
|
create={() =>
|
|
120
|
-
new LimitSpeedOverLife(
|
|
121
|
-
buildScalar({mode: 'curve', value: 1, min: 0, max: 1, curve: [4, 2, 1, 1]}) as never,
|
|
122
|
-
0.5
|
|
123
|
-
)
|
|
116
|
+
new LimitSpeedOverLife(buildScalar({mode: 'curve', value: 1, min: 0, max: 1, curve: [4, 2, 1, 1]}), 0.5)
|
|
124
117
|
}
|
|
125
118
|
>
|
|
126
119
|
{(behavior) => (
|
|
@@ -129,7 +122,7 @@ export function LimitSpeedOverLifeModule({binding}: ModuleProps) {
|
|
|
129
122
|
<CurveEditor
|
|
130
123
|
pieces={readPieces(behavior.speed)}
|
|
131
124
|
maxValue={8}
|
|
132
|
-
onChange={(pieces) => binding.apply(() => (behavior.speed = buildCurve(pieces)
|
|
125
|
+
onChange={(pieces) => binding.apply(() => (behavior.speed = buildCurve(pieces)))}
|
|
133
126
|
/>
|
|
134
127
|
</div>
|
|
135
128
|
<Row label="Dampen">
|
|
@@ -146,6 +139,7 @@ export function LimitSpeedOverLifeModule({binding}: ModuleProps) {
|
|
|
146
139
|
);
|
|
147
140
|
}
|
|
148
141
|
|
|
142
|
+
/** The Force over Lifetime module (Unity's Force over Lifetime): a constant push per axis. */
|
|
149
143
|
export function ForceOverLifeModule({binding}: ModuleProps) {
|
|
150
144
|
return (
|
|
151
145
|
<BehaviorModule<ForceOverLife>
|
|
@@ -156,6 +150,16 @@ export function ForceOverLifeModule({binding}: ModuleProps) {
|
|
|
156
150
|
>
|
|
157
151
|
{(behavior) => (
|
|
158
152
|
<>
|
|
153
|
+
<Row label="Space">
|
|
154
|
+
<SelectField
|
|
155
|
+
value={behavior.space}
|
|
156
|
+
options={[
|
|
157
|
+
{value: 'world', label: 'World'},
|
|
158
|
+
{value: 'local', label: 'Local'},
|
|
159
|
+
]}
|
|
160
|
+
onChange={(v) => binding.apply(() => (behavior.space = v))}
|
|
161
|
+
/>
|
|
162
|
+
</Row>
|
|
159
163
|
<ValueField
|
|
160
164
|
hintKey="force.x"
|
|
161
165
|
label="X"
|
|
@@ -288,6 +292,43 @@ export function InheritVelocityModule({binding}: ModuleProps) {
|
|
|
288
292
|
);
|
|
289
293
|
}
|
|
290
294
|
|
|
295
|
+
/** A point attractor (`GravityForce`): particles fall towards a point, harder the nearer they are. */
|
|
296
|
+
export function AttractorModule({binding}: ModuleProps) {
|
|
297
|
+
return (
|
|
298
|
+
<BehaviorModule<GravityForce>
|
|
299
|
+
binding={binding}
|
|
300
|
+
title="Attractor"
|
|
301
|
+
type="GravityForce"
|
|
302
|
+
create={() => new GravityForce(new Vector3(0, 0, 0), 1)}
|
|
303
|
+
>
|
|
304
|
+
{(behavior) => (
|
|
305
|
+
<>
|
|
306
|
+
<Row label="Center" hintKey="attractor.center">
|
|
307
|
+
<div style={{display: 'flex', gap: 6}}>
|
|
308
|
+
{(['x', 'y', 'z'] as const).map((axis) => (
|
|
309
|
+
<NumberField
|
|
310
|
+
key={axis}
|
|
311
|
+
value={behavior.center[axis]}
|
|
312
|
+
step={0.1}
|
|
313
|
+
onChange={(v) => binding.apply(() => (behavior.center[axis] = v))}
|
|
314
|
+
/>
|
|
315
|
+
))}
|
|
316
|
+
</div>
|
|
317
|
+
</Row>
|
|
318
|
+
<Row label="Strength" hintKey="attractor.magnitude">
|
|
319
|
+
<NumberField
|
|
320
|
+
value={behavior.magnitude}
|
|
321
|
+
step={0.1}
|
|
322
|
+
onChange={(v) => binding.apply(() => (behavior.magnitude = v))}
|
|
323
|
+
/>
|
|
324
|
+
</Row>
|
|
325
|
+
</>
|
|
326
|
+
)}
|
|
327
|
+
</BehaviorModule>
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/** The Gravity module: pulls particles towards a point. */
|
|
291
332
|
export function GravityModule({binding}: ModuleProps) {
|
|
292
333
|
return (
|
|
293
334
|
<BehaviorModule<ApplyForce>
|
|
@@ -297,18 +338,31 @@ export function GravityModule({binding}: ModuleProps) {
|
|
|
297
338
|
create={() => new ApplyForce(new Vector3(0, -1, 0), new ConstantValue(9.81))}
|
|
298
339
|
>
|
|
299
340
|
{(behavior) => (
|
|
300
|
-
|
|
301
|
-
<
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
341
|
+
<>
|
|
342
|
+
<Row label="Strength" hintKey="gravity.strength">
|
|
343
|
+
<NumberField
|
|
344
|
+
value={readScalar(behavior.magnitude).value}
|
|
345
|
+
step={0.5}
|
|
346
|
+
onChange={(v) => binding.apply(() => (behavior.magnitude = new ConstantValue(v)))}
|
|
347
|
+
/>
|
|
348
|
+
</Row>
|
|
349
|
+
<Row label="Space">
|
|
350
|
+
<SelectField
|
|
351
|
+
value={behavior.space}
|
|
352
|
+
options={[
|
|
353
|
+
{value: 'world', label: 'World'},
|
|
354
|
+
{value: 'local', label: 'Local'},
|
|
355
|
+
]}
|
|
356
|
+
onChange={(v) => binding.apply(() => (behavior.space = v))}
|
|
357
|
+
/>
|
|
358
|
+
</Row>
|
|
359
|
+
</>
|
|
307
360
|
)}
|
|
308
361
|
</BehaviorModule>
|
|
309
362
|
);
|
|
310
363
|
}
|
|
311
364
|
|
|
365
|
+
/** The Rotation over Lifetime module (Unity's Rotation over Lifetime): spin rate over the particle's life. */
|
|
312
366
|
export function RotationOverLifeModule({binding}: ModuleProps) {
|
|
313
367
|
return (
|
|
314
368
|
<BehaviorModule<RotationOverLife>
|
|
@@ -595,6 +649,7 @@ export function SequencesModule({binding, textureOptions}: ModuleProps & {textur
|
|
|
595
649
|
);
|
|
596
650
|
}
|
|
597
651
|
|
|
652
|
+
/** The Noise module (Unity's Noise): moves and turns particles by a noise field. */
|
|
598
653
|
export function NoiseModule({binding}: ModuleProps) {
|
|
599
654
|
return (
|
|
600
655
|
<BehaviorModule<Noise>
|
|
@@ -695,6 +750,7 @@ function Vector3Row(props: {
|
|
|
695
750
|
);
|
|
696
751
|
}
|
|
697
752
|
|
|
753
|
+
/** The Turbulence module: a curl noise force field. */
|
|
698
754
|
export function TurbulenceModule({binding}: ModuleProps) {
|
|
699
755
|
return (
|
|
700
756
|
<BehaviorModule<TurbulenceField>
|
|
@@ -735,12 +791,36 @@ export function TurbulenceModule({binding}: ModuleProps) {
|
|
|
735
791
|
step={0.1}
|
|
736
792
|
onChange={(v) => binding.apply(() => (behavior.timeScale = v))}
|
|
737
793
|
/>
|
|
794
|
+
{/* The field is built from the seed, so a different seed is a different swirl. */}
|
|
795
|
+
<Row label="Seed">
|
|
796
|
+
<NumberField
|
|
797
|
+
value={behavior.seed}
|
|
798
|
+
step={1}
|
|
799
|
+
onChange={(v) =>
|
|
800
|
+
binding.apply(() => {
|
|
801
|
+
const seed = Math.round(v);
|
|
802
|
+
const next = new TurbulenceField(
|
|
803
|
+
behavior.scale,
|
|
804
|
+
behavior.octaves,
|
|
805
|
+
behavior.velocityMultiplier,
|
|
806
|
+
behavior.timeScale,
|
|
807
|
+
seed
|
|
808
|
+
);
|
|
809
|
+
behavior.seed = seed;
|
|
810
|
+
behavior.generator = next.generator;
|
|
811
|
+
behavior.initialOffset.copy(next.initialOffset);
|
|
812
|
+
behavior.timeOffset.copy(next.initialOffset);
|
|
813
|
+
})
|
|
814
|
+
}
|
|
815
|
+
/>
|
|
816
|
+
</Row>
|
|
738
817
|
</>
|
|
739
818
|
)}
|
|
740
819
|
</BehaviorModule>
|
|
741
820
|
);
|
|
742
821
|
}
|
|
743
822
|
|
|
823
|
+
/** The Randomize Direction module: tilts each particle's start direction by a random angle. */
|
|
744
824
|
export function EmitDirectionModule({binding}: ModuleProps) {
|
|
745
825
|
return (
|
|
746
826
|
<BehaviorModule<ChangeEmitDirection>
|
|
@@ -752,25 +832,26 @@ export function EmitDirectionModule({binding}: ModuleProps) {
|
|
|
752
832
|
{(behavior) => (
|
|
753
833
|
<ValueField
|
|
754
834
|
label="Angle (rad)"
|
|
755
|
-
generator={behavior.angle
|
|
835
|
+
generator={behavior.angle}
|
|
836
|
+
curve={false}
|
|
756
837
|
min={0}
|
|
757
|
-
|
|
758
|
-
onChange={(g) => binding.apply(() => (behavior.angle = g as never))}
|
|
838
|
+
onChange={(g) => binding.apply(() => (behavior.angle = g))}
|
|
759
839
|
/>
|
|
760
840
|
)}
|
|
761
841
|
</BehaviorModule>
|
|
762
842
|
);
|
|
763
843
|
}
|
|
764
844
|
|
|
845
|
+
/** The Collision module (Unity's Collision): particles bounce off or die on a ground plane. */
|
|
765
846
|
export function CollisionModule({binding}: ModuleProps) {
|
|
766
847
|
// Like Unity: Collision stores bounce only. Colliders are host-provided via
|
|
767
|
-
// setPhysicsResolver — the editor never invents a ground plane.
|
|
848
|
+
// setPhysicsResolver, asked when a particle is tested — the editor never invents a ground plane.
|
|
768
849
|
return (
|
|
769
850
|
<BehaviorModule<ApplyCollision>
|
|
770
851
|
binding={binding}
|
|
771
852
|
title="Collision"
|
|
772
853
|
type="ApplyCollision"
|
|
773
|
-
create={() => new ApplyCollision(
|
|
854
|
+
create={() => new ApplyCollision(undefined, 0.5)}
|
|
774
855
|
>
|
|
775
856
|
{(behavior) => (
|
|
776
857
|
<>
|
|
@@ -816,6 +897,7 @@ function SpeedRangeRows({binding, behavior}: {binding: EffectBinding; behavior:
|
|
|
816
897
|
);
|
|
817
898
|
}
|
|
818
899
|
|
|
900
|
+
/** The Color by Speed module (Unity's Color by Speed): a gradient over a speed range. */
|
|
819
901
|
export function ColorBySpeedModule({binding}: ModuleProps) {
|
|
820
902
|
return (
|
|
821
903
|
<BehaviorModule<ColorBySpeed>
|
|
@@ -843,6 +925,7 @@ export function ColorBySpeedModule({binding}: ModuleProps) {
|
|
|
843
925
|
);
|
|
844
926
|
}
|
|
845
927
|
|
|
928
|
+
/** The Size by Speed module (Unity's Size by Speed): size over a speed range. */
|
|
846
929
|
export function SizeBySpeedModule({binding}: ModuleProps) {
|
|
847
930
|
return (
|
|
848
931
|
<BehaviorModule<SizeBySpeed>
|
|
@@ -851,7 +934,7 @@ export function SizeBySpeedModule({binding}: ModuleProps) {
|
|
|
851
934
|
type="SizeBySpeed"
|
|
852
935
|
create={() =>
|
|
853
936
|
new SizeBySpeed(
|
|
854
|
-
buildScalar({mode: 'curve', value: 1, min: 0, max: 1, curve: [0.5, 0.8, 1.2, 1.5]})
|
|
937
|
+
buildScalar({mode: 'curve', value: 1, min: 0, max: 1, curve: [0.5, 0.8, 1.2, 1.5]}),
|
|
855
938
|
new IntervalValue(0, 5)
|
|
856
939
|
)
|
|
857
940
|
}
|
|
@@ -860,9 +943,9 @@ export function SizeBySpeedModule({binding}: ModuleProps) {
|
|
|
860
943
|
<>
|
|
861
944
|
<div style={{marginTop: 6}}>
|
|
862
945
|
<CurveEditor
|
|
863
|
-
pieces={readPieces(behavior.size
|
|
946
|
+
pieces={readPieces(behavior.size)}
|
|
864
947
|
maxValue={2}
|
|
865
|
-
onChange={(pieces) => binding.apply(() => (behavior.size = buildCurve(pieces)
|
|
948
|
+
onChange={(pieces) => binding.apply(() => (behavior.size = buildCurve(pieces)))}
|
|
866
949
|
/>
|
|
867
950
|
</div>
|
|
868
951
|
<SpeedRangeRows binding={binding} behavior={behavior} />
|
|
@@ -872,6 +955,7 @@ export function SizeBySpeedModule({binding}: ModuleProps) {
|
|
|
872
955
|
);
|
|
873
956
|
}
|
|
874
957
|
|
|
958
|
+
/** The Rotation by Speed module (Unity's Rotation by Speed): spin rate over a speed range. */
|
|
875
959
|
export function RotationBySpeedModule({binding}: ModuleProps) {
|
|
876
960
|
return (
|
|
877
961
|
<BehaviorModule<RotationBySpeed>
|
|
@@ -896,6 +980,7 @@ export function RotationBySpeedModule({binding}: ModuleProps) {
|
|
|
896
980
|
);
|
|
897
981
|
}
|
|
898
982
|
|
|
983
|
+
/** The Orbit over Lifetime module (Unity's Velocity over Lifetime orbital): turns particles about an axis. */
|
|
899
984
|
export function OrbitOverLifeModule({binding}: ModuleProps) {
|
|
900
985
|
return (
|
|
901
986
|
<BehaviorModule<OrbitOverLife>
|
|
@@ -916,6 +1001,7 @@ export function OrbitOverLifeModule({binding}: ModuleProps) {
|
|
|
916
1001
|
);
|
|
917
1002
|
}
|
|
918
1003
|
|
|
1004
|
+
/** The Width over Trail module (Unity's Trails width over trail): a trail's width along its length. */
|
|
919
1005
|
export function WidthOverTrailModule({binding}: ModuleProps) {
|
|
920
1006
|
return (
|
|
921
1007
|
<BehaviorModule<WidthOverLength>
|
|
@@ -923,18 +1009,16 @@ export function WidthOverTrailModule({binding}: ModuleProps) {
|
|
|
923
1009
|
title="Width over Trail"
|
|
924
1010
|
type="WidthOverLength"
|
|
925
1011
|
create={() =>
|
|
926
|
-
new WidthOverLength(
|
|
927
|
-
buildScalar({mode: 'curve', value: 1, min: 0, max: 1, curve: [1, 0.75, 0.4, 0]}) as never
|
|
928
|
-
)
|
|
1012
|
+
new WidthOverLength(buildScalar({mode: 'curve', value: 1, min: 0, max: 1, curve: [1, 0.75, 0.4, 0]}))
|
|
929
1013
|
}
|
|
930
1014
|
>
|
|
931
1015
|
{(behavior) => (
|
|
932
1016
|
<>
|
|
933
1017
|
<div style={{marginTop: 6}}>
|
|
934
1018
|
<CurveEditor
|
|
935
|
-
pieces={readPieces(behavior.width
|
|
1019
|
+
pieces={readPieces(behavior.width)}
|
|
936
1020
|
maxValue={2}
|
|
937
|
-
onChange={(pieces) => binding.apply(() => (behavior.width = buildCurve(pieces)
|
|
1021
|
+
onChange={(pieces) => binding.apply(() => (behavior.width = buildCurve(pieces)))}
|
|
938
1022
|
/>
|
|
939
1023
|
</div>
|
|
940
1024
|
{/* Off: the curve multiplies the particle size. On: it is the width in world units (files from 0.21 and earlier). */}
|
|
@@ -950,6 +1034,13 @@ export function WidthOverTrailModule({binding}: ModuleProps) {
|
|
|
950
1034
|
);
|
|
951
1035
|
}
|
|
952
1036
|
|
|
1037
|
+
/** How many of the first frames a start-frame generator picks from: its exclusive upper bound. */
|
|
1038
|
+
function startFrameCount(startFrame: FrameOverLife['startFrame']): number {
|
|
1039
|
+
const state = readScalar(startFrame);
|
|
1040
|
+
return Math.round(state.mode === 'random' ? state.max : state.value);
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
/** The Texture Sheet Animation module (Unity's Texture Sheet Animation): plays the sheet's frames by life or by frame rate. */
|
|
953
1044
|
export function TextureSheetModule({binding}: ModuleProps) {
|
|
954
1045
|
const system = binding.system;
|
|
955
1046
|
const frameBehavior = findBehavior<FrameOverLife>(system.behaviors, 'FrameOverLife');
|
|
@@ -958,7 +1049,7 @@ export function TextureSheetModule({binding}: ModuleProps) {
|
|
|
958
1049
|
const u = Math.max(1, system.uTileCount);
|
|
959
1050
|
const v = Math.max(1, system.vTileCount);
|
|
960
1051
|
// Start tile → cell (row-major, top-left origin — matches the shader's tile UV mapping).
|
|
961
|
-
const startIdx = Math.min(Math.max(0, Math.round(readScalar(system.startTileIndex
|
|
1052
|
+
const startIdx = Math.min(Math.max(0, Math.round(readScalar(system.startTileIndex).value)), tiles - 1);
|
|
962
1053
|
const startCol = startIdx % u;
|
|
963
1054
|
const startRow = Math.floor(startIdx / u);
|
|
964
1055
|
return (
|
|
@@ -1047,7 +1138,7 @@ export function TextureSheetModule({binding}: ModuleProps) {
|
|
|
1047
1138
|
</Row>
|
|
1048
1139
|
<Row label="Start tile">
|
|
1049
1140
|
<NumberField
|
|
1050
|
-
value={readScalar(system.startTileIndex
|
|
1141
|
+
value={readScalar(system.startTileIndex).value}
|
|
1051
1142
|
min={0}
|
|
1052
1143
|
step={1}
|
|
1053
1144
|
onChange={(v) => binding.apply((s) => (s.startTileIndex = new ConstantValue(Math.round(v))))}
|
|
@@ -1071,7 +1162,7 @@ export function TextureSheetModule({binding}: ModuleProps) {
|
|
|
1071
1162
|
min: 0,
|
|
1072
1163
|
max: tiles - 1,
|
|
1073
1164
|
curve: [0, (tiles - 1) / 3, (2 * (tiles - 1)) / 3, tiles - 1],
|
|
1074
|
-
})
|
|
1165
|
+
})
|
|
1075
1166
|
)
|
|
1076
1167
|
);
|
|
1077
1168
|
}
|
|
@@ -1080,13 +1171,107 @@ export function TextureSheetModule({binding}: ModuleProps) {
|
|
|
1080
1171
|
/>
|
|
1081
1172
|
</Row>
|
|
1082
1173
|
{frameBehavior && (
|
|
1083
|
-
|
|
1084
|
-
<
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1174
|
+
<>
|
|
1175
|
+
<Row label="Time mode">
|
|
1176
|
+
<SelectField<'life' | 'fps'>
|
|
1177
|
+
value={frameBehavior.fps > 0 ? 'fps' : 'life'}
|
|
1178
|
+
options={[
|
|
1179
|
+
{value: 'life', label: 'Lifetime (curve over life)'},
|
|
1180
|
+
{value: 'fps', label: 'FPS (frame from curve, then by age)'},
|
|
1181
|
+
]}
|
|
1182
|
+
onChange={(mode) =>
|
|
1183
|
+
binding.apply(() => {
|
|
1184
|
+
frameBehavior.fps = mode === 'fps' ? 12 : 0;
|
|
1185
|
+
if (mode === 'fps' && frameBehavior.frameCount <= 0) {
|
|
1186
|
+
frameBehavior.frameCount = tiles;
|
|
1187
|
+
}
|
|
1188
|
+
})
|
|
1189
|
+
}
|
|
1190
|
+
/>
|
|
1191
|
+
</Row>
|
|
1192
|
+
{frameBehavior.fps > 0 ? (
|
|
1193
|
+
<>
|
|
1194
|
+
<Row label="FPS">
|
|
1195
|
+
<NumberField
|
|
1196
|
+
value={frameBehavior.fps}
|
|
1197
|
+
min={0.01}
|
|
1198
|
+
step={1}
|
|
1199
|
+
onChange={(v) => binding.apply(() => (frameBehavior.fps = v))}
|
|
1200
|
+
/>
|
|
1201
|
+
</Row>
|
|
1202
|
+
<Row label="Loop">
|
|
1203
|
+
<CheckboxField
|
|
1204
|
+
value={frameBehavior.loop}
|
|
1205
|
+
onChange={(v) => binding.apply(() => (frameBehavior.loop = v))}
|
|
1206
|
+
/>
|
|
1207
|
+
</Row>
|
|
1208
|
+
<Row label="Random frame">
|
|
1209
|
+
<CheckboxField
|
|
1210
|
+
value={frameBehavior.random}
|
|
1211
|
+
onChange={(v) => binding.apply(() => (frameBehavior.random = v))}
|
|
1212
|
+
/>
|
|
1213
|
+
</Row>
|
|
1214
|
+
</>
|
|
1215
|
+
) : (
|
|
1216
|
+
<Row label="Cycles">
|
|
1217
|
+
<NumberField
|
|
1218
|
+
value={frameBehavior.cycles}
|
|
1219
|
+
min={0.01}
|
|
1220
|
+
step={1}
|
|
1221
|
+
onChange={(v) => binding.apply(() => (frameBehavior.cycles = v))}
|
|
1222
|
+
/>
|
|
1223
|
+
</Row>
|
|
1224
|
+
)}
|
|
1225
|
+
<Row label="Frame count">
|
|
1226
|
+
<NumberField
|
|
1227
|
+
value={frameBehavior.frameCount}
|
|
1228
|
+
min={frameBehavior.fps > 0 || frameBehavior.startFrame ? 1 : 0}
|
|
1229
|
+
max={tiles}
|
|
1230
|
+
step={1}
|
|
1231
|
+
onChange={(v) =>
|
|
1232
|
+
binding.apply(() => (frameBehavior.frameCount = Math.max(0, Math.round(v))))
|
|
1233
|
+
}
|
|
1234
|
+
/>
|
|
1235
|
+
</Row>
|
|
1236
|
+
<Row label="Start frame">
|
|
1237
|
+
<CheckboxField
|
|
1238
|
+
value={!!frameBehavior.startFrame}
|
|
1239
|
+
onChange={(v) =>
|
|
1240
|
+
binding.apply(() => {
|
|
1241
|
+
frameBehavior.startFrame = v ? new IntervalValue(0, tiles) : undefined;
|
|
1242
|
+
// The offset only wraps into a segment; without one it runs off the sheet.
|
|
1243
|
+
if (v && frameBehavior.frameCount <= 0) {
|
|
1244
|
+
frameBehavior.frameCount = tiles;
|
|
1245
|
+
}
|
|
1246
|
+
})
|
|
1247
|
+
}
|
|
1248
|
+
/>
|
|
1249
|
+
{frameBehavior.startFrame && (
|
|
1250
|
+
<NumberField
|
|
1251
|
+
value={startFrameCount(frameBehavior.startFrame)}
|
|
1252
|
+
min={1}
|
|
1253
|
+
max={tiles}
|
|
1254
|
+
step={1}
|
|
1255
|
+
onChange={(v) =>
|
|
1256
|
+
binding.apply(
|
|
1257
|
+
() =>
|
|
1258
|
+
(frameBehavior.startFrame = new IntervalValue(
|
|
1259
|
+
0,
|
|
1260
|
+
Math.max(1, Math.round(v))
|
|
1261
|
+
))
|
|
1262
|
+
)
|
|
1263
|
+
}
|
|
1264
|
+
/>
|
|
1265
|
+
)}
|
|
1266
|
+
</Row>
|
|
1267
|
+
<div style={{marginTop: 6}}>
|
|
1268
|
+
<CurveEditor
|
|
1269
|
+
pieces={readPieces(frameBehavior.frame)}
|
|
1270
|
+
maxValue={Math.max(1, tiles - 1)}
|
|
1271
|
+
onChange={(pieces) => binding.apply(() => (frameBehavior.frame = buildCurve(pieces)))}
|
|
1272
|
+
/>
|
|
1273
|
+
</div>
|
|
1274
|
+
</>
|
|
1090
1275
|
)}
|
|
1091
1276
|
</ModuleSection>
|
|
1092
1277
|
);
|
package/src/react/fields.tsx
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React, {useEffect, useState} from 'react';
|
|
2
|
-
import {InspectorLabel} from './InspectorLabel';
|
|
3
|
-
import {inputStyle, rowStyle} from './theme';
|
|
2
|
+
import {InspectorLabel} from './InspectorLabel.js';
|
|
3
|
+
import {inputStyle, rowStyle} from './theme.js';
|
|
4
4
|
|
|
5
|
+
/** An inspector row: a label, with an optional hint, and its controls. */
|
|
5
6
|
export function Row(props: {label: string; hint?: string; hintKey?: string; children: React.ReactNode}) {
|
|
6
7
|
return (
|
|
7
8
|
<div style={rowStyle}>
|
|
@@ -12,7 +13,13 @@ export function Row(props: {label: string; hint?: string; hintKey?: string; chil
|
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
/** Number input that keeps focus-friendly local text state and commits parsed values. */
|
|
15
|
-
export function NumberField(props: {
|
|
16
|
+
export function NumberField(props: {
|
|
17
|
+
value: number;
|
|
18
|
+
onChange: (value: number) => void;
|
|
19
|
+
step?: number;
|
|
20
|
+
min?: number;
|
|
21
|
+
max?: number;
|
|
22
|
+
}) {
|
|
16
23
|
const [text, setText] = useState(String(round(props.value)));
|
|
17
24
|
const focusedRef = React.useRef(false);
|
|
18
25
|
useEffect(() => {
|
|
@@ -28,6 +35,7 @@ export function NumberField(props: {value: number; onChange: (value: number) =>
|
|
|
28
35
|
value={text}
|
|
29
36
|
step={props.step ?? 0.1}
|
|
30
37
|
min={props.min}
|
|
38
|
+
max={props.max}
|
|
31
39
|
onFocus={() => {
|
|
32
40
|
focusedRef.current = true;
|
|
33
41
|
}}
|
|
@@ -46,6 +54,7 @@ export function NumberField(props: {value: number; onChange: (value: number) =>
|
|
|
46
54
|
);
|
|
47
55
|
}
|
|
48
56
|
|
|
57
|
+
/** A checkbox bound to a boolean. */
|
|
49
58
|
export function CheckboxField(props: {value: boolean; onChange: (value: boolean) => void}) {
|
|
50
59
|
return (
|
|
51
60
|
<input
|
|
@@ -58,9 +67,18 @@ export function CheckboxField(props: {value: boolean; onChange: (value: boolean)
|
|
|
58
67
|
);
|
|
59
68
|
}
|
|
60
69
|
|
|
70
|
+
/** A select bound to a value, from a list of options. */
|
|
61
71
|
export function SelectField<T extends string | number>(props: {
|
|
72
|
+
/** The selected value. */
|
|
62
73
|
value: T;
|
|
63
|
-
options
|
|
74
|
+
/** The options, in menu order. */
|
|
75
|
+
options: Array<{
|
|
76
|
+
/** The option's value. */
|
|
77
|
+
value: T;
|
|
78
|
+
/** Its text. */
|
|
79
|
+
label: string;
|
|
80
|
+
}>;
|
|
81
|
+
/** Receives the picked value, a number when the values are numbers. */
|
|
64
82
|
onChange: (value: T) => void;
|
|
65
83
|
}) {
|
|
66
84
|
const numeric = typeof props.value === 'number';
|