react-three-game 0.0.11 → 0.0.13
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/EditorTree.js +1 -1
- package/dist/tools/prefabeditor/PrefabRoot.js +5 -3
- package/dist/tools/prefabeditor/components/SpotLightComponent.js +22 -8
- package/package.json +1 -1
- package/src/tools/prefabeditor/EditorTree.tsx +1 -1
- package/src/tools/prefabeditor/PrefabRoot.tsx +4 -3
- package/src/tools/prefabeditor/components/SpotLightComponent.tsx +86 -14
|
@@ -136,7 +136,7 @@ export default function EditorTree({ prefabData, setPrefabData, selectedId, setS
|
|
|
136
136
|
const hasChildren = node.children && node.children.length > 0;
|
|
137
137
|
return (_jsxs("div", { className: "select-none", children: [_jsxs("div", { className: `flex items-center py-0.5 px-1 cursor-pointer border-b border-cyan-500/10 ${isSelected ? 'bg-cyan-500/30 hover:bg-cyan-500/40 border-cyan-400/30' : 'hover:bg-cyan-500/10'}`, style: { paddingLeft: `${depth * 8 + 4}px` }, onClick: (e) => { e.stopPropagation(); setSelectedId(node.id); }, onContextMenu: (e) => handleContextMenu(e, node.id), draggable: node.id !== prefabData.root.id, onDragStart: (e) => handleDragStart(e, node.id), onDragOver: (e) => handleDragOver(e, node.id), onDrop: (e) => handleDrop(e, node.id), children: [_jsx("span", { className: `mr-0.5 w-3 text-center text-cyan-400/50 hover:text-cyan-400 cursor-pointer text-[8px] ${hasChildren ? '' : 'invisible'}`, onClick: (e) => hasChildren && toggleCollapse(e, node.id), children: isCollapsed ? '▶' : '▼' }), _jsx("span", { className: "text-[10px] truncate font-mono text-cyan-300", children: node.id })] }), !isCollapsed && node.children && (_jsx("div", { children: node.children.map(child => renderNode(child, depth + 1)) }))] }, node.id));
|
|
138
138
|
};
|
|
139
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "bg-black/70 backdrop-blur-sm text-white border border-cyan-500/30 max-h-[85vh] overflow-y-auto flex flex-col", style: { width: isTreeCollapsed ? 'auto' : '14rem' }, onClick: closeContextMenu, children: [_jsxs("div", { className: "px-1.5 py-1 font-mono text-[10px] bg-cyan-500/10 border-b border-cyan-500/30 sticky top-0 uppercase tracking-wider text-cyan-400/80 cursor-pointer hover:bg-cyan-500/20 flex items-center justify-between", onClick: (e) => { e.stopPropagation(); setIsTreeCollapsed(!isTreeCollapsed); }, children: [_jsx("span", { children: "Prefab Graph" }), _jsx("span", { className: "text-[8px]", children: isTreeCollapsed ? '▶' : '◀' })] }), !isTreeCollapsed && (_jsx("div", { className: "flex-1 py-0.5", children: renderNode(prefabData.root) }))] }), contextMenu && (_jsxs("div", { className: "fixed bg-black/90 backdrop-blur-sm border border-cyan-500/40 z-50 min-w-[100px]", style: { top: contextMenu.y, left: contextMenu.x }, onClick: (e) => e.stopPropagation(), children: [_jsx("button", { className: "w-full text-left px-2 py-1 hover:bg-cyan-500/20 text-[10px] text-cyan-300 font-mono border-b border-cyan-500/20", onClick: () => handleAddChild(contextMenu.nodeId),
|
|
139
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "bg-black/70 backdrop-blur-sm text-white border border-cyan-500/30 max-h-[85vh] overflow-y-auto flex flex-col", style: { width: isTreeCollapsed ? 'auto' : '14rem' }, onClick: closeContextMenu, children: [_jsxs("div", { className: "px-1.5 py-1 font-mono text-[10px] bg-cyan-500/10 border-b border-cyan-500/30 sticky top-0 uppercase tracking-wider text-cyan-400/80 cursor-pointer hover:bg-cyan-500/20 flex items-center justify-between", onClick: (e) => { e.stopPropagation(); setIsTreeCollapsed(!isTreeCollapsed); }, children: [_jsx("span", { children: "Prefab Graph" }), _jsx("span", { className: "text-[8px]", children: isTreeCollapsed ? '▶' : '◀' })] }), !isTreeCollapsed && (_jsx("div", { className: "flex-1 py-0.5", children: renderNode(prefabData.root) }))] }), contextMenu && (_jsxs("div", { className: "fixed bg-black/90 backdrop-blur-sm border border-cyan-500/40 z-50 min-w-[100px]", style: { top: contextMenu.y, left: contextMenu.x }, onClick: (e) => e.stopPropagation(), onPointerLeave: closeContextMenu, children: [_jsx("button", { className: "w-full text-left px-2 py-1 hover:bg-cyan-500/20 text-[10px] text-cyan-300 font-mono border-b border-cyan-500/20", onClick: () => handleAddChild(contextMenu.nodeId), children: "Add Child" }), contextMenu.nodeId !== prefabData.root.id && (_jsxs(_Fragment, { children: [_jsx("button", { className: "w-full text-left px-2 py-1 hover:bg-cyan-500/20 text-[10px] text-cyan-300 font-mono border-b border-cyan-500/20", onClick: () => handleDuplicate(contextMenu.nodeId), children: "Duplicate" }), _jsx("button", { className: "w-full text-left px-2 py-1 hover:bg-red-500/20 text-[10px] text-red-400 font-mono", onClick: () => handleDelete(contextMenu.nodeId), children: "Delete" })] }))] }))] }));
|
|
140
140
|
}
|
|
141
141
|
// --- Helpers ---
|
|
142
142
|
function findNode(root, id) {
|
|
@@ -211,10 +211,12 @@ function renderCoreNode(gameObject, ctx, parentMatrix) {
|
|
|
211
211
|
};
|
|
212
212
|
const allComponentViews = gameObject.components
|
|
213
213
|
? Object.entries(gameObject.components)
|
|
214
|
-
.filter(([key]) => key !== 'geometry' && key !== 'material' && key !== 'model')
|
|
214
|
+
.filter(([key]) => key !== 'geometry' && key !== 'material' && key !== 'model' && key !== 'transform' && key !== 'physics')
|
|
215
215
|
.map(([key, comp]) => {
|
|
216
|
-
|
|
217
|
-
|
|
216
|
+
if (!comp || !comp.type)
|
|
217
|
+
return null;
|
|
218
|
+
const def = getComponent(comp.type);
|
|
219
|
+
if (!def || !def.View)
|
|
218
220
|
return null;
|
|
219
221
|
return _jsx(def.View, Object.assign({ properties: comp.properties }, contextProps), key);
|
|
220
222
|
})
|
|
@@ -1,19 +1,33 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
function SpotLightComponentEditor({ component, onUpdate }) {
|
|
3
|
-
|
|
3
|
+
var _a, _b, _c, _d, _e, _f;
|
|
4
|
+
// Provide default values to prevent NaN
|
|
5
|
+
const props = {
|
|
6
|
+
color: (_a = component.properties.color) !== null && _a !== void 0 ? _a : '#ffffff',
|
|
7
|
+
intensity: (_b = component.properties.intensity) !== null && _b !== void 0 ? _b : 1.0,
|
|
8
|
+
angle: (_c = component.properties.angle) !== null && _c !== void 0 ? _c : Math.PI / 6,
|
|
9
|
+
penumbra: (_d = component.properties.penumbra) !== null && _d !== void 0 ? _d : 0.5,
|
|
10
|
+
distance: (_e = component.properties.distance) !== null && _e !== void 0 ? _e : 100,
|
|
11
|
+
castShadow: (_f = component.properties.castShadow) !== null && _f !== void 0 ? _f : true
|
|
12
|
+
};
|
|
13
|
+
return _jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("div", { children: [_jsx("label", { className: "block text-[9px] text-cyan-400/60 uppercase tracking-wider mb-0.5", children: "Color" }), _jsxs("div", { className: "flex gap-0.5", children: [_jsx("input", { type: "color", className: "h-5 w-5 bg-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", className: "flex-1 bg-black/40 border border-cyan-500/30 px-1 py-0.5 text-[10px] text-cyan-300 font-mono focus:outline-none focus:border-cyan-400/50", value: props.color, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { 'color': e.target.value })) })] })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-[9px] text-cyan-400/60 uppercase tracking-wider mb-0.5", children: "Intensity" }), _jsx("input", { type: "number", step: "0.1", className: "w-full bg-black/40 border border-cyan-500/30 px-1 py-0.5 text-[10px] text-cyan-300 font-mono focus:outline-none focus:border-cyan-400/50", value: props.intensity, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { 'intensity': parseFloat(e.target.value) })) })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-[9px] text-cyan-400/60 uppercase tracking-wider mb-0.5", children: "Angle" }), _jsx("input", { type: "number", step: "0.1", min: "0", max: Math.PI, className: "w-full bg-black/40 border border-cyan-500/30 px-1 py-0.5 text-[10px] text-cyan-300 font-mono focus:outline-none focus:border-cyan-400/50", value: props.angle, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { 'angle': parseFloat(e.target.value) })) })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-[9px] text-cyan-400/60 uppercase tracking-wider mb-0.5", children: "Penumbra" }), _jsx("input", { type: "number", step: "0.1", min: "0", max: "1", className: "w-full bg-black/40 border border-cyan-500/30 px-1 py-0.5 text-[10px] text-cyan-300 font-mono focus:outline-none focus:border-cyan-400/50", value: props.penumbra, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { 'penumbra': parseFloat(e.target.value) })) })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-[9px] text-cyan-400/60 uppercase tracking-wider mb-0.5", children: "Distance" }), _jsx("input", { type: "number", step: "1", min: "0", className: "w-full bg-black/40 border border-cyan-500/30 px-1 py-0.5 text-[10px] text-cyan-300 font-mono focus:outline-none focus:border-cyan-400/50", value: props.distance, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { 'distance': parseFloat(e.target.value) })) })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-[9px] text-cyan-400/60 uppercase tracking-wider mb-0.5", children: "Cast Shadow" }), _jsx("input", { type: "checkbox", className: "h-4 w-4 bg-black/40 border border-cyan-500/30 cursor-pointer", checked: props.castShadow, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { 'castShadow': e.target.checked })) })] })] });
|
|
4
14
|
}
|
|
5
15
|
// The view component for SpotLight
|
|
6
16
|
function SpotLightView({ properties }) {
|
|
7
|
-
|
|
8
|
-
|
|
17
|
+
var _a, _b, _c, _d, _e, _f;
|
|
18
|
+
// Provide defaults in case properties are missing
|
|
19
|
+
const color = (_a = properties.color) !== null && _a !== void 0 ? _a : '#ffffff';
|
|
20
|
+
const intensity = (_b = properties.intensity) !== null && _b !== void 0 ? _b : 1.0;
|
|
21
|
+
const angle = (_c = properties.angle) !== null && _c !== void 0 ? _c : Math.PI / 6;
|
|
22
|
+
const penumbra = (_d = properties.penumbra) !== null && _d !== void 0 ? _d : 0.5;
|
|
23
|
+
const distance = (_e = properties.distance) !== null && _e !== void 0 ? _e : 100;
|
|
24
|
+
const castShadow = (_f = properties.castShadow) !== null && _f !== void 0 ? _f : true;
|
|
25
|
+
return (_jsx(_Fragment, { children: _jsx("spotLight", { color: color, intensity: intensity, angle: angle, penumbra: penumbra, distance: distance, castShadow: castShadow, "target-position": [0, 0, 0], position: [0, 0, 0] }) }));
|
|
9
26
|
}
|
|
10
27
|
const SpotLightComponent = {
|
|
11
28
|
name: 'SpotLight',
|
|
12
29
|
Editor: SpotLightComponentEditor,
|
|
13
30
|
View: SpotLightView,
|
|
14
|
-
defaultProperties: {
|
|
15
|
-
color: '#ffffff',
|
|
16
|
-
intensity: 1.0
|
|
17
|
-
}
|
|
31
|
+
defaultProperties: {}
|
|
18
32
|
};
|
|
19
33
|
export default SpotLightComponent;
|
package/package.json
CHANGED
|
@@ -206,11 +206,11 @@ export default function EditorTree({ prefabData, setPrefabData, selectedId, setS
|
|
|
206
206
|
className="fixed bg-black/90 backdrop-blur-sm border border-cyan-500/40 z-50 min-w-[100px]"
|
|
207
207
|
style={{ top: contextMenu.y, left: contextMenu.x }}
|
|
208
208
|
onClick={(e) => e.stopPropagation()}
|
|
209
|
+
onPointerLeave={closeContextMenu}
|
|
209
210
|
>
|
|
210
211
|
<button
|
|
211
212
|
className="w-full text-left px-2 py-1 hover:bg-cyan-500/20 text-[10px] text-cyan-300 font-mono border-b border-cyan-500/20"
|
|
212
213
|
onClick={() => handleAddChild(contextMenu.nodeId)}
|
|
213
|
-
onPointerLeave={closeContextMenu}
|
|
214
214
|
>
|
|
215
215
|
Add Child
|
|
216
216
|
</button>
|
|
@@ -340,10 +340,11 @@ function renderCoreNode(gameObject: GameObjectType, ctx: any, parentMatrix: Matr
|
|
|
340
340
|
};
|
|
341
341
|
const allComponentViews = gameObject.components
|
|
342
342
|
? Object.entries(gameObject.components)
|
|
343
|
-
.filter(([key]) => key !== 'geometry' && key !== 'material' && key !== 'model')
|
|
343
|
+
.filter(([key]) => key !== 'geometry' && key !== 'material' && key !== 'model' && key !== 'transform' && key !== 'physics')
|
|
344
344
|
.map(([key, comp]) => {
|
|
345
|
-
|
|
346
|
-
|
|
345
|
+
if (!comp || !comp.type) return null;
|
|
346
|
+
const def = getComponent(comp.type);
|
|
347
|
+
if (!def || !def.View) return null;
|
|
347
348
|
return <def.View key={key} properties={comp.properties} {...contextProps} />;
|
|
348
349
|
})
|
|
349
350
|
: null;
|
|
@@ -1,22 +1,33 @@
|
|
|
1
1
|
|
|
2
2
|
import { Component } from "./ComponentRegistry";
|
|
3
|
+
import { useRef } from "react";
|
|
3
4
|
|
|
4
5
|
function SpotLightComponentEditor({ component, onUpdate }: { component: any; onUpdate: (newComp: any) => void }) {
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
// Provide default values to prevent NaN
|
|
7
|
+
const props = {
|
|
8
|
+
color: component.properties.color ?? '#ffffff',
|
|
9
|
+
intensity: component.properties.intensity ?? 1.0,
|
|
10
|
+
angle: component.properties.angle ?? Math.PI / 6,
|
|
11
|
+
penumbra: component.properties.penumbra ?? 0.5,
|
|
12
|
+
distance: component.properties.distance ?? 100,
|
|
13
|
+
castShadow: component.properties.castShadow ?? true
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
return <div className="flex flex-col gap-2">
|
|
17
|
+
<div>
|
|
7
18
|
<label className="block text-[9px] text-cyan-400/60 uppercase tracking-wider mb-0.5">Color</label>
|
|
8
19
|
<div className="flex gap-0.5">
|
|
9
20
|
<input
|
|
10
21
|
type="color"
|
|
11
22
|
className="h-5 w-5 bg-transparent border-none cursor-pointer"
|
|
12
|
-
value={
|
|
13
|
-
onChange={e => onUpdate({ 'color': e.target.value })}
|
|
23
|
+
value={props.color}
|
|
24
|
+
onChange={e => onUpdate({ ...component.properties, 'color': e.target.value })}
|
|
14
25
|
/>
|
|
15
26
|
<input
|
|
16
27
|
type="text"
|
|
17
28
|
className="flex-1 bg-black/40 border border-cyan-500/30 px-1 py-0.5 text-[10px] text-cyan-300 font-mono focus:outline-none focus:border-cyan-400/50"
|
|
18
|
-
value={
|
|
19
|
-
onChange={e => onUpdate({ 'color': e.target.value })}
|
|
29
|
+
value={props.color}
|
|
30
|
+
onChange={e => onUpdate({ ...component.properties, 'color': e.target.value })}
|
|
20
31
|
/>
|
|
21
32
|
</div>
|
|
22
33
|
</div>
|
|
@@ -26,8 +37,52 @@ function SpotLightComponentEditor({ component, onUpdate }: { component: any; onU
|
|
|
26
37
|
type="number"
|
|
27
38
|
step="0.1"
|
|
28
39
|
className="w-full bg-black/40 border border-cyan-500/30 px-1 py-0.5 text-[10px] text-cyan-300 font-mono focus:outline-none focus:border-cyan-400/50"
|
|
29
|
-
value={
|
|
30
|
-
onChange={e => onUpdate({ 'intensity': parseFloat(e.target.value) })}
|
|
40
|
+
value={props.intensity}
|
|
41
|
+
onChange={e => onUpdate({ ...component.properties, 'intensity': parseFloat(e.target.value) })}
|
|
42
|
+
/>
|
|
43
|
+
</div>
|
|
44
|
+
<div>
|
|
45
|
+
<label className="block text-[9px] text-cyan-400/60 uppercase tracking-wider mb-0.5">Angle</label>
|
|
46
|
+
<input
|
|
47
|
+
type="number"
|
|
48
|
+
step="0.1"
|
|
49
|
+
min="0"
|
|
50
|
+
max={Math.PI}
|
|
51
|
+
className="w-full bg-black/40 border border-cyan-500/30 px-1 py-0.5 text-[10px] text-cyan-300 font-mono focus:outline-none focus:border-cyan-400/50"
|
|
52
|
+
value={props.angle}
|
|
53
|
+
onChange={e => onUpdate({ ...component.properties, 'angle': parseFloat(e.target.value) })}
|
|
54
|
+
/>
|
|
55
|
+
</div>
|
|
56
|
+
<div>
|
|
57
|
+
<label className="block text-[9px] text-cyan-400/60 uppercase tracking-wider mb-0.5">Penumbra</label>
|
|
58
|
+
<input
|
|
59
|
+
type="number"
|
|
60
|
+
step="0.1"
|
|
61
|
+
min="0"
|
|
62
|
+
max="1"
|
|
63
|
+
className="w-full bg-black/40 border border-cyan-500/30 px-1 py-0.5 text-[10px] text-cyan-300 font-mono focus:outline-none focus:border-cyan-400/50"
|
|
64
|
+
value={props.penumbra}
|
|
65
|
+
onChange={e => onUpdate({ ...component.properties, 'penumbra': parseFloat(e.target.value) })}
|
|
66
|
+
/>
|
|
67
|
+
</div>
|
|
68
|
+
<div>
|
|
69
|
+
<label className="block text-[9px] text-cyan-400/60 uppercase tracking-wider mb-0.5">Distance</label>
|
|
70
|
+
<input
|
|
71
|
+
type="number"
|
|
72
|
+
step="1"
|
|
73
|
+
min="0"
|
|
74
|
+
className="w-full bg-black/40 border border-cyan-500/30 px-1 py-0.5 text-[10px] text-cyan-300 font-mono focus:outline-none focus:border-cyan-400/50"
|
|
75
|
+
value={props.distance}
|
|
76
|
+
onChange={e => onUpdate({ ...component.properties, 'distance': parseFloat(e.target.value) })}
|
|
77
|
+
/>
|
|
78
|
+
</div>
|
|
79
|
+
<div>
|
|
80
|
+
<label className="block text-[9px] text-cyan-400/60 uppercase tracking-wider mb-0.5">Cast Shadow</label>
|
|
81
|
+
<input
|
|
82
|
+
type="checkbox"
|
|
83
|
+
className="h-4 w-4 bg-black/40 border border-cyan-500/30 cursor-pointer"
|
|
84
|
+
checked={props.castShadow}
|
|
85
|
+
onChange={e => onUpdate({ ...component.properties, 'castShadow': e.target.checked })}
|
|
31
86
|
/>
|
|
32
87
|
</div>
|
|
33
88
|
</div>;
|
|
@@ -36,18 +91,35 @@ function SpotLightComponentEditor({ component, onUpdate }: { component: any; onU
|
|
|
36
91
|
|
|
37
92
|
// The view component for SpotLight
|
|
38
93
|
function SpotLightView({ properties }: { properties: any }) {
|
|
39
|
-
//
|
|
40
|
-
|
|
94
|
+
// Provide defaults in case properties are missing
|
|
95
|
+
const color = properties.color ?? '#ffffff';
|
|
96
|
+
const intensity = properties.intensity ?? 1.0;
|
|
97
|
+
const angle = properties.angle ?? Math.PI / 6;
|
|
98
|
+
const penumbra = properties.penumbra ?? 0.5;
|
|
99
|
+
const distance = properties.distance ?? 100;
|
|
100
|
+
const castShadow = properties.castShadow ?? true;
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<>
|
|
104
|
+
<spotLight
|
|
105
|
+
color={color}
|
|
106
|
+
intensity={intensity}
|
|
107
|
+
angle={angle}
|
|
108
|
+
penumbra={penumbra}
|
|
109
|
+
distance={distance}
|
|
110
|
+
castShadow={castShadow}
|
|
111
|
+
target-position={[0, 0, 0]}
|
|
112
|
+
position={[0, 0, 0]}
|
|
113
|
+
/>
|
|
114
|
+
</>
|
|
115
|
+
);
|
|
41
116
|
}
|
|
42
117
|
|
|
43
118
|
const SpotLightComponent: Component = {
|
|
44
119
|
name: 'SpotLight',
|
|
45
120
|
Editor: SpotLightComponentEditor,
|
|
46
121
|
View: SpotLightView,
|
|
47
|
-
defaultProperties: {
|
|
48
|
-
color: '#ffffff',
|
|
49
|
-
intensity: 1.0
|
|
50
|
-
}
|
|
122
|
+
defaultProperties: {}
|
|
51
123
|
};
|
|
52
124
|
|
|
53
125
|
export default SpotLightComponent;
|