react-three-game 0.0.69 → 0.0.71
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/helpers/SoundManager.d.ts +2 -0
- package/dist/helpers/SoundManager.js +6 -0
- package/dist/index.d.ts +20 -13
- package/dist/index.js +14 -7
- package/dist/shared/GameCanvas.js +0 -2
- package/dist/tools/assetviewer/page.d.ts +5 -0
- package/dist/tools/assetviewer/page.js +3 -0
- package/dist/tools/dragdrop/DragDropLoader.d.ts +3 -2
- package/dist/tools/dragdrop/DragDropLoader.js +18 -3
- package/dist/tools/dragdrop/index.d.ts +2 -2
- package/dist/tools/dragdrop/index.js +1 -1
- package/dist/tools/dragdrop/modelLoader.d.ts +10 -0
- package/dist/tools/dragdrop/modelLoader.js +60 -0
- package/dist/tools/prefabeditor/EditorTree.js +6 -40
- package/dist/tools/prefabeditor/EditorTreeMenus.js +2 -20
- package/dist/tools/prefabeditor/EditorUI.js +8 -5
- package/dist/tools/prefabeditor/InstanceProvider.d.ts +2 -0
- package/dist/tools/prefabeditor/InstanceProvider.js +54 -52
- package/dist/tools/prefabeditor/PrefabEditor.d.ts +23 -1
- package/dist/tools/prefabeditor/PrefabEditor.js +79 -47
- package/dist/tools/prefabeditor/PrefabRoot.d.ts +26 -9
- package/dist/tools/prefabeditor/PrefabRoot.js +195 -159
- package/dist/tools/prefabeditor/RefBridge.d.ts +24 -0
- package/dist/tools/prefabeditor/RefBridge.js +44 -0
- package/dist/tools/prefabeditor/components/AmbientLightComponent.js +10 -7
- package/dist/tools/prefabeditor/components/CameraComponent.js +8 -14
- package/dist/tools/prefabeditor/components/ClickComponent.js +12 -7
- package/dist/tools/prefabeditor/components/ComponentRegistry.d.ts +31 -5
- package/dist/tools/prefabeditor/components/ComponentRegistry.js +6 -6
- package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +124 -52
- package/dist/tools/prefabeditor/components/EnvironmentComponent.js +5 -3
- package/dist/tools/prefabeditor/components/GeometryComponent.js +1 -1
- package/dist/tools/prefabeditor/components/Input.d.ts +16 -0
- package/dist/tools/prefabeditor/components/Input.js +33 -0
- package/dist/tools/prefabeditor/components/MaterialComponent.js +19 -8
- package/dist/tools/prefabeditor/components/ModelComponent.js +39 -45
- package/dist/tools/prefabeditor/components/PhysicsComponent.d.ts +10 -1
- package/dist/tools/prefabeditor/components/PhysicsComponent.js +127 -31
- package/dist/tools/prefabeditor/components/PointLightComponent.d.ts +3 -0
- package/dist/tools/prefabeditor/components/PointLightComponent.js +55 -0
- package/dist/tools/prefabeditor/components/SoundComponent.d.ts +3 -0
- package/dist/tools/prefabeditor/components/SoundComponent.js +244 -0
- package/dist/tools/prefabeditor/components/SpotLightComponent.js +53 -24
- package/dist/tools/prefabeditor/components/TransformComponent.js +2 -2
- package/dist/tools/prefabeditor/components/index.js +4 -0
- package/dist/tools/prefabeditor/components/lightUtils.d.ts +13 -0
- package/dist/tools/prefabeditor/components/lightUtils.js +64 -0
- package/dist/tools/prefabeditor/prefab.d.ts +37 -0
- package/dist/tools/prefabeditor/prefab.js +229 -0
- package/dist/tools/prefabeditor/prefabStore.d.ts +4 -16
- package/dist/tools/prefabeditor/prefabStore.js +32 -173
- package/dist/tools/prefabeditor/{sceneApi.d.ts → scene.d.ts} +15 -1
- package/dist/tools/prefabeditor/{sceneApi.js → scene.js} +66 -32
- package/dist/tools/prefabeditor/styles.d.ts +1 -0
- package/dist/tools/prefabeditor/styles.js +9 -0
- package/dist/tools/prefabeditor/types.d.ts +13 -0
- package/dist/tools/prefabeditor/types.js +28 -1
- package/dist/tools/prefabeditor/useClickValid.d.ts +13 -0
- package/dist/tools/prefabeditor/useClickValid.js +21 -0
- package/dist/tools/prefabeditor/utils.d.ts +2 -4
- package/dist/tools/prefabeditor/utils.js +8 -46
- package/package.json +1 -1
- package/dist/tools/prefabeditor/EditorContext.d.ts +0 -16
- package/dist/tools/prefabeditor/EditorContext.js +0 -9
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { ModelPicker } from '../../assetviewer/page';
|
|
3
3
|
import { useContext, useMemo } from 'react';
|
|
4
|
-
import { BooleanField, FieldGroup, Label, NumberInput, SelectInput } from './Input';
|
|
5
|
-
import {
|
|
4
|
+
import { BooleanField, FieldGroup, Label, ListEditor, NumberInput, SelectInput } from './Input';
|
|
5
|
+
import { useSceneRuntime } from '../PrefabRoot';
|
|
6
|
+
import { EditorContext } from '../PrefabEditor';
|
|
6
7
|
import { DEFAULT_REPEAT_AXES, getRepeatAxesFromModelProperties, normalizeRepeatAxes } from '../InstanceProvider';
|
|
7
8
|
import { colors } from '../styles';
|
|
8
9
|
const AXIS_OPTIONS = [
|
|
@@ -18,12 +19,10 @@ function quantize(value, step) {
|
|
|
18
19
|
return Math.round(value / step) * step;
|
|
19
20
|
}
|
|
20
21
|
function RepeatAxisEditor({ axes, onChange, positionSnap, }) {
|
|
21
|
-
const addAxis = () => {
|
|
22
|
-
|
|
23
|
-
const nextAxis = AXIS_OPTIONS.find(option => !used.has(option.value));
|
|
24
|
-
if (!nextAxis)
|
|
22
|
+
const addAxis = (axisValue) => {
|
|
23
|
+
if (!axisValue)
|
|
25
24
|
return;
|
|
26
|
-
onChange([...axes, { axis:
|
|
25
|
+
onChange([...axes, { axis: axisValue, count: 1, offset: 1 }]);
|
|
27
26
|
};
|
|
28
27
|
const updateAxis = (index, patch) => {
|
|
29
28
|
const nextAxes = axes.map((axis, axisIndex) => axisIndex === index ? Object.assign(Object.assign({}, axis), patch) : axis);
|
|
@@ -32,41 +31,30 @@ function RepeatAxisEditor({ axes, onChange, positionSnap, }) {
|
|
|
32
31
|
const removeAxis = (index) => {
|
|
33
32
|
onChange(axes.filter((_, axisIndex) => axisIndex !== index));
|
|
34
33
|
};
|
|
35
|
-
const
|
|
36
|
-
return (
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
height: 24,
|
|
60
|
-
width: 28,
|
|
61
|
-
borderRadius: 3,
|
|
62
|
-
border: `1px solid ${colors.border}`,
|
|
63
|
-
background: colors.bgInput,
|
|
64
|
-
color: colors.text,
|
|
65
|
-
cursor: 'pointer',
|
|
66
|
-
padding: 0,
|
|
67
|
-
flexShrink: 0,
|
|
68
|
-
}, title: "Remove repeat axis", children: "\u00D7" })) : null] }), _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 6 }, children: [_jsxs("div", { children: [_jsx(Label, { children: "Count" }), _jsx(NumberInput, { value: axisConfig.count, onChange: (count) => updateAxis(index, { count: Math.max(1, Math.floor(count)) }), step: 1, min: 1, style: { width: '100%', minWidth: 0, boxSizing: 'border-box' } })] }), _jsxs("div", { children: [_jsx(Label, { children: "Offset" }), _jsx(NumberInput, { value: axisConfig.offset, onChange: (offset) => updateAxis(index, { offset: quantize(offset, positionSnap) }), step: positionSnap > 0 ? positionSnap : 0.1, style: { width: '100%', minWidth: 0, boxSizing: 'border-box' } })] })] })] }, `${axisConfig.axis}-${index}`));
|
|
69
|
-
})] }));
|
|
34
|
+
const availableAxisOptions = AXIS_OPTIONS.filter(option => !axes.some(axis => axis.axis === option.value));
|
|
35
|
+
return (_jsx(ListEditor, { label: "Repeat Axes", items: axes, onAdd: addAxis, addOptions: availableAxisOptions, canAdd: availableAxisOptions.length > 0, emptyMessage: "No repeat axes added.", addButtonTitle: "Add repeat axis", addDisabledTitle: "All axes already in use", renderItem: (axisConfig, index) => {
|
|
36
|
+
const usedByOthers = new Set(axes.filter((_, axisIndex) => axisIndex !== index).map(axis => axis.axis));
|
|
37
|
+
const axisOptions = AXIS_OPTIONS.filter(option => option.value === axisConfig.axis || !usedByOthers.has(option.value));
|
|
38
|
+
return (_jsxs("div", { style: {
|
|
39
|
+
display: 'flex',
|
|
40
|
+
flexDirection: 'column',
|
|
41
|
+
gap: 6,
|
|
42
|
+
padding: 8,
|
|
43
|
+
border: `1px solid ${colors.border}`,
|
|
44
|
+
borderRadius: 4,
|
|
45
|
+
background: colors.bgSurface,
|
|
46
|
+
}, children: [_jsxs("div", { style: { display: 'flex', gap: 6, alignItems: 'end' }, children: [_jsx("div", { style: { flex: 1, minWidth: 0 }, children: _jsx(SelectInput, { label: "Axis", value: axisConfig.axis, onChange: (axis) => updateAxis(index, { axis: axis }), options: axisOptions }) }), _jsx("button", { type: "button", onClick: () => removeAxis(index), style: {
|
|
47
|
+
height: 24,
|
|
48
|
+
width: 28,
|
|
49
|
+
borderRadius: 3,
|
|
50
|
+
border: `1px solid ${colors.border}`,
|
|
51
|
+
background: colors.bgInput,
|
|
52
|
+
color: colors.text,
|
|
53
|
+
cursor: 'pointer',
|
|
54
|
+
padding: 0,
|
|
55
|
+
flexShrink: 0,
|
|
56
|
+
}, title: "Remove repeat axis", children: "\u00D7" })] }), _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 6 }, children: [_jsxs("div", { children: [_jsx(Label, { children: "Count" }), _jsx(NumberInput, { value: axisConfig.count, onChange: (count) => updateAxis(index, { count: Math.max(1, Math.floor(count)) }), step: 1, min: 1, style: { width: '100%', minWidth: 0, boxSizing: 'border-box' } })] }), _jsxs("div", { children: [_jsx(Label, { children: "Offset" }), _jsx(NumberInput, { value: axisConfig.offset, onChange: (offset) => updateAxis(index, { offset: quantize(offset, positionSnap) }), step: positionSnap > 0 ? positionSnap : 0.1, style: { width: '100%', minWidth: 0, boxSizing: 'border-box' } })] })] })] }, `${axisConfig.axis}-${index}`));
|
|
57
|
+
} }));
|
|
70
58
|
}
|
|
71
59
|
function ModelComponentEditor({ component, node, onUpdate, basePath = "" }) {
|
|
72
60
|
var _a;
|
|
@@ -76,11 +64,12 @@ function ModelComponentEditor({ component, node, onUpdate, basePath = "" }) {
|
|
|
76
64
|
return (_jsxs(FieldGroup, { children: [_jsx(ModelPicker, { value: component.properties.filename, onChange: (filename) => onUpdate({ filename }), basePath: basePath, pickerKey: node === null || node === void 0 ? void 0 : node.id }), _jsx(BooleanField, { name: "instanced", label: "Instanced", values: component.properties, onChange: onUpdate, fallback: false }), component.properties.instanced && (_jsxs(_Fragment, { children: [_jsx(BooleanField, { name: "repeat", label: "Repeat", values: component.properties, onChange: onUpdate, fallback: false }), component.properties.repeat && (_jsx(RepeatAxisEditor, { axes: repeatAxes, onChange: (nextAxes) => onUpdate({ repeatAxes: nextAxes }), positionSnap: positionSnap }))] }))] }));
|
|
77
65
|
}
|
|
78
66
|
// View for Model component
|
|
79
|
-
function ModelComponentView({ properties,
|
|
67
|
+
function ModelComponentView({ properties, children }) {
|
|
68
|
+
const { getModel } = useSceneRuntime();
|
|
80
69
|
// Instanced models are handled elsewhere (GameInstance), so only render non-instanced here
|
|
81
70
|
if (!properties.filename || properties.instanced)
|
|
82
71
|
return _jsx(_Fragment, { children: children });
|
|
83
|
-
const sourceModel =
|
|
72
|
+
const sourceModel = getModel(properties.filename);
|
|
84
73
|
// Clone model once and set up shadows - memoized to avoid cloning on every render
|
|
85
74
|
const clonedModel = useMemo(() => {
|
|
86
75
|
if (!sourceModel)
|
|
@@ -107,6 +96,11 @@ const ModelComponent = {
|
|
|
107
96
|
instanced: false,
|
|
108
97
|
repeat: false,
|
|
109
98
|
repeatAxes: DEFAULT_REPEAT_AXES
|
|
110
|
-
}
|
|
99
|
+
},
|
|
100
|
+
getAssetRefs: (properties) => {
|
|
101
|
+
if (properties.filename)
|
|
102
|
+
return [{ type: 'model', path: properties.filename }];
|
|
103
|
+
return [];
|
|
104
|
+
},
|
|
111
105
|
};
|
|
112
106
|
export default ModelComponent;
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import type { RigidBodyOptions } from "@react-three/rapier";
|
|
2
2
|
import { Component } from "./ComponentRegistry";
|
|
3
|
-
|
|
3
|
+
type PhysicsColliderType = NonNullable<RigidBodyOptions['colliders']> | 'capsule';
|
|
4
|
+
export type PhysicsProps = Omit<RigidBodyOptions, 'colliders'> & {
|
|
5
|
+
colliders?: PhysicsColliderType;
|
|
4
6
|
activeCollisionTypes?: 'all' | undefined;
|
|
5
7
|
linearVelocity?: [number, number, number];
|
|
6
8
|
angularVelocity?: [number, number, number];
|
|
9
|
+
capsuleRadius?: number;
|
|
10
|
+
capsuleHalfHeight?: number;
|
|
11
|
+
sensorEnterEventName?: string;
|
|
12
|
+
sensorExitEventName?: string;
|
|
13
|
+
collisionEnterEventName?: string;
|
|
14
|
+
collisionExitEventName?: string;
|
|
7
15
|
};
|
|
16
|
+
export declare function isPhysicsProps(v: any): v is PhysicsProps;
|
|
8
17
|
declare const PhysicsComponent: Component;
|
|
9
18
|
export default PhysicsComponent;
|
|
@@ -9,22 +9,112 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
9
9
|
}
|
|
10
10
|
return t;
|
|
11
11
|
};
|
|
12
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
|
-
import { RigidBody, useRapier } from "@react-three/rapier";
|
|
12
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
13
|
+
import { CapsuleCollider, RigidBody, useRapier } from "@react-three/rapier";
|
|
14
14
|
import { useRef, useEffect, useCallback } from 'react';
|
|
15
|
-
import {
|
|
15
|
+
import { useSceneRuntime } from "../PrefabRoot";
|
|
16
|
+
import { BooleanField, FieldGroup, ListEditor, NumberField, SelectField, SelectInput, StringInput, Vector3Field } from "./Input";
|
|
16
17
|
import { gameEvents, getEntityIdFromRigidBody } from "../GameEvents";
|
|
17
18
|
import { colors } from "../styles";
|
|
19
|
+
export function isPhysicsProps(v) {
|
|
20
|
+
return (v === null || v === void 0 ? void 0 : v.type) === "fixed" || (v === null || v === void 0 ? void 0 : v.type) === "dynamic" || (v === null || v === void 0 ? void 0 : v.type) === "kinematicPosition" || (v === null || v === void 0 ? void 0 : v.type) === "kinematicVelocity";
|
|
21
|
+
}
|
|
18
22
|
const enabledAxesFallback = [true, true, true];
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
const capsuleRadiusFallback = 0.35;
|
|
24
|
+
const capsuleHalfHeightFallback = 0.45;
|
|
25
|
+
const PHYSICS_EVENT_OPTIONS = [
|
|
26
|
+
{
|
|
27
|
+
key: 'sensorEnterEventName',
|
|
28
|
+
label: 'Sensor Enter',
|
|
29
|
+
defaultName: 'sensor:enter',
|
|
30
|
+
requiresSensor: true,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
key: 'sensorExitEventName',
|
|
34
|
+
label: 'Sensor Exit',
|
|
35
|
+
defaultName: 'sensor:exit',
|
|
36
|
+
requiresSensor: true,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
key: 'collisionEnterEventName',
|
|
40
|
+
label: 'Collision Enter',
|
|
41
|
+
defaultName: 'collision:enter',
|
|
42
|
+
requiresSensor: false,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
key: 'collisionExitEventName',
|
|
46
|
+
label: 'Collision Exit',
|
|
47
|
+
defaultName: 'collision:exit',
|
|
48
|
+
requiresSensor: false,
|
|
49
|
+
},
|
|
50
|
+
];
|
|
51
|
+
function getPhysicsEventOption(key) {
|
|
52
|
+
return PHYSICS_EVENT_OPTIONS.find(option => option.key === key);
|
|
53
|
+
}
|
|
54
|
+
function getConfiguredPhysicsEvents(values) {
|
|
55
|
+
return PHYSICS_EVENT_OPTIONS.filter(option => typeof values[option.key] === 'string' && values[option.key].trim().length > 0);
|
|
56
|
+
}
|
|
57
|
+
function getAvailablePhysicsEvents(values, currentKey) {
|
|
58
|
+
const configuredKeys = new Set(getConfiguredPhysicsEvents(values).map(option => option.key));
|
|
59
|
+
return PHYSICS_EVENT_OPTIONS
|
|
60
|
+
.filter(option => option.key === currentKey || !configuredKeys.has(option.key))
|
|
61
|
+
.map(option => ({ value: option.key, label: option.label }));
|
|
62
|
+
}
|
|
63
|
+
function PhysicsEventBindingsEditor({ values, onChange, }) {
|
|
64
|
+
const configuredEvents = getConfiguredPhysicsEvents(values);
|
|
65
|
+
const nextEventOptions = getAvailablePhysicsEvents(values);
|
|
66
|
+
const addEvent = (eventKey) => {
|
|
67
|
+
if (!eventKey)
|
|
68
|
+
return;
|
|
69
|
+
const option = getPhysicsEventOption(eventKey);
|
|
70
|
+
if (!option)
|
|
71
|
+
return;
|
|
72
|
+
onChange(Object.assign({ [option.key]: option.defaultName }, (option.requiresSensor ? { sensor: true } : null)));
|
|
73
|
+
};
|
|
74
|
+
const updateEventKey = (currentKey, nextKey) => {
|
|
75
|
+
const nextOption = getPhysicsEventOption(nextKey);
|
|
76
|
+
if (!nextOption)
|
|
77
|
+
return;
|
|
78
|
+
onChange(Object.assign({ [currentKey]: undefined, [nextOption.key]: values[currentKey] || nextOption.defaultName }, (nextOption.requiresSensor ? { sensor: true } : null)));
|
|
79
|
+
};
|
|
80
|
+
const updateEventName = (key, eventName) => {
|
|
81
|
+
onChange({ [key]: eventName });
|
|
82
|
+
};
|
|
83
|
+
const removeEvent = (key) => {
|
|
84
|
+
onChange({ [key]: undefined });
|
|
85
|
+
};
|
|
86
|
+
return (_jsx(ListEditor, { label: "Events", items: configuredEvents, onAdd: addEvent, addOptions: nextEventOptions, canAdd: nextEventOptions.length > 0, emptyMessage: "No physics events configured.", addButtonTitle: "Add physics event", addDisabledTitle: "All physics events already added", renderItem: (option) => {
|
|
87
|
+
var _a;
|
|
88
|
+
return (_jsxs("div", { style: {
|
|
89
|
+
display: 'flex',
|
|
90
|
+
flexDirection: 'column',
|
|
91
|
+
gap: 6,
|
|
92
|
+
padding: 8,
|
|
93
|
+
border: `1px solid ${colors.border}`,
|
|
94
|
+
borderRadius: 4,
|
|
95
|
+
background: colors.bgSurface,
|
|
96
|
+
}, children: [_jsxs("div", { style: { display: 'flex', gap: 6, alignItems: 'end' }, children: [_jsx("div", { style: { flex: 1, minWidth: 0 }, children: _jsx(SelectInput, { label: "Type", value: option.key, onChange: (nextKey) => updateEventKey(option.key, nextKey), options: getAvailablePhysicsEvents(values, option.key) }) }), _jsx("button", { type: "button", onClick: () => removeEvent(option.key), style: {
|
|
97
|
+
height: 24,
|
|
98
|
+
width: 28,
|
|
99
|
+
borderRadius: 3,
|
|
100
|
+
border: `1px solid ${colors.border}`,
|
|
101
|
+
background: colors.bgInput,
|
|
102
|
+
color: colors.text,
|
|
103
|
+
cursor: 'pointer',
|
|
104
|
+
padding: 0,
|
|
105
|
+
flexShrink: 0,
|
|
106
|
+
}, title: "Remove physics event", children: "\u00D7" })] }), _jsx(StringInput, { label: "Event Name", value: (_a = values[option.key]) !== null && _a !== void 0 ? _a : option.defaultName, onChange: (eventName) => updateEventName(option.key, eventName), placeholder: option.defaultName })] }, option.key));
|
|
107
|
+
} }));
|
|
108
|
+
}
|
|
109
|
+
function LockedAxisField({ label, name, values, onChange, }) {
|
|
110
|
+
const enabledAxes = Array.isArray(values[name])
|
|
111
|
+
? values[name]
|
|
22
112
|
: enabledAxesFallback;
|
|
23
113
|
const axisLabels = ['X', 'Y', 'Z'];
|
|
24
114
|
const toggleAxisLock = (index) => {
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
onChange({
|
|
115
|
+
const nextEnabledAxes = [...enabledAxes];
|
|
116
|
+
nextEnabledAxes[index] = !nextEnabledAxes[index];
|
|
117
|
+
onChange({ [name]: nextEnabledAxes });
|
|
28
118
|
};
|
|
29
119
|
return (_jsxs("div", { children: [_jsxs("div", { style: {
|
|
30
120
|
display: 'flex',
|
|
@@ -42,7 +132,7 @@ function LockedAxisField({ label, values, onChange, }) {
|
|
|
42
132
|
fontSize: '10px',
|
|
43
133
|
color: colors.textDim,
|
|
44
134
|
}, children: "Active means locked" })] }), _jsx("div", { style: { display: 'flex', gap: 4 }, children: axisLabels.map((axisLabel, index) => {
|
|
45
|
-
const isLocked = !
|
|
135
|
+
const isLocked = !enabledAxes[index];
|
|
46
136
|
return (_jsx("button", { type: "button", onClick: () => toggleAxisLock(index), style: {
|
|
47
137
|
flex: 1,
|
|
48
138
|
backgroundColor: isLocked ? colors.dangerBg : colors.bgInput,
|
|
@@ -67,20 +157,23 @@ function PhysicsComponentEditor({ component, onUpdate }) {
|
|
|
67
157
|
{ value: 'trimesh', label: 'Trimesh (exact)' },
|
|
68
158
|
{ value: 'cuboid', label: 'Cuboid (box)' },
|
|
69
159
|
{ value: 'ball', label: 'Ball (sphere)' },
|
|
70
|
-
|
|
160
|
+
{ value: 'capsule', label: 'Capsule' },
|
|
161
|
+
] }), component.properties.colliders === 'capsule' ? (_jsxs(_Fragment, { children: [_jsx(NumberField, { name: "capsuleRadius", label: "Capsule Radius", values: component.properties, onChange: onUpdate, fallback: capsuleRadiusFallback, min: 0.01, step: 0.01 }), _jsx(NumberField, { name: "capsuleHalfHeight", label: "Capsule Half Height", values: component.properties, onChange: onUpdate, fallback: capsuleHalfHeightFallback, min: 0.01, step: 0.01 })] })) : null, _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(Vector3Field, { name: "linearVelocity", label: "Linear Velocity", values: component.properties, onChange: onUpdate, fallback: [0, 0, 0] }), _jsx(Vector3Field, { name: "angularVelocity", label: "Angular Velocity", values: component.properties, onChange: onUpdate, fallback: [0, 0, 0] }), _jsx(LockedAxisField, { label: "Lock Movement", name: "enabledTranslations", values: component.properties, onChange: onUpdate }), _jsx(LockedAxisField, { label: "Lock Rotations", name: "enabledRotations", values: component.properties, onChange: onUpdate }), _jsx(BooleanField, { name: "sensor", label: "Sensor (Trigger Only)", values: component.properties, onChange: onUpdate, fallback: false }), _jsx(PhysicsEventBindingsEditor, { values: component.properties, onChange: onUpdate }), _jsx(SelectField, { name: "activeCollisionTypes", label: "Collision Detection", values: component.properties, onChange: onUpdate, options: [
|
|
71
162
|
{ value: '', label: 'Default (Dynamic only)' },
|
|
72
163
|
{ value: 'all', label: 'All (includes kinematic & fixed)' },
|
|
73
164
|
] })] }));
|
|
74
165
|
}
|
|
75
|
-
function PhysicsComponentView({ properties, children, position, rotation, scale, editMode, nodeId
|
|
76
|
-
const {
|
|
166
|
+
function PhysicsComponentView({ properties, children, position, rotation, scale, editMode, nodeId }) {
|
|
167
|
+
const { registerRigidBodyRef } = useSceneRuntime();
|
|
168
|
+
const { type, colliders, sensor, activeCollisionTypes, linearVelocity = [0, 0, 0], angularVelocity = [0, 0, 0], capsuleRadius = capsuleRadiusFallback, capsuleHalfHeight = capsuleHalfHeightFallback, sensorEnterEventName, sensorExitEventName, collisionEnterEventName, collisionExitEventName, enabledTranslations = enabledAxesFallback, enabledRotations = enabledAxesFallback } = properties, otherProps = __rest(properties, ["type", "colliders", "sensor", "activeCollisionTypes", "linearVelocity", "angularVelocity", "capsuleRadius", "capsuleHalfHeight", "sensorEnterEventName", "sensorExitEventName", "collisionEnterEventName", "collisionExitEventName", "enabledTranslations", "enabledRotations"]);
|
|
77
169
|
const colliderType = colliders || (type === 'fixed' ? 'trimesh' : 'hull');
|
|
170
|
+
const usesManualCapsuleCollider = colliderType === 'capsule';
|
|
78
171
|
const rigidBodyRef = useRef(null);
|
|
79
172
|
const linearVelocityKey = linearVelocity.join(',');
|
|
80
173
|
const angularVelocityKey = angularVelocity.join(',');
|
|
81
174
|
const rbKey = editMode
|
|
82
|
-
? `${type || 'dynamic'}_${colliderType}_${position === null || position === void 0 ? void 0 : position.join(',')}_${rotation === null || rotation === void 0 ? void 0 : rotation.join(',')}`
|
|
83
|
-
: `${type || 'dynamic'}_${colliderType}`;
|
|
175
|
+
? `${type || 'dynamic'}_${colliderType}_${capsuleRadius}_${capsuleHalfHeight}_${position === null || position === void 0 ? void 0 : position.join(',')}_${rotation === null || rotation === void 0 ? void 0 : rotation.join(',')}`
|
|
176
|
+
: `${type || 'dynamic'}_${colliderType}_${capsuleRadius}_${capsuleHalfHeight}`;
|
|
84
177
|
// Try to get rapier context - will be null if not inside <Physics>
|
|
85
178
|
let rapier = null;
|
|
86
179
|
try {
|
|
@@ -92,11 +185,11 @@ function PhysicsComponentView({ properties, children, position, rotation, scale,
|
|
|
92
185
|
}
|
|
93
186
|
// Register RigidBody ref when it's available
|
|
94
187
|
useEffect(() => {
|
|
95
|
-
if (nodeId &&
|
|
188
|
+
if (nodeId && rigidBodyRef.current) {
|
|
96
189
|
registerRigidBodyRef(nodeId, rigidBodyRef.current);
|
|
97
190
|
}
|
|
98
191
|
return () => {
|
|
99
|
-
if (nodeId
|
|
192
|
+
if (nodeId) {
|
|
100
193
|
registerRigidBodyRef(nodeId, null);
|
|
101
194
|
}
|
|
102
195
|
};
|
|
@@ -135,54 +228,57 @@ function PhysicsComponentView({ properties, children, position, rotation, scale,
|
|
|
135
228
|
}, [rbKey, angularVelocityKey]);
|
|
136
229
|
// Event handlers for physics interactions
|
|
137
230
|
const handleIntersectionEnter = useCallback((payload) => {
|
|
138
|
-
if (!nodeId)
|
|
231
|
+
if (!nodeId || !sensorEnterEventName)
|
|
139
232
|
return;
|
|
140
|
-
gameEvents.emit(
|
|
233
|
+
gameEvents.emit(sensorEnterEventName, {
|
|
141
234
|
sourceEntityId: nodeId,
|
|
142
235
|
targetEntityId: getEntityIdFromRigidBody(payload.other.rigidBody),
|
|
143
236
|
targetRigidBody: payload.other.rigidBody,
|
|
144
237
|
});
|
|
145
|
-
}, [nodeId]);
|
|
238
|
+
}, [nodeId, sensorEnterEventName]);
|
|
146
239
|
const handleIntersectionExit = useCallback((payload) => {
|
|
147
|
-
if (!nodeId)
|
|
240
|
+
if (!nodeId || !sensorExitEventName)
|
|
148
241
|
return;
|
|
149
|
-
gameEvents.emit(
|
|
242
|
+
gameEvents.emit(sensorExitEventName, {
|
|
150
243
|
sourceEntityId: nodeId,
|
|
151
244
|
targetEntityId: getEntityIdFromRigidBody(payload.other.rigidBody),
|
|
152
245
|
targetRigidBody: payload.other.rigidBody,
|
|
153
246
|
});
|
|
154
|
-
}, [nodeId]);
|
|
247
|
+
}, [nodeId, sensorExitEventName]);
|
|
155
248
|
const handleCollisionEnter = useCallback((payload) => {
|
|
156
|
-
if (!nodeId)
|
|
249
|
+
if (!nodeId || !collisionEnterEventName)
|
|
157
250
|
return;
|
|
158
|
-
gameEvents.emit(
|
|
251
|
+
gameEvents.emit(collisionEnterEventName, {
|
|
159
252
|
sourceEntityId: nodeId,
|
|
160
253
|
targetEntityId: getEntityIdFromRigidBody(payload.other.rigidBody),
|
|
161
254
|
targetRigidBody: payload.other.rigidBody,
|
|
162
255
|
});
|
|
163
|
-
}, [nodeId]);
|
|
256
|
+
}, [collisionEnterEventName, nodeId]);
|
|
164
257
|
const handleCollisionExit = useCallback((payload) => {
|
|
165
|
-
if (!nodeId)
|
|
258
|
+
if (!nodeId || !collisionExitEventName)
|
|
166
259
|
return;
|
|
167
|
-
gameEvents.emit(
|
|
260
|
+
gameEvents.emit(collisionExitEventName, {
|
|
168
261
|
sourceEntityId: nodeId,
|
|
169
262
|
targetEntityId: getEntityIdFromRigidBody(payload.other.rigidBody),
|
|
170
263
|
targetRigidBody: payload.other.rigidBody,
|
|
171
264
|
});
|
|
172
|
-
}, [nodeId]);
|
|
173
|
-
return (
|
|
265
|
+
}, [collisionExitEventName, nodeId]);
|
|
266
|
+
return (_jsxs(RigidBody, Object.assign({ ref: rigidBodyRef, type: type, colliders: usesManualCapsuleCollider ? false : colliderType, position: position, rotation: rotation, scale: scale, sensor: sensor, enabledTranslations: enabledTranslations, enabledRotations: enabledRotations, userData: { entityId: nodeId }, onIntersectionEnter: handleIntersectionEnter, onIntersectionExit: handleIntersectionExit, onCollisionEnter: handleCollisionEnter, onCollisionExit: handleCollisionExit }, otherProps, { children: [usesManualCapsuleCollider ? _jsx(CapsuleCollider, { args: [capsuleHalfHeight, capsuleRadius], sensor: sensor }) : null, children] }), rbKey));
|
|
174
267
|
}
|
|
175
268
|
const PhysicsComponent = {
|
|
176
269
|
name: 'Physics',
|
|
177
270
|
Editor: PhysicsComponentEditor,
|
|
178
271
|
View: PhysicsComponentView,
|
|
179
|
-
|
|
272
|
+
isWrapper: true,
|
|
180
273
|
defaultProperties: {
|
|
181
274
|
type: 'dynamic',
|
|
182
275
|
colliders: 'hull',
|
|
276
|
+
capsuleRadius: capsuleRadiusFallback,
|
|
277
|
+
capsuleHalfHeight: capsuleHalfHeightFallback,
|
|
183
278
|
linearVelocity: [0, 0, 0],
|
|
184
279
|
angularVelocity: [0, 0, 0],
|
|
185
280
|
enabledTranslations: [true, true, true],
|
|
281
|
+
enabledRotations: [true, true, true],
|
|
186
282
|
}
|
|
187
283
|
};
|
|
188
284
|
export default PhysicsComponent;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
3
|
+
import { useHelper } from '@react-three/drei';
|
|
4
|
+
import { PointLightHelper } from 'three';
|
|
5
|
+
import { BooleanField, ColorField, NumberField } from './Input';
|
|
6
|
+
import { LightSection, ShadowBiasField, mergeWithDefaults } from './lightUtils';
|
|
7
|
+
const pointLightDefaults = {
|
|
8
|
+
color: '#ffffff',
|
|
9
|
+
intensity: 1,
|
|
10
|
+
distance: 0,
|
|
11
|
+
decay: 2,
|
|
12
|
+
castShadow: false,
|
|
13
|
+
shadowMapSize: 512,
|
|
14
|
+
shadowBias: 0,
|
|
15
|
+
shadowNormalBias: 0,
|
|
16
|
+
shadowAutoUpdate: true,
|
|
17
|
+
shadowCameraNear: 0.5,
|
|
18
|
+
shadowCameraFar: 500,
|
|
19
|
+
};
|
|
20
|
+
function PointLightComponentEditor({ component, onUpdate }) {
|
|
21
|
+
const values = mergeWithDefaults(pointLightDefaults, component.properties);
|
|
22
|
+
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: [_jsxs(LightSection, { title: "Light", children: [_jsx(ColorField, { name: "color", label: "Color", values: values, onChange: onUpdate }), _jsx(NumberField, { name: "intensity", label: "Intensity", values: values, onChange: onUpdate, min: 0, step: 0.1, fallback: 1 }), _jsx(NumberField, { name: "distance", label: "Distance", values: values, onChange: onUpdate, min: 0, step: 1, fallback: 0 }), _jsx(NumberField, { name: "decay", label: "Decay", values: values, onChange: onUpdate, min: 0, step: 0.1, fallback: 2 })] }), _jsxs(LightSection, { title: "Shadow", children: [_jsx(BooleanField, { name: "castShadow", label: "Cast Shadow", values: values, onChange: onUpdate, fallback: false }), values.castShadow ? (_jsxs(_Fragment, { children: [_jsx(BooleanField, { name: "shadowAutoUpdate", label: "Auto Update", values: values, onChange: onUpdate, fallback: true }), _jsx(NumberField, { name: "shadowMapSize", label: "Map Size", values: values, onChange: onUpdate, min: 128, step: 128, fallback: 512 }), _jsx(ShadowBiasField, { name: "shadowBias", label: "Bias", values: values, onChange: onUpdate, fallback: 0 }), _jsx(ShadowBiasField, { name: "shadowNormalBias", label: "Normal Bias", values: values, onChange: onUpdate, fallback: 0 }), _jsx(NumberField, { name: "shadowCameraNear", label: "Near", values: values, onChange: onUpdate, min: 0.001, step: 0.1, fallback: 0.5 }), _jsx(NumberField, { name: "shadowCameraFar", label: "Far", values: values, onChange: onUpdate, min: 0.1, step: 1, fallback: 500 })] })) : null] })] }));
|
|
23
|
+
}
|
|
24
|
+
function PointLightView({ properties, children, editMode, isSelected }) {
|
|
25
|
+
const merged = mergeWithDefaults(pointLightDefaults, properties);
|
|
26
|
+
const color = merged.color;
|
|
27
|
+
const intensity = merged.intensity;
|
|
28
|
+
const distance = merged.distance;
|
|
29
|
+
const decay = merged.decay;
|
|
30
|
+
const castShadow = merged.castShadow;
|
|
31
|
+
const shadowMapSize = merged.shadowMapSize;
|
|
32
|
+
const shadowBias = merged.shadowBias;
|
|
33
|
+
const shadowNormalBias = merged.shadowNormalBias;
|
|
34
|
+
const shadowAutoUpdate = merged.shadowAutoUpdate;
|
|
35
|
+
const shadowCameraNear = merged.shadowCameraNear;
|
|
36
|
+
const shadowCameraFar = merged.shadowCameraFar;
|
|
37
|
+
const lightRef = useRef(null);
|
|
38
|
+
useHelper(editMode && isSelected ? lightRef : null, PointLightHelper, 0.5, color);
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
var _a;
|
|
41
|
+
const shadow = (_a = lightRef.current) === null || _a === void 0 ? void 0 : _a.shadow;
|
|
42
|
+
if (!shadow)
|
|
43
|
+
return;
|
|
44
|
+
shadow.needsUpdate = true;
|
|
45
|
+
shadow.camera.updateProjectionMatrix();
|
|
46
|
+
}, [castShadow, shadowMapSize, shadowBias, shadowNormalBias, shadowAutoUpdate, shadowCameraNear, shadowCameraFar]);
|
|
47
|
+
return (_jsxs(_Fragment, { children: [_jsx("pointLight", { ref: lightRef, color: color, intensity: intensity, distance: distance, decay: decay, castShadow: castShadow, "shadow-mapSize-width": shadowMapSize, "shadow-mapSize-height": shadowMapSize, "shadow-bias": shadowBias, "shadow-normalBias": shadowNormalBias, "shadow-autoUpdate": shadowAutoUpdate, "shadow-camera-near": shadowCameraNear, "shadow-camera-far": shadowCameraFar }), editMode && isSelected ? (_jsxs("mesh", { children: [_jsx("sphereGeometry", { args: [0.2, 10, 8] }), _jsx("meshBasicMaterial", { color: color, wireframe: true })] })) : null, children] }));
|
|
48
|
+
}
|
|
49
|
+
const PointLightComponent = {
|
|
50
|
+
name: 'PointLight',
|
|
51
|
+
Editor: PointLightComponentEditor,
|
|
52
|
+
View: PointLightView,
|
|
53
|
+
defaultProperties: {},
|
|
54
|
+
};
|
|
55
|
+
export default PointLightComponent;
|