react-three-game 0.0.32 → 0.0.33
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/dist/tools/prefabeditor/components/DirectionalLightComponent.js +13 -1
- package/dist/tools/prefabeditor/components/GeometryComponent.js +17 -9
- package/dist/tools/prefabeditor/components/Input.d.ts +19 -0
- package/dist/tools/prefabeditor/components/Input.js +123 -0
- package/dist/tools/prefabeditor/components/MaterialComponent.js +15 -4
- package/dist/tools/prefabeditor/components/ModelComponent.js +2 -1
- package/dist/tools/prefabeditor/components/PhysicsComponent.js +12 -3
- package/dist/tools/prefabeditor/components/SpotLightComponent.js +12 -1
- package/dist/tools/prefabeditor/components/TransformComponent.d.ts +0 -5
- package/dist/tools/prefabeditor/components/TransformComponent.js +21 -89
- package/package.json +1 -1
- package/src/tools/prefabeditor/components/DirectionalLightComponent.tsx +32 -125
- package/src/tools/prefabeditor/components/GeometryComponent.tsx +27 -24
- package/src/tools/prefabeditor/components/Input.tsx +200 -0
- package/src/tools/prefabeditor/components/MaterialComponent.tsx +31 -22
- package/src/tools/prefabeditor/components/ModelComponent.tsx +5 -4
- package/src/tools/prefabeditor/components/PhysicsComponent.tsx +31 -17
- package/src/tools/prefabeditor/components/SpotLightComponent.tsx +26 -43
- package/src/tools/prefabeditor/components/TransformComponent.tsx +34 -165
|
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { useRef, useEffect } from "react";
|
|
3
3
|
import { useFrame } from "@react-three/fiber";
|
|
4
4
|
import { Vector3 } from "three";
|
|
5
|
+
import { Input, Label } from "./Input";
|
|
5
6
|
function DirectionalLightComponentEditor({ component, onUpdate }) {
|
|
6
7
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
7
8
|
const props = {
|
|
@@ -17,7 +18,18 @@ function DirectionalLightComponentEditor({ component, onUpdate }) {
|
|
|
17
18
|
shadowCameraRight: (_k = component.properties.shadowCameraRight) !== null && _k !== void 0 ? _k : 30,
|
|
18
19
|
targetOffset: (_l = component.properties.targetOffset) !== null && _l !== void 0 ? _l : [0, -5, 0]
|
|
19
20
|
};
|
|
20
|
-
|
|
21
|
+
const textInputStyle = {
|
|
22
|
+
flex: 1,
|
|
23
|
+
backgroundColor: 'rgba(0, 0, 0, 0.4)',
|
|
24
|
+
border: '1px solid rgba(34, 211, 238, 0.3)',
|
|
25
|
+
padding: '2px 4px',
|
|
26
|
+
fontSize: '10px',
|
|
27
|
+
color: 'rgba(165, 243, 252, 1)',
|
|
28
|
+
fontFamily: 'monospace',
|
|
29
|
+
outline: 'none',
|
|
30
|
+
};
|
|
31
|
+
const smallLabel = { display: 'block', fontSize: '8px', color: 'rgba(34, 211, 238, 0.5)', marginBottom: 2 };
|
|
32
|
+
return _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: [_jsxs("div", { children: [_jsx(Label, { children: "Color" }), _jsxs("div", { style: { display: 'flex', gap: 2 }, children: [_jsx("input", { type: "color", style: { height: 20, width: 20, backgroundColor: 'transparent', border: 'none', cursor: 'pointer' }, value: props.color, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { color: e.target.value })) }), _jsx("input", { type: "text", style: textInputStyle, value: props.color, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { color: e.target.value })) })] })] }), _jsxs("div", { children: [_jsx(Label, { children: "Intensity" }), _jsx(Input, { step: "0.1", value: props.intensity, onChange: value => onUpdate(Object.assign(Object.assign({}, component.properties), { intensity: value })) })] }), _jsxs("div", { children: [_jsx(Label, { children: "Cast Shadow" }), _jsx("input", { type: "checkbox", style: { height: 16, width: 16, backgroundColor: 'rgba(0, 0, 0, 0.4)', border: '1px solid rgba(34, 211, 238, 0.3)', cursor: 'pointer' }, checked: props.castShadow, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { castShadow: e.target.checked })) })] }), _jsxs("div", { children: [_jsx(Label, { children: "Shadow Map Size" }), _jsx(Input, { step: "256", value: props.shadowMapSize, onChange: value => onUpdate(Object.assign(Object.assign({}, component.properties), { shadowMapSize: value })) })] }), _jsxs("div", { style: { borderTop: '1px solid rgba(34, 211, 238, 0.2)', paddingTop: 8, marginTop: 8 }, children: [_jsx("label", { style: { display: 'block', fontSize: '9px', color: 'rgba(34, 211, 238, 0.6)', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 4 }, children: "Shadow Camera" }), _jsxs("div", { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 4 }, children: [_jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Near" }), _jsx(Input, { step: "0.1", value: props.shadowCameraNear, onChange: value => onUpdate(Object.assign(Object.assign({}, component.properties), { shadowCameraNear: value })) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Far" }), _jsx(Input, { step: "1", value: props.shadowCameraFar, onChange: value => onUpdate(Object.assign(Object.assign({}, component.properties), { shadowCameraFar: value })) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Top" }), _jsx(Input, { step: "1", value: props.shadowCameraTop, onChange: value => onUpdate(Object.assign(Object.assign({}, component.properties), { shadowCameraTop: value })) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Bottom" }), _jsx(Input, { step: "1", value: props.shadowCameraBottom, onChange: value => onUpdate(Object.assign(Object.assign({}, component.properties), { shadowCameraBottom: value })) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Left" }), _jsx(Input, { step: "1", value: props.shadowCameraLeft, onChange: value => onUpdate(Object.assign(Object.assign({}, component.properties), { shadowCameraLeft: value })) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Right" }), _jsx(Input, { step: "1", value: props.shadowCameraRight, onChange: value => onUpdate(Object.assign(Object.assign({}, component.properties), { shadowCameraRight: value })) })] })] })] }), _jsxs("div", { style: { borderTop: '1px solid rgba(34, 211, 238, 0.2)', paddingTop: 8, marginTop: 8 }, children: [_jsx("label", { style: { display: 'block', fontSize: '9px', color: 'rgba(34, 211, 238, 0.6)', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 4 }, children: "Target Offset" }), _jsxs("div", { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 4 }, children: [_jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "X" }), _jsx(Input, { step: "0.5", value: props.targetOffset[0], onChange: value => onUpdate(Object.assign(Object.assign({}, component.properties), { targetOffset: [value, props.targetOffset[1], props.targetOffset[2]] })) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Y" }), _jsx(Input, { step: "0.5", value: props.targetOffset[1], onChange: value => onUpdate(Object.assign(Object.assign({}, component.properties), { targetOffset: [props.targetOffset[0], value, props.targetOffset[2]] })) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Z" }), _jsx(Input, { step: "0.5", value: props.targetOffset[2], onChange: value => onUpdate(Object.assign(Object.assign({}, component.properties), { targetOffset: [props.targetOffset[0], props.targetOffset[1], value] })) })] })] })] })] });
|
|
21
33
|
}
|
|
22
34
|
function DirectionalLightView({ properties, editMode }) {
|
|
23
35
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Input, Label } from "./Input";
|
|
2
3
|
const GEOMETRY_ARGS = {
|
|
3
4
|
box: {
|
|
4
5
|
labels: ["Width", "Height", "Depth"],
|
|
@@ -16,17 +17,24 @@ const GEOMETRY_ARGS = {
|
|
|
16
17
|
function GeometryComponentEditor({ component, onUpdate, }) {
|
|
17
18
|
const { geometryType, args = [] } = component.properties;
|
|
18
19
|
const schema = GEOMETRY_ARGS[geometryType];
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
const selectStyle = {
|
|
21
|
+
width: '100%',
|
|
22
|
+
backgroundColor: 'rgba(0, 0, 0, 0.4)',
|
|
23
|
+
border: '1px solid rgba(34, 211, 238, 0.3)',
|
|
24
|
+
padding: '2px 4px',
|
|
25
|
+
fontSize: '10px',
|
|
26
|
+
color: 'rgba(165, 243, 252, 1)',
|
|
27
|
+
fontFamily: 'monospace',
|
|
28
|
+
outline: 'none',
|
|
29
|
+
};
|
|
30
|
+
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: [_jsxs("div", { children: [_jsx(Label, { children: "Type" }), _jsxs("select", { style: selectStyle, value: geometryType, onChange: e => {
|
|
31
|
+
const type = e.target.value;
|
|
32
|
+
onUpdate({ geometryType: type, args: GEOMETRY_ARGS[type].defaults });
|
|
33
|
+
}, children: [_jsx("option", { value: "box", children: "Box" }), _jsx("option", { value: "sphere", children: "Sphere" }), _jsx("option", { value: "plane", children: "Plane" })] })] }), schema.labels.map((label, i) => {
|
|
26
34
|
var _a;
|
|
27
|
-
return (_jsxs("div", { children: [_jsx(
|
|
35
|
+
return (_jsxs("div", { children: [_jsx(Label, { children: label }), _jsx(Input, { value: (_a = args[i]) !== null && _a !== void 0 ? _a : schema.defaults[i], step: "0.1", onChange: value => {
|
|
28
36
|
const next = [...args];
|
|
29
|
-
next[i] =
|
|
37
|
+
next[i] = value;
|
|
30
38
|
onUpdate({ args: next });
|
|
31
39
|
} })] }, label));
|
|
32
40
|
})] }));
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface InputProps {
|
|
3
|
+
value: number;
|
|
4
|
+
onChange: (value: number) => void;
|
|
5
|
+
step?: string | number;
|
|
6
|
+
min?: number;
|
|
7
|
+
max?: number;
|
|
8
|
+
style?: React.CSSProperties;
|
|
9
|
+
}
|
|
10
|
+
export declare function Input({ value, onChange, step, min, max, style }: InputProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export declare function Label({ children }: {
|
|
12
|
+
children: React.ReactNode;
|
|
13
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export declare function Vector3Input({ label, value, onChange }: {
|
|
15
|
+
label: string;
|
|
16
|
+
value: [number, number, number];
|
|
17
|
+
onChange: (v: [number, number, number]) => void;
|
|
18
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from 'react';
|
|
3
|
+
// Shared styles
|
|
4
|
+
const styles = {
|
|
5
|
+
input: {
|
|
6
|
+
width: '100%',
|
|
7
|
+
backgroundColor: 'rgba(0, 0, 0, 0.4)',
|
|
8
|
+
border: '1px solid rgba(34, 211, 238, 0.3)',
|
|
9
|
+
padding: '2px 4px',
|
|
10
|
+
fontSize: '10px',
|
|
11
|
+
color: 'rgba(165, 243, 252, 1)',
|
|
12
|
+
fontFamily: 'monospace',
|
|
13
|
+
outline: 'none',
|
|
14
|
+
},
|
|
15
|
+
label: {
|
|
16
|
+
display: 'block',
|
|
17
|
+
fontSize: '9px',
|
|
18
|
+
color: 'rgba(34, 211, 238, 0.6)',
|
|
19
|
+
textTransform: 'uppercase',
|
|
20
|
+
letterSpacing: '0.05em',
|
|
21
|
+
marginBottom: 2,
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
export function Input({ value, onChange, step, min, max, style }) {
|
|
25
|
+
return (_jsx("input", { type: "number", value: value, onChange: (e) => onChange(parseFloat(e.target.value)), step: step, min: min, max: max, style: Object.assign(Object.assign({}, styles.input), style) }));
|
|
26
|
+
}
|
|
27
|
+
export function Label({ children }) {
|
|
28
|
+
return _jsx("label", { style: styles.label, children: children });
|
|
29
|
+
}
|
|
30
|
+
export function Vector3Input({ label, value, onChange }) {
|
|
31
|
+
const [draft, setDraft] = useState(() => value.map(v => v.toString()));
|
|
32
|
+
// Sync external changes (gizmo, undo, etc.)
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
setDraft(value.map(v => v.toString()));
|
|
35
|
+
}, [value[0], value[1], value[2]]);
|
|
36
|
+
const dragState = useRef(null);
|
|
37
|
+
const commit = (index) => {
|
|
38
|
+
const num = parseFloat(draft[index]);
|
|
39
|
+
if (Number.isFinite(num)) {
|
|
40
|
+
const next = [...value];
|
|
41
|
+
next[index] = num;
|
|
42
|
+
onChange(next);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const startScrub = (e, index) => {
|
|
46
|
+
e.preventDefault();
|
|
47
|
+
dragState.current = {
|
|
48
|
+
index,
|
|
49
|
+
startX: e.clientX,
|
|
50
|
+
startValue: value[index]
|
|
51
|
+
};
|
|
52
|
+
e.target.setPointerCapture(e.pointerId);
|
|
53
|
+
document.body.style.cursor = "ew-resize";
|
|
54
|
+
};
|
|
55
|
+
const onScrubMove = (e) => {
|
|
56
|
+
if (!dragState.current)
|
|
57
|
+
return;
|
|
58
|
+
const { index, startX, startValue } = dragState.current;
|
|
59
|
+
const dx = e.clientX - startX;
|
|
60
|
+
let speed = 0.02;
|
|
61
|
+
if (e.shiftKey)
|
|
62
|
+
speed *= 0.1; // fine
|
|
63
|
+
if (e.altKey)
|
|
64
|
+
speed *= 5; // coarse
|
|
65
|
+
const nextValue = startValue + dx * speed;
|
|
66
|
+
const next = [...value];
|
|
67
|
+
next[index] = nextValue;
|
|
68
|
+
setDraft(d => {
|
|
69
|
+
const copy = [...d];
|
|
70
|
+
copy[index] = nextValue.toFixed(3);
|
|
71
|
+
return copy;
|
|
72
|
+
});
|
|
73
|
+
onChange(next);
|
|
74
|
+
};
|
|
75
|
+
const endScrub = (e) => {
|
|
76
|
+
if (!dragState.current)
|
|
77
|
+
return;
|
|
78
|
+
dragState.current = null;
|
|
79
|
+
document.body.style.cursor = "";
|
|
80
|
+
e.target.releasePointerCapture(e.pointerId);
|
|
81
|
+
};
|
|
82
|
+
const axes = [
|
|
83
|
+
{ key: "x", color: 'rgba(248, 113, 113, 1)', index: 0 },
|
|
84
|
+
{ key: "y", color: 'rgba(134, 239, 172, 1)', index: 1 },
|
|
85
|
+
{ key: "z", color: 'rgba(96, 165, 250, 1)', index: 2 }
|
|
86
|
+
];
|
|
87
|
+
return (_jsxs("div", { style: { marginBottom: 8 }, children: [_jsx("label", { style: Object.assign(Object.assign({}, styles.label), { marginBottom: 4 }), children: label }), _jsx("div", { style: { display: 'flex', gap: 4 }, children: axes.map(({ key, color, index }) => (_jsxs("div", { style: {
|
|
88
|
+
flex: 1,
|
|
89
|
+
display: 'flex',
|
|
90
|
+
alignItems: 'center',
|
|
91
|
+
gap: 4,
|
|
92
|
+
backgroundColor: 'rgba(0, 0, 0, 0.3)',
|
|
93
|
+
border: '1px solid rgba(34, 211, 238, 0.2)',
|
|
94
|
+
borderRadius: 4,
|
|
95
|
+
padding: '4px 6px',
|
|
96
|
+
minHeight: 32,
|
|
97
|
+
}, children: [_jsx("span", { style: {
|
|
98
|
+
fontSize: '12px',
|
|
99
|
+
fontWeight: 'bold',
|
|
100
|
+
color,
|
|
101
|
+
width: 12,
|
|
102
|
+
cursor: 'ew-resize',
|
|
103
|
+
userSelect: 'none',
|
|
104
|
+
}, onPointerDown: e => startScrub(e, index), onPointerMove: onScrubMove, onPointerUp: endScrub, children: key.toUpperCase() }), _jsx("input", { style: {
|
|
105
|
+
flex: 1,
|
|
106
|
+
backgroundColor: 'transparent',
|
|
107
|
+
border: 'none',
|
|
108
|
+
fontSize: '12px',
|
|
109
|
+
color: 'rgba(165, 243, 252, 1)',
|
|
110
|
+
fontFamily: 'monospace',
|
|
111
|
+
outline: 'none',
|
|
112
|
+
width: '100%',
|
|
113
|
+
minWidth: 0,
|
|
114
|
+
}, type: "text", value: draft[index], onChange: e => {
|
|
115
|
+
const next = [...draft];
|
|
116
|
+
next[index] = e.target.value;
|
|
117
|
+
setDraft(next);
|
|
118
|
+
}, onBlur: () => commit(index), onKeyDown: e => {
|
|
119
|
+
if (e.key === "Enter") {
|
|
120
|
+
e.target.blur();
|
|
121
|
+
}
|
|
122
|
+
} })] }, key))) })] }));
|
|
123
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { TextureListViewer } from '../../assetviewer/page';
|
|
3
3
|
import { useEffect, useState } from 'react';
|
|
4
|
+
import { Input, Label } from './Input';
|
|
4
5
|
function MaterialComponentEditor({ component, onUpdate, basePath = "" }) {
|
|
5
6
|
var _a, _b, _c, _d;
|
|
6
7
|
const [textureFiles, setTextureFiles] = useState([]);
|
|
@@ -11,14 +12,24 @@ function MaterialComponentEditor({ component, onUpdate, basePath = "" }) {
|
|
|
11
12
|
.then(data => setTextureFiles(Array.isArray(data) ? data : data.files || []))
|
|
12
13
|
.catch(console.error);
|
|
13
14
|
}, [basePath]);
|
|
14
|
-
|
|
15
|
+
const textInputStyle = {
|
|
16
|
+
flex: 1,
|
|
17
|
+
backgroundColor: 'rgba(0, 0, 0, 0.4)',
|
|
18
|
+
border: '1px solid rgba(34, 211, 238, 0.3)',
|
|
19
|
+
padding: '2px 4px',
|
|
20
|
+
fontSize: '10px',
|
|
21
|
+
color: 'rgba(165, 243, 252, 1)',
|
|
22
|
+
fontFamily: 'monospace',
|
|
23
|
+
outline: 'none',
|
|
24
|
+
};
|
|
25
|
+
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 4 }, children: [_jsxs("div", { children: [_jsx(Label, { children: "Color" }), _jsxs("div", { style: { display: 'flex', gap: 2 }, children: [_jsx("input", { type: "color", style: { height: 20, width: 20, backgroundColor: 'transparent', border: 'none', cursor: 'pointer' }, value: component.properties.color, onChange: e => onUpdate({ color: e.target.value }) }), _jsx("input", { type: "text", style: textInputStyle, value: component.properties.color, onChange: e => onUpdate({ color: e.target.value }) })] })] }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 4 }, children: [_jsx("input", { type: "checkbox", style: { width: 12, height: 12 }, checked: component.properties.wireframe || false, onChange: e => onUpdate({ wireframe: e.target.checked }) }), _jsx("label", { style: { fontSize: '9px', color: 'rgba(34, 211, 238, 0.6)' }, children: "Wireframe" })] }), _jsxs("div", { children: [_jsx(Label, { children: "Texture" }), _jsx("div", { style: { maxHeight: 128, overflowY: 'auto' }, children: _jsx(TextureListViewer, { files: textureFiles, selected: component.properties.texture || undefined, onSelect: file => onUpdate({ texture: file }), basePath: basePath }) })] }), component.properties.texture && (_jsxs("div", { style: { borderTop: '1px solid rgba(34, 211, 238, 0.2)', paddingTop: 4, marginTop: 4 }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 4, marginBottom: 4 }, children: [_jsx("input", { type: "checkbox", style: { width: 12, height: 12 }, checked: component.properties.repeat || false, onChange: e => onUpdate({ repeat: e.target.checked }) }), _jsx("label", { style: { fontSize: '9px', color: 'rgba(34, 211, 238, 0.6)' }, children: "Repeat Texture" })] }), component.properties.repeat && (_jsxs("div", { children: [_jsx(Label, { children: "Repeat (X, Y)" }), _jsxs("div", { style: { display: 'flex', gap: 2 }, children: [_jsx(Input, { value: (_b = (_a = component.properties.repeatCount) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : 1, onChange: value => {
|
|
15
26
|
var _a, _b;
|
|
16
27
|
const y = (_b = (_a = component.properties.repeatCount) === null || _a === void 0 ? void 0 : _a[1]) !== null && _b !== void 0 ? _b : 1;
|
|
17
|
-
onUpdate({
|
|
18
|
-
} }), _jsx(
|
|
28
|
+
onUpdate({ repeatCount: [value, y] });
|
|
29
|
+
} }), _jsx(Input, { value: (_d = (_c = component.properties.repeatCount) === null || _c === void 0 ? void 0 : _c[1]) !== null && _d !== void 0 ? _d : 1, onChange: value => {
|
|
19
30
|
var _a, _b;
|
|
20
31
|
const x = (_b = (_a = component.properties.repeatCount) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : 1;
|
|
21
|
-
onUpdate({
|
|
32
|
+
onUpdate({ repeatCount: [x, value] });
|
|
22
33
|
} })] })] }))] }))] }));
|
|
23
34
|
}
|
|
24
35
|
;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { ModelListViewer } from '../../assetviewer/page';
|
|
3
3
|
import { useEffect, useState, useMemo } from 'react';
|
|
4
|
+
import { Label } from './Input';
|
|
4
5
|
function ModelComponentEditor({ component, onUpdate, basePath = "" }) {
|
|
5
6
|
const [modelFiles, setModelFiles] = useState([]);
|
|
6
7
|
useEffect(() => {
|
|
@@ -15,7 +16,7 @@ function ModelComponentEditor({ component, onUpdate, basePath = "" }) {
|
|
|
15
16
|
const filename = file.startsWith('/') ? file.slice(1) : file;
|
|
16
17
|
onUpdate({ 'filename': filename });
|
|
17
18
|
};
|
|
18
|
-
return _jsxs("div", {
|
|
19
|
+
return _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 4 }, children: [_jsxs("div", { children: [_jsx(Label, { children: "Model" }), _jsx("div", { style: { maxHeight: 128, overflowY: 'auto' }, children: _jsx(ModelListViewer, { files: modelFiles, selected: component.properties.filename ? `/${component.properties.filename}` : undefined, onSelect: handleModelSelect, basePath: basePath }) })] }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 4 }, children: [_jsx("input", { type: "checkbox", id: "instanced-checkbox", checked: component.properties.instanced || false, onChange: e => onUpdate({ instanced: e.target.checked }), style: { width: 12, height: 12 } }), _jsx("label", { htmlFor: "instanced-checkbox", style: { fontSize: '9px', color: 'rgba(34, 211, 238, 0.6)' }, children: "Instanced" })] })] });
|
|
19
20
|
}
|
|
20
21
|
// View for Model component
|
|
21
22
|
function ModelComponentView({ properties, loadedModels, children }) {
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { RigidBody } from "@react-three/rapier";
|
|
3
|
-
|
|
4
|
-
const labelClass = { display: 'block', fontSize: '9px', color: 'rgba(34, 211, 238, 0.6)', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 2 };
|
|
3
|
+
import { Label } from "./Input";
|
|
5
4
|
function PhysicsComponentEditor({ component, onUpdate }) {
|
|
6
5
|
const { type = 'dynamic', collider = 'hull' } = component.properties;
|
|
7
|
-
|
|
6
|
+
const selectStyle = {
|
|
7
|
+
width: '100%',
|
|
8
|
+
backgroundColor: 'rgba(0, 0, 0, 0.4)',
|
|
9
|
+
border: '1px solid rgba(34, 211, 238, 0.3)',
|
|
10
|
+
padding: '2px 4px',
|
|
11
|
+
fontSize: '10px',
|
|
12
|
+
color: 'rgba(165, 243, 252, 1)',
|
|
13
|
+
fontFamily: 'monospace',
|
|
14
|
+
outline: 'none',
|
|
15
|
+
};
|
|
16
|
+
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: [_jsxs("div", { children: [_jsx(Label, { children: "Type" }), _jsxs("select", { style: selectStyle, value: type, onChange: e => onUpdate({ type: e.target.value }), children: [_jsx("option", { value: "dynamic", children: "Dynamic" }), _jsx("option", { value: "fixed", children: "Fixed" })] })] }), _jsxs("div", { children: [_jsx(Label, { children: "Collider" }), _jsxs("select", { style: selectStyle, value: collider, onChange: e => onUpdate({ collider: e.target.value }), children: [_jsx("option", { value: "hull", children: "Hull (convex)" }), _jsx("option", { value: "trimesh", children: "Trimesh (exact)" }), _jsx("option", { value: "cuboid", children: "Cuboid (box)" }), _jsx("option", { value: "ball", children: "Ball (sphere)" })] })] })] }));
|
|
8
17
|
}
|
|
9
18
|
function PhysicsComponentView({ properties, editMode, children }) {
|
|
10
19
|
if (editMode)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useRef, useEffect } from "react";
|
|
3
|
+
import { Input, Label } from "./Input";
|
|
3
4
|
function SpotLightComponentEditor({ component, onUpdate }) {
|
|
4
5
|
var _a, _b, _c, _d, _e, _f;
|
|
5
6
|
const props = {
|
|
@@ -10,7 +11,17 @@ function SpotLightComponentEditor({ component, onUpdate }) {
|
|
|
10
11
|
distance: (_e = component.properties.distance) !== null && _e !== void 0 ? _e : 100,
|
|
11
12
|
castShadow: (_f = component.properties.castShadow) !== null && _f !== void 0 ? _f : true
|
|
12
13
|
};
|
|
13
|
-
|
|
14
|
+
const textInputStyle = {
|
|
15
|
+
flex: 1,
|
|
16
|
+
backgroundColor: 'rgba(0, 0, 0, 0.4)',
|
|
17
|
+
border: '1px solid rgba(34, 211, 238, 0.3)',
|
|
18
|
+
padding: '2px 4px',
|
|
19
|
+
fontSize: '10px',
|
|
20
|
+
color: 'rgba(165, 243, 252, 1)',
|
|
21
|
+
fontFamily: 'monospace',
|
|
22
|
+
outline: 'none',
|
|
23
|
+
};
|
|
24
|
+
return _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: [_jsxs("div", { children: [_jsx(Label, { children: "Color" }), _jsxs("div", { style: { display: 'flex', gap: 2 }, children: [_jsx("input", { type: "color", style: { height: 20, width: 20, backgroundColor: 'transparent', border: 'none', cursor: 'pointer' }, value: props.color, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { color: e.target.value })) }), _jsx("input", { type: "text", style: textInputStyle, value: props.color, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { color: e.target.value })) })] })] }), _jsxs("div", { children: [_jsx(Label, { children: "Intensity" }), _jsx(Input, { step: "0.1", value: props.intensity, onChange: value => onUpdate(Object.assign(Object.assign({}, component.properties), { intensity: value })) })] }), _jsxs("div", { children: [_jsx(Label, { children: "Angle" }), _jsx(Input, { step: "0.1", min: 0, max: Math.PI, value: props.angle, onChange: value => onUpdate(Object.assign(Object.assign({}, component.properties), { angle: value })) })] }), _jsxs("div", { children: [_jsx(Label, { children: "Penumbra" }), _jsx(Input, { step: "0.1", min: 0, max: 1, value: props.penumbra, onChange: value => onUpdate(Object.assign(Object.assign({}, component.properties), { penumbra: value })) })] }), _jsxs("div", { children: [_jsx(Label, { children: "Distance" }), _jsx(Input, { step: "1", min: 0, value: props.distance, onChange: value => onUpdate(Object.assign(Object.assign({}, component.properties), { distance: value })) })] }), _jsxs("div", { children: [_jsx(Label, { children: "Cast Shadow" }), _jsx("input", { type: "checkbox", style: { height: 16, width: 16, backgroundColor: 'rgba(0, 0, 0, 0.4)', border: '1px solid rgba(34, 211, 238, 0.3)', cursor: 'pointer' }, checked: props.castShadow, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { castShadow: e.target.checked })) })] })] });
|
|
14
25
|
}
|
|
15
26
|
function SpotLightView({ properties, editMode }) {
|
|
16
27
|
var _a, _b, _c, _d, _e, _f;
|
|
@@ -1,8 +1,3 @@
|
|
|
1
1
|
import { Component } from "./ComponentRegistry";
|
|
2
2
|
declare const TransformComponent: Component;
|
|
3
3
|
export default TransformComponent;
|
|
4
|
-
export declare function Vector3Input({ label, value, onChange }: {
|
|
5
|
-
label: string;
|
|
6
|
-
value: [number, number, number];
|
|
7
|
-
onChange: (v: [number, number, number]) => void;
|
|
8
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,27 +1,26 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { Vector3Input, Label } from "./Input";
|
|
3
|
+
const buttonStyle = {
|
|
4
|
+
padding: '2px 6px',
|
|
5
|
+
background: 'transparent',
|
|
6
|
+
color: 'rgba(255,255,255,0.9)',
|
|
7
|
+
border: '1px solid rgba(255,255,255,0.14)',
|
|
8
|
+
borderRadius: 4,
|
|
9
|
+
cursor: 'pointer',
|
|
10
|
+
font: 'inherit',
|
|
11
|
+
flex: 1,
|
|
12
|
+
};
|
|
3
13
|
function TransformComponentEditor({ component, onUpdate, transformMode, setTransformMode }) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
buttonActive: {
|
|
15
|
-
background: 'rgba(255,255,255,0.10)',
|
|
16
|
-
},
|
|
17
|
-
};
|
|
18
|
-
return _jsxs("div", { style: { display: 'flex', flexDirection: 'column' }, children: [transformMode && setTransformMode && (_jsxs("div", { style: { marginBottom: 8 }, children: [_jsx("label", { style: { display: 'block', fontSize: '9px', color: 'rgba(34, 211, 238, 0.6)', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 4 }, children: "Transform Mode" }), _jsx("div", { style: { display: 'flex', gap: 6 }, children: ["translate", "rotate", "scale"].map(mode => (_jsx("button", { onClick: () => setTransformMode(mode), style: Object.assign(Object.assign(Object.assign({}, s.button), { flex: 1 }), (transformMode === mode ? s.buttonActive : {})), onPointerEnter: (e) => {
|
|
19
|
-
if (transformMode !== mode)
|
|
20
|
-
e.currentTarget.style.background = 'rgba(255,255,255,0.08)';
|
|
21
|
-
}, onPointerLeave: (e) => {
|
|
22
|
-
if (transformMode !== mode)
|
|
23
|
-
e.currentTarget.style.background = 'transparent';
|
|
24
|
-
}, children: mode }, mode))) })] })), _jsx(Vector3Input, { label: "Position", value: component.properties.position, onChange: v => onUpdate({ position: v }) }), _jsx(Vector3Input, { label: "Rotation", value: component.properties.rotation, onChange: v => onUpdate({ rotation: v }) }), _jsx(Vector3Input, { label: "Scale", value: component.properties.scale, onChange: v => onUpdate({ scale: v }) })] });
|
|
14
|
+
return _jsxs("div", { style: { display: 'flex', flexDirection: 'column' }, children: [transformMode && setTransformMode && (_jsxs("div", { style: { marginBottom: 8 }, children: [_jsx(Label, { children: "Transform Mode" }), _jsx("div", { style: { display: 'flex', gap: 6 }, children: ["translate", "rotate", "scale"].map(mode => {
|
|
15
|
+
const isActive = transformMode === mode;
|
|
16
|
+
return (_jsx("button", { onClick: () => setTransformMode(mode), style: Object.assign(Object.assign({}, buttonStyle), { background: isActive ? 'rgba(255,255,255,0.10)' : 'transparent' }), onPointerEnter: (e) => {
|
|
17
|
+
if (!isActive)
|
|
18
|
+
e.currentTarget.style.background = 'rgba(255,255,255,0.08)';
|
|
19
|
+
}, onPointerLeave: (e) => {
|
|
20
|
+
if (!isActive)
|
|
21
|
+
e.currentTarget.style.background = 'transparent';
|
|
22
|
+
}, children: mode }, mode));
|
|
23
|
+
}) })] })), _jsx(Vector3Input, { label: "Position", value: component.properties.position, onChange: v => onUpdate({ position: v }) }), _jsx(Vector3Input, { label: "Rotation", value: component.properties.rotation, onChange: v => onUpdate({ rotation: v }) }), _jsx(Vector3Input, { label: "Scale", value: component.properties.scale, onChange: v => onUpdate({ scale: v }) })] });
|
|
25
24
|
}
|
|
26
25
|
const TransformComponent = {
|
|
27
26
|
name: 'Transform',
|
|
@@ -33,70 +32,3 @@ const TransformComponent = {
|
|
|
33
32
|
}
|
|
34
33
|
};
|
|
35
34
|
export default TransformComponent;
|
|
36
|
-
export function Vector3Input({ label, value, onChange }) {
|
|
37
|
-
const [draft, setDraft] = useState(() => value.map(v => v.toString()));
|
|
38
|
-
// Sync external changes (gizmo, undo, etc.)
|
|
39
|
-
useEffect(() => {
|
|
40
|
-
setDraft(value.map(v => v.toString()));
|
|
41
|
-
}, [value[0], value[1], value[2]]);
|
|
42
|
-
const dragState = useRef(null);
|
|
43
|
-
const commit = (index) => {
|
|
44
|
-
const num = parseFloat(draft[index]);
|
|
45
|
-
if (Number.isFinite(num)) {
|
|
46
|
-
const next = [...value];
|
|
47
|
-
next[index] = num;
|
|
48
|
-
onChange(next);
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
const startScrub = (e, index) => {
|
|
52
|
-
e.preventDefault();
|
|
53
|
-
dragState.current = {
|
|
54
|
-
index,
|
|
55
|
-
startX: e.clientX,
|
|
56
|
-
startValue: value[index]
|
|
57
|
-
};
|
|
58
|
-
e.target.setPointerCapture(e.pointerId);
|
|
59
|
-
document.body.style.cursor = "ew-resize";
|
|
60
|
-
};
|
|
61
|
-
const onScrubMove = (e) => {
|
|
62
|
-
if (!dragState.current)
|
|
63
|
-
return;
|
|
64
|
-
const { index, startX, startValue } = dragState.current;
|
|
65
|
-
const dx = e.clientX - startX;
|
|
66
|
-
let speed = 0.02;
|
|
67
|
-
if (e.shiftKey)
|
|
68
|
-
speed *= 0.1; // fine
|
|
69
|
-
if (e.altKey)
|
|
70
|
-
speed *= 5; // coarse
|
|
71
|
-
const nextValue = startValue + dx * speed;
|
|
72
|
-
const next = [...value];
|
|
73
|
-
next[index] = nextValue;
|
|
74
|
-
setDraft(d => {
|
|
75
|
-
const copy = [...d];
|
|
76
|
-
copy[index] = nextValue.toFixed(3);
|
|
77
|
-
return copy;
|
|
78
|
-
});
|
|
79
|
-
onChange(next);
|
|
80
|
-
};
|
|
81
|
-
const endScrub = (e) => {
|
|
82
|
-
if (!dragState.current)
|
|
83
|
-
return;
|
|
84
|
-
dragState.current = null;
|
|
85
|
-
document.body.style.cursor = "";
|
|
86
|
-
e.target.releasePointerCapture(e.pointerId);
|
|
87
|
-
};
|
|
88
|
-
const axes = [
|
|
89
|
-
{ key: "x", color: "red", index: 0 },
|
|
90
|
-
{ key: "y", color: "green", index: 1 },
|
|
91
|
-
{ key: "z", color: "blue", index: 2 }
|
|
92
|
-
];
|
|
93
|
-
return (_jsxs("div", { style: { marginBottom: 8 }, children: [_jsx("label", { style: { display: 'block', fontSize: '9px', color: 'rgba(34, 211, 238, 0.6)', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 4 }, children: label }), _jsx("div", { style: { display: 'flex', gap: 4 }, children: axes.map(({ key, color, index }) => (_jsxs("div", { style: { flex: 1, display: 'flex', alignItems: 'center', gap: 4, backgroundColor: 'rgba(0, 0, 0, 0.3)', border: '1px solid rgba(34, 211, 238, 0.2)', borderRadius: 4, padding: '4px 6px', minHeight: 32 }, children: [_jsx("span", { style: { fontSize: '12px', fontWeight: 'bold', color: color === 'red' ? 'rgba(248, 113, 113, 1)' : color === 'green' ? 'rgba(134, 239, 172, 1)' : 'rgba(96, 165, 250, 1)', width: 12, cursor: 'ew-resize', userSelect: 'none' }, onPointerDown: e => startScrub(e, index), onPointerMove: onScrubMove, onPointerUp: endScrub, children: key.toUpperCase() }), _jsx("input", { style: { flex: 1, backgroundColor: 'transparent', fontSize: '12px', color: 'rgba(165, 243, 252, 1)', fontFamily: 'monospace', outline: 'none', width: '100%', minWidth: 0 }, type: "text", value: draft[index], onChange: e => {
|
|
94
|
-
const next = [...draft];
|
|
95
|
-
next[index] = e.target.value;
|
|
96
|
-
setDraft(next);
|
|
97
|
-
}, onBlur: () => commit(index), onKeyDown: e => {
|
|
98
|
-
if (e.key === "Enter") {
|
|
99
|
-
e.target.blur();
|
|
100
|
-
}
|
|
101
|
-
} })] }, key))) })] }));
|
|
102
|
-
}
|