babylon.quarks-editor 0.18.1 → 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
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from 'react';
|
|
3
2
|
import { CheckIcon, ChevronDownIcon, ChevronRightIcon, EyeIcon, EyeSlashIcon, PencilSquareIcon, PlusIcon, SparklesIcon, SquaresPlusIcon, XMarkIcon, } from '@heroicons/react/24/solid';
|
|
3
|
+
import { useState } from 'react';
|
|
4
4
|
import { buildEffectTree, collectSystems } from '../core/effectTree';
|
|
5
|
-
import { createChildSystem, removeSystems } from '../core/systems';
|
|
6
5
|
import { ungroupNode } from '../core/groups';
|
|
6
|
+
import { createChildSystem, removeSystems } from '../core/systems';
|
|
7
7
|
import { PromptDialog } from './PromptDialog';
|
|
8
8
|
import { iconStyle } from './icons';
|
|
9
9
|
import { theme } from './theme';
|
|
@@ -20,7 +20,7 @@ const ROW_HEIGHT = 28;
|
|
|
20
20
|
const checkboxButton = { ...iconButton, width: 12, height: 12 };
|
|
21
21
|
/** One row of the timeline: a label cell (name + actions) and, for tracks, a bar cell. */
|
|
22
22
|
export function TimelineTrackRow(props) {
|
|
23
|
-
const { row, binding, selected, onSelectNode, pxPerSecond, offsetPx, collapsed, onToggleCollapse, hidden, onToggleVisible, groupSelected, onToggleGroupSelect } = props;
|
|
23
|
+
const { row, binding, selected, onSelectNode, pxPerSecond, offsetPx, collapsed, onToggleCollapse, hidden, onToggleVisible, groupSelected, onToggleGroupSelect, } = props;
|
|
24
24
|
const [renaming, setRenaming] = useState(false);
|
|
25
25
|
const isRoot = row.node === binding.root;
|
|
26
26
|
const labelStyle = {
|
|
@@ -44,10 +44,17 @@ export function TimelineTrackRow(props) {
|
|
|
44
44
|
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "qe-hover-bg", style: { ...labelStyle, borderTop: `1px solid ${theme.border}` }, onClick: () => onSelectNode(row.node), children: [!isRoot && (_jsx("button", { className: "qe-hover", title: groupSelected ? 'Remove from group selection' : 'Select for grouping', style: { ...checkboxButton, color: groupSelected ? theme.accent : theme.textDim }, onClick: (e) => {
|
|
45
45
|
e.stopPropagation();
|
|
46
46
|
onToggleGroupSelect();
|
|
47
|
-
}, children: groupSelected ? (_jsx(CheckIcon, { style: iconStyle(12) })) : (_jsx("span", { style: {
|
|
47
|
+
}, children: groupSelected ? (_jsx(CheckIcon, { style: iconStyle(12) })) : (_jsx("span", { style: {
|
|
48
|
+
width: 10,
|
|
49
|
+
height: 10,
|
|
50
|
+
border: `1px solid currentColor`,
|
|
51
|
+
borderRadius: 2,
|
|
52
|
+
display: 'block',
|
|
53
|
+
boxSizing: 'border-box',
|
|
54
|
+
} })) })), _jsx("span", { className: "qe-hover", title: collapsed ? 'Expand' : 'Collapse', style: { color: theme.textDim, width: 12, cursor: 'pointer', display: 'inline-flex' }, onClick: (e) => {
|
|
48
55
|
e.stopPropagation();
|
|
49
56
|
onToggleCollapse?.();
|
|
50
|
-
}, children: collapsed ? _jsx(ChevronRightIcon, { style: iconStyle(12) }) : _jsx(ChevronDownIcon, { style: iconStyle(12) }) }), _jsx("span", { style: { flex: 1, overflow: 'hidden', textOverflow: 'ellipsis' }, children: row.name }), _jsx("button", { className: "qe-hover", title: "Add system to this group", style: { ...iconButton, color: theme.accent }, onClick: (e) => {
|
|
57
|
+
}, children: collapsed ? (_jsx(ChevronRightIcon, { style: iconStyle(12) })) : (_jsx(ChevronDownIcon, { style: iconStyle(12) })) }), _jsx("span", { style: { flex: 1, overflow: 'hidden', textOverflow: 'ellipsis' }, children: row.name }), _jsx("button", { className: "qe-hover", title: "Add system to this group", style: { ...iconButton, color: theme.accent }, onClick: (e) => {
|
|
51
58
|
e.stopPropagation();
|
|
52
59
|
let created;
|
|
53
60
|
binding.apply(() => {
|
|
@@ -82,7 +89,14 @@ export function TimelineTrackRow(props) {
|
|
|
82
89
|
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "qe-hover-bg", style: { ...labelStyle, opacity: hidden ? 0.5 : 1 }, onClick: () => onSelectNode(row.node), children: [!isRoot && (_jsx("button", { className: "qe-hover", title: groupSelected ? 'Remove from group selection' : 'Select for grouping', style: { ...checkboxButton, color: groupSelected ? theme.accent : theme.textDim }, onClick: (e) => {
|
|
83
90
|
e.stopPropagation();
|
|
84
91
|
onToggleGroupSelect();
|
|
85
|
-
}, children: groupSelected ? (_jsx(CheckIcon, { style: iconStyle(12) })) : (_jsx("span", { style: {
|
|
92
|
+
}, children: groupSelected ? (_jsx(CheckIcon, { style: iconStyle(12) })) : (_jsx("span", { style: {
|
|
93
|
+
width: 10,
|
|
94
|
+
height: 10,
|
|
95
|
+
border: `1px solid currentColor`,
|
|
96
|
+
borderRadius: 2,
|
|
97
|
+
display: 'block',
|
|
98
|
+
boxSizing: 'border-box',
|
|
99
|
+
} })) })), _jsx("button", { className: "qe-hover", title: hidden ? 'Show in viewport' : 'Hide in viewport', style: { ...iconButton, color: hidden ? theme.textDim : theme.accent }, onClick: (e) => {
|
|
86
100
|
e.stopPropagation();
|
|
87
101
|
onToggleVisible?.();
|
|
88
102
|
}, children: hidden ? _jsx(EyeSlashIcon, { style: iconStyle(12) }) : _jsx(EyeIcon, { style: iconStyle(12) }) }), _jsx("span", { style: { color: theme.accent, display: 'inline-flex' }, children: _jsx(SparklesIcon, { style: iconStyle(11) }) }), _jsxs("span", { style: { flex: 1, overflow: 'hidden', textOverflow: 'ellipsis' }, children: [row.name, system.onlyUsedByOther ? ' (sub)' : ''] }), !isRoot && (_jsx("button", { className: "qe-hover", title: "Rename", style: { ...iconButton, color: theme.textDim }, onClick: (e) => {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
1
|
import { ScalarGenerator } from '../core/values';
|
|
3
2
|
/**
|
|
4
3
|
* Unity-like value field: Constant | Random Between Two Constants | Curve,
|
|
@@ -12,4 +11,4 @@ export declare function ValueField(props: {
|
|
|
12
11
|
onChange: (generator: ScalarGenerator) => void;
|
|
13
12
|
min?: number;
|
|
14
13
|
curveMax?: number;
|
|
15
|
-
}):
|
|
14
|
+
}): import("react").JSX.Element;
|
|
@@ -3,10 +3,10 @@ import { ApplyCollision, ApplyForce, ChangeEmitDirection, ColorBySpeed, Constant
|
|
|
3
3
|
import { ensureGroundResolver } from '../core/collision';
|
|
4
4
|
import { DEFAULT_GRADIENT_STOPS, buildGradient, findBehavior, readGradientStops } from '../core/colors';
|
|
5
5
|
import { buildCurve, buildScalar, readPieces, readScalar } from '../core/values';
|
|
6
|
-
import { GradientEditor } from './GradientEditor';
|
|
7
6
|
import { CurveEditor } from './CurveEditor';
|
|
8
|
-
import { ModuleSection } from './ModuleSection';
|
|
9
7
|
import { NumberField, Row, SelectField } from './fields';
|
|
8
|
+
import { GradientEditor } from './GradientEditor';
|
|
9
|
+
import { ModuleSection } from './ModuleSection';
|
|
10
10
|
import { ValueField } from './ValueField';
|
|
11
11
|
function removeBehaviorOfType(behaviors, type) {
|
|
12
12
|
for (let i = behaviors.length - 1; i >= 0; i--) {
|
|
@@ -81,7 +81,9 @@ function SpeedRangeRows({ binding, behavior }) {
|
|
|
81
81
|
return (_jsx(Row, { label: "Speed range", children: _jsxs("div", { style: { display: 'flex', gap: 6 }, children: [_jsx(NumberField, { value: behavior.speedRange.a, min: 0, onChange: (v) => binding.apply(() => (behavior.speedRange = new IntervalValue(v, behavior.speedRange.b))) }), _jsx(NumberField, { value: behavior.speedRange.b, min: 0, onChange: (v) => binding.apply(() => (behavior.speedRange = new IntervalValue(behavior.speedRange.a, v))) })] }) }));
|
|
82
82
|
}
|
|
83
83
|
export function ColorBySpeedModule({ binding }) {
|
|
84
|
-
return (_jsx(BehaviorModule, { binding: binding, title: "Color by Speed", type: "ColorBySpeed", create: () => new ColorBySpeed(buildGradient(DEFAULT_GRADIENT_STOPS), new IntervalValue(0, 5)), children: (behavior) => (_jsxs(_Fragment, { children: [_jsx("div", { style: { marginTop: 6 }, children: _jsx(GradientEditor, { stops: behavior.color instanceof Gradient
|
|
84
|
+
return (_jsx(BehaviorModule, { binding: binding, title: "Color by Speed", type: "ColorBySpeed", create: () => new ColorBySpeed(buildGradient(DEFAULT_GRADIENT_STOPS), new IntervalValue(0, 5)), children: (behavior) => (_jsxs(_Fragment, { children: [_jsx("div", { style: { marginTop: 6 }, children: _jsx(GradientEditor, { stops: behavior.color instanceof Gradient
|
|
85
|
+
? readGradientStops(behavior.color)
|
|
86
|
+
: DEFAULT_GRADIENT_STOPS, onChange: (stops) => binding.apply(() => (behavior.color = buildGradient(stops))) }) }), _jsx(SpeedRangeRows, { binding: binding, behavior: behavior })] })) }));
|
|
85
87
|
}
|
|
86
88
|
export function SizeBySpeedModule({ binding }) {
|
|
87
89
|
return (_jsx(BehaviorModule, { binding: binding, title: "Size by Speed", type: "SizeBySpeed", create: () => new SizeBySpeed(buildScalar({ mode: 'curve', value: 1, min: 0, max: 1, curve: [0.5, 0.8, 1.2, 1.5] }), new IntervalValue(0, 5)), children: (behavior) => (_jsxs(_Fragment, { children: [_jsx("div", { style: { marginTop: 6 }, children: _jsx(CurveEditor, { pieces: readPieces(behavior.size), maxValue: 2, onChange: (pieces) => binding.apply(() => (behavior.size = buildCurve(pieces))) }) }), _jsx(SpeedRangeRows, { binding: binding, behavior: behavior })] })) }));
|
|
@@ -106,7 +108,13 @@ export function TextureSheetModule({ binding }) {
|
|
|
106
108
|
const startIdx = Math.min(Math.max(0, Math.round(readScalar(system.startTileIndex).value)), tiles - 1);
|
|
107
109
|
const startCol = startIdx % u;
|
|
108
110
|
const startRow = Math.floor(startIdx / u);
|
|
109
|
-
return (_jsxs(ModuleSection, { title: "Texture Sheet Animation", defaultOpen: false, children: [textureUrl && (_jsxs("div", { style: { marginTop: 6 }, children: [_jsxs("div", { style: {
|
|
111
|
+
return (_jsxs(ModuleSection, { title: "Texture Sheet Animation", defaultOpen: false, children: [textureUrl && (_jsxs("div", { style: { marginTop: 6 }, children: [_jsxs("div", { style: {
|
|
112
|
+
position: 'relative',
|
|
113
|
+
width: 218,
|
|
114
|
+
border: '1px solid #2b3761',
|
|
115
|
+
borderRadius: 6,
|
|
116
|
+
overflow: 'hidden',
|
|
117
|
+
}, children: [_jsx("img", { src: textureUrl, alt: "texture atlas", style: { width: '100%', display: 'block' } }), _jsx("div", { style: {
|
|
110
118
|
position: 'absolute',
|
|
111
119
|
inset: 0,
|
|
112
120
|
pointerEvents: 'none',
|
|
@@ -133,7 +141,13 @@ export function TextureSheetModule({ binding }) {
|
|
|
133
141
|
}, children: startIdx }) })] }), _jsxs("div", { style: { fontSize: 11, color: '#b7c6ea', marginTop: 3 }, children: [u, " \u00D7 ", v, " = ", tiles, " tiles; frame indices 0\u2013", tiles - 1, "; start tile", ' ', _jsx("span", { style: { color: '#ffce6b', fontWeight: 600 }, children: startIdx })] })] })), _jsx(Row, { label: "Tiles U", children: _jsx(NumberField, { value: system.uTileCount, min: 1, step: 1, onChange: (v) => binding.apply((s) => (s.uTileCount = Math.round(v))) }) }), _jsx(Row, { label: "Tiles V", children: _jsx(NumberField, { value: system.vTileCount, min: 1, step: 1, onChange: (v) => binding.apply((s) => (s.vTileCount = Math.round(v))) }) }), _jsx(Row, { label: "Blend tiles", children: _jsx("input", { type: "checkbox", className: "qe-hover", checked: system.blendTiles, style: { justifySelf: 'start', width: 15, height: 15, accentColor: '#78a5ff', cursor: 'pointer' }, onChange: (e) => binding.apply((s) => (s.blendTiles = e.target.checked)) }) }), _jsx(Row, { label: "Start tile", children: _jsx(NumberField, { value: readScalar(system.startTileIndex).value, min: 0, step: 1, onChange: (v) => binding.apply((s) => (s.startTileIndex = new ConstantValue(Math.round(v)))) }) }), _jsx(Row, { label: "Animate frames", children: _jsx("input", { type: "checkbox", className: "qe-hover", checked: !!frameBehavior, style: { justifySelf: 'start', width: 15, height: 15, accentColor: '#78a5ff', cursor: 'pointer' }, onChange: (e) => binding.apply((s) => {
|
|
134
142
|
removeBehaviorOfType(s.behaviors, 'FrameOverLife');
|
|
135
143
|
if (e.target.checked) {
|
|
136
|
-
s.addBehavior(new FrameOverLife(buildScalar({
|
|
144
|
+
s.addBehavior(new FrameOverLife(buildScalar({
|
|
145
|
+
mode: 'curve',
|
|
146
|
+
value: 0,
|
|
147
|
+
min: 0,
|
|
148
|
+
max: tiles - 1,
|
|
149
|
+
curve: [0, (tiles - 1) / 3, (2 * (tiles - 1)) / 3, tiles - 1],
|
|
150
|
+
})));
|
|
137
151
|
}
|
|
138
152
|
}) }) }), frameBehavior && (_jsx("div", { style: { marginTop: 6 }, children: _jsx(CurveEditor, { pieces: readPieces(frameBehavior.frame), maxValue: Math.max(1, tiles - 1), onChange: (pieces) => binding.apply(() => (frameBehavior.frame = buildCurve(pieces))) }) }))] }));
|
|
139
153
|
}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
+
export { CollisionModule, ColorBySpeedModule, EmitDirectionModule, ForceOverLifeModule, GravityModule, LimitSpeedOverLifeModule, NoiseModule, OrbitOverLifeModule, RotationBySpeedModule, RotationOverLifeModule, SizeBySpeedModule, SpeedOverLifeModule, TextureSheetModule, TurbulenceModule, WidthOverTrailModule, } from './behaviorModules';
|
|
2
|
+
export { CurveEditor } from './CurveEditor';
|
|
1
3
|
export { EffectEditor } from './EffectEditor';
|
|
2
4
|
export type { EffectEditorProps } from './EffectEditor';
|
|
3
|
-
export {
|
|
4
|
-
export type {
|
|
5
|
+
export { EffectEditorHost, QuarksEffectEditor } from './EffectEditorHost';
|
|
6
|
+
export type { EffectEditorHostHandle, EffectEditorHostProps } from './EffectEditorHost';
|
|
7
|
+
export { CheckboxField, NumberField, Row, SelectField } from './fields';
|
|
8
|
+
export { GalleryPanel } from './GalleryPanel';
|
|
9
|
+
export type { GalleryPanelProps } from './GalleryPanel';
|
|
10
|
+
export { GradientEditor } from './GradientEditor';
|
|
11
|
+
export { MessageDialog } from './MessageDialog';
|
|
12
|
+
export type { MessageDialogProps } from './MessageDialog';
|
|
13
|
+
export { ColorOverLifeModule, EmissionModule, MainModule, RendererModule, ShapeModule, SizeOverLifeModule, } from './modules';
|
|
14
|
+
export type { GeometryData, GeometryOption, TextureOption } from './modules';
|
|
15
|
+
export { ModuleSection } from './ModuleSection';
|
|
16
|
+
export { PromptDialog } from './PromptDialog';
|
|
17
|
+
export type { PromptDialogProps } from './PromptDialog';
|
|
5
18
|
export { SubEmittersModule } from './SubEmittersModule';
|
|
19
|
+
export { buttonStyle, inputStyle, labelStyle, rowStyle, theme } from './theme';
|
|
6
20
|
export { TimelinePanel } from './TimelinePanel';
|
|
7
|
-
export type {
|
|
21
|
+
export type { PlaybackState, TimelinePanelProps } from './TimelinePanel';
|
|
8
22
|
export { TimelineTrackRow } from './TimelineTrackRow';
|
|
9
23
|
export type { TimelineTrackRowProps } from './TimelineTrackRow';
|
|
10
|
-
export { ModuleSection } from './ModuleSection';
|
|
11
|
-
export { PromptDialog } from './PromptDialog';
|
|
12
|
-
export type { PromptDialogProps } from './PromptDialog';
|
|
13
|
-
export { CurveEditor } from './CurveEditor';
|
|
14
|
-
export { GradientEditor } from './GradientEditor';
|
|
15
24
|
export { ValueField } from './ValueField';
|
|
16
|
-
export { Row, NumberField, CheckboxField, SelectField } from './fields';
|
|
17
|
-
export { MainModule, EmissionModule, ShapeModule, SizeOverLifeModule, ColorOverLifeModule, RendererModule, } from './modules';
|
|
18
|
-
export { theme, inputStyle, rowStyle, labelStyle, buttonStyle } from './theme';
|
|
19
|
-
export { MessageDialog } from './MessageDialog';
|
|
20
|
-
export type { MessageDialogProps } from './MessageDialog';
|
|
21
|
-
export { GalleryPanel } from './GalleryPanel';
|
|
22
|
-
export type { GalleryPanelProps } from './GalleryPanel';
|
|
23
|
-
export { EffectEditorHost, QuarksEffectEditor } from './EffectEditorHost';
|
|
24
|
-
export type { EffectEditorHostProps, EffectEditorHostHandle } from './EffectEditorHost';
|
package/dist/react/index.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
+
export { CollisionModule, ColorBySpeedModule, EmitDirectionModule, ForceOverLifeModule, GravityModule, LimitSpeedOverLifeModule, NoiseModule, OrbitOverLifeModule, RotationBySpeedModule, RotationOverLifeModule, SizeBySpeedModule, SpeedOverLifeModule, TextureSheetModule, TurbulenceModule, WidthOverTrailModule, } from './behaviorModules';
|
|
2
|
+
export { CurveEditor } from './CurveEditor';
|
|
1
3
|
export { EffectEditor } from './EffectEditor';
|
|
2
|
-
export {
|
|
4
|
+
export { EffectEditorHost, QuarksEffectEditor } from './EffectEditorHost';
|
|
5
|
+
export { CheckboxField, NumberField, Row, SelectField } from './fields';
|
|
6
|
+
export { GalleryPanel } from './GalleryPanel';
|
|
7
|
+
export { GradientEditor } from './GradientEditor';
|
|
8
|
+
export { MessageDialog } from './MessageDialog';
|
|
9
|
+
export { ColorOverLifeModule, EmissionModule, MainModule, RendererModule, ShapeModule, SizeOverLifeModule, } from './modules';
|
|
10
|
+
export { ModuleSection } from './ModuleSection';
|
|
11
|
+
export { PromptDialog } from './PromptDialog';
|
|
3
12
|
export { SubEmittersModule } from './SubEmittersModule';
|
|
13
|
+
export { buttonStyle, inputStyle, labelStyle, rowStyle, theme } from './theme';
|
|
4
14
|
export { TimelinePanel } from './TimelinePanel';
|
|
5
15
|
export { TimelineTrackRow } from './TimelineTrackRow';
|
|
6
|
-
export { ModuleSection } from './ModuleSection';
|
|
7
|
-
export { PromptDialog } from './PromptDialog';
|
|
8
|
-
export { CurveEditor } from './CurveEditor';
|
|
9
|
-
export { GradientEditor } from './GradientEditor';
|
|
10
16
|
export { ValueField } from './ValueField';
|
|
11
|
-
export { Row, NumberField, CheckboxField, SelectField } from './fields';
|
|
12
|
-
export { MainModule, EmissionModule, ShapeModule, SizeOverLifeModule, ColorOverLifeModule, RendererModule, } from './modules';
|
|
13
|
-
export { theme, inputStyle, rowStyle, labelStyle, buttonStyle } from './theme';
|
|
14
|
-
export { MessageDialog } from './MessageDialog';
|
|
15
|
-
export { GalleryPanel } from './GalleryPanel';
|
|
16
|
-
export { EffectEditorHost, QuarksEffectEditor } from './EffectEditorHost';
|
package/dist/react/modules.js
CHANGED
|
@@ -1,32 +1,31 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import React, { useMemo, useState } from 'react';
|
|
3
|
-
import { Mesh } from '@babylonjs/core/Meshes/mesh';
|
|
4
2
|
import { Texture } from '@babylonjs/core/Materials/Textures/texture';
|
|
3
|
+
import { Mesh } from '@babylonjs/core/Meshes/mesh';
|
|
4
|
+
import { PlusIcon, XMarkIcon } from '@heroicons/react/24/solid';
|
|
5
5
|
import { ColorOverLife, ConstantColor, ConstantValue, EulerGenerator, Gradient, IntervalValue, RandomColor, RandomColorBetweenGradient, RandomQuatGenerator, RenderMode, SizeOverLife, Vector3Function, Vector4, } from 'babylon.quarks';
|
|
6
|
+
import React, { useMemo, useState } from 'react';
|
|
6
7
|
import { DEFAULT_GRADIENT_STOPS, buildGradient, findBehavior, hexToRgb, readGradientStops, rgbToHex, } from '../core/colors';
|
|
7
|
-
import { DEFAULT_SHAPE_PARAMS, SHAPE_PARAM_KEYS, SHAPE_TYPES, createShape, getShapeType, readShapeParams } from '../core/shapes';
|
|
8
|
-
import { buildCurve, buildScalar, readPieces, readScalar } from '../core/values';
|
|
9
8
|
import { applyRendererMaterial, getMaterialLabel } from '../core/material';
|
|
9
|
+
import { DEFAULT_SHAPE_PARAMS, SHAPE_PARAM_KEYS, SHAPE_TYPES, createShape, getShapeType, readShapeParams, } from '../core/shapes';
|
|
10
|
+
import { buildCurve, buildScalar, readPieces, readScalar } from '../core/values';
|
|
10
11
|
import { CurveEditor } from './CurveEditor';
|
|
11
12
|
import { GradientEditor } from './GradientEditor';
|
|
13
|
+
import { InspectorInfoIcon } from './InspectorInfoIcon';
|
|
12
14
|
import { ModuleSection } from './ModuleSection';
|
|
13
15
|
import { PromptDialog } from './PromptDialog';
|
|
14
|
-
import { CheckboxField, NumberField, Row, SelectField } from './fields';
|
|
15
16
|
import { ValueField } from './ValueField';
|
|
17
|
+
import { CheckboxField, NumberField, Row, SelectField } from './fields';
|
|
16
18
|
import { iconStyle } from './icons';
|
|
17
19
|
import { theme } from './theme';
|
|
18
|
-
import { PlusIcon, XMarkIcon } from '@heroicons/react/24/solid';
|
|
19
|
-
import { InspectorInfoIcon } from './InspectorInfoIcon';
|
|
20
20
|
const MESH_QUAD_POSITIONS = new Float32Array([-0.5, -0.5, 0, 0.5, -0.5, 0, 0.5, 0.5, 0, -0.5, 0.5, 0]);
|
|
21
21
|
const MESH_QUAD_INDICES = new Uint32Array([0, 1, 2, 0, 2, 3]);
|
|
22
22
|
const MESH_QUAD_UVS = new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]);
|
|
23
23
|
const MESH_CUBE_POSITIONS = new Float32Array([
|
|
24
|
-
-0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5,
|
|
25
|
-
-0.5,
|
|
24
|
+
-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,
|
|
25
|
+
-0.5, 0.5, 0.5,
|
|
26
26
|
]);
|
|
27
27
|
const MESH_CUBE_INDICES = new Uint32Array([
|
|
28
|
-
0, 1, 2, 0, 2, 3, 4, 6, 5, 4, 7, 6, 0, 4, 5, 0, 5, 1,
|
|
29
|
-
3, 2, 6, 3, 6, 7, 0, 3, 7, 0, 7, 4, 1, 5, 6, 1, 6, 2,
|
|
28
|
+
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,
|
|
30
29
|
]);
|
|
31
30
|
function arraysEqual(a, b) {
|
|
32
31
|
if (a.length !== b.length) {
|
|
@@ -65,21 +64,33 @@ function ColorInput(props) {
|
|
|
65
64
|
return (_jsxs("div", { style: { display: 'flex', gap: 6, alignItems: 'center' }, children: [_jsx("input", { type: "color", className: "qe-hover", value: rgbToHex(value.x, value.y, value.z), onChange: (e) => {
|
|
66
65
|
const rgb = hexToRgb(e.target.value);
|
|
67
66
|
onChange(new Vector4(rgb.r, rgb.g, rgb.b, value.w));
|
|
68
|
-
}, style: {
|
|
67
|
+
}, style: {
|
|
68
|
+
width: 42,
|
|
69
|
+
height: 24,
|
|
70
|
+
border: 'none',
|
|
71
|
+
background: 'transparent',
|
|
72
|
+
padding: 0,
|
|
73
|
+
cursor: 'pointer',
|
|
74
|
+
} }), _jsx(NumberField, { value: value.w, min: 0, step: 0.05, onChange: (a) => onChange(new Vector4(value.x, value.y, value.z, Math.min(1, a))) })] }));
|
|
69
75
|
}
|
|
70
76
|
export function MainModule({ binding }) {
|
|
71
77
|
const system = binding.system;
|
|
72
78
|
const startColor = system.startColor;
|
|
73
79
|
const colorMode = startColor instanceof RandomColor ? 'random' : 'constant';
|
|
74
|
-
const colorA = startColor instanceof RandomColor
|
|
75
|
-
|
|
80
|
+
const colorA = startColor instanceof RandomColor
|
|
81
|
+
? startColor.a
|
|
82
|
+
: startColor instanceof ConstantColor
|
|
83
|
+
? startColor.color
|
|
76
84
|
: new Vector4(1, 1, 1, 1);
|
|
77
85
|
const colorB = startColor instanceof RandomColor ? startColor.b : new Vector4(1, 0.5, 0.2, 1);
|
|
78
86
|
const rotation = system.startRotation;
|
|
79
87
|
const isEuler = rotation instanceof EulerGenerator;
|
|
80
|
-
const rotationMode = rotation instanceof RandomQuatGenerator
|
|
81
|
-
|
|
82
|
-
|
|
88
|
+
const rotationMode = rotation instanceof RandomQuatGenerator
|
|
89
|
+
? '3d'
|
|
90
|
+
: isEuler
|
|
91
|
+
? 'euler'
|
|
92
|
+
: readScalar(rotation).mode === 'random'
|
|
93
|
+
? 'random'
|
|
83
94
|
: 'angle';
|
|
84
95
|
const rotationState = readScalar(rotation instanceof RandomQuatGenerator || isEuler ? undefined : rotation);
|
|
85
96
|
const eulerAngles = {
|
|
@@ -93,8 +104,10 @@ export function MainModule({ binding }) {
|
|
|
93
104
|
});
|
|
94
105
|
const startSize = system.startSize;
|
|
95
106
|
const size3D = startSize.type === 'vec3function';
|
|
96
|
-
const colorGenMode = startColor instanceof RandomColorBetweenGradient
|
|
97
|
-
|
|
107
|
+
const colorGenMode = startColor instanceof RandomColorBetweenGradient
|
|
108
|
+
? 'gradient2'
|
|
109
|
+
: startColor instanceof Gradient
|
|
110
|
+
? 'gradient'
|
|
98
111
|
: colorMode;
|
|
99
112
|
return (_jsxs(ModuleSection, { title: "Main", children: [_jsx(Row, { label: "Duration", children: _jsx(NumberField, { value: system.duration, min: 0.05, onChange: (v) => binding.apply((s) => (s.duration = v)) }) }), _jsx(Row, { label: "Looping", children: _jsx(CheckboxField, { value: system.looping, onChange: (v) => binding.apply((s) => (s.looping = v)) }) }), _jsx(Row, { label: "World space", children: _jsx(CheckboxField, { value: system.worldSpace, onChange: (v) => binding.apply((s) => (s.worldSpace = v)) }) }), _jsx(Row, { label: "Prewarm", children: _jsx(CheckboxField, { value: system.prewarm, onChange: (v) => binding.apply((s) => (s.prewarm = v)) }) }), _jsx(ValueField, { label: "Start delay", generator: system.startDelay, min: 0, curveMax: 5, onChange: (g) => binding.apply((s) => {
|
|
100
113
|
s.startDelay = g;
|
|
@@ -134,7 +147,8 @@ export function MainModule({ binding }) {
|
|
|
134
147
|
s.startColor = new ConstantColor(colorA);
|
|
135
148
|
}
|
|
136
149
|
}) }) }), (colorGenMode === 'constant' || colorGenMode === 'random') && (_jsx(Row, { label: "", children: _jsx(ColorInput, { value: colorA, onChange: (next) => binding.apply((s) => {
|
|
137
|
-
s.startColor =
|
|
150
|
+
s.startColor =
|
|
151
|
+
colorGenMode === 'random' ? new RandomColor(next, colorB) : new ConstantColor(next);
|
|
138
152
|
}) }) })), colorGenMode === 'random' && (_jsx(Row, { label: "", children: _jsx(ColorInput, { value: colorB, onChange: (next) => binding.apply((s) => (s.startColor = new RandomColor(colorA, next))) }) })), colorGenMode === 'gradient' && (_jsx("div", { style: { marginTop: 6 }, children: _jsx(GradientEditor, { stops: readGradientStops(startColor), onChange: (stops) => binding.apply((s) => (s.startColor = buildGradient(stops))) }) })), colorGenMode === 'gradient2' && (_jsxs("div", { style: { marginTop: 6, display: 'flex', flexDirection: 'column', gap: 6 }, children: [_jsx(GradientEditor, { stops: readGradientStops(startColor.gradient1), onChange: (stops) => binding.apply((s) => {
|
|
139
153
|
const g = s.startColor;
|
|
140
154
|
s.startColor = new RandomColorBetweenGradient(buildGradient(stops), g.gradient2);
|
|
@@ -160,7 +174,14 @@ export function MainModule({ binding }) {
|
|
|
160
174
|
export function EmissionModule({ binding }) {
|
|
161
175
|
const system = binding.system;
|
|
162
176
|
const bursts = system.emissionBursts ?? [];
|
|
163
|
-
return (_jsxs(ModuleSection, { title: "Emission", children: [_jsx(ValueField, { label: "Rate over time", generator: system.emissionOverTime, min: 0, curveMax: 500, onChange: (g) => binding.apply((s) => (s.emissionOverTime = g)) }), _jsx(ValueField, { label: "Rate over distance", generator: system.emissionOverDistance, min: 0, curveMax: 50, onChange: (g) => binding.apply((s) => (s.emissionOverDistance = g)) }), bursts.map((burst, i) => (_jsxs("div", { style: { marginTop: 8, padding: '6px 8px', border: '1px solid #22305c', borderRadius: 8 }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' }, children: [_jsxs("span", { style: { fontSize: 11.5, color: '#9eb9ff' }, children: ["Burst ", i + 1] }), _jsx("button", { className: "qe-hover", style: {
|
|
177
|
+
return (_jsxs(ModuleSection, { title: "Emission", children: [_jsx(ValueField, { label: "Rate over time", generator: system.emissionOverTime, min: 0, curveMax: 500, onChange: (g) => binding.apply((s) => (s.emissionOverTime = g)) }), _jsx(ValueField, { label: "Rate over distance", generator: system.emissionOverDistance, min: 0, curveMax: 50, onChange: (g) => binding.apply((s) => (s.emissionOverDistance = g)) }), bursts.map((burst, i) => (_jsxs("div", { style: { marginTop: 8, padding: '6px 8px', border: '1px solid #22305c', borderRadius: 8 }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' }, children: [_jsxs("span", { style: { fontSize: 11.5, color: '#9eb9ff' }, children: ["Burst ", i + 1] }), _jsx("button", { className: "qe-hover", style: {
|
|
178
|
+
background: 'none',
|
|
179
|
+
border: 'none',
|
|
180
|
+
color: '#e08c8c',
|
|
181
|
+
cursor: 'pointer',
|
|
182
|
+
padding: 0,
|
|
183
|
+
display: 'inline-flex',
|
|
184
|
+
}, title: "Remove burst", onClick: () => binding.apply((s) => s.emissionBursts.splice(i, 1)), children: _jsx(XMarkIcon, { style: iconStyle(14) }) })] }), _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 2, marginTop: 4 }, children: [_jsx(Row, { label: "Time", children: _jsx(NumberField, { value: burst.time, min: 0, onChange: (time) => binding.apply(() => (burst.time = time)) }) }), _jsx(Row, { label: "Count", children: _jsx(NumberField, { value: readScalar(burst.count).value, min: 0, step: 1, onChange: (count) => binding.apply(() => (burst.count = new ConstantValue(Math.round(count)))) }) }), _jsx(Row, { label: "Cycles", children: _jsx(NumberField, { value: burst.cycle, min: 1, step: 1, onChange: (v) => binding.apply(() => (burst.cycle = Math.round(v))) }) }), _jsx(Row, { label: "Interval", children: _jsx(NumberField, { value: burst.interval, min: 0.01, onChange: (v) => binding.apply(() => (burst.interval = v)) }) }), _jsx(Row, { label: "Probability", children: _jsx(NumberField, { value: burst.probability, min: 0, step: 0.05, onChange: (v) => binding.apply(() => (burst.probability = Math.min(1, v))) }) })] })] }, i))), _jsxs("button", { className: "qe-hover", style: {
|
|
164
185
|
marginTop: 8,
|
|
165
186
|
background: 'none',
|
|
166
187
|
border: '1px dashed #34477f',
|
|
@@ -172,7 +193,13 @@ export function EmissionModule({ binding }) {
|
|
|
172
193
|
display: 'inline-flex',
|
|
173
194
|
alignItems: 'center',
|
|
174
195
|
gap: 6,
|
|
175
|
-
}, onClick: () => binding.apply((s) => s.emissionBursts.push({
|
|
196
|
+
}, onClick: () => binding.apply((s) => s.emissionBursts.push({
|
|
197
|
+
time: 0,
|
|
198
|
+
count: new ConstantValue(20),
|
|
199
|
+
cycle: 1,
|
|
200
|
+
interval: 0.01,
|
|
201
|
+
probability: 1,
|
|
202
|
+
})), children: [_jsx(PlusIcon, { style: iconStyle(13) }), "Add burst"] })] }));
|
|
176
203
|
}
|
|
177
204
|
// Shapes whose constructor doesn't accept `mode`/`spread` at all — showing those controls
|
|
178
205
|
// for them would edit a value the shape silently ignores, just like the old "default to
|
|
@@ -202,7 +229,9 @@ export function ShapeModule({ binding }) {
|
|
|
202
229
|
export function SizeOverLifeModule({ binding }) {
|
|
203
230
|
const system = binding.system;
|
|
204
231
|
const behavior = findBehavior(system.behaviors, 'SizeOverLife');
|
|
205
|
-
const curve = behavior
|
|
232
|
+
const curve = behavior
|
|
233
|
+
? readScalar(behavior.size).curve
|
|
234
|
+
: [0.2, 1, 1, 0.4];
|
|
206
235
|
return (_jsx(ModuleSection, { title: "Size over Lifetime", enabled: !!behavior, onToggle: (enabled) => binding.apply((s) => {
|
|
207
236
|
removeBehavior(s, 'SizeOverLife');
|
|
208
237
|
if (enabled) {
|
|
@@ -233,7 +262,7 @@ export function ColorOverLifeModule({ binding }) {
|
|
|
233
262
|
* isometric rotation, scales them to fit, and strokes the triangle edges on a 2D canvas — enough
|
|
234
263
|
* for the user to recognise which mesh they picked without spinning up a second Babylon scene.
|
|
235
264
|
*/
|
|
236
|
-
function MeshPreview({ positions, indices, size = 96 }) {
|
|
265
|
+
function MeshPreview({ positions, indices, size = 96, }) {
|
|
237
266
|
const canvasRef = React.useRef(null);
|
|
238
267
|
React.useEffect(() => {
|
|
239
268
|
const canvas = canvasRef.current;
|
|
@@ -318,7 +347,8 @@ export function RendererModule({ binding, textureOptions, geometryOptions, resol
|
|
|
318
347
|
{ value: RenderMode.Trail, label: 'Trail' },
|
|
319
348
|
{ value: RenderMode.Mesh, label: 'Mesh' },
|
|
320
349
|
], onChange: (mode) => binding.apply((s) => (s.renderMode = mode)) }) }), system.renderMode === RenderMode.Trail && (_jsxs(_Fragment, { children: [_jsx(Row, { label: "Trail length", children: _jsx(NumberField, { value: readScalar(system.rendererEmitterSettings.startLength).value, min: 1, step: 1, onChange: (v) => binding.apply((s) => {
|
|
321
|
-
s.rendererEmitterSettings.startLength =
|
|
350
|
+
s.rendererEmitterSettings.startLength =
|
|
351
|
+
new ConstantValue(Math.round(v));
|
|
322
352
|
}) }) }), _jsx(Row, { label: "Follow origin", children: _jsx(CheckboxField, { value: !!system.rendererEmitterSettings.followLocalOrigin, onChange: (v) => binding.apply((s) => {
|
|
323
353
|
s.rendererEmitterSettings.followLocalOrigin = v;
|
|
324
354
|
}) }) })] })), system.renderMode === RenderMode.StretchedBillBoard && (_jsxs(_Fragment, { children: [_jsx(Row, { label: "Speed factor", children: _jsx(NumberField, { value: system.rendererEmitterSettings.speedFactor ?? 0, step: 0.1, onChange: (v) => binding.apply((s) => (s.rendererEmitterSettings.speedFactor = v)) }) }), _jsx(Row, { label: "Length factor", children: _jsx(NumberField, { value: system.rendererEmitterSettings.lengthFactor ?? 2, step: 0.1, onChange: (v) => binding.apply((s) => (s.rendererEmitterSettings.lengthFactor = v)) }) })] })), _jsxs("div", { style: {
|
package/package.json
CHANGED
|
@@ -1,72 +1,72 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
"name": "babylon.quarks-editor",
|
|
3
|
+
"version": "0.19.0",
|
|
4
|
+
"description": "Shuriken-style effect editor for babylon.quarks: headless edit-model core + React module-stack UI",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"./react": {
|
|
14
|
+
"import": "./dist/react/index.js",
|
|
15
|
+
"types": "./dist/react/index.d.ts"
|
|
16
|
+
}
|
|
12
17
|
},
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"src",
|
|
21
|
+
"LICENSE",
|
|
22
|
+
"README.md",
|
|
23
|
+
"CHANGELOG.md"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"clean": "rimraf dist",
|
|
27
|
+
"build": "npm run clean && tsc -p tsconfig.json"
|
|
28
|
+
},
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/Soullnik/babylon.quarks-standalone.git"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"babylonjs",
|
|
35
|
+
"babylon.js",
|
|
36
|
+
"particles",
|
|
37
|
+
"particle-system",
|
|
38
|
+
"particle-editor",
|
|
39
|
+
"vfx",
|
|
40
|
+
"vfx-editor",
|
|
41
|
+
"shuriken",
|
|
42
|
+
"editor",
|
|
43
|
+
"react",
|
|
44
|
+
"quarks"
|
|
45
|
+
],
|
|
46
|
+
"author": "soullnik <soullnik96@gmail.com>",
|
|
47
|
+
"license": "MIT",
|
|
48
|
+
"bugs": {
|
|
49
|
+
"url": "https://github.com/Soullnik/babylon.quarks-standalone/issues"
|
|
50
|
+
},
|
|
51
|
+
"homepage": "https://github.com/Soullnik/babylon.quarks-standalone#readme",
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"@babylonjs/core": ">=9.0.0",
|
|
54
|
+
"@babylonjs/materials": ">=9.0.0",
|
|
55
|
+
"@heroicons/react": ">=2.0.0",
|
|
56
|
+
"@tanstack/react-virtual": ">=3.0.0",
|
|
57
|
+
"babylon.quarks": ">=0.17.0",
|
|
58
|
+
"react": ">=18",
|
|
59
|
+
"react-dom": ">=18"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@babylonjs/materials": "9.6.2",
|
|
63
|
+
"@heroicons/react": "^2.2.0",
|
|
64
|
+
"@tanstack/react-virtual": "^3.14.6",
|
|
65
|
+
"@types/react": "^19.0.0",
|
|
66
|
+
"@types/react-dom": "^19.0.0",
|
|
67
|
+
"react": "^19.0.0",
|
|
68
|
+
"react-dom": "^19.0.0",
|
|
69
|
+
"rimraf": "^6.0.0",
|
|
70
|
+
"typescript": "^5.9.3"
|
|
16
71
|
}
|
|
17
|
-
},
|
|
18
|
-
"files": [
|
|
19
|
-
"dist",
|
|
20
|
-
"src",
|
|
21
|
-
"LICENSE",
|
|
22
|
-
"README.md",
|
|
23
|
-
"CHANGELOG.md"
|
|
24
|
-
],
|
|
25
|
-
"scripts": {
|
|
26
|
-
"clean": "rimraf dist",
|
|
27
|
-
"build": "npm run clean && tsc -p tsconfig.json"
|
|
28
|
-
},
|
|
29
|
-
"repository": {
|
|
30
|
-
"type": "git",
|
|
31
|
-
"url": "git+https://github.com/Soullnik/babylon.quarks-standalone.git"
|
|
32
|
-
},
|
|
33
|
-
"keywords": [
|
|
34
|
-
"babylonjs",
|
|
35
|
-
"babylon.js",
|
|
36
|
-
"particles",
|
|
37
|
-
"particle-system",
|
|
38
|
-
"particle-editor",
|
|
39
|
-
"vfx",
|
|
40
|
-
"vfx-editor",
|
|
41
|
-
"shuriken",
|
|
42
|
-
"editor",
|
|
43
|
-
"react",
|
|
44
|
-
"quarks"
|
|
45
|
-
],
|
|
46
|
-
"author": "soullnik <soullnik96@gmail.com>",
|
|
47
|
-
"license": "MIT",
|
|
48
|
-
"bugs": {
|
|
49
|
-
"url": "https://github.com/Soullnik/babylon.quarks-standalone/issues"
|
|
50
|
-
},
|
|
51
|
-
"homepage": "https://github.com/Soullnik/babylon.quarks-standalone#readme",
|
|
52
|
-
"peerDependencies": {
|
|
53
|
-
"@babylonjs/core": ">=9.0.0",
|
|
54
|
-
"@babylonjs/materials": ">=9.0.0",
|
|
55
|
-
"@heroicons/react": ">=2.0.0",
|
|
56
|
-
"@tanstack/react-virtual": ">=3.0.0",
|
|
57
|
-
"babylon.quarks": ">=0.17.0",
|
|
58
|
-
"react": ">=18",
|
|
59
|
-
"react-dom": ">=18"
|
|
60
|
-
},
|
|
61
|
-
"devDependencies": {
|
|
62
|
-
"@babylonjs/materials": "9.6.2",
|
|
63
|
-
"@heroicons/react": "^2.2.0",
|
|
64
|
-
"@tanstack/react-virtual": "^3.14.6",
|
|
65
|
-
"@types/react": "^19.0.0",
|
|
66
|
-
"@types/react-dom": "^19.0.0",
|
|
67
|
-
"react": "^19.0.0",
|
|
68
|
-
"react-dom": "^19.0.0",
|
|
69
|
-
"rimraf": "^6.0.0",
|
|
70
|
-
"typescript": "^5.9.3"
|
|
71
|
-
}
|
|
72
72
|
}
|
package/src/core/binding.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type {ParticleSystem} from 'babylon.quarks';
|
|
2
1
|
import type {TransformNode} from '@babylonjs/core/Meshes/transformNode';
|
|
3
|
-
import {
|
|
2
|
+
import type {ParticleSystem} from 'babylon.quarks';
|
|
4
3
|
import type {EffectTreeNode} from './effectTree';
|
|
4
|
+
import {buildEffectTree, serializeEffectTree} from './effectTree';
|
|
5
5
|
|
|
6
6
|
export type EditorListener = () => void;
|
|
7
7
|
|
package/src/core/colors.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {ColorOverLife, Gradient, Vector3, Vector4} from 'babylon.quarks';
|
|
2
1
|
import type {Behavior} from 'babylon.quarks';
|
|
2
|
+
import {ColorOverLife, Gradient, Vector3, Vector4} from 'babylon.quarks';
|
|
3
3
|
|
|
4
4
|
/** One RGBA stop of the editable gradient (all channels 0..1, pos 0..1). */
|
|
5
5
|
export interface GradientStop {
|
package/src/core/effectTree.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {ParticleEmitter} from 'babylon.quarks';
|
|
2
|
-
import type {ParticleSystem} from 'babylon.quarks';
|
|
3
|
-
import {TransformNode} from '@babylonjs/core/Meshes/transformNode';
|
|
4
1
|
import {Matrix, Quaternion} from '@babylonjs/core/Maths/math.vector';
|
|
2
|
+
import {TransformNode} from '@babylonjs/core/Meshes/transformNode';
|
|
3
|
+
import type {ParticleSystem} from 'babylon.quarks';
|
|
4
|
+
import {ParticleEmitter} from 'babylon.quarks';
|
|
5
5
|
import {isEditorSceneNode} from './editorScene';
|
|
6
6
|
|
|
7
7
|
/** A node in the editor's effect hierarchy: a Group (organizational) or a ParticleEmitter. */
|
|
@@ -46,7 +46,7 @@ export function collectSystems(tree: EffectTreeNode): ParticleSystem[] {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
interface SerializeMeta {
|
|
49
|
-
textures: {[k: string]: {url?: string; name?: string}};
|
|
49
|
+
textures: {[k: string]: {url?: string; name?: string; invertY?: boolean}};
|
|
50
50
|
materials: {[k: string]: {[key: string]: unknown}};
|
|
51
51
|
geometries: {[k: string]: unknown};
|
|
52
52
|
}
|
|
@@ -72,7 +72,11 @@ function serializeNode(tree: EffectTreeNode, meta: SerializeMeta, fallbackUuid:
|
|
|
72
72
|
children: children.length > 0 ? children : undefined,
|
|
73
73
|
};
|
|
74
74
|
if (tree.system) {
|
|
75
|
-
return {
|
|
75
|
+
return {
|
|
76
|
+
...base,
|
|
77
|
+
type: 'ParticleEmitter',
|
|
78
|
+
ps: tree.system.toJSON(meta as never, {useUrlForImage: true} as never),
|
|
79
|
+
};
|
|
76
80
|
}
|
|
77
81
|
return {...base, type: 'Group'};
|
|
78
82
|
}
|
|
@@ -113,13 +117,24 @@ function finishEnvelope(object: Record<string, unknown>, meta: SerializeMeta): s
|
|
|
113
117
|
// toJSON stashes live Texture instances and material records in meta; flatten them into
|
|
114
118
|
// the serializable images/textures/materials arrays QuarksLoader expects.
|
|
115
119
|
const images: Array<{uuid: string; url: string}> = [];
|
|
116
|
-
const textures: Array<{uuid: string; image?: string}> = [];
|
|
120
|
+
const textures: Array<{uuid: string; image?: string; invertY?: boolean; noMipmap?: boolean}> = [];
|
|
117
121
|
for (const [uuid, tex] of Object.entries(meta.textures)) {
|
|
118
122
|
const url = tex?.url ?? tex?.name;
|
|
123
|
+
const invertY = typeof tex?.invertY === 'boolean' ? tex.invertY : undefined;
|
|
124
|
+
const isEnvAtlas =
|
|
125
|
+
tex?.name === 'quarksEnvAtlas' || (typeof tex?.name === 'string' && tex.name.endsWith('_envAtlas'));
|
|
119
126
|
if (url) {
|
|
120
127
|
const imageUuid = `${uuid}-image`;
|
|
121
128
|
images.push({uuid: imageUuid, url});
|
|
122
|
-
|
|
129
|
+
const entry: {uuid: string; image?: string; invertY?: boolean; noMipmap?: boolean} = {
|
|
130
|
+
uuid,
|
|
131
|
+
image: imageUuid,
|
|
132
|
+
};
|
|
133
|
+
if (invertY === false || isEnvAtlas) {
|
|
134
|
+
entry.invertY = false;
|
|
135
|
+
entry.noMipmap = true;
|
|
136
|
+
}
|
|
137
|
+
textures.push(entry);
|
|
123
138
|
} else {
|
|
124
139
|
textures.push({uuid});
|
|
125
140
|
}
|