babylon.quarks-editor 0.28.0 → 0.29.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 +21 -0
- package/README.md +1 -1
- package/dist/core/defaultEffect.js +2 -0
- package/dist/core/emitterShapeWireframe.js +11 -4
- package/dist/core/loadEffect.js +2 -0
- package/dist/core/shapes.d.ts +7 -1
- package/dist/core/shapes.js +13 -3
- package/dist/core/systems.js +2 -0
- package/dist/core/values.d.ts +23 -6
- package/dist/core/values.js +47 -9
- package/dist/react/AxesCurveField.d.ts +21 -0
- package/dist/react/AxesCurveField.js +25 -0
- package/dist/react/LifeCurveField.d.ts +18 -0
- package/dist/react/LifeCurveField.js +26 -0
- package/dist/react/ValueField.d.ts +3 -2
- package/dist/react/ValueField.js +20 -6
- package/dist/react/behaviorModules.js +12 -8
- package/dist/react/inspectorHints.js +1 -1
- package/dist/react/modules.js +39 -14
- package/package.json +2 -2
- package/src/core/defaultEffect.ts +2 -0
- package/src/core/emitterShapeWireframe.ts +12 -4
- package/src/core/loadEffect.ts +2 -0
- package/src/core/shapes.ts +20 -2
- package/src/core/systems.ts +2 -0
- package/src/core/values.ts +73 -12
- package/src/react/AxesCurveField.tsx +72 -0
- package/src/react/LifeCurveField.tsx +57 -0
- package/src/react/ValueField.tsx +45 -5
- package/src/react/behaviorModules.tsx +31 -17
- package/src/react/inspectorHints.ts +1 -1
- package/src/react/modules.tsx +124 -29
package/src/react/ValueField.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type {ValueGenerator} from 'babylon.quarks';
|
|
2
|
+
import {RandomBetweenCurves} from 'babylon.quarks';
|
|
2
3
|
import React from 'react';
|
|
3
4
|
import {
|
|
4
5
|
AnyGenerator,
|
|
@@ -8,6 +9,7 @@ import {
|
|
|
8
9
|
buildScalar,
|
|
9
10
|
readPieces,
|
|
10
11
|
readScalar,
|
|
12
|
+
withCurves,
|
|
11
13
|
} from '../core/values.js';
|
|
12
14
|
import {CurveEditor} from './CurveEditor.js';
|
|
13
15
|
import {NumberField, Row, SelectField} from './fields.js';
|
|
@@ -51,13 +53,15 @@ function isValueGenerator(generator: ScalarGenerator): generator is ValueGenerat
|
|
|
51
53
|
}
|
|
52
54
|
|
|
53
55
|
/**
|
|
54
|
-
* Unity-like value field: Constant | Random Between Two Constants | Curve
|
|
55
|
-
* generator. With `curve={false}` it offers only the first two,
|
|
56
|
+
* Unity-like value field: Constant | Random Between Two Constants | Curve | Random Between Two
|
|
57
|
+
* Curves, bound to a quarks value generator. With `curve={false}` it offers only the first two,
|
|
58
|
+
* for settings sampled once.
|
|
56
59
|
*/
|
|
57
60
|
export function ValueField(props: CurveValueFieldProps | ConstantValueFieldProps): React.ReactElement {
|
|
58
61
|
const read = readScalar(props.generator);
|
|
59
62
|
// A setting without curves shows a curve it was loaded with as a constant, so an edit replaces it.
|
|
60
|
-
const
|
|
63
|
+
const curved = read.mode === 'curve' || read.mode === 'curves';
|
|
64
|
+
const state: ScalarValueState = props.curve === false && curved ? {...read, mode: 'constant'} : read;
|
|
61
65
|
|
|
62
66
|
const emit = (generator: ScalarGenerator) => {
|
|
63
67
|
if (props.curve !== false) {
|
|
@@ -76,9 +80,19 @@ export function ValueField(props: CurveValueFieldProps | ConstantValueFieldProps
|
|
|
76
80
|
options={[
|
|
77
81
|
{value: 'constant', label: 'Constant'},
|
|
78
82
|
{value: 'random', label: 'Random between two'},
|
|
79
|
-
...(props.curve !== false
|
|
83
|
+
...(props.curve !== false
|
|
84
|
+
? [
|
|
85
|
+
{value: 'curve' as const, label: 'Curve'},
|
|
86
|
+
{value: 'curves' as const, label: 'Random between two curves'},
|
|
87
|
+
]
|
|
88
|
+
: []),
|
|
80
89
|
]}
|
|
81
|
-
onChange={(mode) =>
|
|
90
|
+
onChange={(mode) =>
|
|
91
|
+
// Between one curve and two, every segment is kept; other switches start afresh.
|
|
92
|
+
(mode === 'curve' || mode === 'curves') && curved
|
|
93
|
+
? emit(withCurves(props.generator, mode))
|
|
94
|
+
: commit({mode})
|
|
95
|
+
}
|
|
82
96
|
/>
|
|
83
97
|
</Row>
|
|
84
98
|
{state.mode === 'constant' && (
|
|
@@ -105,6 +119,32 @@ export function ValueField(props: CurveValueFieldProps | ConstantValueFieldProps
|
|
|
105
119
|
/>
|
|
106
120
|
</div>
|
|
107
121
|
)}
|
|
122
|
+
{state.mode === 'curves' && (
|
|
123
|
+
<>
|
|
124
|
+
{(['lower', 'upper'] as const).map((bound, index) => (
|
|
125
|
+
<div key={bound} style={{marginTop: 6}}>
|
|
126
|
+
<span style={captionStyle}>{bound}</span>
|
|
127
|
+
<CurveEditor
|
|
128
|
+
pieces={readPieces(props.generator, index === 1)}
|
|
129
|
+
maxValue={props.curveMax}
|
|
130
|
+
onChange={(pieces) =>
|
|
131
|
+
emit(
|
|
132
|
+
index === 0
|
|
133
|
+
? new RandomBetweenCurves(
|
|
134
|
+
buildCurve(pieces),
|
|
135
|
+
buildCurve(readPieces(props.generator, true))
|
|
136
|
+
)
|
|
137
|
+
: new RandomBetweenCurves(
|
|
138
|
+
buildCurve(readPieces(props.generator)),
|
|
139
|
+
buildCurve(pieces)
|
|
140
|
+
)
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
/>
|
|
144
|
+
</div>
|
|
145
|
+
))}
|
|
146
|
+
</>
|
|
147
|
+
)}
|
|
108
148
|
</>
|
|
109
149
|
);
|
|
110
150
|
}
|
|
@@ -29,17 +29,19 @@ import {
|
|
|
29
29
|
TextureSequencer,
|
|
30
30
|
TurbulenceField,
|
|
31
31
|
Vector3,
|
|
32
|
+
Vector3Function,
|
|
32
33
|
VelocityOverLife,
|
|
33
34
|
WidthOverLength,
|
|
34
35
|
} from 'babylon.quarks';
|
|
35
36
|
import React from 'react';
|
|
36
37
|
import {EffectBinding} from '../core/binding.js';
|
|
37
38
|
import {buildGradient, DEFAULT_GRADIENT_STOPS, findBehavior, readGradientStops} from '../core/colors.js';
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
39
|
+
import {buildScalar, readScalar} from '../core/values.js';
|
|
40
|
+
import {AxesCurveField} from './AxesCurveField.js';
|
|
40
41
|
import {CheckboxField, NumberField, Row, SelectField} from './fields.js';
|
|
41
42
|
import {GradientEditor} from './GradientEditor.js';
|
|
42
43
|
import {iconStyle} from './icons.js';
|
|
44
|
+
import {LifeCurveField} from './LifeCurveField.js';
|
|
43
45
|
import type {TextureOption} from './modules.js';
|
|
44
46
|
import {ModuleSection, type ModuleProps} from './ModuleSection.js';
|
|
45
47
|
import {theme} from './theme.js';
|
|
@@ -94,10 +96,10 @@ export function SpeedOverLifeModule({binding}: ModuleProps) {
|
|
|
94
96
|
>
|
|
95
97
|
{(behavior) => (
|
|
96
98
|
<div style={{marginTop: 6}}>
|
|
97
|
-
<
|
|
98
|
-
|
|
99
|
+
<LifeCurveField
|
|
100
|
+
generator={behavior.speed}
|
|
99
101
|
maxValue={2}
|
|
100
|
-
onChange={(
|
|
102
|
+
onChange={(curve) => binding.apply(() => (behavior.speed = curve))}
|
|
101
103
|
/>
|
|
102
104
|
</div>
|
|
103
105
|
)}
|
|
@@ -119,12 +121,24 @@ export function LimitSpeedOverLifeModule({binding}: ModuleProps) {
|
|
|
119
121
|
{(behavior) => (
|
|
120
122
|
<>
|
|
121
123
|
<div style={{marginTop: 6}}>
|
|
122
|
-
<
|
|
123
|
-
|
|
124
|
+
<AxesCurveField
|
|
125
|
+
generator={behavior.speed}
|
|
124
126
|
maxValue={8}
|
|
125
|
-
onChange={(
|
|
127
|
+
onChange={(speed) => binding.apply(() => (behavior.speed = speed))}
|
|
126
128
|
/>
|
|
127
129
|
</div>
|
|
130
|
+
{behavior.speed instanceof Vector3Function && (
|
|
131
|
+
<Row label="Space" hint="The axes each limit is measured along: the emitter's or the world's.">
|
|
132
|
+
<SelectField
|
|
133
|
+
value={behavior.space}
|
|
134
|
+
options={[
|
|
135
|
+
{value: 'local', label: 'Local'},
|
|
136
|
+
{value: 'world', label: 'World'},
|
|
137
|
+
]}
|
|
138
|
+
onChange={(v) => binding.apply(() => (behavior.space = v))}
|
|
139
|
+
/>
|
|
140
|
+
</Row>
|
|
141
|
+
)}
|
|
128
142
|
<Row label="Dampen">
|
|
129
143
|
<NumberField
|
|
130
144
|
value={behavior.dampen}
|
|
@@ -942,10 +956,10 @@ export function SizeBySpeedModule({binding}: ModuleProps) {
|
|
|
942
956
|
{(behavior) => (
|
|
943
957
|
<>
|
|
944
958
|
<div style={{marginTop: 6}}>
|
|
945
|
-
<
|
|
946
|
-
|
|
959
|
+
<AxesCurveField
|
|
960
|
+
generator={behavior.size}
|
|
947
961
|
maxValue={2}
|
|
948
|
-
onChange={(
|
|
962
|
+
onChange={(size) => binding.apply(() => (behavior.size = size))}
|
|
949
963
|
/>
|
|
950
964
|
</div>
|
|
951
965
|
<SpeedRangeRows binding={binding} behavior={behavior} />
|
|
@@ -1015,10 +1029,10 @@ export function WidthOverTrailModule({binding}: ModuleProps) {
|
|
|
1015
1029
|
{(behavior) => (
|
|
1016
1030
|
<>
|
|
1017
1031
|
<div style={{marginTop: 6}}>
|
|
1018
|
-
<
|
|
1019
|
-
|
|
1032
|
+
<LifeCurveField
|
|
1033
|
+
generator={behavior.width}
|
|
1020
1034
|
maxValue={2}
|
|
1021
|
-
onChange={(
|
|
1035
|
+
onChange={(curve) => binding.apply(() => (behavior.width = curve))}
|
|
1022
1036
|
/>
|
|
1023
1037
|
</div>
|
|
1024
1038
|
{/* Off: the curve multiplies the particle size. On: it is the width in world units (files from 0.21 and earlier). */}
|
|
@@ -1265,10 +1279,10 @@ export function TextureSheetModule({binding}: ModuleProps) {
|
|
|
1265
1279
|
)}
|
|
1266
1280
|
</Row>
|
|
1267
1281
|
<div style={{marginTop: 6}}>
|
|
1268
|
-
<
|
|
1269
|
-
|
|
1282
|
+
<LifeCurveField
|
|
1283
|
+
generator={frameBehavior.frame}
|
|
1270
1284
|
maxValue={Math.max(1, tiles - 1)}
|
|
1271
|
-
onChange={(
|
|
1285
|
+
onChange={(curve) => binding.apply(() => (frameBehavior.frame = curve))}
|
|
1272
1286
|
/>
|
|
1273
1287
|
</div>
|
|
1274
1288
|
</>
|
|
@@ -33,7 +33,7 @@ const INSPECTOR_HINTS: Record<string, string> = {
|
|
|
33
33
|
Looping: 'When enabled, the effect restarts from the beginning after duration ends.',
|
|
34
34
|
'World space': 'Simulates in world coordinates so moving the emitter does not drag existing particles.',
|
|
35
35
|
'Scaling mode':
|
|
36
|
-
'
|
|
36
|
+
'Which scale reaches the effect (Unity’s Scaling Mode): Local, the emitter’s own; Hierarchy, its parents’ too; Shape, the whole scale on the emission shape alone.',
|
|
37
37
|
Prewarm: 'Simulates the effect once at start so it already looks mid-playback when play begins.',
|
|
38
38
|
'Warm-up time':
|
|
39
39
|
'Seconds of the cycle simulated before the first frame of a looping system (Niagara’s Warmup Time); 0 warms up a whole cycle, as Unity does.',
|
package/src/react/modules.tsx
CHANGED
|
@@ -20,10 +20,12 @@ import {
|
|
|
20
20
|
BoxEmitFrom,
|
|
21
21
|
ColorOverLife,
|
|
22
22
|
ColorRange,
|
|
23
|
+
ConeEmitFrom,
|
|
23
24
|
ConstantColor,
|
|
24
25
|
ConstantValue,
|
|
25
26
|
EulerGenerator,
|
|
26
27
|
Gradient,
|
|
28
|
+
GridSampling,
|
|
27
29
|
IntervalValue,
|
|
28
30
|
RandomColor,
|
|
29
31
|
RandomColorBetweenGradient,
|
|
@@ -56,8 +58,8 @@ import {
|
|
|
56
58
|
getShapeType,
|
|
57
59
|
readShapeParams,
|
|
58
60
|
} from '../core/shapes.js';
|
|
59
|
-
import {buildCurve, buildScalar,
|
|
60
|
-
import {
|
|
61
|
+
import {buildCurve, buildScalar, readScalar} from '../core/values.js';
|
|
62
|
+
import {AxesCurveField} from './AxesCurveField.js';
|
|
61
63
|
import {GradientEditor} from './GradientEditor.js';
|
|
62
64
|
import {InspectorInfoIcon} from './InspectorInfoIcon.js';
|
|
63
65
|
import {ModuleSection, type ModuleProps} from './ModuleSection.js';
|
|
@@ -113,6 +115,7 @@ const PARAM_LABELS: {[key: string]: string} = {
|
|
|
113
115
|
column: 'Columns',
|
|
114
116
|
row: 'Rows',
|
|
115
117
|
depth: 'Depth',
|
|
118
|
+
length: 'Length',
|
|
116
119
|
speed: 'Speed (lengths/s)',
|
|
117
120
|
};
|
|
118
121
|
|
|
@@ -260,18 +263,17 @@ export function MainModule({binding}: ModuleProps) {
|
|
|
260
263
|
<Row label="World space">
|
|
261
264
|
<CheckboxField value={system.worldSpace} onChange={(v) => binding.apply((s) => (s.worldSpace = v))} />
|
|
262
265
|
</Row>
|
|
263
|
-
|
|
264
|
-
<
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
)}
|
|
266
|
+
<Row label="Scaling mode">
|
|
267
|
+
<SelectField<ScalingMode>
|
|
268
|
+
value={system.scalingMode}
|
|
269
|
+
options={[
|
|
270
|
+
{value: 'local', label: 'Local (own scale only)'},
|
|
271
|
+
{value: 'hierarchy', label: 'Hierarchy (parents too)'},
|
|
272
|
+
{value: 'shape', label: 'Shape (shape only)'},
|
|
273
|
+
]}
|
|
274
|
+
onChange={(mode) => binding.apply((s) => (s.scalingMode = mode))}
|
|
275
|
+
/>
|
|
276
|
+
</Row>
|
|
275
277
|
<Row label="Prewarm">
|
|
276
278
|
<CheckboxField value={system.prewarm} onChange={(v) => binding.apply((s) => (s.prewarm = v))} />
|
|
277
279
|
</Row>
|
|
@@ -318,6 +320,20 @@ export function MainModule({binding}: ModuleProps) {
|
|
|
318
320
|
onChange={(mode) => binding.apply((s) => (s.cullingMode = mode as CullingMode))}
|
|
319
321
|
/>
|
|
320
322
|
</Row>
|
|
323
|
+
<Row
|
|
324
|
+
label="Stop action"
|
|
325
|
+
hint="What the system does once it has finished, as Unity's Stop Action. The editor saves it but does not apply it, so previews replay."
|
|
326
|
+
>
|
|
327
|
+
<SelectField
|
|
328
|
+
value={system.stopAction}
|
|
329
|
+
options={[
|
|
330
|
+
{value: 'none', label: 'None'},
|
|
331
|
+
{value: 'disable', label: 'Disable'},
|
|
332
|
+
{value: 'destroy', label: 'Destroy'},
|
|
333
|
+
]}
|
|
334
|
+
onChange={(v) => binding.apply((s) => (s.stopAction = v))}
|
|
335
|
+
/>
|
|
336
|
+
</Row>
|
|
321
337
|
<ValueField
|
|
322
338
|
label="Start delay"
|
|
323
339
|
generator={system.startDelay}
|
|
@@ -725,17 +741,20 @@ export function ShapeModule({binding}: ModuleProps) {
|
|
|
725
741
|
onChange={(next) => binding.apply((s) => (s.emitterShape = createShape(next, params)))}
|
|
726
742
|
/>
|
|
727
743
|
</Row>
|
|
728
|
-
{keys
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
744
|
+
{keys
|
|
745
|
+
// A cone's Length only shapes Emit from Volume; Unity shows it for Cone Volume alone.
|
|
746
|
+
.filter((key) => key !== 'length' || params.coneEmitFrom === ConeEmitFrom.Volume)
|
|
747
|
+
.map((key) => (
|
|
748
|
+
<Row key={key} label={PARAM_LABELS[key]}>
|
|
749
|
+
<NumberField
|
|
750
|
+
value={params[key] as number}
|
|
751
|
+
min={0}
|
|
752
|
+
onChange={(v) =>
|
|
753
|
+
binding.apply((s) => (s.emitterShape = createShape(type, {...params, [key]: v})))
|
|
754
|
+
}
|
|
755
|
+
/>
|
|
756
|
+
</Row>
|
|
757
|
+
))}
|
|
739
758
|
{type === 'mesh_surface' && (
|
|
740
759
|
<Row label="Source mesh">
|
|
741
760
|
<SelectField
|
|
@@ -767,6 +786,34 @@ export function ShapeModule({binding}: ModuleProps) {
|
|
|
767
786
|
/>
|
|
768
787
|
</Row>
|
|
769
788
|
)}
|
|
789
|
+
{type === 'cone' && (
|
|
790
|
+
<Row label="Emit from">
|
|
791
|
+
<SelectField
|
|
792
|
+
value={params.coneEmitFrom}
|
|
793
|
+
options={[
|
|
794
|
+
{value: ConeEmitFrom.Base, label: 'Base'},
|
|
795
|
+
{value: ConeEmitFrom.Volume, label: 'Volume'},
|
|
796
|
+
]}
|
|
797
|
+
onChange={(coneEmitFrom) =>
|
|
798
|
+
binding.apply((s) => (s.emitterShape = createShape(type, {...params, coneEmitFrom})))
|
|
799
|
+
}
|
|
800
|
+
/>
|
|
801
|
+
</Row>
|
|
802
|
+
)}
|
|
803
|
+
{type === 'grid' && (
|
|
804
|
+
<Row label="Sampling">
|
|
805
|
+
<SelectField
|
|
806
|
+
value={params.sampling}
|
|
807
|
+
options={[
|
|
808
|
+
{value: GridSampling.Random, label: 'Random'},
|
|
809
|
+
{value: GridSampling.Sequential, label: 'Sequential'},
|
|
810
|
+
]}
|
|
811
|
+
onChange={(sampling) =>
|
|
812
|
+
binding.apply((s) => (s.emitterShape = createShape(type, {...params, sampling})))
|
|
813
|
+
}
|
|
814
|
+
/>
|
|
815
|
+
</Row>
|
|
816
|
+
)}
|
|
770
817
|
{!SHAPES_WITHOUT_MODE_SPREAD.has(type) && (
|
|
771
818
|
<>
|
|
772
819
|
<Row label="Emit mode">
|
|
@@ -824,13 +871,13 @@ export function SizeOverLifeModule({binding}: ModuleProps) {
|
|
|
824
871
|
}
|
|
825
872
|
>
|
|
826
873
|
<div style={{marginTop: 6}}>
|
|
827
|
-
<
|
|
828
|
-
|
|
874
|
+
<AxesCurveField
|
|
875
|
+
generator={behavior ? behavior.size : buildCurve([{start: 0, p: curve}])}
|
|
829
876
|
maxValue={2}
|
|
830
|
-
onChange={(
|
|
877
|
+
onChange={(size) =>
|
|
831
878
|
binding.apply((s) => {
|
|
832
879
|
removeBehavior(s, 'SizeOverLife');
|
|
833
|
-
s.addBehavior(new SizeOverLife(
|
|
880
|
+
s.addBehavior(new SizeOverLife(size));
|
|
834
881
|
})
|
|
835
882
|
}
|
|
836
883
|
/>
|
|
@@ -1629,6 +1676,48 @@ export function RendererModule({
|
|
|
1629
1676
|
/>
|
|
1630
1677
|
)}
|
|
1631
1678
|
</Row>
|
|
1679
|
+
{system.maxParticles !== undefined && system.renderMode !== RenderMode.Trail && (
|
|
1680
|
+
<Row
|
|
1681
|
+
label="Ring buffer"
|
|
1682
|
+
hint="What a full system does with new particles, as Unity's Ring Buffer Mode."
|
|
1683
|
+
>
|
|
1684
|
+
<SelectField
|
|
1685
|
+
value={system.ringBufferMode}
|
|
1686
|
+
options={[
|
|
1687
|
+
{value: 'disabled', label: 'Disabled'},
|
|
1688
|
+
{value: 'pauseUntilReplaced', label: 'Pause until replaced'},
|
|
1689
|
+
{value: 'loopUntilReplaced', label: 'Loop until replaced'},
|
|
1690
|
+
]}
|
|
1691
|
+
onChange={(v) => binding.apply((s) => (s.ringBufferMode = v))}
|
|
1692
|
+
/>
|
|
1693
|
+
</Row>
|
|
1694
|
+
)}
|
|
1695
|
+
{system.maxParticles !== undefined &&
|
|
1696
|
+
system.renderMode !== RenderMode.Trail &&
|
|
1697
|
+
system.ringBufferMode === 'loopUntilReplaced' && (
|
|
1698
|
+
<Row label="Loop range" hint="The part of its life a particle plays again, from 0 to 1.">
|
|
1699
|
+
<div style={{display: 'flex', gap: 6}}>
|
|
1700
|
+
{(['x', 'y'] as const).map((end) => (
|
|
1701
|
+
<NumberField
|
|
1702
|
+
key={end}
|
|
1703
|
+
value={system.ringBufferLoopRange[end]}
|
|
1704
|
+
min={0}
|
|
1705
|
+
step={0.05}
|
|
1706
|
+
onChange={(v) =>
|
|
1707
|
+
binding.apply((s) => {
|
|
1708
|
+
const next = s.ringBufferLoopRange.clone();
|
|
1709
|
+
next[end] = Math.min(1, Math.max(0, v));
|
|
1710
|
+
s.ringBufferLoopRange = new Vector2(
|
|
1711
|
+
Math.min(next.x, next.y),
|
|
1712
|
+
Math.max(next.x, next.y)
|
|
1713
|
+
);
|
|
1714
|
+
})
|
|
1715
|
+
}
|
|
1716
|
+
/>
|
|
1717
|
+
))}
|
|
1718
|
+
</div>
|
|
1719
|
+
</Row>
|
|
1720
|
+
)}
|
|
1632
1721
|
{system.renderMode !== RenderMode.Mesh && system.renderMode !== RenderMode.Trail && (
|
|
1633
1722
|
<Row label="Pivot">
|
|
1634
1723
|
<div style={{display: 'flex', gap: 6}}>
|
|
@@ -1730,6 +1819,12 @@ export function RendererModule({
|
|
|
1730
1819
|
<Row label="Apply fog">
|
|
1731
1820
|
<CheckboxField value={system.applyFog} onChange={(v) => binding.apply((s) => (s.applyFog = v))} />
|
|
1732
1821
|
</Row>
|
|
1822
|
+
<Row label="Image processing">
|
|
1823
|
+
<CheckboxField
|
|
1824
|
+
value={system.applyImageProcessing}
|
|
1825
|
+
onChange={(v) => binding.apply((s) => (s.applyImageProcessing = v))}
|
|
1826
|
+
/>
|
|
1827
|
+
</Row>
|
|
1733
1828
|
<Row label="Soft particles">
|
|
1734
1829
|
<CheckboxField
|
|
1735
1830
|
value={system.softParticles}
|