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.
Files changed (64) hide show
  1. package/dist/helpers/SoundManager.d.ts +2 -0
  2. package/dist/helpers/SoundManager.js +6 -0
  3. package/dist/index.d.ts +20 -13
  4. package/dist/index.js +14 -7
  5. package/dist/shared/GameCanvas.js +0 -2
  6. package/dist/tools/assetviewer/page.d.ts +5 -0
  7. package/dist/tools/assetviewer/page.js +3 -0
  8. package/dist/tools/dragdrop/DragDropLoader.d.ts +3 -2
  9. package/dist/tools/dragdrop/DragDropLoader.js +18 -3
  10. package/dist/tools/dragdrop/index.d.ts +2 -2
  11. package/dist/tools/dragdrop/index.js +1 -1
  12. package/dist/tools/dragdrop/modelLoader.d.ts +10 -0
  13. package/dist/tools/dragdrop/modelLoader.js +60 -0
  14. package/dist/tools/prefabeditor/EditorTree.js +6 -40
  15. package/dist/tools/prefabeditor/EditorTreeMenus.js +2 -20
  16. package/dist/tools/prefabeditor/EditorUI.js +8 -5
  17. package/dist/tools/prefabeditor/InstanceProvider.d.ts +2 -0
  18. package/dist/tools/prefabeditor/InstanceProvider.js +54 -52
  19. package/dist/tools/prefabeditor/PrefabEditor.d.ts +23 -1
  20. package/dist/tools/prefabeditor/PrefabEditor.js +79 -47
  21. package/dist/tools/prefabeditor/PrefabRoot.d.ts +26 -9
  22. package/dist/tools/prefabeditor/PrefabRoot.js +195 -159
  23. package/dist/tools/prefabeditor/RefBridge.d.ts +24 -0
  24. package/dist/tools/prefabeditor/RefBridge.js +44 -0
  25. package/dist/tools/prefabeditor/components/AmbientLightComponent.js +10 -7
  26. package/dist/tools/prefabeditor/components/CameraComponent.js +8 -14
  27. package/dist/tools/prefabeditor/components/ClickComponent.js +12 -7
  28. package/dist/tools/prefabeditor/components/ComponentRegistry.d.ts +31 -5
  29. package/dist/tools/prefabeditor/components/ComponentRegistry.js +6 -6
  30. package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +124 -52
  31. package/dist/tools/prefabeditor/components/EnvironmentComponent.js +5 -3
  32. package/dist/tools/prefabeditor/components/GeometryComponent.js +1 -1
  33. package/dist/tools/prefabeditor/components/Input.d.ts +16 -0
  34. package/dist/tools/prefabeditor/components/Input.js +33 -0
  35. package/dist/tools/prefabeditor/components/MaterialComponent.js +19 -8
  36. package/dist/tools/prefabeditor/components/ModelComponent.js +39 -45
  37. package/dist/tools/prefabeditor/components/PhysicsComponent.d.ts +10 -1
  38. package/dist/tools/prefabeditor/components/PhysicsComponent.js +127 -31
  39. package/dist/tools/prefabeditor/components/PointLightComponent.d.ts +3 -0
  40. package/dist/tools/prefabeditor/components/PointLightComponent.js +55 -0
  41. package/dist/tools/prefabeditor/components/SoundComponent.d.ts +3 -0
  42. package/dist/tools/prefabeditor/components/SoundComponent.js +244 -0
  43. package/dist/tools/prefabeditor/components/SpotLightComponent.js +53 -24
  44. package/dist/tools/prefabeditor/components/TransformComponent.js +2 -2
  45. package/dist/tools/prefabeditor/components/index.js +4 -0
  46. package/dist/tools/prefabeditor/components/lightUtils.d.ts +13 -0
  47. package/dist/tools/prefabeditor/components/lightUtils.js +64 -0
  48. package/dist/tools/prefabeditor/prefab.d.ts +37 -0
  49. package/dist/tools/prefabeditor/prefab.js +229 -0
  50. package/dist/tools/prefabeditor/prefabStore.d.ts +4 -16
  51. package/dist/tools/prefabeditor/prefabStore.js +32 -173
  52. package/dist/tools/prefabeditor/{sceneApi.d.ts → scene.d.ts} +15 -1
  53. package/dist/tools/prefabeditor/{sceneApi.js → scene.js} +66 -32
  54. package/dist/tools/prefabeditor/styles.d.ts +1 -0
  55. package/dist/tools/prefabeditor/styles.js +9 -0
  56. package/dist/tools/prefabeditor/types.d.ts +13 -0
  57. package/dist/tools/prefabeditor/types.js +28 -1
  58. package/dist/tools/prefabeditor/useClickValid.d.ts +13 -0
  59. package/dist/tools/prefabeditor/useClickValid.js +21 -0
  60. package/dist/tools/prefabeditor/utils.d.ts +2 -4
  61. package/dist/tools/prefabeditor/utils.js +8 -46
  62. package/package.json +1 -1
  63. package/dist/tools/prefabeditor/EditorContext.d.ts +0 -16
  64. 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 { EditorContext } from '../EditorContext';
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
- const used = new Set(axes.map(axis => axis.axis));
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: nextAxis.value, count: 1, offset: 1 }]);
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 canAddAxis = axes.length < AXIS_OPTIONS.length;
36
- return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' }, children: [_jsx(Label, { children: "Repeat Axes" }), _jsx("button", { type: "button", onClick: addAxis, disabled: !canAddAxis, style: {
37
- width: 22,
38
- height: 22,
39
- borderRadius: 3,
40
- border: `1px solid ${canAddAxis ? colors.accentBorder : colors.border}`,
41
- background: canAddAxis ? colors.accentBg : colors.bgSurface,
42
- color: canAddAxis ? colors.accent : colors.textMuted,
43
- cursor: canAddAxis ? 'pointer' : 'not-allowed',
44
- fontSize: 14,
45
- lineHeight: 1,
46
- padding: 0,
47
- }, title: canAddAxis ? 'Add repeat axis' : 'All axes already in use', children: "+" })] }), axes.map((axisConfig, index) => {
48
- const usedByOthers = new Set(axes.filter((_, axisIndex) => axisIndex !== index).map(axis => axis.axis));
49
- const axisOptions = AXIS_OPTIONS.filter(option => option.value === axisConfig.axis || !usedByOthers.has(option.value));
50
- return (_jsxs("div", { style: {
51
- display: 'flex',
52
- flexDirection: 'column',
53
- gap: 6,
54
- padding: 8,
55
- border: `1px solid ${colors.border}`,
56
- borderRadius: 4,
57
- background: colors.bgSurface,
58
- }, 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 }) }), index > 0 ? (_jsx("button", { type: "button", onClick: () => removeAxis(index), style: {
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, loadedModels, children }) {
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 = loadedModels === null || loadedModels === void 0 ? void 0 : loadedModels[properties.filename];
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
- export type PhysicsProps = RigidBodyOptions & {
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 { BooleanField, FieldGroup, NumberField, SelectField, Vector3Field } from "./Input";
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
- function LockedAxisField({ label, values, onChange, }) {
20
- const enabledTranslations = Array.isArray(values.enabledTranslations)
21
- ? values.enabledTranslations
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 nextEnabledTranslations = [...enabledTranslations];
26
- nextEnabledTranslations[index] = !nextEnabledTranslations[index];
27
- onChange({ enabledTranslations: nextEnabledTranslations });
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 = !enabledTranslations[index];
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
- ] }), _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", values: component.properties, onChange: onUpdate }), _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: [
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, registerRigidBodyRef }) {
76
- const { type, colliders, sensor, activeCollisionTypes, linearVelocity = [0, 0, 0], angularVelocity = [0, 0, 0], enabledTranslations = enabledAxesFallback } = properties, otherProps = __rest(properties, ["type", "colliders", "sensor", "activeCollisionTypes", "linearVelocity", "angularVelocity", "enabledTranslations"]);
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 && registerRigidBodyRef && rigidBodyRef.current) {
188
+ if (nodeId && rigidBodyRef.current) {
96
189
  registerRigidBodyRef(nodeId, rigidBodyRef.current);
97
190
  }
98
191
  return () => {
99
- if (nodeId && registerRigidBodyRef) {
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('sensor:enter', {
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('sensor:exit', {
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('collision:enter', {
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('collision:exit', {
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 (_jsx(RigidBody, Object.assign({ ref: rigidBodyRef, type: type, colliders: colliderType, position: position, rotation: rotation, scale: scale, sensor: sensor, enabledTranslations: enabledTranslations, userData: { entityId: nodeId }, onIntersectionEnter: handleIntersectionEnter, onIntersectionExit: handleIntersectionExit, onCollisionEnter: handleCollisionEnter, onCollisionExit: handleCollisionExit }, otherProps, { children: children }), rbKey));
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
- nonComposable: true,
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,3 @@
1
+ import { Component } from './ComponentRegistry';
2
+ declare const PointLightComponent: Component;
3
+ export default PointLightComponent;
@@ -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;
@@ -0,0 +1,3 @@
1
+ import { Component } from './ComponentRegistry';
2
+ declare const SoundComponent: Component;
3
+ export default SoundComponent;