react-three-game 0.0.34 → 0.0.35
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/dist/tools/prefabeditor/InstanceProvider.d.ts +3 -6
- package/dist/tools/prefabeditor/PrefabRoot.d.ts +13 -2
- package/dist/tools/prefabeditor/PrefabRoot.js +169 -193
- package/dist/tools/prefabeditor/components/PhysicsComponent.d.ts +7 -0
- package/package.json +1 -1
- package/src/tools/prefabeditor/InstanceProvider.tsx +3 -2
- package/src/tools/prefabeditor/PrefabRoot.tsx +298 -303
- package/src/tools/prefabeditor/components/PhysicsComponent.tsx +8 -0
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Object3D, Group } from "three";
|
|
3
|
+
import { PhysicsProps } from "./components/PhysicsComponent";
|
|
3
4
|
export type InstanceData = {
|
|
4
5
|
id: string;
|
|
5
6
|
position: [number, number, number];
|
|
6
7
|
rotation: [number, number, number];
|
|
7
8
|
scale: [number, number, number];
|
|
8
9
|
meshPath: string;
|
|
9
|
-
physics?:
|
|
10
|
-
type: 'dynamic' | 'fixed';
|
|
11
|
-
};
|
|
10
|
+
physics?: PhysicsProps | undefined;
|
|
12
11
|
};
|
|
13
12
|
export declare function GameInstanceProvider({ children, models, onSelect, registerRef, selectedId, editMode }: {
|
|
14
13
|
children: React.ReactNode;
|
|
@@ -26,7 +25,5 @@ export declare const GameInstance: React.ForwardRefExoticComponent<{
|
|
|
26
25
|
position: [number, number, number];
|
|
27
26
|
rotation: [number, number, number];
|
|
28
27
|
scale: [number, number, number];
|
|
29
|
-
physics?:
|
|
30
|
-
type: "dynamic" | "fixed";
|
|
31
|
-
};
|
|
28
|
+
physics?: PhysicsProps | undefined;
|
|
32
29
|
} & React.RefAttributes<Group<import("three").Object3DEventMap>>>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Group } from "three";
|
|
2
|
-
import { Prefab } from "./types";
|
|
1
|
+
import { Group, Matrix4, Object3D, Texture } from "three";
|
|
2
|
+
import { Prefab, GameObject as GameObjectType } from "./types";
|
|
3
3
|
export declare const PrefabRoot: import("react").ForwardRefExoticComponent<{
|
|
4
4
|
editMode?: boolean;
|
|
5
5
|
data: Prefab;
|
|
@@ -9,4 +9,15 @@ export declare const PrefabRoot: import("react").ForwardRefExoticComponent<{
|
|
|
9
9
|
transformMode?: "translate" | "rotate" | "scale";
|
|
10
10
|
basePath?: string;
|
|
11
11
|
} & import("react").RefAttributes<Group<import("three").Object3DEventMap>>>;
|
|
12
|
+
export declare function GameObjectRenderer(props: RendererProps): import("react/jsx-runtime").JSX.Element | null;
|
|
13
|
+
interface RendererProps {
|
|
14
|
+
gameObject: GameObjectType;
|
|
15
|
+
selectedId?: string | null;
|
|
16
|
+
onSelect?: (id: string) => void;
|
|
17
|
+
registerRef: (id: string, obj: Object3D | null) => void;
|
|
18
|
+
loadedModels: Record<string, Object3D>;
|
|
19
|
+
loadedTextures: Record<string, Texture>;
|
|
20
|
+
editMode?: boolean;
|
|
21
|
+
parentMatrix?: Matrix4;
|
|
22
|
+
}
|
|
12
23
|
export default PrefabRoot;
|
|
@@ -10,19 +10,25 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
12
12
|
import { MapControls, TransformControls, useHelper } from "@react-three/drei";
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
13
|
+
import { forwardRef, useCallback, useEffect, useRef, useState, } from "react";
|
|
14
|
+
import { BoxHelper, Euler, Matrix4, Quaternion, SRGBColorSpace, TextureLoader, Vector3, } from "three";
|
|
15
15
|
import { getComponent, registerComponent } from "./components/ComponentRegistry";
|
|
16
|
+
import components from "./components";
|
|
16
17
|
import { loadModel } from "../dragdrop/modelLoader";
|
|
17
18
|
import { GameInstance, GameInstanceProvider } from "./InstanceProvider";
|
|
18
19
|
import { updateNode } from "./utils";
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
/* -------------------------------------------------- */
|
|
21
|
+
/* Setup */
|
|
22
|
+
/* -------------------------------------------------- */
|
|
21
23
|
components.forEach(registerComponent);
|
|
24
|
+
const IDENTITY = new Matrix4();
|
|
25
|
+
/* -------------------------------------------------- */
|
|
26
|
+
/* PrefabRoot */
|
|
27
|
+
/* -------------------------------------------------- */
|
|
22
28
|
export const PrefabRoot = forwardRef(({ editMode, data, onPrefabChange, selectedId, onSelect, transformMode, basePath = "" }, ref) => {
|
|
23
|
-
const [
|
|
24
|
-
const [
|
|
25
|
-
const
|
|
29
|
+
const [models, setModels] = useState({});
|
|
30
|
+
const [textures, setTextures] = useState({});
|
|
31
|
+
const loading = useRef(new Set());
|
|
26
32
|
const objectRefs = useRef({});
|
|
27
33
|
const [selectedObject, setSelectedObject] = useState(null);
|
|
28
34
|
const registerRef = useCallback((id, obj) => {
|
|
@@ -31,165 +37,179 @@ export const PrefabRoot = forwardRef(({ editMode, data, onPrefabChange, selected
|
|
|
31
37
|
setSelectedObject(obj);
|
|
32
38
|
}, [selectedId]);
|
|
33
39
|
useEffect(() => {
|
|
34
|
-
|
|
40
|
+
var _a;
|
|
41
|
+
setSelectedObject(selectedId ? (_a = objectRefs.current[selectedId]) !== null && _a !== void 0 ? _a : null : null);
|
|
35
42
|
}, [selectedId]);
|
|
43
|
+
/* ---------------- Transform writeback ---------------- */
|
|
36
44
|
const onTransformChange = () => {
|
|
37
45
|
if (!selectedId || !onPrefabChange)
|
|
38
46
|
return;
|
|
39
47
|
const obj = objectRefs.current[selectedId];
|
|
40
48
|
if (!obj)
|
|
41
49
|
return;
|
|
42
|
-
// 1. Get world matrix from the actual Three object
|
|
43
|
-
const worldMatrix = obj.matrixWorld.clone();
|
|
44
|
-
// 2. Compute parent world matrix from the prefab tree
|
|
45
50
|
const parentWorld = computeParentWorldMatrix(data.root, selectedId);
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
const lp = new Vector3();
|
|
50
|
-
const lq = new Quaternion();
|
|
51
|
-
const ls = new Vector3();
|
|
52
|
-
localMatrix.decompose(lp, lq, ls);
|
|
53
|
-
const le = new Euler().setFromQuaternion(lq);
|
|
54
|
-
// 4. Write back LOCAL transform into the prefab node
|
|
55
|
-
const newRoot = updateNode(data.root, selectedId, (node) => (Object.assign(Object.assign({}, node), { components: Object.assign(Object.assign({}, node.components), { transform: {
|
|
51
|
+
const local = parentWorld.clone().invert().multiply(obj.matrixWorld);
|
|
52
|
+
const { position, rotation, scale } = decompose(local);
|
|
53
|
+
const root = updateNode(data.root, selectedId, node => (Object.assign(Object.assign({}, node), { components: Object.assign(Object.assign({}, node.components), { transform: {
|
|
56
54
|
type: "Transform",
|
|
57
|
-
properties: {
|
|
58
|
-
position: [lp.x, lp.y, lp.z],
|
|
59
|
-
rotation: [le.x, le.y, le.z],
|
|
60
|
-
scale: [ls.x, ls.y, ls.z],
|
|
61
|
-
},
|
|
55
|
+
properties: { position, rotation, scale },
|
|
62
56
|
} }) })));
|
|
63
|
-
onPrefabChange(Object.assign(Object.assign({}, data), { root
|
|
57
|
+
onPrefabChange(Object.assign(Object.assign({}, data), { root }));
|
|
64
58
|
};
|
|
59
|
+
/* ---------------- Asset loading ---------------- */
|
|
65
60
|
useEffect(() => {
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const texturePath = filename.startsWith('/') ? filename : `/${filename}`;
|
|
99
|
-
textureLoader.load(texturePath, (texture) => {
|
|
100
|
-
texture.colorSpace = SRGBColorSpace;
|
|
101
|
-
setLoadedTextures(prev => (Object.assign(Object.assign({}, prev), { [filename]: texture })));
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
}
|
|
61
|
+
const modelsToLoad = new Set();
|
|
62
|
+
const texturesToLoad = new Set();
|
|
63
|
+
walk(data.root, node => {
|
|
64
|
+
var _a, _b, _c, _d, _e, _f;
|
|
65
|
+
((_c = (_b = (_a = node.components) === null || _a === void 0 ? void 0 : _a.model) === null || _b === void 0 ? void 0 : _b.properties) === null || _c === void 0 ? void 0 : _c.filename) &&
|
|
66
|
+
modelsToLoad.add(node.components.model.properties.filename);
|
|
67
|
+
((_f = (_e = (_d = node.components) === null || _d === void 0 ? void 0 : _d.material) === null || _e === void 0 ? void 0 : _e.properties) === null || _f === void 0 ? void 0 : _f.texture) &&
|
|
68
|
+
texturesToLoad.add(node.components.material.properties.texture);
|
|
69
|
+
});
|
|
70
|
+
modelsToLoad.forEach((file) => __awaiter(void 0, void 0, void 0, function* () {
|
|
71
|
+
if (models[file] || loading.current.has(file))
|
|
72
|
+
return;
|
|
73
|
+
loading.current.add(file);
|
|
74
|
+
const path = file.startsWith("/")
|
|
75
|
+
? `${basePath}${file}`
|
|
76
|
+
: `${basePath}/${file}`;
|
|
77
|
+
const res = yield loadModel(path);
|
|
78
|
+
res.success && res.model &&
|
|
79
|
+
setModels(m => (Object.assign(Object.assign({}, m), { [file]: res.model })));
|
|
80
|
+
}));
|
|
81
|
+
const loader = new TextureLoader();
|
|
82
|
+
texturesToLoad.forEach(file => {
|
|
83
|
+
if (textures[file] || loading.current.has(file))
|
|
84
|
+
return;
|
|
85
|
+
loading.current.add(file);
|
|
86
|
+
const path = file.startsWith("/")
|
|
87
|
+
? `${basePath}${file}`
|
|
88
|
+
: `${basePath}/${file}`;
|
|
89
|
+
loader.load(path, tex => {
|
|
90
|
+
tex.colorSpace = SRGBColorSpace;
|
|
91
|
+
setTextures(t => (Object.assign(Object.assign({}, t), { [file]: tex })));
|
|
92
|
+
});
|
|
105
93
|
});
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
return _jsxs("group", { ref: ref, children: [_jsx(GameInstanceProvider, { models:
|
|
94
|
+
}, [data, models, textures]);
|
|
95
|
+
/* ---------------- Render ---------------- */
|
|
96
|
+
return (_jsxs("group", { ref: ref, children: [_jsx(GameInstanceProvider, { models: models, selectedId: selectedId, editMode: editMode, onSelect: editMode ? onSelect : undefined, registerRef: registerRef, children: _jsx(GameObjectRenderer, { gameObject: data.root, selectedId: selectedId, onSelect: editMode ? onSelect : undefined, registerRef: registerRef, loadedModels: models, loadedTextures: textures, editMode: editMode, parentMatrix: IDENTITY }) }), editMode && (_jsxs(_Fragment, { children: [_jsx(MapControls, { makeDefault: true }), selectedObject && (_jsx(TransformControls, { object: selectedObject, mode: transformMode, space: "local", onObjectChange: onTransformChange }))] }))] }));
|
|
109
97
|
});
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
98
|
+
/* -------------------------------------------------- */
|
|
99
|
+
/* Renderer Switch */
|
|
100
|
+
/* -------------------------------------------------- */
|
|
101
|
+
export function GameObjectRenderer(props) {
|
|
102
|
+
var _a, _b, _c;
|
|
103
|
+
const node = props.gameObject;
|
|
104
|
+
if (!node || node.hidden || node.disabled)
|
|
116
105
|
return null;
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
106
|
+
return ((_c = (_b = (_a = node.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)
|
|
107
|
+
? _jsx(InstancedNode, Object.assign({}, props))
|
|
108
|
+
: _jsx(StandardNode, Object.assign({}, props));
|
|
109
|
+
}
|
|
110
|
+
/* -------------------------------------------------- */
|
|
111
|
+
/* InstancedNode (terminal) */
|
|
112
|
+
/* -------------------------------------------------- */
|
|
113
|
+
function isPhysicsProps(v) {
|
|
114
|
+
return (v === null || v === void 0 ? void 0 : v.type) === "fixed" || (v === null || v === void 0 ? void 0 : v.type) === "dynamic";
|
|
115
|
+
}
|
|
116
|
+
function InstancedNode({ gameObject, parentMatrix = IDENTITY, editMode }) {
|
|
117
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
118
|
+
const world = parentMatrix.clone().multiply(compose(gameObject));
|
|
119
|
+
const { position, rotation, scale } = decompose(world);
|
|
120
|
+
const physicsProps = isPhysicsProps((_b = (_a = gameObject.components) === null || _a === void 0 ? void 0 : _a.physics) === null || _b === void 0 ? void 0 : _b.properties)
|
|
121
|
+
? (_d = (_c = gameObject.components) === null || _c === void 0 ? void 0 : _c.physics) === null || _d === void 0 ? void 0 : _d.properties
|
|
122
|
+
: undefined;
|
|
123
|
+
return (_jsx(GameInstance, { id: gameObject.id, modelUrl: (_g = (_f = (_e = gameObject.components) === null || _e === void 0 ? void 0 : _e.model) === null || _f === void 0 ? void 0 : _f.properties) === null || _g === void 0 ? void 0 : _g.filename, position: position, rotation: rotation, scale: scale, physics: editMode ? undefined : physicsProps }));
|
|
124
|
+
}
|
|
125
|
+
/* -------------------------------------------------- */
|
|
126
|
+
/* StandardNode */
|
|
127
|
+
/* -------------------------------------------------- */
|
|
128
|
+
function StandardNode({ gameObject, selectedId, onSelect, registerRef, loadedModels, loadedTextures, editMode, parentMatrix = IDENTITY, }) {
|
|
129
|
+
var _a, _b, _c;
|
|
130
|
+
const groupRef = useRef(null);
|
|
124
131
|
const clickValid = useRef(false);
|
|
125
|
-
const
|
|
132
|
+
const isSelected = selectedId === gameObject.id;
|
|
133
|
+
const helperRef = groupRef;
|
|
134
|
+
useHelper(editMode && isSelected ? helperRef : null, BoxHelper, "cyan");
|
|
135
|
+
useEffect(() => {
|
|
136
|
+
registerRef(gameObject.id, groupRef.current);
|
|
137
|
+
return () => registerRef(gameObject.id, null);
|
|
138
|
+
}, [gameObject.id, registerRef]);
|
|
139
|
+
const world = parentMatrix.clone().multiply(compose(gameObject));
|
|
140
|
+
const onDown = (e) => {
|
|
126
141
|
e.stopPropagation();
|
|
127
142
|
clickValid.current = true;
|
|
128
143
|
};
|
|
129
|
-
const
|
|
130
|
-
if (clickValid.current)
|
|
131
|
-
clickValid.current = false;
|
|
132
|
-
};
|
|
133
|
-
const handlePointerUp = (e) => {
|
|
144
|
+
const onUp = (e) => {
|
|
134
145
|
if (clickValid.current) {
|
|
135
146
|
e.stopPropagation();
|
|
136
147
|
onSelect === null || onSelect === void 0 ? void 0 : onSelect(gameObject.id);
|
|
137
148
|
}
|
|
138
149
|
clickValid.current = false;
|
|
139
150
|
};
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
// --- 6. Inner content group with full transform and selection helper ---
|
|
150
|
-
const groupRef = useRef(null);
|
|
151
|
-
const isSelected = selectedId === gameObject.id;
|
|
152
|
-
// Show BoxHelper when selected in edit mode
|
|
153
|
-
useHelper(editMode && isSelected ? groupRef : null, BoxHelper, 'cyan');
|
|
154
|
-
useEffect(() => {
|
|
155
|
-
registerRef(gameObject.id, groupRef.current);
|
|
156
|
-
}, [gameObject.id, registerRef]);
|
|
157
|
-
const innerGroup = (_jsxs("group", { ref: groupRef, position: transformProps.position, rotation: transformProps.rotation, scale: transformProps.scale, onPointerDown: handlePointerDown, onPointerMove: handlePointerMove, onPointerUp: handlePointerUp, children: [core, children] }));
|
|
158
|
-
// --- 7. Wrap with physics if needed (RigidBody as outer parent, no transform) ---
|
|
159
|
-
const physics = (_e = gameObject.components) === null || _e === void 0 ? void 0 : _e.physics;
|
|
160
|
-
// Determine if model is safe/ready for physics. No model => safe; model => only safe once loaded.
|
|
161
|
-
const modelReady = !((_f = gameObject.components) === null || _f === void 0 ? void 0 : _f.model) ||
|
|
162
|
-
!!loadedModels[gameObject.components.model.properties.filename];
|
|
163
|
-
if (physics && !editMode && modelReady) {
|
|
164
|
-
const physicsDef = getComponent('Physics');
|
|
165
|
-
if (physicsDef === null || physicsDef === void 0 ? void 0 : physicsDef.View) {
|
|
166
|
-
return (_jsx(physicsDef.View, { properties: physics.properties, children: innerGroup }));
|
|
167
|
-
}
|
|
151
|
+
const inner = (_jsxs("group", Object.assign({ ref: groupRef }, getNodeTransformProps(gameObject), { onPointerDown: onDown, onPointerMove: () => (clickValid.current = false), onPointerUp: onUp, children: [renderCoreNode(gameObject, { loadedModels, loadedTextures, editMode, registerRef }, parentMatrix), (_a = gameObject.children) === null || _a === void 0 ? void 0 : _a.map(child => (_jsx(GameObjectRenderer, { child, gameObject: child, selectedId: selectedId, onSelect: onSelect, registerRef: registerRef, loadedModels: loadedModels, loadedTextures: loadedTextures, editMode: editMode, parentMatrix: world }, child.id)))] })));
|
|
152
|
+
const physics = (_b = gameObject.components) === null || _b === void 0 ? void 0 : _b.physics;
|
|
153
|
+
const ready = !((_c = gameObject.components) === null || _c === void 0 ? void 0 : _c.model) ||
|
|
154
|
+
loadedModels[gameObject.components.model.properties.filename];
|
|
155
|
+
if (physics && !editMode && ready) {
|
|
156
|
+
const def = getComponent("Physics");
|
|
157
|
+
return (def === null || def === void 0 ? void 0 : def.View)
|
|
158
|
+
? _jsx(def.View, { properties: physics.properties, children: inner })
|
|
159
|
+
: inner;
|
|
168
160
|
}
|
|
169
|
-
return
|
|
161
|
+
return inner;
|
|
162
|
+
}
|
|
163
|
+
function walk(node, fn) {
|
|
164
|
+
var _a;
|
|
165
|
+
fn(node);
|
|
166
|
+
(_a = node.children) === null || _a === void 0 ? void 0 : _a.forEach(c => walk(c, fn));
|
|
167
|
+
}
|
|
168
|
+
function compose(node) {
|
|
169
|
+
const { position, rotation, scale } = getNodeTransformProps(node);
|
|
170
|
+
return new Matrix4().compose(new Vector3(...position), new Quaternion().setFromEuler(new Euler(...rotation)), new Vector3(...scale));
|
|
171
|
+
}
|
|
172
|
+
function decompose(m) {
|
|
173
|
+
const p = new Vector3(), q = new Quaternion(), s = new Vector3();
|
|
174
|
+
m.decompose(p, q, s);
|
|
175
|
+
const e = new Euler().setFromQuaternion(q);
|
|
176
|
+
return {
|
|
177
|
+
position: [p.x, p.y, p.z],
|
|
178
|
+
rotation: [e.x, e.y, e.z],
|
|
179
|
+
scale: [s.x, s.y, s.z],
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function getNodeTransformProps(node) {
|
|
183
|
+
var _a, _b, _c, _d, _e;
|
|
184
|
+
const t = (_b = (_a = node === null || node === void 0 ? void 0 : node.components) === null || _a === void 0 ? void 0 : _a.transform) === null || _b === void 0 ? void 0 : _b.properties;
|
|
185
|
+
return {
|
|
186
|
+
position: (_c = t === null || t === void 0 ? void 0 : t.position) !== null && _c !== void 0 ? _c : [0, 0, 0],
|
|
187
|
+
rotation: (_d = t === null || t === void 0 ? void 0 : t.rotation) !== null && _d !== void 0 ? _d : [0, 0, 0],
|
|
188
|
+
scale: (_e = t === null || t === void 0 ? void 0 : t.scale) !== null && _e !== void 0 ? _e : [1, 1, 1],
|
|
189
|
+
};
|
|
170
190
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
191
|
+
function computeParentWorldMatrix(root, targetId) {
|
|
192
|
+
let result = null;
|
|
193
|
+
const visit = (node, parent) => {
|
|
194
|
+
var _a;
|
|
195
|
+
if (node.id === targetId) {
|
|
196
|
+
result = parent.clone();
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const world = parent.clone().multiply(compose(node));
|
|
200
|
+
(_a = node.children) === null || _a === void 0 ? void 0 : _a.forEach(c => !result && visit(c, world));
|
|
201
|
+
};
|
|
202
|
+
visit(root, IDENTITY);
|
|
203
|
+
return result !== null && result !== void 0 ? result : IDENTITY;
|
|
182
204
|
}
|
|
183
|
-
// Helper: render main content for a non-instanced node using the component system
|
|
184
205
|
function renderCoreNode(gameObject, ctx, parentMatrix) {
|
|
185
206
|
var _a, _b, _c;
|
|
186
207
|
const geometry = (_a = gameObject.components) === null || _a === void 0 ? void 0 : _a.geometry;
|
|
187
208
|
const material = (_b = gameObject.components) === null || _b === void 0 ? void 0 : _b.material;
|
|
188
209
|
const model = (_c = gameObject.components) === null || _c === void 0 ? void 0 : _c.model;
|
|
189
|
-
const geometryDef = geometry
|
|
190
|
-
const materialDef = material
|
|
191
|
-
const modelDef = model
|
|
192
|
-
// Context props for all component Views
|
|
210
|
+
const geometryDef = geometry && getComponent("Geometry");
|
|
211
|
+
const materialDef = material && getComponent("Material");
|
|
212
|
+
const modelDef = model && getComponent("Model");
|
|
193
213
|
const contextProps = {
|
|
194
214
|
loadedModels: ctx.loadedModels,
|
|
195
215
|
loadedTextures: ctx.loadedTextures,
|
|
@@ -197,80 +217,36 @@ function renderCoreNode(gameObject, ctx, parentMatrix) {
|
|
|
197
217
|
parentMatrix,
|
|
198
218
|
registerRef: ctx.registerRef,
|
|
199
219
|
};
|
|
200
|
-
|
|
201
|
-
const
|
|
202
|
-
const leafComponents = [];
|
|
220
|
+
const wrappers = [];
|
|
221
|
+
const leaves = [];
|
|
203
222
|
if (gameObject.components) {
|
|
204
223
|
Object.entries(gameObject.components)
|
|
205
|
-
.filter(([
|
|
224
|
+
.filter(([k]) => !["geometry", "material", "model", "transform", "physics"].includes(k))
|
|
206
225
|
.forEach(([key, comp]) => {
|
|
207
|
-
if (!comp ||
|
|
226
|
+
if (!(comp === null || comp === void 0 ? void 0 : comp.type))
|
|
208
227
|
return;
|
|
209
228
|
const def = getComponent(comp.type);
|
|
210
|
-
if (!def ||
|
|
229
|
+
if (!(def === null || def === void 0 ? void 0 : def.View))
|
|
211
230
|
return;
|
|
212
|
-
//
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
wrapperComponents.push({ key, View: def.View, properties: comp.properties });
|
|
231
|
+
// crude but works with your existing component API
|
|
232
|
+
if (def.View.toString().includes("children")) {
|
|
233
|
+
wrappers.push({ key, View: def.View, properties: comp.properties });
|
|
216
234
|
}
|
|
217
235
|
else {
|
|
218
|
-
|
|
236
|
+
leaves.push(_jsx(def.View, Object.assign({ properties: comp.properties }, contextProps), key));
|
|
219
237
|
}
|
|
220
238
|
});
|
|
221
239
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
if (model && modelDef && modelDef.View) {
|
|
226
|
-
// Model component wraps its children (including material override)
|
|
227
|
-
coreContent = (_jsxs(modelDef.View, Object.assign({ properties: model.properties }, contextProps, { children: [material && materialDef && materialDef.View && (_jsx(materialDef.View, Object.assign({ properties: material.properties }, contextProps), "material")), leafComponents] })));
|
|
240
|
+
let core;
|
|
241
|
+
if (model && (modelDef === null || modelDef === void 0 ? void 0 : modelDef.View)) {
|
|
242
|
+
core = (_jsxs(modelDef.View, Object.assign({ properties: model.properties }, contextProps, { children: [material && (materialDef === null || materialDef === void 0 ? void 0 : materialDef.View) && (_jsx(materialDef.View, Object.assign({ properties: material.properties }, contextProps), "material")), leaves] })));
|
|
228
243
|
}
|
|
229
|
-
else if (geometry && geometryDef
|
|
230
|
-
|
|
231
|
-
coreContent = (_jsxs("mesh", { castShadow: true, receiveShadow: true, children: [_jsx(geometryDef.View, Object.assign({ properties: geometry.properties }, contextProps)), material && materialDef && materialDef.View && (_jsx(materialDef.View, Object.assign({ properties: material.properties }, contextProps), "material")), leafComponents] }));
|
|
244
|
+
else if (geometry && (geometryDef === null || geometryDef === void 0 ? void 0 : geometryDef.View)) {
|
|
245
|
+
core = (_jsxs("mesh", { castShadow: true, receiveShadow: true, children: [_jsx(geometryDef.View, Object.assign({ properties: geometry.properties }, contextProps)), material && (materialDef === null || materialDef === void 0 ? void 0 : materialDef.View) && (_jsx(materialDef.View, Object.assign({ properties: material.properties }, contextProps), "material")), leaves] }));
|
|
232
246
|
}
|
|
233
247
|
else {
|
|
234
|
-
|
|
235
|
-
coreContent = _jsx(_Fragment, { children: leafComponents });
|
|
248
|
+
core = _jsx(_Fragment, { children: leaves });
|
|
236
249
|
}
|
|
237
|
-
|
|
238
|
-
return wrapperComponents.reduce((content, { key, View, properties }) => {
|
|
239
|
-
return _jsx(View, Object.assign({ properties: properties }, contextProps, { children: content }), key);
|
|
240
|
-
}, coreContent);
|
|
250
|
+
return wrappers.reduce((acc, { key, View, properties }) => (_jsx(View, Object.assign({ properties: properties }, contextProps, { children: acc }), key)), core);
|
|
241
251
|
}
|
|
242
252
|
export default PrefabRoot;
|
|
243
|
-
function getNodeTransformProps(node) {
|
|
244
|
-
var _a, _b, _c, _d, _e;
|
|
245
|
-
const t = (_b = (_a = node === null || node === void 0 ? void 0 : node.components) === null || _a === void 0 ? void 0 : _a.transform) === null || _b === void 0 ? void 0 : _b.properties;
|
|
246
|
-
return {
|
|
247
|
-
position: (_c = t === null || t === void 0 ? void 0 : t.position) !== null && _c !== void 0 ? _c : [0, 0, 0],
|
|
248
|
-
rotation: (_d = t === null || t === void 0 ? void 0 : t.rotation) !== null && _d !== void 0 ? _d : [0, 0, 0],
|
|
249
|
-
scale: (_e = t === null || t === void 0 ? void 0 : t.scale) !== null && _e !== void 0 ? _e : [1, 1, 1],
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
function computeParentWorldMatrix(root, targetId) {
|
|
253
|
-
var _a;
|
|
254
|
-
const identity = new Matrix4();
|
|
255
|
-
function traverse(node, parentWorld) {
|
|
256
|
-
if (node.id === targetId) {
|
|
257
|
-
// parentWorld is what we want
|
|
258
|
-
return parentWorld.clone();
|
|
259
|
-
}
|
|
260
|
-
const { position, rotation, scale } = getNodeTransformProps(node);
|
|
261
|
-
const localPos = new Vector3(...position);
|
|
262
|
-
const localRot = new Euler(...rotation);
|
|
263
|
-
const localScale = new Vector3(...scale);
|
|
264
|
-
const localMat = new Matrix4().compose(localPos, new Quaternion().setFromEuler(localRot), localScale);
|
|
265
|
-
const worldMat = parentWorld.clone().multiply(localMat);
|
|
266
|
-
if (node.children) {
|
|
267
|
-
for (const child of node.children) {
|
|
268
|
-
const res = traverse(child, worldMat);
|
|
269
|
-
if (res)
|
|
270
|
-
return res;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
return null;
|
|
274
|
-
}
|
|
275
|
-
return (_a = traverse(root, identity)) !== null && _a !== void 0 ? _a : identity;
|
|
276
|
-
}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
1
|
import { Component } from "./ComponentRegistry";
|
|
2
|
+
export interface PhysicsProps {
|
|
3
|
+
type: "fixed" | "dynamic";
|
|
4
|
+
collider?: string;
|
|
5
|
+
mass?: number;
|
|
6
|
+
restitution?: number;
|
|
7
|
+
friction?: number;
|
|
8
|
+
}
|
|
2
9
|
declare const PhysicsComponent: Component;
|
|
3
10
|
export default PhysicsComponent;
|
package/package.json
CHANGED
|
@@ -2,6 +2,7 @@ import React, { createContext, useContext, useMemo, useRef, useState, useEffect,
|
|
|
2
2
|
import { Merged, useHelper } from '@react-three/drei';
|
|
3
3
|
import { InstancedRigidBodies } from "@react-three/rapier";
|
|
4
4
|
import { Mesh, Matrix4, Object3D, Group, Vector3, Quaternion, Euler, InstancedMesh, BoxHelper } from "three";
|
|
5
|
+
import { PhysicsProps } from "./components/PhysicsComponent";
|
|
5
6
|
|
|
6
7
|
// --- Types ---
|
|
7
8
|
export type InstanceData = {
|
|
@@ -10,7 +11,7 @@ export type InstanceData = {
|
|
|
10
11
|
rotation: [number, number, number];
|
|
11
12
|
scale: [number, number, number];
|
|
12
13
|
meshPath: string;
|
|
13
|
-
physics?:
|
|
14
|
+
physics?: PhysicsProps | undefined;
|
|
14
15
|
};
|
|
15
16
|
|
|
16
17
|
// Helper functions for comparison
|
|
@@ -374,7 +375,7 @@ export const GameInstance = React.forwardRef<Group, {
|
|
|
374
375
|
position: [number, number, number];
|
|
375
376
|
rotation: [number, number, number];
|
|
376
377
|
scale: [number, number, number];
|
|
377
|
-
physics?:
|
|
378
|
+
physics?: PhysicsProps | undefined;
|
|
378
379
|
}>(({
|
|
379
380
|
id,
|
|
380
381
|
modelUrl,
|