react-three-game 0.0.85 → 0.0.87
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/README.md +87 -35
- package/dist/index.d.ts +5 -7
- package/dist/index.js +2 -4
- package/dist/tools/prefabeditor/GameEvents.d.ts +36 -117
- package/dist/tools/prefabeditor/GameEvents.js +44 -96
- package/dist/tools/prefabeditor/InstanceProvider.d.ts +0 -4
- package/dist/tools/prefabeditor/InstanceProvider.js +13 -44
- package/dist/tools/prefabeditor/PrefabEditor.d.ts +7 -2
- package/dist/tools/prefabeditor/PrefabEditor.js +13 -24
- package/dist/tools/prefabeditor/PrefabRoot.js +93 -44
- package/dist/tools/prefabeditor/{runtime.d.ts → assetRuntime.d.ts} +0 -25
- package/dist/tools/prefabeditor/assetRuntime.js +37 -0
- package/dist/tools/prefabeditor/components/BufferGeometryComponent.js +4 -2
- package/dist/tools/prefabeditor/components/CameraComponent.js +1 -1
- package/dist/tools/prefabeditor/components/ComponentRegistry.d.ts +0 -3
- package/dist/tools/prefabeditor/components/DataComponent.d.ts +3 -0
- package/dist/tools/prefabeditor/components/DataComponent.js +87 -0
- package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +1 -1
- package/dist/tools/prefabeditor/components/EnvironmentComponent.js +1 -1
- package/dist/tools/prefabeditor/components/GeometryComponent.js +4 -2
- package/dist/tools/prefabeditor/components/Input.d.ts +2 -13
- package/dist/tools/prefabeditor/components/Input.js +0 -55
- package/dist/tools/prefabeditor/components/MaterialComponent.js +1 -1
- package/dist/tools/prefabeditor/components/ModelComponent.js +3 -3
- package/dist/tools/prefabeditor/components/PhysicsComponent.d.ts +4 -0
- package/dist/tools/prefabeditor/components/PhysicsComponent.js +64 -130
- package/dist/tools/prefabeditor/components/PointLightComponent.js +1 -1
- package/dist/tools/prefabeditor/components/SoundComponent.js +18 -11
- package/dist/tools/prefabeditor/components/SpotLightComponent.js +1 -1
- package/dist/tools/prefabeditor/components/index.js +2 -2
- package/dist/tools/prefabeditor/types.d.ts +1 -0
- package/dist/tools/prefabeditor/types.js +18 -0
- package/dist/tools/prefabeditor/usePointerEvents.d.ts +27 -0
- package/dist/tools/prefabeditor/usePointerEvents.js +52 -0
- package/package.json +1 -1
- package/dist/tools/prefabeditor/components/ClickComponent.d.ts +0 -3
- package/dist/tools/prefabeditor/components/ClickComponent.js +0 -52
- package/dist/tools/prefabeditor/runtime.js +0 -184
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { colors } from "../styles";
|
|
4
|
+
const RESERVED_USER_DATA_KEYS = new Set([
|
|
5
|
+
'prefabNodeId',
|
|
6
|
+
'prefabNodeName',
|
|
7
|
+
]);
|
|
8
|
+
const inputStyle = {
|
|
9
|
+
width: '100%',
|
|
10
|
+
backgroundColor: colors.bgInput,
|
|
11
|
+
border: `1px solid ${colors.border}`,
|
|
12
|
+
padding: '6px 8px',
|
|
13
|
+
fontSize: '11px',
|
|
14
|
+
color: colors.text,
|
|
15
|
+
fontFamily: 'monospace',
|
|
16
|
+
outline: 'none',
|
|
17
|
+
borderRadius: 3,
|
|
18
|
+
boxSizing: 'border-box',
|
|
19
|
+
};
|
|
20
|
+
function isRecord(value) {
|
|
21
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
22
|
+
}
|
|
23
|
+
function normalizeData(value) {
|
|
24
|
+
if (!isRecord(value))
|
|
25
|
+
return {};
|
|
26
|
+
return Object.entries(value).reduce((result, [key, entry]) => {
|
|
27
|
+
if (!key.trim() || entry === undefined) {
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
result[key] = entry;
|
|
31
|
+
return result;
|
|
32
|
+
}, {});
|
|
33
|
+
}
|
|
34
|
+
function formatData(value) {
|
|
35
|
+
return JSON.stringify(normalizeData(value), null, 2);
|
|
36
|
+
}
|
|
37
|
+
function parseData(raw) {
|
|
38
|
+
const trimmed = raw.trim();
|
|
39
|
+
if (!trimmed) {
|
|
40
|
+
return { ok: true, value: {} };
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const parsed = JSON.parse(trimmed);
|
|
44
|
+
if (!isRecord(parsed)) {
|
|
45
|
+
return { ok: false, error: 'Data must be a JSON object' };
|
|
46
|
+
}
|
|
47
|
+
const nextData = normalizeData(parsed);
|
|
48
|
+
for (const key of Object.keys(nextData)) {
|
|
49
|
+
if (RESERVED_USER_DATA_KEYS.has(key)) {
|
|
50
|
+
return { ok: false, error: `Reserved key: ${key}` };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return { ok: true, value: nextData };
|
|
54
|
+
}
|
|
55
|
+
catch (_a) {
|
|
56
|
+
return { ok: false, error: 'Data must be valid JSON' };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function DataComponentEditor({ component, onUpdate }) {
|
|
60
|
+
var _a;
|
|
61
|
+
const [draft, setDraft] = useState(() => { var _a; return formatData((_a = component.properties) === null || _a === void 0 ? void 0 : _a.data); });
|
|
62
|
+
const [error, setError] = useState(null);
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
var _a;
|
|
65
|
+
setDraft(formatData((_a = component.properties) === null || _a === void 0 ? void 0 : _a.data));
|
|
66
|
+
setError(null);
|
|
67
|
+
}, [(_a = component.properties) === null || _a === void 0 ? void 0 : _a.data]);
|
|
68
|
+
const commitDraft = () => {
|
|
69
|
+
const parsed = parseData(draft);
|
|
70
|
+
if (!parsed.ok) {
|
|
71
|
+
setError(parsed.error);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
setError(null);
|
|
75
|
+
setDraft(formatData(parsed.value));
|
|
76
|
+
onUpdate({ data: parsed.value });
|
|
77
|
+
};
|
|
78
|
+
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: [_jsx("textarea", { rows: 10, spellCheck: false, value: draft, onChange: (event) => setDraft(event.target.value), onBlur: commitDraft, style: Object.assign(Object.assign({}, inputStyle), { resize: 'vertical', minHeight: 140 }) }), error ? (_jsx("div", { style: { fontSize: 10, color: colors.accent, fontFamily: 'monospace' }, children: error })) : null, _jsx("div", { style: { fontSize: 10, color: colors.textMuted, lineHeight: 1.4 }, children: "Enter a JSON object. Keys map directly onto `object.userData`." })] }));
|
|
79
|
+
}
|
|
80
|
+
const DataComponent = {
|
|
81
|
+
name: 'Data',
|
|
82
|
+
Editor: DataComponentEditor,
|
|
83
|
+
defaultProperties: {
|
|
84
|
+
data: {},
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
export default DataComponent;
|
|
@@ -3,7 +3,7 @@ import { useHelper } from "@react-three/drei";
|
|
|
3
3
|
import { useRef, useEffect, useState } from "react";
|
|
4
4
|
import { useFrame } from "@react-three/fiber";
|
|
5
5
|
import { CameraHelper } from "three";
|
|
6
|
-
import { useEntityRuntime } from "../
|
|
6
|
+
import { useEntityRuntime } from "../assetRuntime";
|
|
7
7
|
import { BooleanField, ColorField, NumberField, NumberInput, Vector3Input } from "./Input";
|
|
8
8
|
import { LightSection, ShadowBiasField, mergeWithDefaults } from "./lightUtils";
|
|
9
9
|
import { colors } from "../styles";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Environment } from '@react-three/drei';
|
|
3
3
|
import { FieldGroup, NumberField } from './Input';
|
|
4
|
-
import { useAssetRuntime } from '../
|
|
4
|
+
import { useAssetRuntime } from '../assetRuntime';
|
|
5
5
|
function EnvironmentView({ properties, children, }) {
|
|
6
6
|
const { getAssetRevision } = useAssetRuntime();
|
|
7
7
|
const { intensity = 1, resolution = 256 } = properties;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { BooleanField, FieldGroup, NumberField, SelectField } from "./Input";
|
|
2
|
+
import { BooleanField, FieldGroup, NumberField, SelectField, StringField } from "./Input";
|
|
3
3
|
const GEOMETRY_ARGS = {
|
|
4
4
|
box: {
|
|
5
5
|
fields: [
|
|
@@ -61,7 +61,7 @@ function GeometryComponentEditor({ component, onUpdate, }) {
|
|
|
61
61
|
] }), schema.fields.map((field, index) => {
|
|
62
62
|
var _a;
|
|
63
63
|
return (_jsx(NumberField, { name: field.name, label: field.label, values: { [field.name]: (_a = args[index]) !== null && _a !== void 0 ? _a : field.defaultValue }, onChange: (next) => updateArg(index, next[field.name]), fallback: field.defaultValue, min: field.min, step: field.step }, field.name));
|
|
64
|
-
}), _jsx(BooleanField, { name: "visible", label: "Visible", values: component.properties, onChange: handleChange, fallback: true }), _jsx(BooleanField, { name: "castShadow", label: "Cast Shadow", values: component.properties, onChange: handleChange, fallback: true }), _jsx(BooleanField, { name: "receiveShadow", label: "Receive Shadow", values: component.properties, onChange: handleChange, fallback: true })] }));
|
|
64
|
+
}), _jsx(BooleanField, { name: "visible", label: "Visible", values: component.properties, onChange: handleChange, fallback: true }), _jsx(BooleanField, { name: "castShadow", label: "Cast Shadow", values: component.properties, onChange: handleChange, fallback: true }), _jsx(BooleanField, { name: "receiveShadow", label: "Receive Shadow", values: component.properties, onChange: handleChange, fallback: true }), _jsx(BooleanField, { name: "emitClickEvent", label: "Emit Click Event", values: component.properties, onChange: handleChange, fallback: false }), component.properties.emitClickEvent ? (_jsx(StringField, { name: "clickEventName", label: "Click Event Name", values: component.properties, onChange: handleChange, placeholder: "cannon:fire" })) : null] }));
|
|
65
65
|
}
|
|
66
66
|
// View for Geometry component
|
|
67
67
|
function GeometryComponentView({ properties, children }) {
|
|
@@ -87,6 +87,8 @@ const GeometryComponent = {
|
|
|
87
87
|
defaultProperties: {
|
|
88
88
|
geometryType: 'box',
|
|
89
89
|
args: getDefaultArgs('box'),
|
|
90
|
+
emitClickEvent: false,
|
|
91
|
+
clickEventName: '',
|
|
90
92
|
}
|
|
91
93
|
};
|
|
92
94
|
export default GeometryComponent;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
export type FieldType = 'vector3' | 'number' | 'string' | 'color' | 'boolean' | 'select' | 'node'
|
|
2
|
+
export type FieldType = 'vector3' | 'number' | 'string' | 'color' | 'boolean' | 'select' | 'node';
|
|
3
3
|
interface BaseFieldDefinition {
|
|
4
4
|
name: string;
|
|
5
5
|
label: string;
|
|
@@ -36,10 +36,6 @@ interface NodeFieldDefinition extends BaseFieldDefinition {
|
|
|
36
36
|
placeholder?: string;
|
|
37
37
|
includeRoot?: boolean;
|
|
38
38
|
}
|
|
39
|
-
interface EventFieldDefinition extends BaseFieldDefinition {
|
|
40
|
-
type: 'event';
|
|
41
|
-
placeholder?: string;
|
|
42
|
-
}
|
|
43
39
|
interface CustomFieldDefinition extends BaseFieldDefinition {
|
|
44
40
|
type: 'custom';
|
|
45
41
|
render: (props: {
|
|
@@ -49,7 +45,7 @@ interface CustomFieldDefinition extends BaseFieldDefinition {
|
|
|
49
45
|
onChangeMultiple: (values: Record<string, any>) => void;
|
|
50
46
|
}) => React.ReactNode;
|
|
51
47
|
}
|
|
52
|
-
export type FieldDefinition = Vector3FieldDefinition | NumberFieldDefinition | StringFieldDefinition | ColorFieldDefinition | BooleanFieldDefinition | SelectFieldDefinition | NodeFieldDefinition |
|
|
48
|
+
export type FieldDefinition = Vector3FieldDefinition | NumberFieldDefinition | StringFieldDefinition | ColorFieldDefinition | BooleanFieldDefinition | SelectFieldDefinition | NodeFieldDefinition | CustomFieldDefinition;
|
|
53
49
|
declare const styles: {
|
|
54
50
|
input: React.CSSProperties;
|
|
55
51
|
label: React.CSSProperties;
|
|
@@ -95,12 +91,6 @@ export declare function NodeInput({ label, value, onChange, placeholder, include
|
|
|
95
91
|
placeholder?: string;
|
|
96
92
|
includeRoot?: boolean;
|
|
97
93
|
}): import("react/jsx-runtime").JSX.Element;
|
|
98
|
-
export declare function EventInput({ label, value, onChange, placeholder, }: {
|
|
99
|
-
label: string;
|
|
100
|
-
value: string;
|
|
101
|
-
onChange: (value: string) => void;
|
|
102
|
-
placeholder?: string;
|
|
103
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
104
94
|
export declare function BooleanInput({ label, value, onChange }: {
|
|
105
95
|
label?: string;
|
|
106
96
|
value: boolean;
|
|
@@ -182,7 +172,6 @@ export declare function SelectField({ name, label, values, onChange, fallback, o
|
|
|
182
172
|
export declare function NodeField({ name, label, values, onChange, fallback, }: BoundStringFieldProps & {
|
|
183
173
|
fallback?: string;
|
|
184
174
|
}): import("react/jsx-runtime").JSX.Element;
|
|
185
|
-
export declare function EventField({ name, label, values, onChange, fallback, placeholder, }: BoundStringFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
186
175
|
export declare function Vector3Field({ name, label, values, onChange, fallback, snap, labelExtra, }: BoundVector3FieldProps): import("react/jsx-runtime").JSX.Element;
|
|
187
176
|
interface FieldRendererProps {
|
|
188
177
|
fields: FieldDefinition[];
|
|
@@ -347,55 +347,6 @@ export function NodeInput({ label, value, onChange, placeholder, includeRoot = t
|
|
|
347
347
|
setQuery('');
|
|
348
348
|
}, emptyMessage: "No matching nodes." })] })) : null] }));
|
|
349
349
|
}
|
|
350
|
-
const BUILT_IN_EVENT_OPTIONS = [
|
|
351
|
-
'sensor:enter',
|
|
352
|
-
'sensor:exit',
|
|
353
|
-
'collision:enter',
|
|
354
|
-
'collision:exit',
|
|
355
|
-
'click',
|
|
356
|
-
].map(eventName => ({
|
|
357
|
-
value: eventName,
|
|
358
|
-
label: eventName,
|
|
359
|
-
searchText: eventName.toLowerCase(),
|
|
360
|
-
}));
|
|
361
|
-
export function EventInput({ label, value, onChange, placeholder, }) {
|
|
362
|
-
const prefabState = useOptionalPrefabSnapshot();
|
|
363
|
-
const [query, setQuery] = useState('');
|
|
364
|
-
const options = useMemo(() => {
|
|
365
|
-
var _a;
|
|
366
|
-
const authoredEvents = new Map();
|
|
367
|
-
Object.values((_a = prefabState === null || prefabState === void 0 ? void 0 : prefabState.nodesById) !== null && _a !== void 0 ? _a : {}).forEach(node => {
|
|
368
|
-
var _a;
|
|
369
|
-
Object.values((_a = node.components) !== null && _a !== void 0 ? _a : {}).forEach(component => {
|
|
370
|
-
var _a;
|
|
371
|
-
Object.entries((_a = component === null || component === void 0 ? void 0 : component.properties) !== null && _a !== void 0 ? _a : {}).forEach(([key, entry]) => {
|
|
372
|
-
var _a, _b;
|
|
373
|
-
if (typeof entry !== 'string')
|
|
374
|
-
return;
|
|
375
|
-
if (!(key === 'eventName' || key.endsWith('EventName')))
|
|
376
|
-
return;
|
|
377
|
-
const eventName = entry.trim();
|
|
378
|
-
if (!eventName)
|
|
379
|
-
return;
|
|
380
|
-
authoredEvents.set(eventName, {
|
|
381
|
-
value: eventName,
|
|
382
|
-
label: eventName,
|
|
383
|
-
description: `${(_a = component === null || component === void 0 ? void 0 : component.type) !== null && _a !== void 0 ? _a : 'Component'} -> ${key}`,
|
|
384
|
-
searchText: `${eventName} ${(_b = component === null || component === void 0 ? void 0 : component.type) !== null && _b !== void 0 ? _b : ''} ${key}`.toLowerCase(),
|
|
385
|
-
});
|
|
386
|
-
});
|
|
387
|
-
});
|
|
388
|
-
});
|
|
389
|
-
const merged = new Map();
|
|
390
|
-
BUILT_IN_EVENT_OPTIONS.forEach(option => merged.set(option.value, option));
|
|
391
|
-
authoredEvents.forEach((option, key) => merged.set(key, option));
|
|
392
|
-
return [...merged.values()].sort((left, right) => left.value.localeCompare(right.value));
|
|
393
|
-
}, [prefabState === null || prefabState === void 0 ? void 0 : prefabState.nodesById]);
|
|
394
|
-
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 6 }, children: [_jsx(StringInput, { label: label, value: value, onChange: onChange, placeholder: placeholder !== null && placeholder !== void 0 ? placeholder : 'Event name' }), _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 4 }, children: [_jsx("input", { type: "text", style: Object.assign(Object.assign({}, styles.input), { width: '100%', textAlign: 'left' }), value: query, onChange: e => setQuery(e.target.value), placeholder: "Search built-in and authored events" }), _jsx(SearchSuggestionList, { query: query, options: options, onSelect: (nextValue) => {
|
|
395
|
-
onChange(nextValue);
|
|
396
|
-
setQuery('');
|
|
397
|
-
}, emptyMessage: "No matching events." })] })] }));
|
|
398
|
-
}
|
|
399
350
|
export function BooleanInput({ label, value, onChange }) {
|
|
400
351
|
return (_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [label && _jsx(Label, { children: label }), _jsx("input", { type: "checkbox", style: {
|
|
401
352
|
height: 16,
|
|
@@ -470,10 +421,6 @@ export function NodeField({ name, label, values, onChange, fallback = '', }) {
|
|
|
470
421
|
var _a;
|
|
471
422
|
return (_jsx(NodeInput, { label: label, value: (_a = values[name]) !== null && _a !== void 0 ? _a : fallback, onChange: bindFieldChange(name, onChange) }));
|
|
472
423
|
}
|
|
473
|
-
export function EventField({ name, label, values, onChange, fallback = '', placeholder, }) {
|
|
474
|
-
var _a;
|
|
475
|
-
return (_jsx(EventInput, { label: label, value: (_a = values[name]) !== null && _a !== void 0 ? _a : fallback, onChange: bindFieldChange(name, onChange), placeholder: placeholder }));
|
|
476
|
-
}
|
|
477
424
|
export function Vector3Field({ name, label, values, onChange, fallback = [0, 0, 0], snap, labelExtra, }) {
|
|
478
425
|
var _a;
|
|
479
426
|
return (_jsx(Vector3Input, { label: label, value: (_a = values[name]) !== null && _a !== void 0 ? _a : fallback, onChange: bindFieldChange(name, onChange), snap: snap, labelExtra: labelExtra }));
|
|
@@ -500,8 +447,6 @@ export function FieldRenderer({ fields, values, onChange }) {
|
|
|
500
447
|
return (_jsx(SelectInput, { label: field.label, value: (_b = value !== null && value !== void 0 ? value : (_a = field.options[0]) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : '', onChange: v => updateField(field.name, v), options: field.options }, field.name));
|
|
501
448
|
case 'node':
|
|
502
449
|
return (_jsx(NodeInput, { label: field.label, value: value !== null && value !== void 0 ? value : '', onChange: v => updateField(field.name, v), placeholder: field.placeholder, includeRoot: field.includeRoot }, field.name));
|
|
503
|
-
case 'event':
|
|
504
|
-
return (_jsx(EventInput, { label: field.label, value: value !== null && value !== void 0 ? value : '', onChange: v => updateField(field.name, v), placeholder: field.placeholder }, field.name));
|
|
505
450
|
case 'custom':
|
|
506
451
|
return (_jsxs("div", { children: [field.label && _jsx(Label, { children: field.label }), field.render({
|
|
507
452
|
value,
|
|
@@ -14,7 +14,7 @@ import { createContext, useContext, useMemo, useRef } from 'react';
|
|
|
14
14
|
import { extend } from '@react-three/fiber';
|
|
15
15
|
import { useFrame } from '@react-three/fiber';
|
|
16
16
|
import { FieldRenderer, Label, NumberInput } from './Input';
|
|
17
|
-
import { useAssetRuntime } from '../
|
|
17
|
+
import { useAssetRuntime } from '../assetRuntime';
|
|
18
18
|
import { MeshBasicNodeMaterial, MeshStandardNodeMaterial } from 'three/webgpu';
|
|
19
19
|
import { TexturePicker } from '../../assetviewer/page';
|
|
20
20
|
import { RepeatWrapping, ClampToEdgeWrapping, SRGBColorSpace, LinearSRGBColorSpace, NearestFilter, LinearFilter, NearestMipmapNearestFilter, NearestMipmapLinearFilter, LinearMipmapNearestFilter, LinearMipmapLinearFilter, FrontSide, BackSide, DoubleSide, } from 'three';
|
|
@@ -1,8 +1,8 @@
|
|
|
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, ListEditor, NumberInput, SelectInput } from './Input';
|
|
5
|
-
import { useAssetRuntime } from '../
|
|
4
|
+
import { BooleanField, FieldGroup, Label, ListEditor, NumberInput, SelectInput, StringField } from './Input';
|
|
5
|
+
import { useAssetRuntime } from '../assetRuntime';
|
|
6
6
|
import { EditorContext } from '../PrefabEditor';
|
|
7
7
|
import { getRepeatAxesFromModelProperties, normalizeRepeatAxes } from '../InstanceProvider';
|
|
8
8
|
import { colors } from '../styles';
|
|
@@ -61,7 +61,7 @@ function ModelComponentEditor({ component, node, onUpdate, basePath = "" }) {
|
|
|
61
61
|
const editorContext = useContext(EditorContext);
|
|
62
62
|
const positionSnap = (_a = editorContext === null || editorContext === void 0 ? void 0 : editorContext.positionSnap) !== null && _a !== void 0 ? _a : 0.5;
|
|
63
63
|
const repeatAxes = getRepeatAxesFromModelProperties(component.properties);
|
|
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 }))] }))] }));
|
|
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: "emitClickEvent", label: "Emit Click Event", values: component.properties, onChange: onUpdate, fallback: false }), component.properties.emitClickEvent ? (_jsx(StringField, { name: "clickEventName", label: "Click Event Name", values: component.properties, onChange: onUpdate, placeholder: "entity:click" })) : null] })) : null, 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 }))] }))] }));
|
|
65
65
|
}
|
|
66
66
|
// View for Model component
|
|
67
67
|
function ModelComponentView({ properties, children }) {
|
|
@@ -8,9 +8,13 @@ export type PhysicsProps = Omit<RigidBodyOptions, 'colliders'> & {
|
|
|
8
8
|
angularVelocity?: [number, number, number];
|
|
9
9
|
capsuleRadius?: number;
|
|
10
10
|
capsuleHalfHeight?: number;
|
|
11
|
+
emitSensorEnterEvent?: boolean;
|
|
11
12
|
sensorEnterEventName?: string;
|
|
13
|
+
emitSensorExitEvent?: boolean;
|
|
12
14
|
sensorExitEventName?: string;
|
|
15
|
+
emitCollisionEnterEvent?: boolean;
|
|
13
16
|
collisionEnterEventName?: string;
|
|
17
|
+
emitCollisionExitEvent?: boolean;
|
|
14
18
|
collisionExitEventName?: string;
|
|
15
19
|
};
|
|
16
20
|
export declare function isPhysicsProps(v: any): v is PhysicsProps;
|
|
@@ -11,10 +11,12 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
11
11
|
};
|
|
12
12
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
13
13
|
import { CapsuleCollider, RigidBody, useRapier } from "@react-three/rapier";
|
|
14
|
-
import {
|
|
15
|
-
import { useAssetRuntime, useEntityRuntime } from "../
|
|
16
|
-
import { BooleanField, EventInput, FieldGroup, ListEditor, NumberField, SelectField, SelectInput, Vector3Field } from "./Input";
|
|
14
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
15
|
+
import { useAssetRuntime, useEntityRuntime } from "../assetRuntime";
|
|
17
16
|
import { gameEvents, getEntityIdFromRigidBody } from "../GameEvents";
|
|
17
|
+
import { usePrefabNode } from "../prefabStore";
|
|
18
|
+
import { BooleanField, FieldGroup, NumberField, SelectField, StringField, Vector3Field } from "./Input";
|
|
19
|
+
import { getNodeUserData } from "../types";
|
|
18
20
|
import { colors } from "../styles";
|
|
19
21
|
export function isPhysicsProps(v) {
|
|
20
22
|
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";
|
|
@@ -22,90 +24,6 @@ export function isPhysicsProps(v) {
|
|
|
22
24
|
const enabledAxesFallback = [true, true, true];
|
|
23
25
|
const capsuleRadiusFallback = 0.35;
|
|
24
26
|
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(EventInput, { 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
27
|
function LockedAxisField({ label, name, values, onChange, }) {
|
|
110
28
|
const enabledAxes = Array.isArray(values[name])
|
|
111
29
|
? values[name]
|
|
@@ -158,15 +76,19 @@ function PhysicsComponentEditor({ component, onUpdate }) {
|
|
|
158
76
|
{ value: 'cuboid', label: 'Cuboid (box)' },
|
|
159
77
|
{ value: 'ball', label: 'Ball (sphere)' },
|
|
160
78
|
{ 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(
|
|
79
|
+
] }), 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(BooleanField, { name: "emitCollisionEnterEvent", label: "Emit Collision Enter", values: component.properties, onChange: onUpdate, fallback: false }), component.properties.emitCollisionEnterEvent ? (_jsx(StringField, { name: "collisionEnterEventName", label: "Collision Enter Event", values: component.properties, onChange: onUpdate, placeholder: "target:hit" })) : null, _jsx(BooleanField, { name: "emitCollisionExitEvent", label: "Emit Collision Exit", values: component.properties, onChange: onUpdate, fallback: false }), component.properties.emitCollisionExitEvent ? (_jsx(StringField, { name: "collisionExitEventName", label: "Collision Exit Event", values: component.properties, onChange: onUpdate, placeholder: "target:reset" })) : null, _jsx(BooleanField, { name: "emitSensorEnterEvent", label: "Emit Sensor Enter", values: component.properties, onChange: onUpdate, fallback: false }), component.properties.emitSensorEnterEvent ? (_jsx(StringField, { name: "sensorEnterEventName", label: "Sensor Enter Event", values: component.properties, onChange: onUpdate, placeholder: "sensor:enter" })) : null, _jsx(BooleanField, { name: "emitSensorExitEvent", label: "Emit Sensor Exit", values: component.properties, onChange: onUpdate, fallback: false }), component.properties.emitSensorExitEvent ? (_jsx(StringField, { name: "sensorExitEventName", label: "Sensor Exit Event", values: component.properties, onChange: onUpdate, placeholder: "sensor:exit" })) : null, _jsx(SelectField, { name: "activeCollisionTypes", label: "Collision Detection", values: component.properties, onChange: onUpdate, options: [
|
|
162
80
|
{ value: '', label: 'Default (Dynamic only)' },
|
|
163
81
|
{ value: 'all', label: 'All (includes kinematic & fixed)' },
|
|
164
82
|
] })] }));
|
|
165
83
|
}
|
|
166
84
|
function PhysicsComponentView({ properties, children, position, rotation, scale }) {
|
|
85
|
+
var _a, _b, _c;
|
|
167
86
|
const { registerRigidBodyRef } = useAssetRuntime();
|
|
168
|
-
const { editMode, nodeId } = useEntityRuntime();
|
|
169
|
-
const
|
|
87
|
+
const { editMode, nodeId, getObject } = useEntityRuntime();
|
|
88
|
+
const gameObject = usePrefabNode(nodeId);
|
|
89
|
+
const nodeName = (_b = (_a = gameObject === null || gameObject === void 0 ? void 0 : gameObject.name) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : '';
|
|
90
|
+
const userData = Object.assign(Object.assign({ prefabNodeId: (_c = gameObject === null || gameObject === void 0 ? void 0 : gameObject.id) !== null && _c !== void 0 ? _c : nodeId }, (nodeName ? { prefabNodeName: nodeName } : {})), getNodeUserData(gameObject));
|
|
91
|
+
const { type, colliders, sensor, activeCollisionTypes, linearVelocity = [0, 0, 0], angularVelocity = [0, 0, 0], capsuleRadius = capsuleRadiusFallback, capsuleHalfHeight = capsuleHalfHeightFallback, emitSensorEnterEvent = false, sensorEnterEventName, emitSensorExitEvent = false, sensorExitEventName, emitCollisionEnterEvent = false, collisionEnterEventName, emitCollisionExitEvent = false, collisionExitEventName, enabledTranslations = enabledAxesFallback, enabledRotations = enabledAxesFallback } = properties, otherProps = __rest(properties, ["type", "colliders", "sensor", "activeCollisionTypes", "linearVelocity", "angularVelocity", "capsuleRadius", "capsuleHalfHeight", "emitSensorEnterEvent", "sensorEnterEventName", "emitSensorExitEvent", "sensorExitEventName", "emitCollisionEnterEvent", "collisionEnterEventName", "emitCollisionExitEvent", "collisionExitEventName", "enabledTranslations", "enabledRotations"]);
|
|
170
92
|
const colliderType = colliders || (type === 'fixed' ? 'trimesh' : 'hull');
|
|
171
93
|
const usesManualCapsuleCollider = colliderType === 'capsule';
|
|
172
94
|
const rigidBodyRef = useRef(null);
|
|
@@ -175,6 +97,12 @@ function PhysicsComponentView({ properties, children, position, rotation, scale
|
|
|
175
97
|
const rbKey = editMode
|
|
176
98
|
? `${type || 'dynamic'}_${colliderType}_${capsuleRadius}_${capsuleHalfHeight}_${position === null || position === void 0 ? void 0 : position.join(',')}_${rotation === null || rotation === void 0 ? void 0 : rotation.join(',')}`
|
|
177
99
|
: `${type || 'dynamic'}_${colliderType}_${capsuleRadius}_${capsuleHalfHeight}`;
|
|
100
|
+
const handleRigidBodyRef = useCallback((rigidBody) => {
|
|
101
|
+
rigidBodyRef.current = rigidBody;
|
|
102
|
+
if (!nodeId)
|
|
103
|
+
return;
|
|
104
|
+
registerRigidBodyRef(nodeId, rigidBody);
|
|
105
|
+
}, [nodeId, registerRigidBodyRef]);
|
|
178
106
|
// Try to get rapier context - will be null if not inside <Physics>
|
|
179
107
|
let rapier = null;
|
|
180
108
|
try {
|
|
@@ -184,17 +112,6 @@ function PhysicsComponentView({ properties, children, position, rotation, scale
|
|
|
184
112
|
catch (e) {
|
|
185
113
|
// Not inside Physics context - that's ok, just won't have rapier features
|
|
186
114
|
}
|
|
187
|
-
// Register RigidBody ref when it's available
|
|
188
|
-
useEffect(() => {
|
|
189
|
-
if (nodeId && rigidBodyRef.current) {
|
|
190
|
-
registerRigidBodyRef(nodeId, rigidBodyRef.current);
|
|
191
|
-
}
|
|
192
|
-
return () => {
|
|
193
|
-
if (nodeId) {
|
|
194
|
-
registerRigidBodyRef(nodeId, null);
|
|
195
|
-
}
|
|
196
|
-
};
|
|
197
|
-
}, [nodeId, registerRigidBodyRef]);
|
|
198
115
|
// Configure active collision types for kinematic/sensor bodies
|
|
199
116
|
useEffect(() => {
|
|
200
117
|
if (activeCollisionTypes === 'all' && rigidBodyRef.current && rapier) {
|
|
@@ -227,44 +144,53 @@ function PhysicsComponentView({ properties, children, position, rotation, scale
|
|
|
227
144
|
z: angularVelocity[2],
|
|
228
145
|
}, true);
|
|
229
146
|
}, [rbKey, angularVelocityKey]);
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
if (!nodeId
|
|
147
|
+
const dispatchPhysicsEvent = useCallback((eventType, payload) => {
|
|
148
|
+
var _a, _b, _c;
|
|
149
|
+
if (!nodeId)
|
|
150
|
+
return;
|
|
151
|
+
const trimmedEventType = eventType === null || eventType === void 0 ? void 0 : eventType.trim();
|
|
152
|
+
if (!trimmedEventType)
|
|
233
153
|
return;
|
|
234
|
-
|
|
154
|
+
const targetEntityId = getEntityIdFromRigidBody(payload.other.rigidBody);
|
|
155
|
+
gameEvents.emit(trimmedEventType, {
|
|
235
156
|
sourceEntityId: nodeId,
|
|
236
|
-
|
|
237
|
-
|
|
157
|
+
sourceNodeId: nodeId,
|
|
158
|
+
sourceObject: getObject(),
|
|
159
|
+
sourceRigidBody: rigidBodyRef.current,
|
|
160
|
+
targetEntityId,
|
|
161
|
+
targetNodeId: targetEntityId,
|
|
162
|
+
targetObject: (_b = (_a = payload.other.rigidBodyObject) !== null && _a !== void 0 ? _a : payload.other.colliderObject) !== null && _b !== void 0 ? _b : null,
|
|
163
|
+
targetRigidBody: (_c = payload.other.rigidBody) !== null && _c !== void 0 ? _c : null,
|
|
164
|
+
rapierEvent: payload,
|
|
238
165
|
});
|
|
239
|
-
}, [
|
|
166
|
+
}, [getObject, nodeId]);
|
|
167
|
+
const handleIntersectionEnter = useCallback((payload) => {
|
|
168
|
+
if (!emitSensorEnterEvent)
|
|
169
|
+
return;
|
|
170
|
+
dispatchPhysicsEvent(sensorEnterEventName, payload);
|
|
171
|
+
}, [dispatchPhysicsEvent, emitSensorEnterEvent, sensorEnterEventName]);
|
|
240
172
|
const handleIntersectionExit = useCallback((payload) => {
|
|
241
|
-
if (!
|
|
173
|
+
if (!emitSensorExitEvent)
|
|
242
174
|
return;
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
targetEntityId: getEntityIdFromRigidBody(payload.other.rigidBody),
|
|
246
|
-
targetRigidBody: payload.other.rigidBody,
|
|
247
|
-
});
|
|
248
|
-
}, [nodeId, sensorExitEventName]);
|
|
175
|
+
dispatchPhysicsEvent(sensorExitEventName, payload);
|
|
176
|
+
}, [dispatchPhysicsEvent, emitSensorExitEvent, sensorExitEventName]);
|
|
249
177
|
const handleCollisionEnter = useCallback((payload) => {
|
|
250
|
-
if (!
|
|
178
|
+
if (!emitCollisionEnterEvent)
|
|
251
179
|
return;
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
targetEntityId: getEntityIdFromRigidBody(payload.other.rigidBody),
|
|
255
|
-
targetRigidBody: payload.other.rigidBody,
|
|
256
|
-
});
|
|
257
|
-
}, [collisionEnterEventName, nodeId]);
|
|
180
|
+
dispatchPhysicsEvent(collisionEnterEventName, payload);
|
|
181
|
+
}, [collisionEnterEventName, dispatchPhysicsEvent, emitCollisionEnterEvent]);
|
|
258
182
|
const handleCollisionExit = useCallback((payload) => {
|
|
259
|
-
if (!
|
|
183
|
+
if (!emitCollisionExitEvent)
|
|
260
184
|
return;
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
185
|
+
dispatchPhysicsEvent(collisionExitEventName, payload);
|
|
186
|
+
}, [collisionExitEventName, dispatchPhysicsEvent, emitCollisionExitEvent]);
|
|
187
|
+
const rigidBodyProps = Object.assign({ ref: handleRigidBodyRef, type, colliders: usesManualCapsuleCollider ? false : colliderType, position,
|
|
188
|
+
rotation,
|
|
189
|
+
scale,
|
|
190
|
+
sensor,
|
|
191
|
+
enabledTranslations,
|
|
192
|
+
enabledRotations, name: nodeName, userData: Object.assign({ entityId: nodeId }, userData), onIntersectionEnter: emitSensorEnterEvent ? handleIntersectionEnter : undefined, onIntersectionExit: emitSensorExitEvent ? handleIntersectionExit : undefined, onCollisionEnter: emitCollisionEnterEvent ? handleCollisionEnter : undefined, onCollisionExit: emitCollisionExitEvent ? handleCollisionExit : undefined }, otherProps);
|
|
193
|
+
return (_jsxs(RigidBody, Object.assign({}, rigidBodyProps, { children: [usesManualCapsuleCollider ? _jsx(CapsuleCollider, { args: [capsuleHalfHeight, capsuleRadius], sensor: sensor }) : null, children] }), rbKey));
|
|
268
194
|
}
|
|
269
195
|
const PhysicsComponent = {
|
|
270
196
|
name: 'Physics',
|
|
@@ -279,6 +205,14 @@ const PhysicsComponent = {
|
|
|
279
205
|
angularVelocity: [0, 0, 0],
|
|
280
206
|
enabledTranslations: [true, true, true],
|
|
281
207
|
enabledRotations: [true, true, true],
|
|
208
|
+
emitSensorEnterEvent: false,
|
|
209
|
+
sensorEnterEventName: '',
|
|
210
|
+
emitSensorExitEvent: false,
|
|
211
|
+
sensorExitEventName: '',
|
|
212
|
+
emitCollisionEnterEvent: false,
|
|
213
|
+
collisionEnterEventName: '',
|
|
214
|
+
emitCollisionExitEvent: false,
|
|
215
|
+
collisionExitEventName: '',
|
|
282
216
|
}
|
|
283
217
|
};
|
|
284
218
|
export default PhysicsComponent;
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { useEffect, useRef } from 'react';
|
|
3
3
|
import { useHelper } from '@react-three/drei';
|
|
4
4
|
import { PointLightHelper } from 'three';
|
|
5
|
-
import { useEntityRuntime } from '../
|
|
5
|
+
import { useEntityRuntime } from '../assetRuntime';
|
|
6
6
|
import { BooleanField, ColorField, NumberField } from './Input';
|
|
7
7
|
import { LightSection, ShadowBiasField, mergeWithDefaults } from './lightUtils';
|
|
8
8
|
const pointLightDefaults = {
|