react-three-game 0.0.91 → 0.0.93

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 (34) hide show
  1. package/README.md +68 -33
  2. package/dist/helpers/index.d.ts +0 -3
  3. package/dist/helpers/index.js +1 -8
  4. package/dist/index.d.ts +5 -8
  5. package/dist/index.js +3 -4
  6. package/dist/tools/assetviewer/page.js +38 -10
  7. package/dist/tools/prefabeditor/EditorTree.js +2 -2
  8. package/dist/tools/prefabeditor/GameEvents.d.ts +6 -12
  9. package/dist/tools/prefabeditor/GameEvents.js +0 -8
  10. package/dist/tools/prefabeditor/InstanceProvider.d.ts +6 -4
  11. package/dist/tools/prefabeditor/InstanceProvider.js +84 -199
  12. package/dist/tools/prefabeditor/PrefabEditor.d.ts +18 -6
  13. package/dist/tools/prefabeditor/PrefabEditor.js +67 -30
  14. package/dist/tools/prefabeditor/PrefabRoot.d.ts +15 -9
  15. package/dist/tools/prefabeditor/PrefabRoot.js +142 -129
  16. package/dist/tools/prefabeditor/assetRuntime.d.ts +13 -11
  17. package/dist/tools/prefabeditor/assetRuntime.js +15 -15
  18. package/dist/tools/prefabeditor/components/BufferGeometryComponent.js +1 -1
  19. package/dist/tools/prefabeditor/components/CameraComponent.js +2 -2
  20. package/dist/tools/prefabeditor/components/ComponentRegistry.d.ts +3 -3
  21. package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +3 -3
  22. package/dist/tools/prefabeditor/components/ModelComponent.js +1 -1
  23. package/dist/tools/prefabeditor/components/PointLightComponent.js +2 -2
  24. package/dist/tools/prefabeditor/components/SoundComponent.js +2 -2
  25. package/dist/tools/prefabeditor/components/SpotLightComponent.js +2 -2
  26. package/dist/tools/prefabeditor/components/index.js +0 -2
  27. package/dist/tools/prefabeditor/types.d.ts +1 -0
  28. package/dist/tools/prefabeditor/usePointerEvents.d.ts +3 -3
  29. package/dist/tools/prefabeditor/usePointerEvents.js +5 -5
  30. package/package.json +1 -3
  31. package/dist/tools/prefabeditor/components/PhysicsComponent.d.ts +0 -26
  32. package/dist/tools/prefabeditor/components/PhysicsComponent.js +0 -287
  33. package/dist/tools/prefabeditor/scene.d.ts +0 -70
  34. package/dist/tools/prefabeditor/scene.js +0 -237
@@ -10,11 +10,11 @@ export interface ComponentViewProps<P = Record<string, any>> {
10
10
  properties: P;
11
11
  /** Children to render for components that wrap the current subtree. */
12
12
  children?: React.ReactNode;
13
- /** Entity local position (passed to wrapper components like Physics). */
13
+ /** Current node local position for wrapper components. */
14
14
  position?: [number, number, number];
15
- /** Entity local rotation in radians (passed to wrapper components like Physics). */
15
+ /** Current node local rotation in radians for wrapper components. */
16
16
  rotation?: [number, number, number];
17
- /** Entity local scale (passed to wrapper components like Physics). */
17
+ /** Current node local scale for wrapper components. */
18
18
  scale?: [number, number, number];
19
19
  }
20
20
  export interface Component {
@@ -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 "../assetRuntime";
6
+ import { useCurrentNode } 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";
@@ -43,7 +43,7 @@ const frustumInputStyle = {
43
43
  const centerLockButtonStyle = {
44
44
  width: 34,
45
45
  height: 34,
46
- borderRadius: 999,
46
+ borderRadius: 0,
47
47
  border: `1px solid ${colors.border}`,
48
48
  background: colors.bgInput,
49
49
  color: colors.textMuted,
@@ -102,7 +102,7 @@ function DirectionalLightComponentEditor({ component, onUpdate }) {
102
102
  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(Vector3Input, { label: "Target Offset", value: values.targetOffset, onChange: targetOffset => onUpdate({ targetOffset }), snap: 0.5 })] }), _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 }), _jsx(ShadowFrustumField, { values: values, onChange: onUpdate })] })) : null] })] }));
103
103
  }
