babylon.quarks-editor 0.18.0 → 0.19.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/README.md +6 -6
- package/dist/core/binding.d.ts +1 -1
- package/dist/core/colors.d.ts +1 -1
- package/dist/core/defaultEffect.d.ts +1 -1
- package/dist/core/effectTree.d.ts +1 -1
- package/dist/core/effectTree.js +18 -4
- package/dist/core/emitterShapeWireframe.d.ts +1 -1
- package/dist/core/emitterShapeWireframe.js +1 -1
- package/dist/core/galleryDropMarker.js +1 -1
- package/dist/core/galleryScene.d.ts +1 -1
- package/dist/core/galleryTree.js +4 -1
- package/dist/core/loadEffect.d.ts +2 -2
- package/dist/core/loadEffect.js +1 -1
- package/dist/core/loadEffectGallery.d.ts +2 -2
- package/dist/core/shapes.d.ts +1 -1
- package/dist/core/shapes.js +35 -8
- package/dist/core/systems.d.ts +1 -1
- package/dist/core/systems.js +3 -1
- package/dist/core/timeline.d.ts +1 -2
- package/dist/core/values.d.ts +1 -1
- package/dist/index.d.ts +11 -11
- package/dist/index.js +6 -6
- package/dist/react/CurveEditor.d.ts +1 -2
- package/dist/react/CurveEditor.js +14 -3
- package/dist/react/EffectEditor.d.ts +2 -2
- package/dist/react/EffectEditor.js +2 -2
- package/dist/react/EffectEditorHost.d.ts +1 -1
- package/dist/react/EffectEditorHost.js +123 -33
- package/dist/react/GalleryPanel.d.ts +1 -1
- package/dist/react/GalleryPanel.js +14 -7
- package/dist/react/GradientEditor.d.ts +1 -2
- package/dist/react/GradientEditor.js +8 -1
- package/dist/react/MessageDialog.js +7 -1
- package/dist/react/ModuleSection.js +10 -3
- package/dist/react/ObjectModule.d.ts +1 -2
- package/dist/react/ObjectModule.js +1 -1
- package/dist/react/SubEmittersModule.d.ts +1 -2
- package/dist/react/TimelinePanel.d.ts +3 -3
- package/dist/react/TimelinePanel.js +55 -9
- package/dist/react/TimelineTrackRow.d.ts +1 -1
- package/dist/react/TimelineTrackRow.js +20 -6
- package/dist/react/ValueField.d.ts +1 -2
- package/dist/react/behaviorModules.js +19 -5
- package/dist/react/index.d.ts +17 -17
- package/dist/react/index.js +11 -11
- package/dist/react/modules.js +55 -25
- package/package.json +68 -68
- package/src/core/binding.ts +2 -2
- package/src/core/colors.ts +1 -1
- package/src/core/defaultEffect.ts +1 -1
- package/src/core/effectTree.ts +22 -7
- package/src/core/emitterShapeWireframe.ts +8 -4
- package/src/core/galleryDropMarker.ts +6 -2
- package/src/core/galleryPlayback.ts +1 -1
- package/src/core/galleryScene.ts +3 -7
- package/src/core/gallerySelectionMarker.ts +5 -1
- package/src/core/galleryTree.ts +8 -5
- package/src/core/loadEffect.ts +3 -3
- package/src/core/loadEffectGallery.ts +7 -3
- package/src/core/pickGalleryFiles.ts +4 -1
- package/src/core/shapes.ts +48 -10
- package/src/core/systems.ts +9 -3
- package/src/core/timeline.ts +8 -8
- package/src/core/values.ts +1 -1
- package/src/core/viewportPicker.ts +1 -1
- package/src/index.ts +18 -18
- package/src/react/CurveEditor.tsx +40 -10
- package/src/react/EffectEditor.tsx +14 -13
- package/src/react/EffectEditorHost.tsx +239 -64
- package/src/react/GalleryPanel.tsx +56 -12
- package/src/react/GradientEditor.tsx +21 -4
- package/src/react/InspectorInfoIcon.tsx +6 -2
- package/src/react/MessageDialog.tsx +9 -1
- package/src/react/ModuleSection.tsx +12 -3
- package/src/react/ObjectModule.tsx +36 -15
- package/src/react/PromptDialog.tsx +5 -1
- package/src/react/SubEmittersModule.tsx +1 -2
- package/src/react/TimelinePanel.tsx +94 -21
- package/src/react/TimelineTrackRow.tsx +71 -18
- package/src/react/ValueField.tsx +0 -1
- package/src/react/behaviorModules.tsx +143 -29
- package/src/react/index.ts +33 -33
- package/src/react/modules.tsx +173 -57
package/src/react/modules.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import React, {useMemo, useState} from 'react';
|
|
2
|
-
import {Mesh} from '@babylonjs/core/Meshes/mesh';
|
|
3
1
|
import {Texture} from '@babylonjs/core/Materials/Textures/texture';
|
|
2
|
+
import {Mesh} from '@babylonjs/core/Meshes/mesh';
|
|
3
|
+
import {PlusIcon, XMarkIcon} from '@heroicons/react/24/solid';
|
|
4
|
+
import type {Behavior, ParticleSystem} from 'babylon.quarks';
|
|
4
5
|
import {
|
|
5
6
|
ColorOverLife,
|
|
6
7
|
ConstantColor,
|
|
@@ -16,7 +17,7 @@ import {
|
|
|
16
17
|
Vector3Function,
|
|
17
18
|
Vector4,
|
|
18
19
|
} from 'babylon.quarks';
|
|
19
|
-
import
|
|
20
|
+
import React, {useMemo, useState} from 'react';
|
|
20
21
|
import {EffectBinding} from '../core/binding';
|
|
21
22
|
import {
|
|
22
23
|
DEFAULT_GRADIENT_STOPS,
|
|
@@ -27,20 +28,26 @@ import {
|
|
|
27
28
|
readGradientStops,
|
|
28
29
|
rgbToHex,
|
|
29
30
|
} from '../core/colors';
|
|
30
|
-
import {
|
|
31
|
+
import {applyRendererMaterial, getMaterialLabel} from '../core/material';
|
|
31
32
|
import type {ShapeType} from '../core/shapes';
|
|
33
|
+
import {
|
|
34
|
+
DEFAULT_SHAPE_PARAMS,
|
|
35
|
+
SHAPE_PARAM_KEYS,
|
|
36
|
+
SHAPE_TYPES,
|
|
37
|
+
createShape,
|
|
38
|
+
getShapeType,
|
|
39
|
+
readShapeParams,
|
|
40
|
+
} from '../core/shapes';
|
|
32
41
|
import {buildCurve, buildScalar, readPieces, readScalar} from '../core/values';
|
|
33
|
-
import {applyRendererMaterial, getMaterialLabel} from '../core/material';
|
|
34
42
|
import {CurveEditor} from './CurveEditor';
|
|
35
43
|
import {GradientEditor} from './GradientEditor';
|
|
44
|
+
import {InspectorInfoIcon} from './InspectorInfoIcon';
|
|
36
45
|
import {ModuleSection} from './ModuleSection';
|
|
37
46
|
import {PromptDialog} from './PromptDialog';
|
|
38
|
-
import {CheckboxField, NumberField, Row, SelectField} from './fields';
|
|
39
47
|
import {ValueField} from './ValueField';
|
|
48
|
+
import {CheckboxField, NumberField, Row, SelectField} from './fields';
|
|
40
49
|
import {iconStyle} from './icons';
|
|
41
50
|
import {theme} from './theme';
|
|
42
|
-
import {PlusIcon, XMarkIcon} from '@heroicons/react/24/solid';
|
|
43
|
-
import {InspectorInfoIcon} from './InspectorInfoIcon';
|
|
44
51
|
|
|
45
52
|
interface ModuleProps {
|
|
46
53
|
binding: EffectBinding;
|
|
@@ -50,12 +57,11 @@ const MESH_QUAD_POSITIONS = new Float32Array([-0.5, -0.5, 0, 0.5, -0.5, 0, 0.5,
|
|
|
50
57
|
const MESH_QUAD_INDICES = new Uint32Array([0, 1, 2, 0, 2, 3]);
|
|
51
58
|
const MESH_QUAD_UVS = new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]);
|
|
52
59
|
const MESH_CUBE_POSITIONS = new Float32Array([
|
|
53
|
-
-0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5,
|
|
54
|
-
-0.5,
|
|
60
|
+
-0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5,
|
|
61
|
+
-0.5, 0.5, 0.5,
|
|
55
62
|
]);
|
|
56
63
|
const MESH_CUBE_INDICES = new Uint32Array([
|
|
57
|
-
0, 1, 2, 0, 2, 3, 4, 6, 5, 4, 7, 6, 0, 4, 5, 0, 5, 1,
|
|
58
|
-
3, 2, 6, 3, 6, 7, 0, 3, 7, 0, 7, 4, 1, 5, 6, 1, 6, 2,
|
|
64
|
+
0, 1, 2, 0, 2, 3, 4, 6, 5, 4, 7, 6, 0, 4, 5, 0, 5, 1, 3, 2, 6, 3, 6, 7, 0, 3, 7, 0, 7, 4, 1, 5, 6, 1, 6, 2,
|
|
59
65
|
]);
|
|
60
66
|
|
|
61
67
|
function arraysEqual(a: ArrayLike<number>, b: ArrayLike<number>): boolean {
|
|
@@ -105,7 +111,14 @@ function ColorInput(props: {value: Vector4; onChange: (next: Vector4) => void})
|
|
|
105
111
|
const rgb = hexToRgb(e.target.value);
|
|
106
112
|
onChange(new Vector4(rgb.r, rgb.g, rgb.b, value.w));
|
|
107
113
|
}}
|
|
108
|
-
style={{
|
|
114
|
+
style={{
|
|
115
|
+
width: 42,
|
|
116
|
+
height: 24,
|
|
117
|
+
border: 'none',
|
|
118
|
+
background: 'transparent',
|
|
119
|
+
padding: 0,
|
|
120
|
+
cursor: 'pointer',
|
|
121
|
+
}}
|
|
109
122
|
/>
|
|
110
123
|
<NumberField
|
|
111
124
|
value={value.w}
|
|
@@ -122,18 +135,26 @@ export function MainModule({binding}: ModuleProps) {
|
|
|
122
135
|
const startColor = system.startColor;
|
|
123
136
|
const colorMode = startColor instanceof RandomColor ? 'random' : 'constant';
|
|
124
137
|
const colorA =
|
|
125
|
-
startColor instanceof RandomColor
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
138
|
+
startColor instanceof RandomColor
|
|
139
|
+
? (startColor as never as {a: Vector4}).a
|
|
140
|
+
: startColor instanceof ConstantColor
|
|
141
|
+
? startColor.color
|
|
142
|
+
: new Vector4(1, 1, 1, 1);
|
|
143
|
+
const colorB =
|
|
144
|
+
startColor instanceof RandomColor ? (startColor as never as {b: Vector4}).b : new Vector4(1, 0.5, 0.2, 1);
|
|
129
145
|
const rotation = system.startRotation;
|
|
130
146
|
const isEuler = rotation instanceof EulerGenerator;
|
|
131
147
|
const rotationMode =
|
|
132
|
-
rotation instanceof RandomQuatGenerator
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
148
|
+
rotation instanceof RandomQuatGenerator
|
|
149
|
+
? '3d'
|
|
150
|
+
: isEuler
|
|
151
|
+
? 'euler'
|
|
152
|
+
: readScalar(rotation as never).mode === 'random'
|
|
153
|
+
? 'random'
|
|
154
|
+
: 'angle';
|
|
155
|
+
const rotationState = readScalar(
|
|
156
|
+
rotation instanceof RandomQuatGenerator || isEuler ? undefined : (rotation as never)
|
|
157
|
+
);
|
|
137
158
|
const eulerAngles = {
|
|
138
159
|
x: isEuler ? readScalar((rotation as EulerGenerator).angleX as never).value : 0,
|
|
139
160
|
y: isEuler ? readScalar((rotation as EulerGenerator).angleY as never).value : 0,
|
|
@@ -151,13 +172,19 @@ export function MainModule({binding}: ModuleProps) {
|
|
|
151
172
|
const startSize = system.startSize as unknown as {type?: string; x?: never; y?: never; z?: never};
|
|
152
173
|
const size3D = startSize.type === 'vec3function';
|
|
153
174
|
const colorGenMode =
|
|
154
|
-
startColor instanceof RandomColorBetweenGradient
|
|
155
|
-
|
|
156
|
-
|
|
175
|
+
startColor instanceof RandomColorBetweenGradient
|
|
176
|
+
? 'gradient2'
|
|
177
|
+
: startColor instanceof Gradient
|
|
178
|
+
? 'gradient'
|
|
179
|
+
: colorMode;
|
|
157
180
|
return (
|
|
158
181
|
<ModuleSection title="Main">
|
|
159
182
|
<Row label="Duration">
|
|
160
|
-
<NumberField
|
|
183
|
+
<NumberField
|
|
184
|
+
value={system.duration}
|
|
185
|
+
min={0.05}
|
|
186
|
+
onChange={(v) => binding.apply((s) => (s.duration = v))}
|
|
187
|
+
/>
|
|
161
188
|
</Row>
|
|
162
189
|
<Row label="Looping">
|
|
163
190
|
<CheckboxField value={system.looping} onChange={(v) => binding.apply((s) => (s.looping = v))} />
|
|
@@ -280,7 +307,8 @@ export function MainModule({binding}: ModuleProps) {
|
|
|
280
307
|
value={colorA}
|
|
281
308
|
onChange={(next) =>
|
|
282
309
|
binding.apply((s) => {
|
|
283
|
-
s.startColor =
|
|
310
|
+
s.startColor =
|
|
311
|
+
colorGenMode === 'random' ? new RandomColor(next, colorB) : new ConstantColor(next);
|
|
284
312
|
})
|
|
285
313
|
}
|
|
286
314
|
/>
|
|
@@ -339,7 +367,11 @@ export function MainModule({binding}: ModuleProps) {
|
|
|
339
367
|
mode === '3d'
|
|
340
368
|
? new RandomQuatGenerator()
|
|
341
369
|
: mode === 'euler'
|
|
342
|
-
? new EulerGenerator(
|
|
370
|
+
? new EulerGenerator(
|
|
371
|
+
new ConstantValue(eulerAngles.x),
|
|
372
|
+
new ConstantValue(eulerAngles.y),
|
|
373
|
+
new ConstantValue(eulerAngles.z)
|
|
374
|
+
)
|
|
343
375
|
: mode === 'random'
|
|
344
376
|
? new IntervalValue(0, Math.PI * 2)
|
|
345
377
|
: new ConstantValue(rotationState.value);
|
|
@@ -371,12 +403,16 @@ export function MainModule({binding}: ModuleProps) {
|
|
|
371
403
|
<NumberField
|
|
372
404
|
value={rotationState.min}
|
|
373
405
|
step={0.1}
|
|
374
|
-
onChange={(v) =>
|
|
406
|
+
onChange={(v) =>
|
|
407
|
+
binding.apply((s) => (s.startRotation = new IntervalValue(v, rotationState.max)))
|
|
408
|
+
}
|
|
375
409
|
/>
|
|
376
410
|
<NumberField
|
|
377
411
|
value={rotationState.max}
|
|
378
412
|
step={0.1}
|
|
379
|
-
onChange={(v) =>
|
|
413
|
+
onChange={(v) =>
|
|
414
|
+
binding.apply((s) => (s.startRotation = new IntervalValue(rotationState.min, v)))
|
|
415
|
+
}
|
|
380
416
|
/>
|
|
381
417
|
</div>
|
|
382
418
|
</Row>
|
|
@@ -410,7 +446,14 @@ export function EmissionModule({binding}: ModuleProps) {
|
|
|
410
446
|
<span style={{fontSize: 11.5, color: '#9eb9ff'}}>Burst {i + 1}</span>
|
|
411
447
|
<button
|
|
412
448
|
className="qe-hover"
|
|
413
|
-
style={{
|
|
449
|
+
style={{
|
|
450
|
+
background: 'none',
|
|
451
|
+
border: 'none',
|
|
452
|
+
color: '#e08c8c',
|
|
453
|
+
cursor: 'pointer',
|
|
454
|
+
padding: 0,
|
|
455
|
+
display: 'inline-flex',
|
|
456
|
+
}}
|
|
414
457
|
title="Remove burst"
|
|
415
458
|
onClick={() => binding.apply((s) => s.emissionBursts.splice(i, 1))}
|
|
416
459
|
>
|
|
@@ -419,14 +462,20 @@ export function EmissionModule({binding}: ModuleProps) {
|
|
|
419
462
|
</div>
|
|
420
463
|
<div style={{display: 'flex', flexDirection: 'column', gap: 2, marginTop: 4}}>
|
|
421
464
|
<Row label="Time">
|
|
422
|
-
<NumberField
|
|
465
|
+
<NumberField
|
|
466
|
+
value={burst.time}
|
|
467
|
+
min={0}
|
|
468
|
+
onChange={(time) => binding.apply(() => (burst.time = time))}
|
|
469
|
+
/>
|
|
423
470
|
</Row>
|
|
424
471
|
<Row label="Count">
|
|
425
472
|
<NumberField
|
|
426
473
|
value={readScalar(burst.count as never).value}
|
|
427
474
|
min={0}
|
|
428
475
|
step={1}
|
|
429
|
-
onChange={(count) =>
|
|
476
|
+
onChange={(count) =>
|
|
477
|
+
binding.apply(() => (burst.count = new ConstantValue(Math.round(count))))
|
|
478
|
+
}
|
|
430
479
|
/>
|
|
431
480
|
</Row>
|
|
432
481
|
<Row label="Cycles">
|
|
@@ -438,7 +487,11 @@ export function EmissionModule({binding}: ModuleProps) {
|
|
|
438
487
|
/>
|
|
439
488
|
</Row>
|
|
440
489
|
<Row label="Interval">
|
|
441
|
-
<NumberField
|
|
490
|
+
<NumberField
|
|
491
|
+
value={burst.interval}
|
|
492
|
+
min={0.01}
|
|
493
|
+
onChange={(v) => binding.apply(() => (burst.interval = v))}
|
|
494
|
+
/>
|
|
442
495
|
</Row>
|
|
443
496
|
<Row label="Probability">
|
|
444
497
|
<NumberField
|
|
@@ -468,7 +521,13 @@ export function EmissionModule({binding}: ModuleProps) {
|
|
|
468
521
|
}}
|
|
469
522
|
onClick={() =>
|
|
470
523
|
binding.apply((s) =>
|
|
471
|
-
s.emissionBursts.push({
|
|
524
|
+
s.emissionBursts.push({
|
|
525
|
+
time: 0,
|
|
526
|
+
count: new ConstantValue(20),
|
|
527
|
+
cycle: 1,
|
|
528
|
+
interval: 0.01,
|
|
529
|
+
probability: 1,
|
|
530
|
+
})
|
|
472
531
|
)
|
|
473
532
|
}
|
|
474
533
|
>
|
|
@@ -508,7 +567,9 @@ export function ShapeModule({binding}: ModuleProps) {
|
|
|
508
567
|
<NumberField
|
|
509
568
|
value={params[key] as number}
|
|
510
569
|
min={0}
|
|
511
|
-
onChange={(v) =>
|
|
570
|
+
onChange={(v) =>
|
|
571
|
+
binding.apply((s) => (s.emitterShape = createShape(type, {...params, [key]: v})))
|
|
572
|
+
}
|
|
512
573
|
/>
|
|
513
574
|
</Row>
|
|
514
575
|
))}
|
|
@@ -521,7 +582,8 @@ export function ShapeModule({binding}: ModuleProps) {
|
|
|
521
582
|
...meshOptions.map((m) => ({value: String(m.uniqueId), label: m.name})),
|
|
522
583
|
]}
|
|
523
584
|
onChange={(id) => {
|
|
524
|
-
const mesh =
|
|
585
|
+
const mesh =
|
|
586
|
+
id === '__none' ? undefined : meshOptions.find((m) => String(m.uniqueId) === id);
|
|
525
587
|
binding.apply((s) => (s.emitterShape = createShape('mesh_surface', {...params, mesh})));
|
|
526
588
|
}}
|
|
527
589
|
/>
|
|
@@ -538,7 +600,9 @@ export function ShapeModule({binding}: ModuleProps) {
|
|
|
538
600
|
{value: 2, label: 'Ping-pong'},
|
|
539
601
|
{value: 3, label: 'Burst spread'},
|
|
540
602
|
]}
|
|
541
|
-
onChange={(mode) =>
|
|
603
|
+
onChange={(mode) =>
|
|
604
|
+
binding.apply((s) => (s.emitterShape = createShape(type, {...params, mode})))
|
|
605
|
+
}
|
|
542
606
|
/>
|
|
543
607
|
</Row>
|
|
544
608
|
<Row label="Spread">
|
|
@@ -546,7 +610,9 @@ export function ShapeModule({binding}: ModuleProps) {
|
|
|
546
610
|
value={params.spread}
|
|
547
611
|
min={0}
|
|
548
612
|
step={0.05}
|
|
549
|
-
onChange={(spread) =>
|
|
613
|
+
onChange={(spread) =>
|
|
614
|
+
binding.apply((s) => (s.emitterShape = createShape(type, {...params, spread})))
|
|
615
|
+
}
|
|
550
616
|
/>
|
|
551
617
|
</Row>
|
|
552
618
|
</>
|
|
@@ -558,7 +624,9 @@ export function ShapeModule({binding}: ModuleProps) {
|
|
|
558
624
|
export function SizeOverLifeModule({binding}: ModuleProps) {
|
|
559
625
|
const system = binding.system;
|
|
560
626
|
const behavior = findBehavior<SizeOverLife>(system.behaviors, 'SizeOverLife');
|
|
561
|
-
const curve = behavior
|
|
627
|
+
const curve = behavior
|
|
628
|
+
? readScalar(behavior.size as never).curve
|
|
629
|
+
: ([0.2, 1, 1, 0.4] as [number, number, number, number]);
|
|
562
630
|
return (
|
|
563
631
|
<ModuleSection
|
|
564
632
|
title="Size over Lifetime"
|
|
@@ -567,7 +635,9 @@ export function SizeOverLifeModule({binding}: ModuleProps) {
|
|
|
567
635
|
binding.apply((s) => {
|
|
568
636
|
removeBehavior(s, 'SizeOverLife');
|
|
569
637
|
if (enabled) {
|
|
570
|
-
s.addBehavior(
|
|
638
|
+
s.addBehavior(
|
|
639
|
+
new SizeOverLife(buildScalar({mode: 'curve', value: 1, min: 0, max: 1, curve}) as never)
|
|
640
|
+
);
|
|
571
641
|
}
|
|
572
642
|
})
|
|
573
643
|
}
|
|
@@ -644,7 +714,15 @@ export type GeometryData = Omit<GeometryOption, 'label'>;
|
|
|
644
714
|
* isometric rotation, scales them to fit, and strokes the triangle edges on a 2D canvas — enough
|
|
645
715
|
* for the user to recognise which mesh they picked without spinning up a second Babylon scene.
|
|
646
716
|
*/
|
|
647
|
-
function MeshPreview({
|
|
717
|
+
function MeshPreview({
|
|
718
|
+
positions,
|
|
719
|
+
indices,
|
|
720
|
+
size = 96,
|
|
721
|
+
}: {
|
|
722
|
+
positions: Float32Array;
|
|
723
|
+
indices?: ArrayLike<number>;
|
|
724
|
+
size?: number;
|
|
725
|
+
}) {
|
|
648
726
|
const canvasRef = React.useRef<HTMLCanvasElement>(null);
|
|
649
727
|
React.useEffect(() => {
|
|
650
728
|
const canvas = canvasRef.current;
|
|
@@ -657,11 +735,19 @@ function MeshPreview({positions, indices, size = 96}: {positions: Float32Array;
|
|
|
657
735
|
}
|
|
658
736
|
const n = positions.length / 3;
|
|
659
737
|
// Fixed iso rotation (yaw ~35°, pitch ~30°) so depth reads clearly.
|
|
660
|
-
const cy = Math.cos(0.6),
|
|
738
|
+
const cy = Math.cos(0.6),
|
|
739
|
+
sy = Math.sin(0.6),
|
|
740
|
+
cx = Math.cos(0.52),
|
|
741
|
+
sx = Math.sin(0.52);
|
|
661
742
|
const pts: Array<[number, number]> = [];
|
|
662
|
-
let minX = Infinity,
|
|
743
|
+
let minX = Infinity,
|
|
744
|
+
minY = Infinity,
|
|
745
|
+
maxX = -Infinity,
|
|
746
|
+
maxY = -Infinity;
|
|
663
747
|
for (let i = 0; i < n; i++) {
|
|
664
|
-
const x = positions[i * 3],
|
|
748
|
+
const x = positions[i * 3],
|
|
749
|
+
y = positions[i * 3 + 1],
|
|
750
|
+
z = positions[i * 3 + 2];
|
|
665
751
|
const rx = x * cy - z * sy;
|
|
666
752
|
const rz = x * sy + z * cy;
|
|
667
753
|
const ry = y * cx - rz * sx;
|
|
@@ -757,19 +843,24 @@ export function RendererModule({
|
|
|
757
843
|
<>
|
|
758
844
|
<Row label="Trail length">
|
|
759
845
|
<NumberField
|
|
760
|
-
value={
|
|
846
|
+
value={
|
|
847
|
+
readScalar((system.rendererEmitterSettings as {startLength?: never}).startLength).value
|
|
848
|
+
}
|
|
761
849
|
min={1}
|
|
762
850
|
step={1}
|
|
763
851
|
onChange={(v) =>
|
|
764
852
|
binding.apply((s) => {
|
|
765
|
-
(s.rendererEmitterSettings as {startLength?: unknown}).startLength =
|
|
853
|
+
(s.rendererEmitterSettings as {startLength?: unknown}).startLength =
|
|
854
|
+
new ConstantValue(Math.round(v));
|
|
766
855
|
})
|
|
767
856
|
}
|
|
768
857
|
/>
|
|
769
858
|
</Row>
|
|
770
859
|
<Row label="Follow origin">
|
|
771
860
|
<CheckboxField
|
|
772
|
-
value={
|
|
861
|
+
value={
|
|
862
|
+
!!(system.rendererEmitterSettings as {followLocalOrigin?: boolean}).followLocalOrigin
|
|
863
|
+
}
|
|
773
864
|
onChange={(v) =>
|
|
774
865
|
binding.apply((s) => {
|
|
775
866
|
(s.rendererEmitterSettings as {followLocalOrigin?: boolean}).followLocalOrigin = v;
|
|
@@ -785,14 +876,22 @@ export function RendererModule({
|
|
|
785
876
|
<NumberField
|
|
786
877
|
value={(system.rendererEmitterSettings as {speedFactor?: number}).speedFactor ?? 0}
|
|
787
878
|
step={0.1}
|
|
788
|
-
onChange={(v) =>
|
|
879
|
+
onChange={(v) =>
|
|
880
|
+
binding.apply(
|
|
881
|
+
(s) => ((s.rendererEmitterSettings as {speedFactor?: number}).speedFactor = v)
|
|
882
|
+
)
|
|
883
|
+
}
|
|
789
884
|
/>
|
|
790
885
|
</Row>
|
|
791
886
|
<Row label="Length factor">
|
|
792
887
|
<NumberField
|
|
793
888
|
value={(system.rendererEmitterSettings as {lengthFactor?: number}).lengthFactor ?? 2}
|
|
794
889
|
step={0.1}
|
|
795
|
-
onChange={(v) =>
|
|
890
|
+
onChange={(v) =>
|
|
891
|
+
binding.apply(
|
|
892
|
+
(s) => ((s.rendererEmitterSettings as {lengthFactor?: number}).lengthFactor = v)
|
|
893
|
+
)
|
|
894
|
+
}
|
|
796
895
|
/>
|
|
797
896
|
</Row>
|
|
798
897
|
</>
|
|
@@ -812,7 +911,10 @@ export function RendererModule({
|
|
|
812
911
|
}}
|
|
813
912
|
>
|
|
814
913
|
Material
|
|
815
|
-
<InspectorInfoIcon
|
|
914
|
+
<InspectorInfoIcon
|
|
915
|
+
content="Shader and blending settings applied to the particle sprite or mesh."
|
|
916
|
+
size={12}
|
|
917
|
+
/>
|
|
816
918
|
</div>
|
|
817
919
|
{materialLabel ? (
|
|
818
920
|
<Row label="Source">
|
|
@@ -920,8 +1022,7 @@ export function RendererModule({
|
|
|
920
1022
|
objectFit: 'contain',
|
|
921
1023
|
borderRadius: 6,
|
|
922
1024
|
border: '1px solid #2b3761',
|
|
923
|
-
background:
|
|
924
|
-
'repeating-conic-gradient(#2a3252 0% 25%, #171d33 0% 50%) 0 0 / 12px 12px',
|
|
1025
|
+
background: 'repeating-conic-gradient(#2a3252 0% 25%, #171d33 0% 50%) 0 0 / 12px 12px',
|
|
925
1026
|
}}
|
|
926
1027
|
/>
|
|
927
1028
|
)}
|
|
@@ -1022,16 +1123,31 @@ export function RendererModule({
|
|
|
1022
1123
|
);
|
|
1023
1124
|
})()}
|
|
1024
1125
|
<Row label="Render order">
|
|
1025
|
-
<NumberField
|
|
1126
|
+
<NumberField
|
|
1127
|
+
value={system.renderOrder}
|
|
1128
|
+
step={1}
|
|
1129
|
+
onChange={(v) => binding.apply((s) => (s.renderOrder = Math.round(v)))}
|
|
1130
|
+
/>
|
|
1026
1131
|
</Row>
|
|
1027
1132
|
<Row label="Soft particles">
|
|
1028
|
-
<CheckboxField
|
|
1133
|
+
<CheckboxField
|
|
1134
|
+
value={system.softParticles}
|
|
1135
|
+
onChange={(v) => binding.apply((s) => (s.softParticles = v))}
|
|
1136
|
+
/>
|
|
1029
1137
|
</Row>
|
|
1030
1138
|
{system.softParticles && (
|
|
1031
1139
|
<Row label="Fade near/far">
|
|
1032
1140
|
<div style={{display: 'flex', gap: 6}}>
|
|
1033
|
-
<NumberField
|
|
1034
|
-
|
|
1141
|
+
<NumberField
|
|
1142
|
+
value={system.softNearFade}
|
|
1143
|
+
min={0}
|
|
1144
|
+
onChange={(v) => binding.apply((s) => (s.softNearFade = v))}
|
|
1145
|
+
/>
|
|
1146
|
+
<NumberField
|
|
1147
|
+
value={system.softFarFade}
|
|
1148
|
+
min={0}
|
|
1149
|
+
onChange={(v) => binding.apply((s) => (s.softFarFade = v))}
|
|
1150
|
+
/>
|
|
1035
1151
|
</div>
|
|
1036
1152
|
</Row>
|
|
1037
1153
|
)}
|