react-three-game 0.0.76 → 0.0.78

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.
@@ -4,6 +4,5 @@ export interface GameCanvasProps extends Omit<CanvasProps, 'children'> {
4
4
  loader?: boolean;
5
5
  children: React.ReactNode;
6
6
  glConfig?: WebGPURendererParameters;
7
- canvasRef?: React.RefObject<HTMLCanvasElement | null>;
8
7
  }
9
- export default function GameCanvas({ loader, children, glConfig, canvasRef, onCreated, style, ...props }: GameCanvasProps): import("react/jsx-runtime").JSX.Element;
8
+ export default function GameCanvas({ loader, children, glConfig, onCreated, style, ...props }: GameCanvasProps): import("react/jsx-runtime").JSX.Element;
@@ -29,7 +29,7 @@ extend({
29
29
  SpriteNodeMaterial: SpriteNodeMaterial,
30
30
  });
31
31
  export default function GameCanvas(_a) {
32
- var { loader = false, children, glConfig, canvasRef, onCreated, style } = _a, props = __rest(_a, ["loader", "children", "glConfig", "canvasRef", "onCreated", "style"]);
32
+ var { loader = false, children, glConfig, onCreated, style } = _a, props = __rest(_a, ["loader", "children", "glConfig", "onCreated", "style"]);
33
33
  const [frameloop, setFrameloop] = useState("never");
34
34
  return _jsx(_Fragment, { children: _jsxs(Canvas, Object.assign({ style: Object.assign({ touchAction: 'none', userSelect: 'none', WebkitUserSelect: 'none', WebkitTouchCallout: 'none', WebkitTapHighlightColor: 'transparent' }, style), shadows: { type: PCFShadowMap }, dpr: [1, 1.5], frameloop: frameloop, gl: (_a) => __awaiter(this, [_a], void 0, function* ({ canvas }) {
35
35
  const canvasElement = canvas;
@@ -1,20 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Canvas } from "@react-three/fiber";
3
3
  import { OrbitControls, View, PerspectiveCamera } from "@react-three/drei";
4
- import { Component as ReactComponent, Suspense, useEffect, useLayoutEffect, useState, useRef } from "react";
4
+ import { Suspense, useEffect, useLayoutEffect, useState, useRef } from "react";
5
5
  import { createPortal } from 'react-dom';
6
6
  import { TextureLoader } from "three";
7
7
  import { loadModel } from "../dragdrop/modelLoader";
8
- class ErrorBoundary extends ReactComponent {
9
- constructor(props) {
10
- super(props);
11
- this.state = { hasError: false };
12
- }
13
- static getDerivedStateFromError() { return { hasError: true }; }
14
- componentDidCatch() { var _a, _b; (_b = (_a = this.props).onError) === null || _b === void 0 ? void 0 : _b.call(_a); }
15
- render() { return this.state.hasError ? null : this.props.children; }
16
- }
17
- // view models and textures in manifest, onselect callback
18
8
  const styles = {
19
9
  errorIcon: { color: '#fca5a5', fontSize: 12 }, // text-red-400 text-xs
20
10
  flexFillRelative: { flex: 1, position: 'relative' },
@@ -46,7 +46,7 @@ export interface PrefabEditorProps {
46
46
  onChange?: (prefab: Prefab) => void;
47
47
  showUI?: boolean;
48
48
  enableWindowDrop?: boolean;
49
- canvasProps?: Omit<React.ComponentProps<typeof GameCanvas>, 'children' | 'canvasRef'>;
49
+ canvasProps?: Omit<React.ComponentProps<typeof GameCanvas>, 'children'>;
50
50
  uiPlugins?: React.ReactNode[] | React.ReactNode;
51
51
  children?: React.ReactNode;
52
52
  }
@@ -10,7 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
11
11
  import { MapControls, TransformControls } from "@react-three/drei";
12
12
  import GameCanvas from "../../shared/GameCanvas";
13
- import { useCallback, useEffect, useMemo, useRef, useState, forwardRef, useImperativeHandle, createContext, useContext } from "react";
13
+ import { useCallback, useEffect, useMemo, useReducer, useRef, useState, forwardRef, useImperativeHandle, createContext, useContext } from "react";
14
14
  import { findComponentEntry } from "./types";
15
15
  import PrefabRoot from "./PrefabRoot";
16
16
  import { Physics } from "@react-three/rapier";
@@ -21,6 +21,17 @@ import { loadFiles } from "../dragdrop";
21
21
  import { denormalizePrefab, createImageNode, createModelNode, createNode } from './prefab';
22
22
  import { createPrefabStore, PrefabStoreProvider } from "./prefabStore";
23
23
  import { createScene } from "./scene";
24
+ function isObjectAttachedToRoot(root, object) {
25
+ if (!root || !object)
26
+ return false;
27
+ let current = object;
28
+ while (current) {
29
+ if (current === root)
30
+ return true;
31
+ current = current.parent;
32
+ }
33
+ return false;
34
+ }
24
35
  export var PrefabEditorMode;
25
36
  (function (PrefabEditorMode) {
26
37
  PrefabEditorMode["Edit"] = "edit";
@@ -42,6 +53,7 @@ const DEFAULT_PREFAB = {
42
53
  root: createNode('Root', {}, { id: 'root' })
43
54
  };
44
55
  const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, mode: initialMode = PrefabEditorMode.Edit, onChange, showUI = true, enableWindowDrop = true, canvasProps, uiPlugins, children }, ref) => {
56
+ var _a, _b;
45
57
  const [mode, setMode] = useState(initialMode);
46
58
  const [selectedId, setSelectedId] = useState(null);
47
59
  const [transformMode, setTransformMode] = useState("translate");
@@ -54,7 +66,7 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, mode
54
66
  const [historyIndex, setHistoryIndex] = useState(0);
55
67
  const changeOriginRef = useRef(null);
56
68
  const historyIndexRef = useRef(0);
57
- const [selectedObject, setSelectedObject] = useState(null);
69
+ const [, bumpSelectedObjectVersion] = useReducer((value) => value + 1, 0);
58
70
  const prefabRootRef = useRef(null);
59
71
  const canvasRef = useRef(null);
60
72
  const controlsRef = useRef(null);
@@ -63,6 +75,11 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, mode
63
75
  const getPrefab = useCallback(() => denormalizePrefab(prefabStore.getState()), [prefabStore]);
64
76
  const getObject = useCallback((nodeId) => { var _a, _b; return (_b = (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.getObject(nodeId)) !== null && _b !== void 0 ? _b : null; }, []);
65
77
  const getRigidBody = useCallback((nodeId) => { var _a, _b; return (_b = (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.getRigidBody(nodeId)) !== null && _b !== void 0 ? _b : null; }, []);
78
+ const handleObjectRefChange = useCallback((nodeId) => {
79
+ if (nodeId !== selectedId)
80
+ return;
81
+ bumpSelectedObjectVersion();
82
+ }, [selectedId]);
66
83
  onChangeRef.current = onChange;
67
84
  const setSelection = useCallback((nodeId) => {
68
85
  const nextNode = nodeId ? prefabStore.getState().nodesById[nodeId] : null;
@@ -77,7 +94,6 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, mode
77
94
  return prev;
78
95
  if (nextMode === PrefabEditorMode.Play) {
79
96
  setSelectedId(null);
80
- setSelectedObject(null);
81
97
  }
82
98
  return nextMode;
83
99
  });
@@ -91,7 +107,6 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, mode
91
107
  const loadPrefab = useCallback((prefab, options) => {
92
108
  changeOriginRef.current = (options === null || options === void 0 ? void 0 : options.notifyChange) === false ? "replace-silent" : "replace";
93
109
  prefabStore.getState().replacePrefab(prefab);
94
- setSelectedObject(null);
95
110
  if (options === null || options === void 0 ? void 0 : options.resetHistory) {
96
111
  setSelectedId(null);
97
112
  setHistory([prefab]);
@@ -154,17 +169,13 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, mode
154
169
  if (state.nodesById[selectedId])
155
170
  return;
156
171
  setSelectedId(null);
157
- setSelectedObject(null);
158
172
  });
159
173
  return () => unsubscribe();
160
174
  }, [prefabStore, selectedId]);
161
- useEffect(() => {
162
- if (!selectedId) {
163
- setSelectedObject(null);
164
- return;
165
- }
166
- setSelectedObject(getObject(selectedId));
167
- }, [getObject, selectedId]);
175
+ const selectedObject = selectedId ? getObject(selectedId) : null;
176
+ const transformObject = isObjectAttachedToRoot((_b = (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.root) !== null && _b !== void 0 ? _b : null, selectedObject)
177
+ ? selectedObject
178
+ : null;
168
179
  const addNode = useCallback((node, options) => {
169
180
  var _a;
170
181
  const { addChild, rootId } = prefabStore.getState();
@@ -196,7 +207,6 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, mode
196
207
  prefabStore.getState().replacePrefab(history[index]);
197
208
  historyIndexRef.current = index;
198
209
  setHistoryIndex(index);
199
- setSelectedObject(null);
200
210
  setSelectedId(prev => prev && prefabStore.getState().nodesById[prev] ? prev : null);
201
211
  };
202
212
  const undo = () => historyIndex > 0 && applyHistory(historyIndex - 1);
@@ -342,7 +352,12 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, mode
342
352
  addModel,
343
353
  addTexture
344
354
  }), [addModel, addTexture, clearSelection, getPrefab, handleExportGLB, handleExportGLBData, handleScreenshot, loadPrefab, scene]);
345
- const content = (_jsxs(_Fragment, { children: [isEditMode ? _jsx("gridHelper", { args: [10, 10], position: [0, -1, 0] }) : null, _jsx(PrefabRoot, { ref: prefabRootRef, store: prefabStore, editMode: isEditMode, selectedId: selectedId, onSelect: setSelection, basePath: basePath }), children] }));
355
+ const content = (_jsxs(_Fragment, { children: [isEditMode ? _jsx("gridHelper", { args: [10, 10], position: [0, -1, 0] }) : null, _jsx(PrefabRoot, { ref: prefabRootRef, store: prefabStore, editMode: isEditMode, selectedId: selectedId, onSelect: setSelection, onObjectRefChange: handleObjectRefChange, basePath: basePath }), children] }));
356
+ const handleCanvasCreated = useCallback((state) => {
357
+ var _a;
358
+ canvasRef.current = state.gl.domElement;
359
+ (_a = canvasProps === null || canvasProps === void 0 ? void 0 : canvasProps.onCreated) === null || _a === void 0 ? void 0 : _a.call(canvasProps, state);
360
+ }, [canvasProps]);
346
361
  return _jsx(PrefabStoreProvider, { store: prefabStore, children: _jsxs(EditorContext.Provider, { value: {
347
362
  mode,
348
363
  setMode: updateMode,
@@ -357,7 +372,7 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, mode
357
372
  onFocusNode: isEditMode ? handleFocusNode : undefined,
358
373
  onScreenshot: handleScreenshot,
359
374
  onExportGLB: handleExportGLB
360
- }, children: [_jsxs(GameCanvas, Object.assign({ camera: { position: [0, 5, 15] }, canvasRef: canvasRef }, canvasProps, { onPointerMissed: isEditMode
375
+ }, children: [_jsxs(GameCanvas, Object.assign({ camera: { position: [0, 5, 15] } }, canvasProps, { onCreated: handleCanvasCreated, onPointerMissed: isEditMode
361
376
  ? (event) => {
362
377
  var _a, _b, _c, _d;
363
378
  const button = (_c = (_a = event.button) !== null && _a !== void 0 ? _a : (_b = event.sourceEvent) === null || _b === void 0 ? void 0 : _b.button) !== null && _c !== void 0 ? _c : 0;
@@ -366,7 +381,7 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, mode
366
381
  }
367
382
  (_d = canvasProps === null || canvasProps === void 0 ? void 0 : canvasProps.onPointerMissed) === null || _d === void 0 ? void 0 : _d.call(canvasProps, event);
368
383
  }
369
- : canvasProps === null || canvasProps === void 0 ? void 0 : canvasProps.onPointerMissed, children: [physics ? (_jsx(Physics, { debug: isEditMode, paused: isEditMode, children: content })) : content, isEditMode && (_jsxs(_Fragment, { children: [_jsx(MapControls, { ref: controlsRef, enableDamping: false, makeDefault: true }), selectedObject && (_jsx(TransformControls, { object: selectedObject, mode: transformMode, space: "local", onObjectChange: handleTransformChange, translationSnap: positionSnap > 0 ? positionSnap : undefined, rotationSnap: rotationSnap > 0 ? rotationSnap : undefined, scaleSnap: scaleSnap > 0 ? scaleSnap : undefined }, `transform-${transformMode}-${positionSnap}-${rotationSnap}-${scaleSnap}`))] }))] })), showUI && (_jsxs(_Fragment, { children: [_jsxs("div", { style: toolbar.panel, children: [_jsx("button", { style: base.btn, onClick: toggleMode, children: isEditMode ? "▶" : "⏸" }), uiPlugins] }), isEditMode && (_jsx(EditorUI, { selectedId: selectedId, setSelectedId: setSelection, getPrefab: getPrefab, onReplacePrefab: (prefab) => loadPrefab(prefab, { resetHistory: true }), onImportPrefab: importPrefab, basePath: basePath, onUndo: undo, onRedo: redo, canUndo: historyIndex > 0, canRedo: historyIndex < history.length - 1 }))] }))] }) });
384
+ : canvasProps === null || canvasProps === void 0 ? void 0 : canvasProps.onPointerMissed, children: [physics ? (_jsx(Physics, { debug: isEditMode, paused: isEditMode, children: content })) : content, isEditMode && (_jsxs(_Fragment, { children: [_jsx(MapControls, { ref: controlsRef, enableDamping: false, makeDefault: true }), transformObject && (_jsx(TransformControls, { object: transformObject, mode: transformMode, space: "local", onObjectChange: handleTransformChange, translationSnap: positionSnap > 0 ? positionSnap : undefined, rotationSnap: rotationSnap > 0 ? rotationSnap : undefined, scaleSnap: scaleSnap > 0 ? scaleSnap : undefined }, `transform-${selectedId}-${transformMode}-${positionSnap}-${rotationSnap}-${scaleSnap}`))] }))] })), showUI && (_jsxs(_Fragment, { children: [_jsxs("div", { style: toolbar.panel, children: [_jsx("button", { style: base.btn, onClick: toggleMode, children: isEditMode ? "▶" : "⏸" }), uiPlugins] }), isEditMode && (_jsx(EditorUI, { selectedId: selectedId, setSelectedId: setSelection, getPrefab: getPrefab, onReplacePrefab: (prefab) => loadPrefab(prefab, { resetHistory: true }), onImportPrefab: importPrefab, basePath: basePath, onUndo: undo, onRedo: redo, canUndo: historyIndex > 0, canRedo: historyIndex < history.length - 1 }))] }))] }) });
370
385
  });
371
386
  PrefabEditor.displayName = "PrefabEditor";
372
387
  export default PrefabEditor;
@@ -18,6 +18,7 @@ export interface PrefabRootProps {
18
18
  selectedId?: string | null;
19
19
  onSelect?: (id: string | null) => void;
20
20
  onClick?: (event: ThreeEvent<PointerEvent>, entity: GameObjectType) => void;
21
+ onObjectRefChange?: (id: string, object: Object3D | null) => void;
21
22
  basePath?: string;
22
23
  }
23
24
  export declare const PrefabRoot: import("react").ForwardRefExoticComponent<PrefabRootProps & import("react").RefAttributes<PrefabRootRef>>;
@@ -45,7 +45,7 @@ function isNodeReady(node, loadedModels) {
45
45
  return true;
46
46
  return Boolean(loadedModels[model.properties.filename]);
47
47
  }
48
- export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSelect, onClick, basePath = "" }, ref) => {
48
+ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSelect, onClick, onObjectRefChange, basePath = "" }, ref) => {
49
49
  var _a;
50
50
  const [models, setModels] = useState({});
51
51
  const [textures, setTextures] = useState({});
@@ -86,7 +86,8 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
86
86
  }), [getObject]);
