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.
Files changed (83) hide show
  1. package/README.md +6 -6
  2. package/dist/core/binding.d.ts +1 -1
  3. package/dist/core/colors.d.ts +1 -1
  4. package/dist/core/defaultEffect.d.ts +1 -1
  5. package/dist/core/effectTree.d.ts +1 -1
  6. package/dist/core/effectTree.js +18 -4
  7. package/dist/core/emitterShapeWireframe.d.ts +1 -1
  8. package/dist/core/emitterShapeWireframe.js +1 -1
  9. package/dist/core/galleryDropMarker.js +1 -1
  10. package/dist/core/galleryScene.d.ts +1 -1
  11. package/dist/core/galleryTree.js +4 -1
  12. package/dist/core/loadEffect.d.ts +2 -2
  13. package/dist/core/loadEffect.js +1 -1
  14. package/dist/core/loadEffectGallery.d.ts +2 -2
  15. package/dist/core/shapes.d.ts +1 -1
  16. package/dist/core/shapes.js +35 -8
  17. package/dist/core/systems.d.ts +1 -1
  18. package/dist/core/systems.js +3 -1
  19. package/dist/core/timeline.d.ts +1 -2
  20. package/dist/core/values.d.ts +1 -1
  21. package/dist/index.d.ts +11 -11
  22. package/dist/index.js +6 -6
  23. package/dist/react/CurveEditor.d.ts +1 -2
  24. package/dist/react/CurveEditor.js +14 -3
  25. package/dist/react/EffectEditor.d.ts +2 -2
  26. package/dist/react/EffectEditor.js +2 -2
  27. package/dist/react/EffectEditorHost.d.ts +1 -1
  28. package/dist/react/EffectEditorHost.js +123 -33
  29. package/dist/react/GalleryPanel.d.ts +1 -1
  30. package/dist/react/GalleryPanel.js +14 -7
  31. package/dist/react/GradientEditor.d.ts +1 -2
  32. package/dist/react/GradientEditor.js +8 -1
  33. package/dist/react/MessageDialog.js +7 -1
  34. package/dist/react/ModuleSection.js +10 -3
  35. package/dist/react/ObjectModule.d.ts +1 -2
  36. package/dist/react/ObjectModule.js +1 -1
  37. package/dist/react/SubEmittersModule.d.ts +1 -2
  38. package/dist/react/TimelinePanel.d.ts +3 -3
  39. package/dist/react/TimelinePanel.js +55 -9
  40. package/dist/react/TimelineTrackRow.d.ts +1 -1
  41. package/dist/react/TimelineTrackRow.js +20 -6
  42. package/dist/react/ValueField.d.ts +1 -2
  43. package/dist/react/behaviorModules.js +19 -5
  44. package/dist/react/index.d.ts +17 -17
  45. package/dist/react/index.js +11 -11
  46. package/dist/react/modules.js +55 -25
  47. package/package.json +68 -68
  48. package/src/core/binding.ts +2 -2
  49. package/src/core/colors.ts +1 -1
  50. package/src/core/defaultEffect.ts +1 -1
  51. package/src/core/effectTree.ts +22 -7
  52. package/src/core/emitterShapeWireframe.ts +8 -4
  53. package/src/core/galleryDropMarker.ts +6 -2
  54. package/src/core/galleryPlayback.ts +1 -1
  55. package/src/core/galleryScene.ts +3 -7
  56. package/src/core/gallerySelectionMarker.ts +5 -1
  57. package/src/core/galleryTree.ts +8 -5
  58. package/src/core/loadEffect.ts +3 -3
  59. package/src/core/loadEffectGallery.ts +7 -3
  60. package/src/core/pickGalleryFiles.ts +4 -1
  61. package/src/core/shapes.ts +48 -10
  62. package/src/core/systems.ts +9 -3
  63. package/src/core/timeline.ts +8 -8
  64. package/src/core/values.ts +1 -1
  65. package/src/core/viewportPicker.ts +1 -1
  66. package/src/index.ts +18 -18
  67. package/src/react/CurveEditor.tsx +40 -10
  68. package/src/react/EffectEditor.tsx +14 -13
  69. package/src/react/EffectEditorHost.tsx +239 -64
  70. package/src/react/GalleryPanel.tsx +56 -12
  71. package/src/react/GradientEditor.tsx +21 -4
  72. package/src/react/InspectorInfoIcon.tsx +6 -2
  73. package/src/react/MessageDialog.tsx +9 -1
  74. package/src/react/ModuleSection.tsx +12 -3
  75. package/src/react/ObjectModule.tsx +36 -15
  76. package/src/react/PromptDialog.tsx +5 -1
  77. package/src/react/SubEmittersModule.tsx +1 -2
  78. package/src/react/TimelinePanel.tsx +94 -21
  79. package/src/react/TimelineTrackRow.tsx +71 -18
  80. package/src/react/ValueField.tsx +0 -1
  81. package/src/react/behaviorModules.tsx +143 -29
  82. package/src/react/index.ts +33 -33
  83. package/src/react/modules.tsx +173 -57
