react-three-game 0.0.55 → 0.0.57
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/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/shared/ContactShadow.d.ts +8 -0
- package/dist/shared/ContactShadow.js +32 -0
- package/dist/shared/GameCanvas.js +1 -3
- package/dist/tools/assetviewer/page.js +36 -15
- package/dist/tools/dragdrop/DragDropLoader.js +17 -40
- package/dist/tools/dragdrop/modelLoader.d.ts +5 -0
- package/dist/tools/dragdrop/modelLoader.js +39 -0
- package/dist/tools/prefabeditor/Dropdown.d.ts +15 -0
- package/dist/tools/prefabeditor/Dropdown.js +82 -0
- package/dist/tools/prefabeditor/EditorContext.d.ts +5 -0
- package/dist/tools/prefabeditor/EditorTree.js +139 -70
- package/dist/tools/prefabeditor/EditorUI.js +5 -10
- package/dist/tools/prefabeditor/PrefabEditor.d.ts +1 -0
- package/dist/tools/prefabeditor/PrefabEditor.js +70 -3
- package/dist/tools/prefabeditor/PrefabRoot.d.ts +3 -0
- package/dist/tools/prefabeditor/PrefabRoot.js +136 -35
- package/dist/tools/prefabeditor/components/AmbientLightComponent.js +3 -7
- package/dist/tools/prefabeditor/components/CameraComponent.d.ts +3 -0
- package/dist/tools/prefabeditor/components/CameraComponent.js +25 -0
- package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +2 -2
- package/dist/tools/prefabeditor/components/EnvironmentComponent.d.ts +3 -0
- package/dist/tools/prefabeditor/components/EnvironmentComponent.js +15 -0
- package/dist/tools/prefabeditor/components/GeometryComponent.js +46 -46
- package/dist/tools/prefabeditor/components/Input.d.ts +51 -1
- package/dist/tools/prefabeditor/components/Input.js +100 -47
- package/dist/tools/prefabeditor/components/MaterialComponent.d.ts +8 -2
- package/dist/tools/prefabeditor/components/MaterialComponent.js +129 -14
- package/dist/tools/prefabeditor/components/ModelComponent.js +44 -3
- package/dist/tools/prefabeditor/components/PhysicsComponent.js +16 -81
- package/dist/tools/prefabeditor/components/SpotLightComponent.js +6 -11
- package/dist/tools/prefabeditor/components/TextComponent.js +7 -53
- package/dist/tools/prefabeditor/components/TransformComponent.js +31 -19
- package/dist/tools/prefabeditor/components/index.js +5 -1
- package/dist/tools/prefabeditor/styles.d.ts +17 -4
- package/dist/tools/prefabeditor/styles.js +69 -32
- package/dist/tools/prefabeditor/utils.d.ts +8 -3
- package/dist/tools/prefabeditor/utils.js +92 -6
- package/package.json +1 -1
- package/react-three-game-skill/react-three-game/rules/LIGHTING.md +6 -0
- package/src/index.ts +7 -0
- package/src/shared/ContactShadow.tsx +74 -0
- package/src/shared/GameCanvas.tsx +0 -3
- package/src/tools/assetviewer/page.tsx +78 -46
- package/src/tools/dragdrop/DragDropLoader.tsx +7 -39
- package/src/tools/dragdrop/modelLoader.ts +36 -0
- package/src/tools/prefabeditor/Dropdown.tsx +112 -0
- package/src/tools/prefabeditor/EditorContext.tsx +5 -0
- package/src/tools/prefabeditor/EditorTree.tsx +237 -115
- package/src/tools/prefabeditor/EditorUI.tsx +6 -11
- package/src/tools/prefabeditor/PrefabEditor.tsx +77 -5
- package/src/tools/prefabeditor/PrefabRoot.tsx +228 -59
- package/src/tools/prefabeditor/components/AmbientLightComponent.tsx +5 -11
- package/src/tools/prefabeditor/components/CameraComponent.tsx +80 -0
- package/src/tools/prefabeditor/components/DirectionalLightComponent.tsx +2 -2
- package/src/tools/prefabeditor/components/EnvironmentComponent.tsx +47 -0
- package/src/tools/prefabeditor/components/GeometryComponent.tsx +69 -63
- package/src/tools/prefabeditor/components/Input.tsx +247 -53
- package/src/tools/prefabeditor/components/MaterialComponent.tsx +191 -20
- package/src/tools/prefabeditor/components/ModelComponent.tsx +52 -5
- package/src/tools/prefabeditor/components/PhysicsComponent.tsx +44 -85
- package/src/tools/prefabeditor/components/SpotLightComponent.tsx +14 -16
- package/src/tools/prefabeditor/components/TextComponent.tsx +58 -57
- package/src/tools/prefabeditor/components/TransformComponent.tsx +78 -20
- package/src/tools/prefabeditor/components/index.ts +5 -1
- package/src/tools/prefabeditor/styles.ts +71 -32
- package/src/tools/prefabeditor/utils.ts +96 -5
|
@@ -9,91 +9,26 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
9
9
|
}
|
|
10
10
|
return t;
|
|
11
11
|
};
|
|
12
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
12
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
13
|
import { RigidBody, useRapier } from "@react-three/rapier";
|
|
14
14
|
import { useRef, useEffect, useCallback } from 'react';
|
|
15
|
-
import {
|
|
15
|
+
import { BooleanField, FieldGroup, NumberField, SelectField } from "./Input";
|
|
16
16
|
import { gameEvents, getEntityIdFromRigidBody } from "../GameEvents";
|
|
17
|
-
const physicsFields = [
|
|
18
|
-
{
|
|
19
|
-
name: 'type',
|
|
20
|
-
type: 'select',
|
|
21
|
-
label: 'Type',
|
|
22
|
-
options: [
|
|
23
|
-
{ value: 'dynamic', label: 'Dynamic' },
|
|
24
|
-
{ value: 'fixed', label: 'Fixed' },
|
|
25
|
-
{ value: 'kinematicPosition', label: 'Kinematic Position' },
|
|
26
|
-
{ value: 'kinematicVelocity', label: 'Kinematic Velocity' },
|
|
27
|
-
],
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
name: 'colliders',
|
|
31
|
-
type: 'select',
|
|
32
|
-
label: 'Collider',
|
|
33
|
-
options: [
|
|
34
|
-
{ value: 'hull', label: 'Hull (convex)' },
|
|
35
|
-
{ value: 'trimesh', label: 'Trimesh (exact)' },
|
|
36
|
-
{ value: 'cuboid', label: 'Cuboid (box)' },
|
|
37
|
-
{ value: 'ball', label: 'Ball (sphere)' },
|
|
38
|
-
],
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
name: 'mass',
|
|
42
|
-
type: 'number',
|
|
43
|
-
label: 'Mass',
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
name: 'restitution',
|
|
47
|
-
type: 'number',
|
|
48
|
-
label: 'Restitution (Bounciness)',
|
|
49
|
-
min: 0,
|
|
50
|
-
max: 1,
|
|
51
|
-
step: 0.1,
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
name: 'friction',
|
|
55
|
-
type: 'number',
|
|
56
|
-
label: 'Friction',
|
|
57
|
-
min: 0,
|
|
58
|
-
step: 0.1,
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
name: 'linearDamping',
|
|
62
|
-
type: 'number',
|
|
63
|
-
label: 'Linear Damping',
|
|
64
|
-
min: 0,
|
|
65
|
-
step: 0.1,
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
name: 'angularDamping',
|
|
69
|
-
type: 'number',
|
|
70
|
-
label: 'Angular Damping',
|
|
71
|
-
min: 0,
|
|
72
|
-
step: 0.1,
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
name: 'gravityScale',
|
|
76
|
-
type: 'number',
|
|
77
|
-
label: 'Gravity Scale',
|
|
78
|
-
step: 0.1,
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
name: 'sensor',
|
|
82
|
-
type: 'boolean',
|
|
83
|
-
label: 'Sensor (Trigger Only)',
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
name: 'activeCollisionTypes',
|
|
87
|
-
type: 'select',
|
|
88
|
-
label: 'Collision Detection',
|
|
89
|
-
options: [
|
|
90
|
-
{ value: '', label: 'Default (Dynamic only)' },
|
|
91
|
-
{ value: 'all', label: 'All (includes kinematic & fixed)' },
|
|
92
|
-
],
|
|
93
|
-
},
|
|
94
|
-
];
|
|
95
17
|
function PhysicsComponentEditor({ component, onUpdate }) {
|
|
96
|
-
return (_jsx(
|
|
18
|
+
return (_jsxs(FieldGroup, { children: [_jsx(SelectField, { name: "type", label: "Type", values: component.properties, onChange: onUpdate, options: [
|
|
19
|
+
{ value: 'dynamic', label: 'Dynamic' },
|
|
20
|
+
{ value: 'fixed', label: 'Fixed' },
|
|
21
|
+
{ value: 'kinematicPosition', label: 'Kinematic Position' },
|
|
22
|
+
{ value: 'kinematicVelocity', label: 'Kinematic Velocity' },
|
|
23
|
+
] }), _jsx(SelectField, { name: "colliders", label: "Collider", values: component.properties, onChange: onUpdate, options: [
|
|
24
|
+
{ value: 'hull', label: 'Hull (convex)' },
|
|
25
|
+
{ value: 'trimesh', label: 'Trimesh (exact)' },
|
|
26
|
+
{ value: 'cuboid', label: 'Cuboid (box)' },
|
|
27
|
+
{ value: 'ball', label: 'Ball (sphere)' },
|
|
28
|
+
] }), _jsx(NumberField, { name: "mass", label: "Mass", values: component.properties, onChange: onUpdate, fallback: 1, step: 0.1, min: 0 }), _jsx(NumberField, { name: "restitution", label: "Restitution (Bounciness)", values: component.properties, onChange: onUpdate, fallback: 0, min: 0, max: 1, step: 0.1 }), _jsx(NumberField, { name: "friction", label: "Friction", values: component.properties, onChange: onUpdate, fallback: 0.5, min: 0, step: 0.1 }), _jsx(NumberField, { name: "linearDamping", label: "Linear Damping", values: component.properties, onChange: onUpdate, fallback: 0, min: 0, step: 0.1 }), _jsx(NumberField, { name: "angularDamping", label: "Angular Damping", values: component.properties, onChange: onUpdate, fallback: 0, min: 0, step: 0.1 }), _jsx(NumberField, { name: "gravityScale", label: "Gravity Scale", values: component.properties, onChange: onUpdate, fallback: 1, step: 0.1 }), _jsx(BooleanField, { name: "sensor", label: "Sensor (Trigger Only)", values: component.properties, onChange: onUpdate, fallback: false }), _jsx(SelectField, { name: "activeCollisionTypes", label: "Collision Detection", values: component.properties, onChange: onUpdate, options: [
|
|
29
|
+
{ value: '', label: 'Default (Dynamic only)' },
|
|
30
|
+
{ value: 'all', label: 'All (includes kinematic & fixed)' },
|
|
31
|
+
] })] }));
|
|
97
32
|
}
|
|
98
33
|
function PhysicsComponentView({ properties, children, position, rotation, scale, editMode, nodeId, registerRigidBodyRef }) {
|
|
99
34
|
const { type, colliders, sensor, activeCollisionTypes } = properties, otherProps = __rest(properties, ["type", "colliders", "sensor", "activeCollisionTypes"]);
|
|
@@ -1,18 +1,12 @@
|
|
|
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 {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
{ name: 'intensity', type: 'number', label: 'Intensity', step: 0.1, min: 0 },
|
|
7
|
-
{ name: 'angle', type: 'number', label: 'Angle', step: 0.1, min: 0, max: Math.PI },
|
|
8
|
-
{ name: 'penumbra', type: 'number', label: 'Penumbra', step: 0.1, min: 0, max: 1 },
|
|
9
|
-
{ name: 'distance', type: 'number', label: 'Distance', step: 1, min: 0 },
|
|
10
|
-
{ name: 'castShadow', type: 'boolean', label: 'Cast Shadow' },
|
|
11
|
-
];
|
|
3
|
+
import { BooleanField, ColorField, FieldGroup, NumberField } from "./Input";
|
|
4
|
+
import { useHelper } from "@react-three/drei";
|
|
5
|
+
import { SpotLightHelper } from "three";
|
|
12
6
|
function SpotLightComponentEditor({ component, onUpdate }) {
|
|
13
|
-
return (_jsx(
|
|
7
|
+
return (_jsxs(FieldGroup, { children: [_jsx(ColorField, { name: "color", label: "Color", values: component.properties, onChange: onUpdate }), _jsx(NumberField, { name: "intensity", label: "Intensity", values: component.properties, onChange: onUpdate, min: 0, step: 0.1, fallback: 1 }), _jsx(NumberField, { name: "angle", label: "Angle", values: component.properties, onChange: onUpdate, min: 0, max: Math.PI, step: 0.05, fallback: Math.PI / 6 }), _jsx(NumberField, { name: "penumbra", label: "Penumbra", values: component.properties, onChange: onUpdate, min: 0, max: 1, step: 0.05, fallback: 0.5 }), _jsx(NumberField, { name: "distance", label: "Distance", values: component.properties, onChange: onUpdate, min: 0, step: 1, fallback: 100 }), _jsx(BooleanField, { name: "castShadow", label: "Cast Shadow", values: component.properties, onChange: onUpdate, fallback: true })] }));
|
|
14
8
|
}
|
|
15
|
-
function SpotLightView({ properties, editMode }) {
|
|
9
|
+
function SpotLightView({ properties, editMode, isSelected }) {
|
|
16
10
|
var _a, _b, _c, _d, _e, _f;
|
|
17
11
|
const color = (_a = properties.color) !== null && _a !== void 0 ? _a : '#ffffff';
|
|
18
12
|
const intensity = (_b = properties.intensity) !== null && _b !== void 0 ? _b : 1.0;
|
|
@@ -22,6 +16,7 @@ function SpotLightView({ properties, editMode }) {
|
|
|
22
16
|
const castShadow = (_f = properties.castShadow) !== null && _f !== void 0 ? _f : true;
|
|
23
17
|
const spotLightRef = useRef(null);
|
|
24
18
|
const targetRef = useRef(null);
|
|
19
|
+
useHelper(editMode && isSelected ? spotLightRef : null, SpotLightHelper, color);
|
|
25
20
|
useEffect(() => {
|
|
26
21
|
if (spotLightRef.current && targetRef.current) {
|
|
27
22
|
spotLightRef.current.target = targetRef.current;
|
|
@@ -1,61 +1,15 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ColorField, FieldGroup, NumberField, SelectField, StringField } from "./Input";
|
|
3
3
|
import { Text } from 'three-text/three/react';
|
|
4
4
|
import { useRef, useState, useCallback } from 'react';
|
|
5
5
|
// Initialize HarfBuzz path for font shaping
|
|
6
6
|
Text.setHarfBuzzPath('/fonts/hb.wasm');
|
|
7
7
|
function TextComponentEditor({ component, onUpdate, }) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
placeholder: 'Enter text...',
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
name: 'color',
|
|
17
|
-
type: 'color',
|
|
18
|
-
label: 'Color',
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
name: 'font',
|
|
22
|
-
type: 'string',
|
|
23
|
-
label: 'Font',
|
|
24
|
-
placeholder: '/fonts/NotoSans-Regular.ttf',
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
name: 'size',
|
|
28
|
-
type: 'number',
|
|
29
|
-
label: 'Size',
|
|
30
|
-
min: 0.01,
|
|
31
|
-
step: 0.1,
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
name: 'depth',
|
|
35
|
-
type: 'number',
|
|
36
|
-
label: 'Depth',
|
|
37
|
-
min: 0,
|
|
38
|
-
step: 0.1,
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
name: 'width',
|
|
42
|
-
type: 'number',
|
|
43
|
-
label: 'Width',
|
|
44
|
-
min: 0,
|
|
45
|
-
step: 0.5,
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
name: 'align',
|
|
49
|
-
type: 'select',
|
|
50
|
-
label: 'Align',
|
|
51
|
-
options: [
|
|
52
|
-
{ value: 'left', label: 'Left' },
|
|
53
|
-
{ value: 'center', label: 'Center' },
|
|
54
|
-
{ value: 'right', label: 'Right' },
|
|
55
|
-
],
|
|
56
|
-
},
|
|
57
|
-
];
|
|
58
|
-
return (_jsx(FieldRenderer, { fields: fields, values: component.properties, onChange: onUpdate }));
|
|
8
|
+
return (_jsxs(FieldGroup, { children: [_jsx(StringField, { name: "text", label: "Text", values: component.properties, onChange: onUpdate, placeholder: "Enter text..." }), _jsx(ColorField, { name: "color", label: "Color", values: component.properties, onChange: onUpdate }), _jsx(StringField, { name: "font", label: "Font", values: component.properties, onChange: onUpdate, placeholder: "/fonts/NotoSans-Regular.ttf" }), _jsx(NumberField, { name: "size", label: "Size", values: component.properties, onChange: onUpdate, min: 0.01, step: 0.1 }), _jsx(NumberField, { name: "depth", label: "Depth", values: component.properties, onChange: onUpdate, min: 0, step: 0.1 }), _jsx(NumberField, { name: "width", label: "Width", values: component.properties, onChange: onUpdate, min: 0, step: 0.5 }), _jsx(SelectField, { name: "align", label: "Align", values: component.properties, onChange: onUpdate, options: [
|
|
9
|
+
{ value: 'left', label: 'Left' },
|
|
10
|
+
{ value: 'center', label: 'Center' },
|
|
11
|
+
{ value: 'right', label: 'Right' },
|
|
12
|
+
] })] }));
|
|
59
13
|
}
|
|
60
14
|
function TextComponentView({ properties }) {
|
|
61
15
|
const { text = '', font, size, depth, width, align, color } = properties;
|
|
@@ -1,42 +1,54 @@
|
|
|
1
1
|
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { Label, Vector3Field, Vector3Input } from "./Input";
|
|
3
3
|
import { useEditorContext } from "../EditorContext";
|
|
4
|
+
import { colors } from "../styles";
|
|
4
5
|
const buttonStyle = {
|
|
5
|
-
padding: '
|
|
6
|
-
background:
|
|
7
|
-
color:
|
|
8
|
-
border:
|
|
9
|
-
borderRadius:
|
|
6
|
+
padding: '4px 8px',
|
|
7
|
+
background: colors.bgSurface,
|
|
8
|
+
color: colors.text,
|
|
9
|
+
border: `1px solid ${colors.border}`,
|
|
10
|
+
borderRadius: 3,
|
|
10
11
|
cursor: 'pointer',
|
|
11
12
|
font: 'inherit',
|
|
13
|
+
fontSize: 11,
|
|
12
14
|
flex: 1,
|
|
13
15
|
};
|
|
14
16
|
function TransformModeSelector({ transformMode, setTransformMode, snapResolution, setSnapResolution }) {
|
|
15
17
|
return (_jsxs("div", { style: { marginBottom: 8 }, children: [_jsxs(Label, { children: ["Transform Mode ", snapResolution > 0 && `(Snap: ${snapResolution})`] }), _jsx("div", { style: { display: 'flex', gap: 6 }, children: ["translate", "rotate", "scale"].map(mode => {
|
|
16
18
|
const isActive = transformMode === mode;
|
|
17
|
-
return (_jsx("button", { onClick: () => setTransformMode(mode), style: Object.assign(Object.assign({}, buttonStyle), { background: isActive ?
|
|
19
|
+
return (_jsx("button", { onClick: () => setTransformMode(mode), style: Object.assign(Object.assign({}, buttonStyle), { background: isActive ? colors.accentBg : colors.bgSurface, borderColor: isActive ? colors.accentBorder : colors.border, color: isActive ? colors.accent : colors.text }), onPointerEnter: (e) => {
|
|
18
20
|
if (!isActive)
|
|
19
|
-
e.currentTarget.style.background =
|
|
21
|
+
e.currentTarget.style.background = colors.bgHover;
|
|
20
22
|
}, onPointerLeave: (e) => {
|
|
21
23
|
if (!isActive)
|
|
22
|
-
e.currentTarget.style.background =
|
|
24
|
+
e.currentTarget.style.background = colors.bgSurface;
|
|
23
25
|
}, children: mode }, mode));
|
|
24
|
-
}) }), _jsx("div", { style: { marginTop: 6 }, children: _jsxs("button", { onClick: () => setSnapResolution(snapResolution > 0 ? 0 : 0.1), style: Object.assign(Object.assign({}, buttonStyle), { background: snapResolution > 0 ?
|
|
26
|
+
}) }), _jsx("div", { style: { marginTop: 6 }, children: _jsxs("button", { onClick: () => setSnapResolution(snapResolution > 0 ? 0 : 0.1), style: Object.assign(Object.assign({}, buttonStyle), { background: snapResolution > 0 ? colors.accentBg : colors.bgSurface, borderColor: snapResolution > 0 ? colors.accentBorder : colors.border, color: snapResolution > 0 ? colors.accent : colors.text, width: '100%' }), onPointerEnter: (e) => {
|
|
25
27
|
if (snapResolution === 0)
|
|
26
|
-
e.currentTarget.style.background =
|
|
28
|
+
e.currentTarget.style.background = colors.bgHover;
|
|
27
29
|
}, onPointerLeave: (e) => {
|
|
28
30
|
if (snapResolution === 0)
|
|
29
|
-
e.currentTarget.style.background =
|
|
31
|
+
e.currentTarget.style.background = colors.bgSurface;
|
|
30
32
|
}, children: ["Snap: ", snapResolution > 0 ? `ON (${snapResolution})` : 'OFF'] }) })] }));
|
|
31
33
|
}
|
|
34
|
+
const snapLockBtnStyle = {
|
|
35
|
+
background: 'none',
|
|
36
|
+
border: 'none',
|
|
37
|
+
cursor: 'pointer',
|
|
38
|
+
padding: '0 2px',
|
|
39
|
+
fontSize: 12,
|
|
40
|
+
lineHeight: 1,
|
|
41
|
+
color: colors.textMuted,
|
|
42
|
+
};
|
|
43
|
+
function SnapLockButton({ locked, onToggle, title }) {
|
|
44
|
+
return (_jsx("button", { style: snapLockBtnStyle, onClick: onToggle, title: title, children: locked ? '🔒' : '🔓' }));
|
|
45
|
+
}
|
|
32
46
|
function TransformComponentEditor({ component, onUpdate }) {
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
];
|
|
39
|
-
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column' }, children: [_jsx(TransformModeSelector, { transformMode: transformMode, setTransformMode: setTransformMode, snapResolution: snapResolution, setSnapResolution: setSnapResolution }), _jsx(FieldRenderer, { fields: fields, values: component.properties, onChange: onUpdate })] }));
|
|
47
|
+
var _a, _b;
|
|
48
|
+
const { transformMode, setTransformMode, snapResolution, setSnapResolution, positionSnap, setPositionSnap, rotationSnap, setRotationSnap } = useEditorContext();
|
|
49
|
+
const positionSnapped = positionSnap > 0;
|
|
50
|
+
const rotationSnapped = rotationSnap > 0;
|
|
51
|
+
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column' }, children: [_jsx(TransformModeSelector, { transformMode: transformMode, setTransformMode: setTransformMode, snapResolution: snapResolution, setSnapResolution: setSnapResolution }), _jsx(Vector3Input, { label: "Position", value: (_a = component.properties.position) !== null && _a !== void 0 ? _a : [0, 0, 0], onChange: v => onUpdate({ position: v }), snap: positionSnap, labelExtra: _jsx(SnapLockButton, { locked: positionSnapped, onToggle: () => setPositionSnap(positionSnapped ? 0 : 0.5), title: positionSnapped ? `Snap ON (0.5) — click to disable` : `Snap OFF — click to enable (0.5)` }) }), _jsx(Vector3Input, { label: "Rotation", value: (_b = component.properties.rotation) !== null && _b !== void 0 ? _b : [0, 0, 0], onChange: v => onUpdate({ rotation: v }), snap: rotationSnap, labelExtra: _jsx(SnapLockButton, { locked: rotationSnapped, onToggle: () => setRotationSnap(rotationSnapped ? 0 : Math.PI / 4), title: rotationSnapped ? `Snap ON (π/4) — click to disable` : `Snap OFF — click to enable (π/4)` }) }), _jsx(Vector3Field, { name: "scale", label: "Scale", values: component.properties, onChange: onUpdate, fallback: [1, 1, 1] })] }));
|
|
40
52
|
}
|
|
41
53
|
const TransformComponent = {
|
|
42
54
|
name: 'Transform',
|
|
@@ -7,6 +7,8 @@ import DirectionalLightComponent from './DirectionalLightComponent';
|
|
|
7
7
|
import AmbientLightComponent from './AmbientLightComponent';
|
|
8
8
|
import ModelComponent from './ModelComponent';
|
|
9
9
|
import TextComponent from './TextComponent';
|
|
10
|
+
import EnvironmentComponent from './EnvironmentComponent';
|
|
11
|
+
import CameraComponent from './CameraComponent';
|
|
10
12
|
export default [
|
|
11
13
|
GeometryComponent,
|
|
12
14
|
TransformComponent,
|
|
@@ -16,5 +18,7 @@ export default [
|
|
|
16
18
|
DirectionalLightComponent,
|
|
17
19
|
AmbientLightComponent,
|
|
18
20
|
ModelComponent,
|
|
19
|
-
TextComponent
|
|
21
|
+
TextComponent,
|
|
22
|
+
EnvironmentComponent,
|
|
23
|
+
CameraComponent,
|
|
20
24
|
];
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
export declare const colors: {
|
|
2
2
|
bg: string;
|
|
3
|
+
bgSurface: string;
|
|
3
4
|
bgLight: string;
|
|
4
5
|
bgHover: string;
|
|
6
|
+
bgInput: string;
|
|
5
7
|
border: string;
|
|
6
8
|
borderLight: string;
|
|
7
9
|
borderFaint: string;
|
|
8
10
|
text: string;
|
|
9
11
|
textMuted: string;
|
|
12
|
+
textDim: string;
|
|
13
|
+
accent: string;
|
|
14
|
+
accentBg: string;
|
|
15
|
+
accentBorder: string;
|
|
10
16
|
danger: string;
|
|
11
17
|
dangerBg: string;
|
|
12
18
|
dangerBorder: string;
|
|
@@ -890,6 +896,8 @@ export declare const inspector: {
|
|
|
890
896
|
padding: number;
|
|
891
897
|
maxHeight: string;
|
|
892
898
|
overflowY: "auto";
|
|
899
|
+
overflowX: "hidden";
|
|
900
|
+
boxSizing: "border-box";
|
|
893
901
|
display: string;
|
|
894
902
|
flexDirection: "column";
|
|
895
903
|
gap: number;
|
|
@@ -1759,19 +1767,21 @@ export declare const tree: {
|
|
|
1759
1767
|
row: React.CSSProperties;
|
|
1760
1768
|
selected: {
|
|
1761
1769
|
background: string;
|
|
1770
|
+
borderBottomColor: string;
|
|
1762
1771
|
};
|
|
1763
1772
|
};
|
|
1764
1773
|
export declare const menu: {
|
|
1765
1774
|
container: {
|
|
1766
1775
|
position: "fixed";
|
|
1767
1776
|
zIndex: number;
|
|
1768
|
-
minWidth:
|
|
1777
|
+
minWidth: string;
|
|
1778
|
+
width: string;
|
|
1779
|
+
maxWidth: string;
|
|
1769
1780
|
background: string;
|
|
1770
1781
|
border: string;
|
|
1771
1782
|
borderRadius: number;
|
|
1772
1783
|
overflow: string;
|
|
1773
1784
|
boxShadow: string;
|
|
1774
|
-
backdropFilter: string;
|
|
1775
1785
|
};
|
|
1776
1786
|
item: React.CSSProperties;
|
|
1777
1787
|
danger: {
|
|
@@ -1783,7 +1793,6 @@ export declare const toolbar: {
|
|
|
1783
1793
|
position: "absolute";
|
|
1784
1794
|
top: number;
|
|
1785
1795
|
left: string;
|
|
1786
|
-
transform: string;
|
|
1787
1796
|
display: string;
|
|
1788
1797
|
gap: number;
|
|
1789
1798
|
padding: string;
|
|
@@ -1793,7 +1802,7 @@ export declare const toolbar: {
|
|
|
1793
1802
|
color: string;
|
|
1794
1803
|
fontFamily: string;
|
|
1795
1804
|
fontSize: number;
|
|
1796
|
-
|
|
1805
|
+
boxShadow: string;
|
|
1797
1806
|
};
|
|
1798
1807
|
divider: {
|
|
1799
1808
|
width: number;
|
|
@@ -1804,3 +1813,7 @@ export declare const toolbar: {
|
|
|
1804
1813
|
cursor: string;
|
|
1805
1814
|
};
|
|
1806
1815
|
};
|
|
1816
|
+
export declare const scrollbarCSS: string;
|
|
1817
|
+
export declare const componentCard: {
|
|
1818
|
+
container: React.CSSProperties;
|
|
1819
|
+
};
|
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
// Shared editor styles - single source of truth for all prefab editor UI
|
|
2
2
|
export const colors = {
|
|
3
|
-
bg: '
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
3
|
+
bg: '#1e1e1e',
|
|
4
|
+
bgSurface: '#252526',
|
|
5
|
+
bgLight: '#2d2d2d',
|
|
6
|
+
bgHover: '#2a2d2e',
|
|
7
|
+
bgInput: '#1a1a1a',
|
|
8
|
+
border: '#3c3c3c',
|
|
9
|
+
borderLight: '#333333',
|
|
10
|
+
borderFaint: '#2a2a2a',
|
|
11
|
+
text: '#cccccc',
|
|
12
|
+
textMuted: '#999999',
|
|
13
|
+
textDim: '#666666',
|
|
14
|
+
accent: '#4c9eff',
|
|
15
|
+
accentBg: 'rgba(76, 158, 255, 0.12)',
|
|
16
|
+
accentBorder: 'rgba(76, 158, 255, 0.4)',
|
|
17
|
+
danger: '#f44747',
|
|
18
|
+
dangerBg: 'rgba(244, 71, 71, 0.12)',
|
|
19
|
+
dangerBorder: 'rgba(244, 71, 71, 0.35)',
|
|
14
20
|
};
|
|
15
21
|
export const fonts = {
|
|
16
|
-
family: 'system-ui, sans-serif',
|
|
22
|
+
family: 'system-ui, -apple-system, sans-serif',
|
|
17
23
|
size: 11,
|
|
18
24
|
sizeSm: 10,
|
|
19
25
|
};
|
|
@@ -24,12 +30,12 @@ export const base = {
|
|
|
24
30
|
color: colors.text,
|
|
25
31
|
border: `1px solid ${colors.border}`,
|
|
26
32
|
borderRadius: 4,
|
|
27
|
-
backdropFilter: 'blur(8px)',
|
|
28
33
|
fontFamily: fonts.family,
|
|
29
34
|
fontSize: fonts.size,
|
|
35
|
+
boxShadow: '0 2px 8px rgba(0,0,0,0.4)',
|
|
30
36
|
},
|
|
31
37
|
header: {
|
|
32
|
-
padding: '
|
|
38
|
+
padding: '7px 10px',
|
|
33
39
|
display: 'flex',
|
|
34
40
|
alignItems: 'center',
|
|
35
41
|
justifyContent: 'space-between',
|
|
@@ -37,22 +43,23 @@ export const base = {
|
|
|
37
43
|
background: colors.bgLight,
|
|
38
44
|
borderBottom: `1px solid ${colors.borderLight}`,
|
|
39
45
|
fontSize: fonts.size,
|
|
40
|
-
fontWeight:
|
|
46
|
+
fontWeight: 600,
|
|
41
47
|
textTransform: 'uppercase',
|
|
42
|
-
letterSpacing: 0.
|
|
48
|
+
letterSpacing: 0.8,
|
|
49
|
+
color: colors.text,
|
|
43
50
|
},
|
|
44
51
|
input: {
|
|
45
52
|
width: '100%',
|
|
46
|
-
background: colors.
|
|
53
|
+
background: colors.bgInput,
|
|
47
54
|
border: `1px solid ${colors.border}`,
|
|
48
55
|
borderRadius: 3,
|
|
49
|
-
padding: '
|
|
56
|
+
padding: '5px 8px',
|
|
50
57
|
color: colors.text,
|
|
51
58
|
fontSize: fonts.size,
|
|
52
59
|
outline: 'none',
|
|
53
60
|
},
|
|
54
61
|
btn: {
|
|
55
|
-
background: colors.
|
|
62
|
+
background: colors.bgLight,
|
|
56
63
|
border: `1px solid ${colors.border}`,
|
|
57
64
|
borderRadius: 3,
|
|
58
65
|
padding: '4px 8px',
|
|
@@ -68,10 +75,11 @@ export const base = {
|
|
|
68
75
|
},
|
|
69
76
|
label: {
|
|
70
77
|
fontSize: fonts.sizeSm,
|
|
71
|
-
|
|
78
|
+
color: colors.textMuted,
|
|
72
79
|
marginBottom: 4,
|
|
73
80
|
textTransform: 'uppercase',
|
|
74
81
|
letterSpacing: 0.5,
|
|
82
|
+
fontWeight: 500,
|
|
75
83
|
},
|
|
76
84
|
row: {
|
|
77
85
|
display: 'flex',
|
|
@@ -89,6 +97,8 @@ export const inspector = {
|
|
|
89
97
|
padding: 8,
|
|
90
98
|
maxHeight: '80vh',
|
|
91
99
|
overflowY: 'auto',
|
|
100
|
+
overflowX: 'hidden',
|
|
101
|
+
boxSizing: 'border-box',
|
|
92
102
|
display: 'flex',
|
|
93
103
|
flexDirection: 'column',
|
|
94
104
|
gap: 8,
|
|
@@ -100,40 +110,46 @@ export const tree = {
|
|
|
100
110
|
overflowY: 'auto',
|
|
101
111
|
padding: 4,
|
|
102
112
|
scrollbarWidth: 'thin',
|
|
103
|
-
scrollbarColor:
|
|
113
|
+
scrollbarColor: `${colors.bgLight} transparent`,
|
|
104
114
|
},
|
|
105
115
|
row: {
|
|
106
116
|
display: 'flex',
|
|
107
117
|
alignItems: 'center',
|
|
108
118
|
padding: '3px 6px',
|
|
109
|
-
|
|
119
|
+
borderBottomWidth: 1,
|
|
120
|
+
borderBottomStyle: 'solid',
|
|
121
|
+
borderBottomColor: colors.borderFaint,
|
|
110
122
|
cursor: 'pointer',
|
|
111
123
|
whiteSpace: 'nowrap',
|
|
124
|
+
borderRadius: 2,
|
|
112
125
|
},
|
|
113
126
|
selected: {
|
|
114
|
-
background:
|
|
127
|
+
background: colors.accentBg,
|
|
128
|
+
borderBottomColor: colors.accentBorder,
|
|
115
129
|
},
|
|
116
130
|
};
|
|
117
131
|
export const menu = {
|
|
118
132
|
container: {
|
|
119
133
|
position: 'fixed',
|
|
120
134
|
zIndex: 50,
|
|
121
|
-
minWidth:
|
|
122
|
-
|
|
135
|
+
minWidth: 'auto',
|
|
136
|
+
width: 'max-content',
|
|
137
|
+
maxWidth: 'min(240px, calc(100vw - 16px))',
|
|
138
|
+
background: colors.bgSurface,
|
|
123
139
|
border: `1px solid ${colors.border}`,
|
|
124
140
|
borderRadius: 4,
|
|
125
141
|
overflow: 'hidden',
|
|
126
|
-
boxShadow: '0
|
|
127
|
-
backdropFilter: 'blur(8px)',
|
|
142
|
+
boxShadow: '0 4px 16px rgba(0,0,0,0.6)',
|
|
128
143
|
},
|
|
129
144
|
item: {
|
|
130
145
|
width: '100%',
|
|
131
146
|
textAlign: 'left',
|
|
132
|
-
padding: '
|
|
147
|
+
padding: '7px 12px',
|
|
133
148
|
background: 'transparent',
|
|
134
149
|
border: 'none',
|
|
135
150
|
color: colors.text,
|
|
136
151
|
fontSize: fonts.size,
|
|
152
|
+
whiteSpace: 'nowrap',
|
|
137
153
|
cursor: 'pointer',
|
|
138
154
|
outline: 'none',
|
|
139
155
|
},
|
|
@@ -145,8 +161,7 @@ export const toolbar = {
|
|
|
145
161
|
panel: {
|
|
146
162
|
position: 'absolute',
|
|
147
163
|
top: 8,
|
|
148
|
-
left: '
|
|
149
|
-
transform: 'translateX(-50%)',
|
|
164
|
+
left: '240px',
|
|
150
165
|
display: 'flex',
|
|
151
166
|
gap: 6,
|
|
152
167
|
padding: '4px 6px',
|
|
@@ -156,14 +171,36 @@ export const toolbar = {
|
|
|
156
171
|
color: colors.text,
|
|
157
172
|
fontFamily: fonts.family,
|
|
158
173
|
fontSize: fonts.size,
|
|
159
|
-
|
|
174
|
+
boxShadow: '0 2px 8px rgba(0,0,0,0.4)',
|
|
160
175
|
},
|
|
161
176
|
divider: {
|
|
162
177
|
width: 1,
|
|
163
|
-
background:
|
|
178
|
+
background: colors.borderLight,
|
|
164
179
|
},
|
|
165
180
|
disabled: {
|
|
166
181
|
opacity: 0.4,
|
|
167
182
|
cursor: 'not-allowed',
|
|
168
183
|
},
|
|
169
184
|
};
|
|
185
|
+
// Shared scrollbar CSS (inject via <style> tag since CSS can't be bundled)
|
|
186
|
+
export const scrollbarCSS = `
|
|
187
|
+
.prefab-scroll::-webkit-scrollbar,
|
|
188
|
+
.tree-scroll::-webkit-scrollbar { width: 6px; height: 6px; }
|
|
189
|
+
.prefab-scroll::-webkit-scrollbar-track,
|
|
190
|
+
.tree-scroll::-webkit-scrollbar-track { background: transparent; }
|
|
191
|
+
.prefab-scroll::-webkit-scrollbar-thumb,
|
|
192
|
+
.tree-scroll::-webkit-scrollbar-thumb { background: ${colors.border}; border-radius: 3px; }
|
|
193
|
+
.prefab-scroll::-webkit-scrollbar-thumb:hover,
|
|
194
|
+
.tree-scroll::-webkit-scrollbar-thumb:hover { background: #555; }
|
|
195
|
+
.prefab-scroll { scrollbar-width: thin; scrollbar-color: ${colors.border} transparent; }
|
|
196
|
+
`;
|
|
197
|
+
// Reusable component card style for inspector sections
|
|
198
|
+
export const componentCard = {
|
|
199
|
+
container: {
|
|
200
|
+
marginBottom: 8,
|
|
201
|
+
backgroundColor: colors.bgSurface,
|
|
202
|
+
padding: 8,
|
|
203
|
+
borderRadius: 4,
|
|
204
|
+
border: `1px solid ${colors.border}`,
|
|
205
|
+
},
|
|
206
|
+
};
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { GameObject, Prefab } from "./types";
|
|
2
|
-
import { Object3D } from 'three';
|
|
2
|
+
import { Object3D, Vector3 } from 'three';
|
|
3
3
|
export interface ExportGLBOptions {
|
|
4
4
|
filename?: string;
|
|
5
5
|
binary?: boolean;
|
|
6
6
|
onComplete?: (result: ArrayBuffer | object) => void;
|
|
7
7
|
onError?: (error: any) => void;
|
|
8
8
|
}
|
|
9
|
-
/** Save a prefab as JSON file */
|
|
10
|
-
export declare function saveJson(data: Prefab, filename: string): void
|
|
9
|
+
/** Save a prefab as JSON file, showing a Save As dialog when supported */
|
|
10
|
+
export declare function saveJson(data: Prefab, filename: string): Promise<void>;
|
|
11
11
|
/** Load a prefab from JSON file */
|
|
12
12
|
export declare function loadJson(): Promise<Prefab | undefined>;
|
|
13
13
|
/**
|
|
@@ -23,6 +23,7 @@ export declare function exportGLB(sceneRoot: Object3D, options?: ExportGLBOption
|
|
|
23
23
|
* @returns Promise that resolves with the GLB data as ArrayBuffer
|
|
24
24
|
*/
|
|
25
25
|
export declare function exportGLBData(sceneRoot: Object3D): Promise<ArrayBuffer>;
|
|
26
|
+
export declare function focusCameraOnObject(object: Object3D, camera: Object3D, target: Vector3, update?: () => void): void;
|
|
26
27
|
/** Find a node by ID in the tree */
|
|
27
28
|
export declare function findNode(root: GameObject, id: string): GameObject | null;
|
|
28
29
|
/** Find the parent of a node by ID */
|
|
@@ -44,3 +45,7 @@ export declare function regenerateIds(node: GameObject): GameObject;
|
|
|
44
45
|
/** Get component data from a node */
|
|
45
46
|
export declare function getComponent<T = any>(node: GameObject, type: string): T | undefined;
|
|
46
47
|
export declare function updateNodeById(root: GameObject, id: string, updater: (node: GameObject) => GameObject): GameObject;
|
|
48
|
+
/** Create a GameObject node for a 3D model file */
|
|
49
|
+
export declare function createModelNode(filename: string, name?: string): GameObject;
|
|
50
|
+
/** Create a GameObject node for an image as a textured plane */
|
|
51
|
+
export declare function createImageNode(texturePath: string, name?: string): GameObject;
|