87
87
  const registerRef = useCallback((id, obj) => {
88
88
  objectRefs.current[id] = obj;
89
- }, []);
89
+ onObjectRefChange === null || onObjectRefChange === void 0 ? void 0 : onObjectRefChange(id, obj);
90
+ }, [onObjectRefChange]);
90
91
  const registerRigidBodyRef = useCallback((id, rb) => {
91
92
  rigidBodyRefs.current.set(id, rb);
92
93
  }, []);
@@ -94,15 +95,6 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
94
95
  var _a;
95
96
  return (_a = rigidBodyRefs.current.get(id)) !== null && _a !== void 0 ? _a : null;
96
97
  }, []);
97
- useEffect(() => {
98
- const originalError = console.error;
99
- console.error = (...args) => {
100
- if (typeof args[0] === 'string' && args[0].includes('TransformControls') && args[0].includes('scene graph'))
101
- return;
102
- originalError.apply(console, args);
103
- };
104
- return () => { console.error = originalError; };
105
- }, []);
106
98
  useEffect(() => {
107
99
  if (usesOwnedStore && data) {
108
100
  resolvedStore.getState().replacePrefab(data);
@@ -281,7 +273,8 @@ function StandardNode({ nodeId, selectedId, onSelect, onClick, registerRef, load
281
273
  ? _jsx(CompositionChildren, { childIds: childIds, selectedId: selectedId, ctx: renderCtx, parentMatrix: world })
282
274
  : _jsx(ChildNodes, { childIds: childIds, parentMatrix: world, selectedId: selectedId, onSelect: onSelect, onClick: onClick, registerRef: registerRef, loadedModels: loadedModels, editMode: editMode });
283
275
  const inner = (_jsx("group", Object.assign({}, clickHandlers, { children: renderCompositionNode(gameObject, renderCtx, childNodes) })));
284
- return (_jsx(EntityRuntimeScope, { nodeId: nodeId, editMode: editMode, isSelected: isSelected, children: editMode ? (_jsxs(_Fragment, { children: [_jsx("group", { ref: groupRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: _jsx("mesh", { visible: false, children: _jsx("boxGeometry", { args: [0.01, 0.01, 0.01] }) }) }), _jsx("group", { ref: helperRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: inner }), hasPhysics && (physicsDef === null || physicsDef === void 0 ? void 0 : physicsDef.View) ? (_jsx(physicsDef.View, { properties: physics.properties, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: inner }, physicsKey)) : null] })) : hasPhysics && (physicsDef === null || physicsDef === void 0 ? void 0 : physicsDef.View) ? (_jsx(physicsDef.View, { properties: physics.properties, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: inner }, physicsKey)) : (_jsx("group", { ref: groupRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: inner })) }));
276
+ const physicsInner = editMode ? _jsx("group", { visible: false, children: inner }) : inner;
277
+ return (_jsx(EntityRuntimeScope, { nodeId: nodeId, editMode: editMode, isSelected: isSelected, children: editMode ? (_jsxs(_Fragment, { children: [_jsx("group", { ref: groupRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: _jsx("mesh", { visible: false, children: _jsx("boxGeometry", { args: [0.01, 0.01, 0.01] }) }) }), _jsx("group", { ref: helperRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: inner }), hasPhysics && (physicsDef === null || physicsDef === void 0 ? void 0 : physicsDef.View) ? (_jsx(physicsDef.View, { properties: physics.properties, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: physicsInner }, physicsKey)) : null] })) : hasPhysics && (physicsDef === null || physicsDef === void 0 ? void 0 : physicsDef.View) ? (_jsx(physicsDef.View, { properties: physics.properties, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: inner }, physicsKey)) : (_jsx("group", { ref: groupRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: inner })) }));
285
278
  }
286
279
  function getChildHostComponents(gameObject) {
287
280
  var _a;
@@ -129,9 +129,9 @@ function DirectionalLightView({ properties, children }) {
129
129
  directionalLightRef.current.target = targetRef.current;
130
130
  const nextShadowCamera = directionalLightRef.current.shadow.camera;
131
131
  shadowCameraRef.current = nextShadowCamera;
132
- setShadowCamera(nextShadowCamera);
132
+ setShadowCamera(castShadow ? nextShadowCamera : null);
133
133
  }
134
- }, []);
134
+ }, [castShadow]);
135
135
  useEffect(() => {
136
136
  var _a;
137
137
  const shadow = (_a = directionalLightRef.current) === null || _a === void 0 ? void 0 : _a.shadow;
@@ -55,7 +55,7 @@ function SpotLightView({ properties, children }) {
55
55
  if (spotLightRef.current && targetRef.current) {
56
56
  spotLightRef.current.target = targetRef.current;
57
57
  }
58
- }, []);
58
+ }, [castShadow]);
59
59
  useEffect(() => {
60
60
  var _a;
61
61
  const shadow = (_a = spotLightRef.current) === null || _a === void 0 ? void 0 : _a.shadow;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-three-game",
3
- "version": "0.0.76",
3
+ "version": "0.0.78",
4
4
  "description": "high performance 3D game engine built in React",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -48,7 +48,6 @@
48
48
  "vite": "^7.3.1"
49
49
  },
50
50
  "dependencies": {
51
- "react-error-boundary": "^6.1.0",
52
51
  "zustand": "^5.0.12"
53
52
  }
54
53
  }