@@ -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 type {Behavior, ParticleSystem} from 'babylon.quarks';
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 {DEFAULT_SHAPE_PARAMS, SHAPE_PARAM_KEYS, SHAPE_TYPES, createShape, getShapeType, readShapeParams} from '../core/shapes';
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, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 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={{width: 42, height: 24, border: 'none', background: 'transparent', padding: 0, cursor: 'pointer'}}
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 ? (startColor as never as {a: Vector4}).a
126
- : startColor instanceof ConstantColor ? startColor.color
127
- : new Vector4(1, 1, 1, 1);
128
- const colorB = startColor instanceof RandomColor ? (startColor as never as {b: Vector4}).b : new Vector4(1, 0.5, 0.2, 1);
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 ? '3d'
133
- : isEuler ? 'euler'
134
- : readScalar(rotation as never).mode === 'random' ? 'random'
135
- : 'angle';
136
- const rotationState = readScalar(rotation instanceof RandomQuatGenerator || isEuler ? undefined : (rotation as never));
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 ? 'gradient2'
155
- : startColor instanceof Gradient ? 'gradient'
156
- : colorMode;
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 value={system.duration} min={0.05} onChange={(v) => binding.apply((s) => (s.duration = v))} />
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 = colorGenMode === 'random' ? new RandomColor(next, colorB) : new ConstantColor(next);
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(new ConstantValue(eulerAngles.x), new ConstantValue(eulerAngles.y), new ConstantValue(eulerAngles.z))
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) => binding.apply((s) => (s.startRotation = new IntervalValue(v, rotationState.max)))}
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) => binding.apply((s) => (s.startRotation = new IntervalValue(rotationState.min, 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={{background: 'none', border: 'none', color: '#e08c8c', cursor: 'pointer', padding: 0, display: 'inline-flex'}}
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 value={burst.time} min={0} onChange={(time) => binding.apply(() => (burst.time = time))} />
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) => binding.apply(() => (burst.count = new ConstantValue(Math.round(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 value={burst.interval} min={0.01} onChange={(v) => binding.apply(() => (burst.interval = v))} />
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({time: 0, count: new ConstantValue(20), cycle: 1, interval: 0.01, probability: 1})
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) => binding.apply((s) => (s.emitterShape = createShape(type, {...params, [key]: 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 = id === '__none' ? undefined : meshOptions.find((m) => String(m.uniqueId) === id);
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) => binding.apply((s) => (s.emitterShape = createShape(type, {...params, 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) => binding.apply((s) => (s.emitterShape = createShape(type, {...params, 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 ? readScalar(behavior.size as never).curve : ([0.2, 1, 1, 0.4] as [number, number, number, number]);
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(new SizeOverLife(buildScalar({mode: 'curve', value: 1, min: 0, max: 1, curve}) as never));
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({positions, indices, size = 96}: {positions: Float32Array; indices?: ArrayLike<number>; size?: number}) {
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), sy = Math.sin(0.6), cx = Math.cos(0.52), sx = Math.sin(0.52);
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, minY = Infinity, maxX = -Infinity, maxY = -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], y = positions[i * 3 + 1], z = positions[i * 3 + 2];
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={readScalar((system.rendererEmitterSettings as {startLength?: never}).startLength).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 = new ConstantValue(Math.round(v));
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={!!(system.rendererEmitterSettings as {followLocalOrigin?: boolean}).followLocalOrigin}
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) => binding.apply((s) => ((s.rendererEmitterSettings as {speedFactor?: number}).speedFactor = 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) => binding.apply((s) => ((s.rendererEmitterSettings as {lengthFactor?: number}).lengthFactor = 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 content="Shader and blending settings applied to the particle sprite or mesh." size={12} />
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 value={system.renderOrder} step={1} onChange={(v) => binding.apply((s) => (s.renderOrder = Math.round(v)))} />
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 value={system.softParticles} onChange={(v) => binding.apply((s) => (s.softParticles = v))} />
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 value={system.softNearFade} min={0} onChange={(v) => binding.apply((s) => (s.softNearFade = v))} />
1034
- <NumberField value={system.softFarFade} min={0} onChange={(v) => binding.apply((s) => (s.softFarFade = v))} />
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
  )}