104
104
  function DirectionalLightView({ properties, children }) {
105
- const { editMode, isSelected } = useEntityRuntime();
105
+ const { editMode, isSelected } = useCurrentNode();
106
106
  const merged = mergeWithDefaults(directionalLightDefaults, properties);
107
107
  const color = merged.color;
108
108
  const intensity = merged.intensity;
@@ -43,7 +43,7 @@ function ModelComponentEditor({ component, node, onUpdate, basePath = "" }) {
43
43
  const editorContext = useContext(EditorContext);
44
44
  const positionSnap = (_a = editorContext === null || editorContext === void 0 ? void 0 : editorContext.positionSnap) !== null && _a !== void 0 ? _a : 0.5;
45
45
  const repeatAxes = getRepeatAxesFromModelProperties(component.properties);
46
- 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 }))] }))] }));
46
+ 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: "node: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 }))] }))] }));
47
47
  }
48
48
  // View for Model component
49
49
  function ModelComponentView({ properties, children }) {
@@ -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 '../assetRuntime';
5
+ import { useCurrentNode } from '../assetRuntime';
6
6
  import { BooleanField, ColorField, NumberField } from './Input';
7
7
  import { LightSection, ShadowBiasField, mergeWithDefaults } from './lightUtils';
8
8
  const pointLightDefaults = {
@@ -23,7 +23,7 @@ function PointLightComponentEditor({ component, onUpdate }) {
23
23
  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] })] }));
24
24
  }
