react-three-game 0.0.13 → 0.0.15

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 CHANGED
@@ -64,7 +64,7 @@ npm install react-three-game @react-three/fiber @react-three/rapier three
64
64
  ```
65
65
 
66
66
  ```jsx
67
- import { GameCanvas, PrefabRoot } from 'react-three-game';
67
+ import { GameCanvas, PrefabRoot, ground } from 'react-three-game';
68
68
 
69
69
  export default function App() {
70
70
  return (
@@ -78,15 +78,13 @@ export default function App() {
78
78
  transform: { type: "Transform", properties: { position: [0, 0, 0] } }
79
79
  },
80
80
  children: [
81
- {
81
+ ground({
82
82
  id: "floor",
83
- components: {
84
- transform: { type: "Transform", properties: { position: [0, -1, 0] } },
85
- geometry: { type: "Geometry", properties: { geometryType: "box", args: [10, 0.5, 10] } },
86
- material: { type: "Material", properties: { color: "#2d5f2e" } },
87
- physics: { type: "Physics", properties: { type: "fixed" } }
88
- }
89
- },
83
+ position: [0, -1, 0],
84
+ size: 50,
85
+ texture: "/textures/GreyboxTextures/greybox_light_grid.png",
86
+ repeatCount: [25, 25]
87
+ }),
90
88
  {
91
89
  id: "player",
92
90
  components: {
@@ -109,8 +107,8 @@ export default function App() {
109
107
  ```typescript
110
108
  interface GameObject {
111
109
  id: string;
112
- enabled?: boolean;
113
- visible?: boolean;
110
+ disabled?: boolean;
111
+ hidden?: boolean;
114
112
  components: {
115
113
  transform?: TransformComponent;
116
114
  geometry?: GeometryComponent;
Binary file
Binary file
@@ -0,0 +1,35 @@
1
+ import type { GameObject } from "../tools/prefabeditor/types";
2
+ export type Vec3 = [number, number, number];
3
+ export interface GroundOptions {
4
+ /** GameObject id. Defaults to "ground". */
5
+ id?: string;
6
+ /** Plane size. Defaults to 50. */
7
+ size?: number;
8
+ /** Transform overrides. */
9
+ position?: Vec3;
10
+ rotation?: Vec3;
11
+ scale?: Vec3;
12
+ /** Material overrides. */
13
+ color?: string;
14
+ texture?: string;
15
+ /** When true, set repeat wrapping. Defaults to true if texture is provided. */
16
+ repeat?: boolean;
17
+ /** Texture repeat counts when repeat=true. Defaults to [25,25]. */
18
+ repeatCount?: [number, number];
19
+ /** Physics body type. Defaults to "fixed". */
20
+ physicsType?: "fixed" | "dynamic" | "kinematic";
21
+ /** Set true to hide the node. */
22
+ hidden?: boolean;
23
+ /** Set true to disable the node. */
24
+ disabled?: boolean;
25
+ }
26
+ /**
27
+ * Create a ready-to-use plane ground GameObject.
28
+ *
29
+ * Designed to reduce prefab boilerplate:
30
+ * - Transform (rotated to lie flat)
31
+ * - Geometry (plane)
32
+ * - Material (optional texture + repeat)
33
+ * - Physics (fixed by default)
34
+ */
35
+ export declare function ground(options?: GroundOptions): GameObject;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Create a ready-to-use plane ground GameObject.
3
+ *
4
+ * Designed to reduce prefab boilerplate:
5
+ * - Transform (rotated to lie flat)
6
+ * - Geometry (plane)
7
+ * - Material (optional texture + repeat)
8
+ * - Physics (fixed by default)
9
+ */
10
+ export function ground(options = {}) {
11
+ const { id = "ground", size = 50, position = [0, 0, 0], rotation = [-Math.PI / 2, 0, 0], scale = [1, 1, 1], color = "white", texture, repeat = texture ? true : false, repeatCount = [25, 25], physicsType = "fixed", hidden = false, disabled = false, } = options;
12
+ return {
13
+ id,
14
+ disabled,
15
+ hidden,
16
+ components: {
17
+ transform: {
18
+ type: "Transform",
19
+ properties: {
20
+ position,
21
+ rotation,
22
+ scale,
23
+ },
24
+ },
25
+ geometry: {
26
+ type: "Geometry",
27
+ properties: {
28
+ geometryType: "plane",
29
+ args: [size, size],
30
+ },
31
+ },
32
+ material: {
33
+ type: "Material",
34
+ properties: Object.assign(Object.assign({ color }, (texture ? { texture } : {})), (repeat ? { repeat: true, repeatCount } : {})),
35
+ },
36
+ physics: {
37
+ type: "Physics",
38
+ properties: {
39
+ type: physicsType,
40
+ },
41
+ },
42
+ },
43
+ };
44
+ }
package/dist/index.d.ts CHANGED
@@ -3,4 +3,5 @@ export { default as PrefabEditor } from './tools/prefabeditor/PrefabEditor';
3
3
  export { default as PrefabRoot } from './tools/prefabeditor/PrefabRoot';
4
4
  export { DragDropLoader } from './tools/dragdrop/DragDropLoader';
5
5
  export { TextureListViewer, ModelListViewer, SoundListViewer, SharedCanvas, } from './tools/assetviewer/page';
6
+ export * from './helpers';
6
7
  export type { Prefab, GameObject } from './tools/prefabeditor/types';
package/dist/index.js CHANGED
@@ -4,3 +4,5 @@ export { default as PrefabEditor } from './tools/prefabeditor/PrefabEditor';
4
4
  export { default as PrefabRoot } from './tools/prefabeditor/PrefabRoot';
5
5
  export { DragDropLoader } from './tools/dragdrop/DragDropLoader';
6
6
  export { TextureListViewer, ModelListViewer, SoundListViewer, SharedCanvas, } from './tools/assetviewer/page';
7
+ // Helpers
8
+ export * from './helpers';
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
3
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
4
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -18,7 +19,7 @@ var __rest = (this && this.__rest) || function (s, e) {
18
19
  }
19
20
  return t;
20
21
  };
21
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
22
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
22
23
  import { Canvas, extend } from "@react-three/fiber";
23
24
  import { WebGPURenderer, MeshBasicNodeMaterial, MeshStandardNodeMaterial, SpriteNodeMaterial, PCFShadowMap } from "three/webgpu";
24
25
  import { Suspense, useState } from "react";
@@ -33,16 +34,15 @@ extend({
33
34
  export default function GameCanvas(_a) {
34
35
  var { loader = false, children } = _a, props = __rest(_a, ["loader", "children"]);
35
36
  const [frameloop, setFrameloop] = useState("never");
36
- const [loading, setLoading] = useState(true);
37
- return _jsxs(_Fragment, { children: [_jsx(Canvas, { shadows: { type: PCFShadowMap, }, frameloop: frameloop, gl: (_a) => __awaiter(this, [_a], void 0, function* ({ canvas }) {
38
- const renderer = new WebGPURenderer(Object.assign({ canvas: canvas,
39
- // @ts-expect-error futuristic
40
- shadowMap: true, antialias: true }, props));
41
- yield renderer.init().then(() => {
42
- setFrameloop("always");
43
- });
44
- return renderer;
45
- }), camera: {
46
- position: [0, 1, 5],
47
- }, children: _jsx(Suspense, { children: children }) }), loader ? _jsx(Loader, {}) : null] });
37
+ return _jsx(_Fragment, { children: _jsxs(Canvas, { style: { touchAction: 'none' }, shadows: { type: PCFShadowMap, }, frameloop: frameloop, gl: (_a) => __awaiter(this, [_a], void 0, function* ({ canvas }) {
38
+ const renderer = new WebGPURenderer(Object.assign({ canvas: canvas,
39
+ // @ts-expect-error futuristic
40
+ shadowMap: true, antialias: true }, props));
41
+ yield renderer.init().then(() => {
42
+ setFrameloop("always");
43
+ });
44
+ return renderer;
45
+ }), camera: {
46
+ position: [0, 1, 5],
47
+ }, children: [_jsx(Suspense, { children: children }), loader ? _jsx(Loader, {}) : null] }) });
48
48
  }
@@ -30,8 +30,6 @@ export default function EditorTree({ prefabData, setPrefabData, selectedId, setS
30
30
  var _a;
31
31
  const newNode = {
32
32
  id: crypto.randomUUID(),
33
- enabled: true,
34
- visible: true,
35
33
  components: {
36
34
  transform: {
37
35
  type: "Transform",
@@ -21,8 +21,6 @@ const PrefabEditor = ({ basePath, initialPrefab, onPrefabChange, children }) =>
21
21
  "name": "New Prefab",
22
22
  "root": {
23
23
  "id": "root",
24
- "enabled": true,
25
- "visible": true,
26
24
  "components": {
27
25
  "transform": {
28
26
  "type": "Transform",
@@ -49,13 +49,6 @@ export const PrefabRoot = forwardRef(({ editMode, data, onPrefabChange, selected
49
49
  setSelectedObject(null);
50
50
  }
51
51
  }, [selectedId]);
52
- // const [transformMode, setTransformMode] = useState<"translate" | "rotate" | "scale">("translate"); // Removed local state
53
- const updateNode = (updater) => {
54
- if (!selectedId || !onPrefabChange)
55
- return;
56
- const newRoot = updatePrefabNode(data.root, selectedId, updater);
57
- onPrefabChange(Object.assign(Object.assign({}, data), { root: newRoot }));
58
- };
59
52
  const onTransformChange = () => {
60
53
  if (!selectedId || !onPrefabChange)
61
54
  return;
@@ -75,17 +68,14 @@ export const PrefabRoot = forwardRef(({ editMode, data, onPrefabChange, selected
75
68
  localMatrix.decompose(lp, lq, ls);
76
69
  const le = new Euler().setFromQuaternion(lq);
77
70
  // 4. Write back LOCAL transform into the prefab node
78
- const newRoot = updatePrefabNode(data.root, selectedId, (node) => {
79
- var _a, _b, _c;
80
- return (Object.assign(Object.assign({}, node), { components: Object.assign(Object.assign({}, node === null || node === void 0 ? void 0 : node.components), { transform: {
81
- type: "Transform",
82
- properties: {
83
- position: [lp.x, lp.y, lp.z],
84
- rotation: [le.x, le.y, le.z],
85
- scale: [ls.x, ls.y, ls.z],
86
- },
87
- }, geometry: (_a = node.components) === null || _a === void 0 ? void 0 : _a.geometry, material: (_b = node.components) === null || _b === void 0 ? void 0 : _b.material, model: (_c = node.components) === null || _c === void 0 ? void 0 : _c.model }) }));
88
- });
71
+ const newRoot = updatePrefabNode(data.root, selectedId, (node) => (Object.assign(Object.assign({}, node), { components: Object.assign(Object.assign({}, node === null || node === void 0 ? void 0 : node.components), { transform: {
72
+ type: "Transform",
73
+ properties: {
74
+ position: [lp.x, lp.y, lp.z],
75
+ rotation: [le.x, le.y, le.z],
76
+ scale: [ls.x, ls.y, ls.z],
77
+ },
78
+ } }) })));
89
79
  onPrefabChange(Object.assign(Object.assign({}, data), { root: newRoot }));
90
80
  };
91
81
  useEffect(() => {
@@ -161,7 +151,7 @@ function GameObjectRenderer({ gameObject, selectedId, onSelect, registerRef, loa
161
151
  }
162
152
  clickValid.current = false;
163
153
  };
164
- if (!gameObject.enabled || !gameObject.visible)
154
+ if (gameObject.disabled === true || gameObject.hidden === true)
165
155
  return null;
166
156
  // --- 2. If instanced, short-circuit to a tiny clean branch ---
167
157
  const isInstanced = !!((_c = (_b = (_a = gameObject.components) === null || _a === void 0 ? void 0 : _a.model) === null || _b === void 0 ? void 0 : _b.properties) === null || _c === void 0 ? void 0 : _c.instanced);
@@ -173,11 +163,9 @@ function GameObjectRenderer({ gameObject, selectedId, onSelect, registerRef, loa
173
163
  // --- 5. Render children (always relative transforms) ---
174
164
  const children = ((_d = gameObject.children) !== null && _d !== void 0 ? _d : []).map((child) => (_jsx(GameObjectRenderer, { gameObject: child, selectedId: selectedId, onSelect: onSelect, registerRef: registerRef, loadedModels: loadedModels, loadedTextures: loadedTextures, editMode: editMode, parentMatrix: worldMatrix }, child.id)));
175
165
  // --- 4. Wrap with physics if needed ---
176
- // Combine core and children so they both get wrapped by physics (if present)
177
- const content = (_jsxs(_Fragment, { children: [core, children] }));
178
- const physicsWrapped = wrapPhysicsIfNeeded(gameObject, content, ctx);
166
+ const physicsWrapped = wrapPhysicsIfNeeded(gameObject, core, ctx);
179
167
  // --- 6. Final group wrapper ---
180
- return (_jsx("group", { ref: (el) => registerRef(gameObject.id, el), position: transformProps.position, rotation: transformProps.rotation, scale: transformProps.scale, onPointerDown: handlePointerDown, onPointerMove: handlePointerMove, onPointerUp: handlePointerUp, children: physicsWrapped }));
168
+ return (_jsxs("group", { ref: (el) => registerRef(gameObject.id, el), position: transformProps.position, rotation: transformProps.rotation, scale: transformProps.scale, onPointerDown: handlePointerDown, onPointerMove: handlePointerMove, onPointerUp: handlePointerUp, children: [physicsWrapped, children] }));
181
169
  }
182
170
  // Helper: render an instanced GameInstance (terminal node)
183
171
  function renderInstancedNode(gameObject, worldMatrix, ctx) {
@@ -242,7 +230,7 @@ function wrapPhysicsIfNeeded(gameObject, content, ctx) {
242
230
  const physicsDef = getComponent('Physics');
243
231
  if (!physicsDef || !physicsDef.View)
244
232
  return content;
245
- return (_jsx(physicsDef.View, { properties: Object.assign(Object.assign({}, physics.properties), { id: gameObject.id }), registerRef: ctx.registerRef, editMode: ctx.editMode, children: content }));
233
+ return (_jsx(physicsDef.View, { properties: Object.assign(Object.assign({}, physics.properties), { id: gameObject.id }), editMode: ctx.editMode, children: content }));
246
234
  }
247
235
  export default PrefabRoot;
248
236
  function getNodeTransformProps(node) {
@@ -3,10 +3,10 @@ function PhysicsComponentEditor({ component, onUpdate }) {
3
3
  return _jsxs("div", { children: [_jsx("label", { className: "block text-[9px] text-cyan-400/60 uppercase tracking-wider mb-0.5", children: "Type" }), _jsxs("select", { className: "w-full bg-black/40 border border-cyan-500/30 px-1 py-0.5 text-[10px] text-cyan-300 font-mono focus:outline-none focus:border-cyan-400/50", value: component.properties.type, onChange: e => onUpdate({ type: e.target.value }), children: [_jsx("option", { value: "dynamic", children: "Dynamic" }), _jsx("option", { value: "fixed", children: "Fixed" })] })] });
4
4
  }
5
5
  import { RigidBody } from "@react-three/rapier";
6
- function PhysicsComponentView({ properties, children, registerRef, transform, editMode }) {
6
+ function PhysicsComponentView({ properties, children, editMode }) {
7
7
  if (editMode)
8
8
  return children;
9
- return (_jsx(RigidBody, { ref: el => registerRef && registerRef(properties.id, el), position: transform === null || transform === void 0 ? void 0 : transform.position, rotation: transform === null || transform === void 0 ? void 0 : transform.rotation, scale: transform === null || transform === void 0 ? void 0 : transform.scale, type: properties.type, colliders: "cuboid", children: children }));
9
+ return (_jsx(RigidBody, { type: properties.type, colliders: "cuboid", children: children }));
10
10
  }
11
11
  const PhysicsComponent = {
12
12
  name: 'Physics',
@@ -22,7 +22,7 @@ function SpotLightView({ properties }) {
22
22
  const penumbra = (_d = properties.penumbra) !== null && _d !== void 0 ? _d : 0.5;
23
23
  const distance = (_e = properties.distance) !== null && _e !== void 0 ? _e : 100;
24
24
  const castShadow = (_f = properties.castShadow) !== null && _f !== void 0 ? _f : true;
25
- return (_jsx(_Fragment, { children: _jsx("spotLight", { color: color, intensity: intensity, angle: angle, penumbra: penumbra, distance: distance, castShadow: castShadow, "target-position": [0, 0, 0], position: [0, 0, 0] }) }));
25
+ return (_jsx(_Fragment, { children: _jsx("spotLight", { color: color, intensity: intensity, angle: angle, penumbra: penumbra, distance: distance, castShadow: castShadow }) }));
26
26
  }
27
27
  const SpotLightComponent = {
28
28
  name: 'SpotLight',
@@ -12,8 +12,8 @@ export interface Prefab {
12
12
  }
13
13
  export interface GameObject {
14
14
  id: string;
15
- enabled: boolean;
16
- visible: boolean;
15
+ disabled?: boolean;
16
+ hidden?: boolean;
17
17
  ref?: any;
18
18
  children?: GameObject[];
19
19
  components?: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-three-game",
3
- "version": "0.0.13",
3
+ "version": "0.0.15",
4
4
  "description": "Batteries included React Three Fiber game engine",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -15,8 +15,11 @@
15
15
  "author": "prnth",
16
16
  "license": "VPL",
17
17
  "type": "module",
18
+ "workspaces": ["docs"],
18
19
  "peerDependencies": {
19
20
  "@react-three/fiber": "^9.0.0",
21
+ "@react-three/drei": "^10.0.0",
22
+ "@react-three/rapier": "^2.0.0",
20
23
  "react": "^18.0.0 || ^19.0.0",
21
24
  "react-dom": "^18.0.0 || ^19.0.0",
22
25
  "three": "^0.181.0"
@@ -0,0 +1,95 @@
1
+ import type { GameObject } from "../tools/prefabeditor/types";
2
+
3
+ export type Vec3 = [number, number, number];
4
+
5
+ export interface GroundOptions {
6
+ /** GameObject id. Defaults to "ground". */
7
+ id?: string;
8
+
9
+ /** Plane size. Defaults to 50. */
10
+ size?: number;
11
+
12
+ /** Transform overrides. */
13
+ position?: Vec3;
14
+ rotation?: Vec3;
15
+ scale?: Vec3;
16
+
17
+ /** Material overrides. */
18
+ color?: string;
19
+ texture?: string;
20
+ /** When true, set repeat wrapping. Defaults to true if texture is provided. */
21
+ repeat?: boolean;
22
+ /** Texture repeat counts when repeat=true. Defaults to [25,25]. */
23
+ repeatCount?: [number, number];
24
+
25
+ /** Physics body type. Defaults to "fixed". */
26
+ physicsType?: "fixed" | "dynamic" | "kinematic";
27
+
28
+ /** Set true to hide the node. */
29
+ hidden?: boolean;
30
+ /** Set true to disable the node. */
31
+ disabled?: boolean;
32
+ }
33
+
34
+ /**
35
+ * Create a ready-to-use plane ground GameObject.
36
+ *
37
+ * Designed to reduce prefab boilerplate:
38
+ * - Transform (rotated to lie flat)
39
+ * - Geometry (plane)
40
+ * - Material (optional texture + repeat)
41
+ * - Physics (fixed by default)
42
+ */
43
+ export function ground(options: GroundOptions = {}): GameObject {
44
+ const {
45
+ id = "ground",
46
+ size = 50,
47
+ position = [0, 0, 0],
48
+ rotation = [-Math.PI / 2, 0, 0],
49
+ scale = [1, 1, 1],
50
+ color = "white",
51
+ texture,
52
+ repeat = texture ? true : false,
53
+ repeatCount = [25, 25],
54
+ physicsType = "fixed",
55
+ hidden = false,
56
+ disabled = false,
57
+ } = options;
58
+
59
+ return {
60
+ id,
61
+ disabled,
62
+ hidden,
63
+ components: {
64
+ transform: {
65
+ type: "Transform",
66
+ properties: {
67
+ position,
68
+ rotation,
69
+ scale,
70
+ },
71
+ },
72
+ geometry: {
73
+ type: "Geometry",
74
+ properties: {
75
+ geometryType: "plane",
76
+ args: [size, size],
77
+ },
78
+ },
79
+ material: {
80
+ type: "Material",
81
+ properties: {
82
+ color,
83
+ ...(texture ? { texture } : {}),
84
+ ...(repeat ? { repeat: true, repeatCount } : {}),
85
+ },
86
+ },
87
+ physics: {
88
+ type: "Physics",
89
+ properties: {
90
+ type: physicsType,
91
+ },
92
+ },
93
+ },
94
+ };
95
+ }
package/src/index.ts CHANGED
@@ -10,5 +10,8 @@ export {
10
10
  SharedCanvas,
11
11
  } from './tools/assetviewer/page';
12
12
 
13
+ // Helpers
14
+ export * from './helpers';
15
+
13
16
  // Types
14
17
  export type { Prefab, GameObject } from './tools/prefabeditor/types';
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { Canvas, extend } from "@react-three/fiber";
2
4
  import { WebGPURenderer, MeshBasicNodeMaterial, MeshStandardNodeMaterial, SpriteNodeMaterial, PCFShadowMap } from "three/webgpu";
3
5
  import { Suspense, useState } from "react";
@@ -16,10 +18,10 @@ extend({
16
18
 
17
19
  export default function GameCanvas({ loader = false, children, ...props }: { loader?: boolean, children: React.ReactNode, props?: WebGPURendererParameters }) {
18
20
  const [frameloop, setFrameloop] = useState<"never" | "always">("never");
19
- const [loading, setLoading] = useState(true);
20
21
 
21
22
  return <>
22
23
  <Canvas
24
+ style={{ touchAction: 'none' }}
23
25
  shadows={{ type: PCFShadowMap, }}
24
26
  frameloop={frameloop}
25
27
  gl={async ({ canvas }) => {
@@ -42,7 +44,8 @@ export default function GameCanvas({ loader = false, children, ...props }: { loa
42
44
  <Suspense>
43
45
  {children}
44
46
  </Suspense>
47
+
48
+ {loader ? <Loader /> : null}
45
49
  </Canvas>
46
- {loader ? <Loader /> : null}
47
50
  </>;
48
51
  }
@@ -39,8 +39,6 @@ export default function EditorTree({ prefabData, setPrefabData, selectedId, setS
39
39
  const handleAddChild = (parentId: string) => {
40
40
  const newNode: GameObject = {
41
41
  id: crypto.randomUUID(),
42
- enabled: true,
43
- visible: true,
44
42
  components: {
45
43
  transform: {
46
44
  type: "Transform",
@@ -15,8 +15,6 @@ const PrefabEditor = ({ basePath, initialPrefab, onPrefabChange, children }: { b
15
15
  "name": "New Prefab",
16
16
  "root": {
17
17
  "id": "root",
18
- "enabled": true,
19
- "visible": true,
20
18
  "components": {
21
19
  "transform": {
22
20
  "type": "Transform",
@@ -59,15 +59,6 @@ export const PrefabRoot = forwardRef<Group, {
59
59
  }
60
60
  }, [selectedId]);
61
61
 
62
-
63
- // const [transformMode, setTransformMode] = useState<"translate" | "rotate" | "scale">("translate"); // Removed local state
64
-
65
- const updateNode = (updater: (node: GameObjectType) => GameObjectType) => {
66
- if (!selectedId || !onPrefabChange) return;
67
- const newRoot = updatePrefabNode(data.root, selectedId, updater);
68
- onPrefabChange({ ...data, root: newRoot });
69
- };
70
-
71
62
  const onTransformChange = () => {
72
63
  if (!selectedId || !onPrefabChange) return;
73
64
  const obj = objectRefs.current[selectedId];
@@ -103,9 +94,6 @@ export const PrefabRoot = forwardRef<Group, {
103
94
  scale: [ls.x, ls.y, ls.z] as [number, number, number],
104
95
  },
105
96
  },
106
- geometry: node.components?.geometry,
107
- material: node.components?.material,
108
- model: node.components?.model,
109
97
  },
110
98
  }));
111
99
 
@@ -170,7 +158,7 @@ export const PrefabRoot = forwardRef<Group, {
170
158
  loadedModels={loadedModels}
171
159
  loadedTextures={loadedTextures}
172
160
  editMode={editMode}
173
- parentMatrix={new Matrix4()} // 👈 identity = world root
161
+ parentMatrix={new Matrix4()}
174
162
  />
175
163
  </GameInstanceProvider>
176
164
 
@@ -245,7 +233,7 @@ function GameObjectRenderer({
245
233
  clickValid.current = false;
246
234
  };
247
235
 
248
- if (!gameObject.enabled || !gameObject.visible) return null;
236
+ if (gameObject.disabled === true || gameObject.hidden === true) return null;
249
237
 
250
238
  // --- 2. If instanced, short-circuit to a tiny clean branch ---
251
239
  const isInstanced = !!gameObject.components?.model?.properties?.instanced;
@@ -272,14 +260,7 @@ function GameObjectRenderer({
272
260
  ));
273
261
 
274
262
  // --- 4. Wrap with physics if needed ---
275
- // Combine core and children so they both get wrapped by physics (if present)
276
- const content = (
277
- <>
278
- {core}
279
- {children}
280
- </>
281
- );
282
- const physicsWrapped = wrapPhysicsIfNeeded(gameObject, content, ctx);
263
+ const physicsWrapped = wrapPhysicsIfNeeded(gameObject, core, ctx);
283
264
 
284
265
  // --- 6. Final group wrapper ---
285
266
  return (
@@ -293,6 +274,7 @@ function GameObjectRenderer({
293
274
  onPointerUp={handlePointerUp}
294
275
  >
295
276
  {physicsWrapped}
277
+ {children}
296
278
  </group>
297
279
  );
298
280
  }
@@ -404,7 +386,6 @@ function wrapPhysicsIfNeeded(gameObject: GameObjectType, content: React.ReactNod
404
386
  return (
405
387
  <physicsDef.View
406
388
  properties={{ ...physics.properties, id: gameObject.id }}
407
- registerRef={ctx.registerRef}
408
389
  editMode={ctx.editMode}
409
390
  >
410
391
  {content}
@@ -16,17 +16,11 @@ function PhysicsComponentEditor({ component, onUpdate }: { component: any; onUpd
16
16
 
17
17
 
18
18
  import { RigidBody } from "@react-three/rapier";
19
- import { Object3D } from "three";
20
- import { useRef } from "react";
21
19
 
22
- function PhysicsComponentView({ properties, children, registerRef, transform, editMode }: any) {
20
+ function PhysicsComponentView({ properties, children, editMode }: any) {
23
21
  if (editMode) return children;
24
22
  return (
25
23
  <RigidBody
26
- ref={el => registerRef && registerRef(properties.id, el as unknown as Object3D)}
27
- position={transform?.position}
28
- rotation={transform?.rotation}
29
- scale={transform?.scale}
30
24
  type={properties.type}
31
25
  colliders="cuboid"
32
26
  >
@@ -108,8 +108,6 @@ function SpotLightView({ properties }: { properties: any }) {
108
108
  penumbra={penumbra}
109
109
  distance={distance}
110
110
  castShadow={castShadow}
111
- target-position={[0, 0, 0]}
112
- position={[0, 0, 0]}
113
111
  />
114
112
  </>
115
113
  );
@@ -13,8 +13,8 @@ export interface Prefab {
13
13
 
14
14
  export interface GameObject {
15
15
  id: string;
16
- enabled: boolean;
17
- visible: boolean;
16
+ disabled?: boolean;
17
+ hidden?: boolean;
18
18
  ref?: any;
19
19
  children?: GameObject[];
20
20
  components?: {