25
25
  function PointLightView({ properties, children }) {
26
- const { editMode, isSelected } = useEntityRuntime();
26
+ const { editMode, isSelected } = useCurrentNode();
27
27
  const merged = mergeWithDefaults(pointLightDefaults, properties);
28
28
  const color = merged.color;
29
29
  const intensity = merged.intensity;
@@ -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 { useThree } from '@react-three/fiber';
4
4
  import { SoundPicker } from '../../assetviewer/page';
5
- import { useAssetRuntime, useEntityRuntime } from '../assetRuntime';
5
+ import { useAssetRuntime, useCurrentNode } from '../assetRuntime';
6
6
  import { gameEvents } from '../GameEvents';
7
7
  import { BooleanField, FieldGroup, FieldRenderer, ListEditor, NumberField, SelectField, StringField } from './Input';
8
8
  import { colors, ui } from '../styles';
@@ -124,7 +124,7 @@ function SoundComponentEditor({ component, onUpdate, basePath = '' }) {
124
124
  }
125
125
  function SoundComponentView({ properties, children }) {
126
126
  const { getSound } = useAssetRuntime();
127
- const { editMode, nodeId } = useEntityRuntime();
127
+ const { editMode, nodeId } = useCurrentNode();
128
128
  const { camera } = useThree();
129
129
  const { eventName, autoplay = false, positional = false, refDistance = 1, maxDistance = 24, rolloffFactor = 1, distanceModel = 'inverse' } = properties;
130
130
  const sequenceIndexRef = useRef(0);
@@ -3,7 +3,7 @@ import { useHelper } from "@react-three/drei";
3
3
  import { useRef, useEffect } from "react";
4
4
  import { BooleanField, ColorField, Label, NumberField, Vector3Input } from "./Input";
5
5
  import { SpotLightHelper } from "three";
6
- import { useAssetRuntime, useEntityRuntime } from "../assetRuntime";
6
+ import { useAssetRuntime, useCurrentNode } from "../assetRuntime";
7
7
  import { useFrame } from "@react-three/fiber";
8
8
  import { TexturePicker } from "../../assetviewer/page";
9
9
  import { LightSection, ShadowBiasField, mergeWithDefaults } from "./lightUtils";
@@ -31,7 +31,7 @@ function SpotLightComponentEditor({ component, onUpdate, basePath = "" }) {
31
31
  function SpotLightView({ properties, children }) {
32
32
  var _a;
33
33
  const { getTexture } = useAssetRuntime();
34
- const { editMode, isSelected } = useEntityRuntime();
34
+ const { editMode, isSelected } = useCurrentNode();
35
35
  const merged = mergeWithDefaults(spotLightDefaults, properties);
36
36
  const color = merged.color;
37
37
  const intensity = merged.intensity;
@@ -4,7 +4,6 @@ import BufferGeometryComponent from './BufferGeometryComponent';
4
4
  import ModelComponent from './ModelComponent';
5
5
  import TextComponent from './TextComponent';
6
6
  import MaterialComponent from './MaterialComponent';
7
- import PhysicsComponent from './PhysicsComponent';
8
7
  import SpotLightComponent from './SpotLightComponent';
9
8
  import PointLightComponent from './PointLightComponent';
10
9
  import DirectionalLightComponent from './DirectionalLightComponent';
@@ -20,7 +19,6 @@ export const builtinComponents = [
20
19
  ModelComponent,
21
20
  TextComponent,
22
21
  MaterialComponent,
23
- PhysicsComponent,
24
22
  SpotLightComponent,
25
23
  PointLightComponent,
26
24
  DirectionalLightComponent,
@@ -7,6 +7,7 @@ export interface GameObject {
7
7
  id: string;
8
8
  name?: string;
9
9
  disabled?: boolean;
10
+ hidden?: boolean;
10
11
  locked?: boolean;
11
12
  children?: GameObject[];
12
13
  components?: {
@@ -1,5 +1,5 @@
1
1
  import type { ThreeEvent } from "@react-three/fiber";
2
- export type PointerHandler<T> = (event: ThreeEvent<PointerEvent>, entity: T) => void;
2
+ export type PointerHandler<T> = (event: ThreeEvent<PointerEvent>, node: T) => void;
3
3
  export interface PointerEventHandlers<T> {
4
4
  onClick?: PointerHandler<T>;
5
5
  onPointerDown?: PointerHandler<T>;
@@ -12,10 +12,10 @@ export interface PointerEventHandlers<T> {
12
12
  }
13
13
  export interface UsePointerEventsOptions<T> extends PointerEventHandlers<T> {
14
14
  enabled: boolean;
15
- entity: T | null | undefined;
15
+ node: T | null | undefined;
16
16
  }
17
17
  export declare function hasPointerEventHandlers<T>(handlers: PointerEventHandlers<T>): boolean;
18
- export declare function usePointerEvents<T>({ enabled, entity, onClick, onPointerDown, onPointerUp, onPointerMove, onPointerEnter, onPointerLeave, onPointerOver, onPointerOut, }: UsePointerEventsOptions<T>): {
18
+ export declare function usePointerEvents<T>({ enabled, node, onClick, onPointerDown, onPointerUp, onPointerMove, onPointerEnter, onPointerLeave, onPointerOver, onPointerOut, }: UsePointerEventsOptions<T>): {
19
19
  onClick: ((event: ThreeEvent<PointerEvent>) => void) | undefined;
20
20
  onPointerDown: ((event: ThreeEvent<PointerEvent>) => void) | undefined;
21
21
  onPointerMove: ((event: ThreeEvent<PointerEvent>) => void) | undefined;
@@ -8,7 +8,7 @@ export function hasPointerEventHandlers(handlers) {
8
8
  || handlers.onPointerOver
9
9
  || handlers.onPointerOut);
10
10
  }
11
- export function usePointerEvents({ enabled, entity, onClick, onPointerDown, onPointerUp, onPointerMove, onPointerEnter, onPointerLeave, onPointerOver, onPointerOut, }) {
11
+ export function usePointerEvents({ enabled, node, onClick, onPointerDown, onPointerUp, onPointerMove, onPointerEnter, onPointerLeave, onPointerOver, onPointerOut, }) {
12
12
  if (!enabled) {
13
13
  return {
14
14
  onClick: undefined,
@@ -26,17 +26,17 @@ export function usePointerEvents({ enabled, entity, onClick, onPointerDown, onPo
26
26
  return undefined;
27
27
  return (event) => {
28
28
  event.stopPropagation();
29
- if (!entity)
29
+ if (!node)
30
30
  return;
31
- handler(event, entity);
31
+ handler(event, node);
32
32
  };
33
33
  };
34
34
  const forwardMove = onPointerMove
35
35
  ? (event) => {
36
36
  event.stopPropagation();
37
- if (!entity)
37
+ if (!node)
38
38
  return;
39
- onPointerMove(event, entity);
39
+ onPointerMove(event, node);
40
40
  }
41
41
  : undefined;
42
42
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-three-game",
3
- "version": "0.0.91",
3
+ "version": "0.0.93",
4
4
  "description": "high performance 3D game engine built in React",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -27,7 +27,6 @@
27
27
  "peerDependencies": {
28
28
  "@react-three/drei": ">=10.0.0",
29
29
  "@react-three/fiber": ">=9.0.0",
30
- "@react-three/rapier": ">=2.0.0",
31
30
  "react": ">=18.0.0",
32
31
  "react-dom": ">=18.0.0",
33
32
  "three": ">=0.182.0",
@@ -36,7 +35,6 @@
36
35
  "devDependencies": {
37
36
  "@react-three/drei": "^10.7.7",
38
37
  "@react-three/fiber": "^9.5.0",
39
- "@react-three/rapier": "^2.2.0",
40
38
  "@types/react": "^19.2.9",
41
39
  "@types/react-dom": "^19.2.3",
42
40
  "@types/three": "^0.182.0",
@@ -1,26 +0,0 @@
1
- import type { RigidBodyOptions } from "@react-three/rapier";
2
- import { Component } from "./ComponentRegistry";
3
- type PhysicsColliderType = NonNullable<RigidBodyOptions['colliders']> | 'capsule';
4
- type ManualColliderShape = 'cuboid' | 'ball' | 'capsule';
5
- export type PhysicsProps = Omit<RigidBodyOptions, 'colliders'> & {
6
- colliders?: PhysicsColliderType;
7
- manualColliderShape?: ManualColliderShape;
8
- activeCollisionTypes?: 'all' | undefined;
9
- linearVelocity?: [number, number, number];
10
- angularVelocity?: [number, number, number];
11
- colliderSize?: [number, number, number];
12
- colliderRadius?: number;
13
- capsuleRadius?: number;
14
- capsuleHalfHeight?: number;
15
- emitSensorEnterEvent?: boolean;
16
- sensorEnterEventName?: string;
17
- emitSensorExitEvent?: boolean;
18
- sensorExitEventName?: string;
19
- emitCollisionEnterEvent?: boolean;
20
- collisionEnterEventName?: string;
21
- emitCollisionExitEvent?: boolean;
22
- collisionExitEventName?: string;
23
- };
24
- export declare function isPhysicsProps(v: any): v is PhysicsProps;
25
- declare const PhysicsComponent: Component;
26
- export default PhysicsComponent;
@@ -1,287 +0,0 @@
1
- var __rest = (this && this.__rest) || function (s, e) {
2
- var t = {};
3
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
- t[p] = s[p];
5
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
- t[p[i]] = s[p[i]];
9
- }
10
- return t;
11
- };
12
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
13
- import { BallCollider, CapsuleCollider, CuboidCollider, RigidBody, useRapier } from "@react-three/rapier";
14
- import { useCallback, useEffect, useRef } from 'react';
15
- import { useAssetRuntime, useEntityRuntime } from "../assetRuntime";
16
- import { gameEvents, getEntityIdFromRigidBody } from "../GameEvents";
17
- import { usePrefabNode, usePrefabStore } from "../prefabStore";
18
- import { BooleanField, FieldGroup, NumberField, SelectField, StringField, Vector3Field } from "./Input";
19
- import { findComponent, getNodeUserData } from "../types";
20
- import { colors } from "../styles";
21
- export function isPhysicsProps(v) {
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";
23
- }
24
- const enabledAxesFallback = [true, true, true];
25
- const manualColliderShapeFallback = 'cuboid';
26
- const colliderSizeFallback = [1, 1, 1];
27
- const colliderRadiusFallback = 0.5;
28
- const capsuleRadiusFallback = 0.35;
29
- const capsuleHalfHeightFallback = 0.45;
30
- function isManualColliderShape(value) {
31
- return value === 'cuboid' || value === 'ball' || value === 'capsule';
32
- }
33
- function hasNodeColliderSource(node) {
34
- return Boolean(findComponent(node, 'Geometry')
35
- || findComponent(node, 'BufferGeometry')
36
- || findComponent(node, 'Model'));
37
- }
38
- function subtreeHasColliderSource(nodeId, nodesById, childIdsById) {
39
- var _a;
40
- if (!nodeId)
41
- return false;
42
- const pending = [nodeId];
43
- while (pending.length > 0) {
44
- const currentId = pending.pop();
45
- if (!currentId)
46
- continue;
47
- const currentNode = nodesById[currentId];
48
- if (hasNodeColliderSource(currentNode)) {
49
- return true;
50
- }
51
- pending.push(...((_a = childIdsById[currentId]) !== null && _a !== void 0 ? _a : []));
52
- }
53
- return false;
54
- }
55
- function renderManualCollider({ shape, sensor, colliderSize, colliderRadius, capsuleRadius, capsuleHalfHeight, }) {
56
- if (shape === 'ball') {
57
- return _jsx(BallCollider, { args: [colliderRadius], sensor: sensor });
58
- }
59
- if (shape === 'capsule') {
60
- return _jsx(CapsuleCollider, { args: [capsuleHalfHeight, capsuleRadius], sensor: sensor });
61
- }
62
- return _jsx(CuboidCollider, { args: colliderSize.map(value => value / 2), sensor: sensor });
63
- }
64
- function LockedAxisField({ label, name, values, onChange, }) {
65
- const enabledAxes = Array.isArray(values[name])
66
- ? values[name]
67
- : enabledAxesFallback;
68
- const axisLabels = ['X', 'Y', 'Z'];
69
- const toggleAxisLock = (index) => {
70
- const nextEnabledAxes = [...enabledAxes];
71
- nextEnabledAxes[index] = !nextEnabledAxes[index];
72
- onChange({ [name]: nextEnabledAxes });
73
- };
74
- return (_jsxs("div", { children: [_jsxs("div", { style: {
75
- display: 'flex',
76
- alignItems: 'center',
77
- justifyContent: 'space-between',
78
- marginBottom: 4,
79
- }, children: [_jsx("span", { style: {
80
- display: 'block',
81
- fontSize: '10px',
82
- color: colors.textMuted,
83
- textTransform: 'uppercase',
84
- letterSpacing: '0.05em',
85
- fontWeight: 500,
86
- }, children: label }), _jsx("span", { style: {
87
- fontSize: '10px',
88
- color: colors.textDim,
89
- }, children: "Active means locked" })] }), _jsx("div", { style: { display: 'flex', gap: 4 }, children: axisLabels.map((axisLabel, index) => {
90
- const isLocked = !enabledAxes[index];
91
- return (_jsx("button", { type: "button", onClick: () => toggleAxisLock(index), style: {
92
- flex: 1,
93
- minHeight: 22,
94
- backgroundColor: isLocked ? colors.dangerBg : colors.bgInput,
95
- border: `1px solid ${isLocked ? colors.dangerBorder : colors.border}`,
96
- borderRadius: 0,
97
- padding: '2px 6px',
98
- color: isLocked ? colors.danger : colors.textMuted,
99
- fontSize: '11px',
100
- fontFamily: 'monospace',
101
- cursor: 'pointer',
102
- }, children: axisLabel }, axisLabel));
103
- }) })] }));
104
- }
105
- function PhysicsComponentEditor({ node, component, onUpdate }) {
106
- const nodeId = node === null || node === void 0 ? void 0 : node.id;
107
- const hasAutomaticColliderSource = usePrefabStore(state => subtreeHasColliderSource(nodeId, state.nodesById, state.childIdsById));
108
- const manualColliderShape = isManualColliderShape(component.properties.manualColliderShape)
109
- ? component.properties.manualColliderShape
110
- : manualColliderShapeFallback;
111
- return (_jsxs(FieldGroup, { children: [_jsx(SelectField, { name: "type", label: "Type", values: component.properties, onChange: onUpdate, options: [
112
- { value: 'dynamic', label: 'Dynamic' },
113
- { value: 'fixed', label: 'Fixed' },
114
- { value: 'kinematicPosition', label: 'Kinematic Position' },
115
- { value: 'kinematicVelocity', label: 'Kinematic Velocity' },
116
- ] }), hasAutomaticColliderSource ? (_jsxs(_Fragment, { children: [_jsx(SelectField, { name: "colliders", label: "Collider", values: component.properties, onChange: onUpdate, options: [
117
- { value: 'hull', label: 'Hull (convex)' },
118
- { value: 'trimesh', label: 'Trimesh (exact)' },
119
- { value: 'cuboid', label: 'Cuboid (box)' },
120
- { value: 'ball', label: 'Ball (sphere)' },
121
- { value: 'capsule', label: 'Capsule' },
122
- ] }), 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] })) : (_jsxs(_Fragment, { children: [_jsx(SelectField, { name: "manualColliderShape", label: "Shape", values: Object.assign(Object.assign({}, component.properties), { manualColliderShape }), onChange: onUpdate, options: [
123
- { value: 'cuboid', label: 'Cuboid (box)' },
124
- { value: 'ball', label: 'Ball (sphere)' },
125
- { value: 'capsule', label: 'Capsule' },
126
- ] }), manualColliderShape === 'cuboid' ? (_jsx(Vector3Field, { name: "colliderSize", label: "Collider Size", values: component.properties, onChange: onUpdate, fallback: colliderSizeFallback })) : null, manualColliderShape === 'ball' ? (_jsx(NumberField, { name: "colliderRadius", label: "Collider Radius", values: component.properties, onChange: onUpdate, fallback: colliderRadiusFallback, min: 0.01, step: 0.01 })) : null, manualColliderShape === '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: [
127
- { value: '', label: 'Default (Dynamic only)' },
128
- { value: 'all', label: 'All (includes kinematic & fixed)' },
129
- ] })] }));
130
- }
131
- function PhysicsComponentView({ properties, children, position, rotation, scale }) {
132
- var _a, _b, _c;
133
- const { registerRigidBodyRef } = useAssetRuntime();
134
- const { editMode, nodeId, getObject } = useEntityRuntime();
135
- const gameObject = usePrefabNode(nodeId);
136
- const hasAutomaticColliderSource = usePrefabStore(state => subtreeHasColliderSource(nodeId, state.nodesById, state.childIdsById));
137
- 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 : '';
138
- 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));
139
- const { type, colliders, sensor, manualColliderShape = manualColliderShapeFallback, activeCollisionTypes, linearVelocity = [0, 0, 0], angularVelocity = [0, 0, 0], colliderSize = colliderSizeFallback, colliderRadius = colliderRadiusFallback, 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", "manualColliderShape", "activeCollisionTypes", "linearVelocity", "angularVelocity", "colliderSize", "colliderRadius", "capsuleRadius", "capsuleHalfHeight", "emitSensorEnterEvent", "sensorEnterEventName", "emitSensorExitEvent", "sensorExitEventName", "emitCollisionEnterEvent", "collisionEnterEventName", "emitCollisionExitEvent", "collisionExitEventName", "enabledTranslations", "enabledRotations"]);
140
- const colliderType = colliders || (type === 'fixed' ? 'trimesh' : 'hull');
141
- const resolvedManualColliderShape = isManualColliderShape(manualColliderShape)
142
- ? manualColliderShape
143
- : manualColliderShapeFallback;
144
- const usesAutomaticColliderSource = hasAutomaticColliderSource && colliderType !== 'capsule';
145
- const manualColliderShapeToRender = hasAutomaticColliderSource
146
- ? 'capsule'
147
- : resolvedManualColliderShape;
148
- const rigidBodyRef = useRef(null);
149
- const linearVelocityKey = linearVelocity.join(',');
150
- const angularVelocityKey = angularVelocity.join(',');
151
- const rbKey = editMode
152
- ? `${type || 'dynamic'}_${colliderType}_${resolvedManualColliderShape}_${colliderSize.join(',')}_${colliderRadius}_${capsuleRadius}_${capsuleHalfHeight}_${position === null || position === void 0 ? void 0 : position.join(',')}_${rotation === null || rotation === void 0 ? void 0 : rotation.join(',')}`
153
- : `${type || 'dynamic'}_${colliderType}_${resolvedManualColliderShape}_${colliderSize.join(',')}_${colliderRadius}_${capsuleRadius}_${capsuleHalfHeight}`;
154
- const handleRigidBodyRef = useCallback((rigidBody) => {
155
- rigidBodyRef.current = rigidBody;
156
- if (!nodeId)
157
- return;
158
- registerRigidBodyRef(nodeId, rigidBody);
159
- }, [nodeId, registerRigidBodyRef]);
160
- // Try to get rapier context - will be null if not inside <Physics>
161
- let rapier = null;
162
- try {
163
- const rapierContext = useRapier();
164
- rapier = rapierContext.rapier;
165
- }
166
- catch (e) {
167
- // Not inside Physics context - that's ok, just won't have rapier features
168
- }
169
- // Configure active collision types for kinematic/sensor bodies
170
- useEffect(() => {
171
- if (activeCollisionTypes === 'all' && rigidBodyRef.current && rapier) {
172
- const rb = rigidBodyRef.current;
173
- // Apply to all colliders on this rigid body
174
- for (let i = 0; i < rb.numColliders(); i++) {
175
- const collider = rb.collider(i);
176
- collider.setActiveCollisionTypes(rapier.ActiveCollisionTypes.DEFAULT |
177
- rapier.ActiveCollisionTypes.KINEMATIC_FIXED |
178
- rapier.ActiveCollisionTypes.KINEMATIC_KINEMATIC);
179
- }
180
- }
181
- }, [activeCollisionTypes, rapier, type, colliders]);
182
- // Seed authored velocities when the body instance changes or the authored values change.
183
- useEffect(() => {
184
- if (!rigidBodyRef.current)
185
- return;
186
- rigidBodyRef.current.setLinvel({
187
- x: linearVelocity[0],
188
- y: linearVelocity[1],
189
- z: linearVelocity[2],
190
- }, true);
191
- }, [rbKey, linearVelocityKey]);
192
- useEffect(() => {
193
- if (!rigidBodyRef.current)
194
- return;
195
- rigidBodyRef.current.setAngvel({
196
- x: angularVelocity[0],
197
- y: angularVelocity[1],
198
- z: angularVelocity[2],
199
- }, true);
200
- }, [rbKey, angularVelocityKey]);
201
- const dispatchPhysicsEvent = useCallback((eventType, payload) => {
202
- var _a, _b, _c;
203
- if (!nodeId)
204
- return;
205
- const trimmedEventType = eventType === null || eventType === void 0 ? void 0 : eventType.trim();
206
- if (!trimmedEventType)
207
- return;
208
- const targetEntityId = getEntityIdFromRigidBody(payload.other.rigidBody);
209
- gameEvents.emit(trimmedEventType, {
210
- sourceEntityId: nodeId,
211
- sourceNodeId: nodeId,
212
- sourceObject: getObject(),
213
- sourceRigidBody: rigidBodyRef.current,
214
- targetEntityId,
215
- targetNodeId: targetEntityId,
216
- targetObject: (_b = (_a = payload.other.rigidBodyObject) !== null && _a !== void 0 ? _a : payload.other.colliderObject) !== null && _b !== void 0 ? _b : null,
217
- targetRigidBody: (_c = payload.other.rigidBody) !== null && _c !== void 0 ? _c : null,
218
- rapierEvent: payload,
219
- });
220
- }, [getObject, nodeId]);
221
- const handleIntersectionEnter = useCallback((payload) => {
222
- if (!emitSensorEnterEvent)
223
- return;
224
- dispatchPhysicsEvent(sensorEnterEventName, payload);
225
- }, [dispatchPhysicsEvent, emitSensorEnterEvent, sensorEnterEventName]);
226
- const handleIntersectionExit = useCallback((payload) => {
227
- if (!emitSensorExitEvent)
228
- return;
229
- dispatchPhysicsEvent(sensorExitEventName, payload);
230
- }, [dispatchPhysicsEvent, emitSensorExitEvent, sensorExitEventName]);
231
- const handleCollisionEnter = useCallback((payload) => {
232
- if (!emitCollisionEnterEvent)
233
- return;
234
- dispatchPhysicsEvent(collisionEnterEventName, payload);
235
- }, [collisionEnterEventName, dispatchPhysicsEvent, emitCollisionEnterEvent]);
236
- const handleCollisionExit = useCallback((payload) => {
237
- if (!emitCollisionExitEvent)
238
- return;
239
- dispatchPhysicsEvent(collisionExitEventName, payload);
240
- }, [collisionExitEventName, dispatchPhysicsEvent, emitCollisionExitEvent]);
241
- const editModeTransformProps = editMode
242
- ? {
243
- position,
244
- rotation,
245
- scale,
246
- }
247
- : undefined;
248
- const rigidBodyProps = Object.assign({ ref: handleRigidBodyRef, type, colliders: usesAutomaticColliderSource ? colliderType : false, position: editMode ? undefined : position, rotation: editMode ? undefined : rotation, scale: editMode ? undefined : scale, sensor,
249
- enabledTranslations,
250
- 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);
251
- const rigidBodyContent = (_jsxs(_Fragment, { children: [!usesAutomaticColliderSource ? renderManualCollider({
252
- shape: manualColliderShapeToRender,
253
- sensor,
254
- colliderSize,
255
- colliderRadius,
256
- capsuleRadius,
257
- capsuleHalfHeight,
258
- }) : null, children] }));
259
- return (_jsx(RigidBody, Object.assign({}, rigidBodyProps, { children: editMode ? (_jsx("group", Object.assign({}, editModeTransformProps, { children: rigidBodyContent }))) : rigidBodyContent }), rbKey));
260
- }
261
- const PhysicsComponent = {
262
- name: 'Physics',
263
- Editor: PhysicsComponentEditor,
264
- View: PhysicsComponentView,
265
- defaultProperties: {
266
- type: 'dynamic',
267
- colliders: 'hull',
268
- manualColliderShape: manualColliderShapeFallback,
269
- colliderSize: colliderSizeFallback,
270
- colliderRadius: colliderRadiusFallback,
271
- capsuleRadius: capsuleRadiusFallback,
272
- capsuleHalfHeight: capsuleHalfHeightFallback,
273
- linearVelocity: [0, 0, 0],
274
- angularVelocity: [0, 0, 0],
275
- enabledTranslations: [true, true, true],
276
- enabledRotations: [true, true, true],
277
- emitSensorEnterEvent: false,
278
- sensorEnterEventName: '',
279
- emitSensorExitEvent: false,
280
- sensorExitEventName: '',
281
- emitCollisionEnterEvent: false,
282
- collisionEnterEventName: '',
283
- emitCollisionExitEvent: false,
284
- collisionExitEventName: '',
285
- }
286
- };
287
- export default PhysicsComponent;
@@ -1,70 +0,0 @@
1
- import type { Object3D } from "three";
2
- import type { GameObject } from "./types";
3
- export interface SpawnOptions {
4
- name?: string;
5
- parentId?: string;
6
- select?: boolean;
7
- }
8
- export type EntityData = Omit<GameObject, "children">;
9
- export type PropertyPath = string | Array<string | number>;
10
- export interface EntityComponent<TProperties = Record<string, any>> {
11
- readonly key: string;
12
- readonly type: string;
13
- get: <TValue = unknown>(path?: PropertyPath) => TValue | undefined;
14
- set: (path: PropertyPath, value: unknown) => void;
15
- update: (update: (properties: TProperties) => TProperties) => void;
16
- }
17
- export interface Entity {
18
- readonly id: string;
19
- readonly name: string | undefined;
20
- readonly enabled: boolean;
21
- readonly parent: Entity | null;
22
- readonly children: Entity[];
23
- readonly object: Object3D | null;
24
- readonly rigidBody: any;
25
- set: (data: EntityData) => void;
26
- update: (update: (node: EntityData) => EntityData) => void;
27
- getComponent: <TProperties = Record<string, any>>(name: string) => EntityComponent<TProperties> | null;
28
- addComponent: (type: string, properties?: Record<string, any>) => EntityComponent | null;
29
- removeComponent: (name: string) => void;
30
- destroy: () => void;
31
- }
32
- export type EntityUpdate = (node: EntityData) => EntityData;
33
- export type SceneUpdates = Record<string, EntityUpdate>;
34
- export interface Scene {
35
- readonly rootId: string;
36
- find: (id: string) => Entity | null;
37
- get: (id: string) => Entity;
38
- create: (name: string, components?: Record<string, {
39
- type: string;
40
- properties?: Record<string, any>;
41
- }>, options?: SpawnOptions) => Entity;
42
- update: {
43
- (id: string, update: EntityUpdate): void;
44
- (updates: SceneUpdates): void;
45
- };
46
- add: (node: GameObject, options?: SpawnOptions) => Entity;
47
- remove: (id: string) => void;
48
- /**
49
- * Coalesce many entity / component updates into a single store revision.
50
- * Entity `update`/`set`, `EntityComponent` `set`/`update`, `addComponent`,
51
- * and `removeComponent` calls inside the callback are buffered and flushed
52
- * as one batched write. `add`, `remove`, and `destroy` (structural tree ops)
53
- * still commit immediately.
54
- */
55
- batch: (fn: () => void) => void;
56
- }
57
- interface SceneAdapter {
58
- getRootId: () => string;
59
- getNode: (id: string) => EntityData | null;
60
- getChildIds: (id: string) => string[];
61
- getParentId: (id: string) => string | null;
62
- updateNode: (id: string, update: (node: EntityData) => EntityData) => void;
63
- updateNodes: (updates: Record<string, (node: EntityData) => EntityData>) => void;
64
- addNode: (node: GameObject, options?: SpawnOptions) => string;
65
- removeNode: (id: string) => void;
66
- getObject?: (id: string) => Object3D | null;
67
- getRigidBody?: (id: string) => any;
68
- }
69
- export declare function createScene(adapter: SceneAdapter): Scene;
70
- export {};