react-three-game 0.0.92 → 0.0.94
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 +81 -88
- package/dist/helpers/index.d.ts +0 -3
- package/dist/helpers/index.js +1 -8
- package/dist/index.d.ts +10 -10
- package/dist/index.js +7 -6
- package/dist/tools/prefabeditor/EditorTree.js +10 -14
- package/dist/tools/prefabeditor/EditorUI.js +4 -4
- package/dist/tools/prefabeditor/GameEvents.d.ts +6 -12
- package/dist/tools/prefabeditor/GameEvents.js +0 -8
- package/dist/tools/prefabeditor/InstanceProvider.d.ts +6 -4
- package/dist/tools/prefabeditor/InstanceProvider.js +84 -199
- package/dist/tools/prefabeditor/PrefabEditor.d.ts +12 -21
- package/dist/tools/prefabeditor/PrefabEditor.js +138 -146
- package/dist/tools/prefabeditor/PrefabRoot.d.ts +30 -11
- package/dist/tools/prefabeditor/PrefabRoot.js +182 -139
- package/dist/tools/prefabeditor/assetRuntime.d.ts +9 -13
- package/dist/tools/prefabeditor/assetRuntime.js +13 -13
- package/dist/tools/prefabeditor/components/BufferGeometryComponent.js +1 -1
- package/dist/tools/prefabeditor/components/CameraComponent.js +2 -2
- package/dist/tools/prefabeditor/components/ComponentRegistry.d.ts +3 -3
- package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +2 -2
- package/dist/tools/prefabeditor/components/Input.js +5 -9
- package/dist/tools/prefabeditor/components/ModelComponent.js +4 -6
- package/dist/tools/prefabeditor/components/PointLightComponent.js +2 -2
- package/dist/tools/prefabeditor/components/SoundComponent.js +2 -2
- package/dist/tools/prefabeditor/components/SpotLightComponent.js +2 -2
- package/dist/tools/prefabeditor/components/index.js +0 -2
- package/dist/tools/prefabeditor/prefab.d.ts +1 -2
- package/dist/tools/prefabeditor/prefab.js +2 -3
- package/dist/tools/prefabeditor/prefabStore.d.ts +0 -6
- package/dist/tools/prefabeditor/prefabStore.js +1 -33
- package/dist/tools/prefabeditor/types.d.ts +1 -0
- package/dist/tools/prefabeditor/usePointerEvents.d.ts +3 -3
- package/dist/tools/prefabeditor/usePointerEvents.js +5 -5
- package/package.json +49 -51
- package/dist/tools/prefabeditor/components/PhysicsComponent.d.ts +0 -26
- package/dist/tools/prefabeditor/components/PhysicsComponent.js +0 -302
- package/dist/tools/prefabeditor/scene.d.ts +0 -70
- package/dist/tools/prefabeditor/scene.js +0 -237
|
@@ -1,20 +1,9 @@
|
|
|
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
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
13
|
-
import React, { createContext, useContext, useMemo, useRef, useState, useEffect
|
|
2
|
+
import React, { createContext, useContext, useMemo, useRef, useState, useEffect } from "react";
|
|
14
3
|
import { Merged, useHelper } from '@react-three/drei';
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
4
|
+
import { Mesh, Matrix4, BoxHelper } from "three";
|
|
5
|
+
import { useStore } from "zustand";
|
|
6
|
+
import { createStore } from "zustand/vanilla";
|
|
18
7
|
import { usePointerEvents } from "./usePointerEvents";
|
|
19
8
|
export const DEFAULT_REPEAT_AXES = [{ axis: 'x', count: 1, offset: 1 }];
|
|
20
9
|
export function normalizeRepeatAxes(value) {
|
|
@@ -81,66 +70,67 @@ function arrayEquals(a, b) {
|
|
|
81
70
|
}
|
|
82
71
|
return true;
|
|
83
72
|
}
|
|
84
|
-
function stableSerialize(value) {
|
|
85
|
-
if (Array.isArray(value)) {
|
|
86
|
-
return `[${value.map(stableSerialize).join(',')}]`;
|
|
87
|
-
}
|
|
88
|
-
if (value && typeof value === 'object') {
|
|
89
|
-
const entries = Object.entries(value)
|
|
90
|
-
.sort(([a], [b]) => a.localeCompare(b))
|
|
91
|
-
.map(([key, entry]) => `${key}:${stableSerialize(entry)}`);
|
|
92
|
-
return `{${entries.join(',')}}`;
|
|
93
|
-
}
|
|
94
|
-
return JSON.stringify(value);
|
|
95
|
-
}
|
|
96
|
-
function getPhysicsSignature(physics) {
|
|
97
|
-
return physics ? stableSerialize(physics) : 'none';
|
|
98
|
-
}
|
|
99
|
-
function hasPhysics(instance) {
|
|
100
|
-
return Boolean(instance.physics);
|
|
101
|
-
}
|
|
102
|
-
function getColliderType(physics) {
|
|
103
|
-
return physics.colliders || (physics.type === 'fixed' ? 'trimesh' : 'hull');
|
|
104
|
-
}
|
|
105
73
|
function instanceEquals(a, b) {
|
|
106
74
|
return a.id === b.id &&
|
|
107
75
|
a.sourceId === b.sourceId &&
|
|
108
76
|
a.locked === b.locked &&
|
|
77
|
+
a.visible === b.visible &&
|
|
109
78
|
a.meshPath === b.meshPath &&
|
|
110
79
|
arrayEquals(a.position, b.position) &&
|
|
111
80
|
arrayEquals(a.rotation, b.rotation) &&
|
|
112
|
-
arrayEquals(a.scale, b.scale)
|
|
113
|
-
getPhysicsSignature(a.physics) === getPhysicsSignature(b.physics);
|
|
81
|
+
arrayEquals(a.scale, b.scale);
|
|
114
82
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
if (
|
|
122
|
-
|
|
123
|
-
if (instanceEquals(prev[idx], instance)) {
|
|
124
|
-
return prev;
|
|
125
|
-
}
|
|
126
|
-
const copy = [...prev];
|
|
127
|
-
copy[idx] = instance;
|
|
128
|
-
return copy;
|
|
83
|
+
function createInstanceRegistryStore() {
|
|
84
|
+
return createStore()((set, get) => ({
|
|
85
|
+
instancesById: {},
|
|
86
|
+
sourceInstanceIdsById: {},
|
|
87
|
+
addInstance: (instance) => {
|
|
88
|
+
const previous = get().instancesById[instance.id];
|
|
89
|
+
if (previous && instanceEquals(previous, instance)) {
|
|
90
|
+
return;
|
|
129
91
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
92
|
+
set(state => {
|
|
93
|
+
var _a, _b;
|
|
94
|
+
const instancesById = Object.assign(Object.assign({}, state.instancesById), { [instance.id]: instance });
|
|
95
|
+
const sourceInstanceIdsById = Object.assign({}, state.sourceInstanceIdsById);
|
|
96
|
+
if (previous && previous.sourceId !== previous.id) {
|
|
97
|
+
const previousSourceInstances = Object.assign({}, ((_a = sourceInstanceIdsById[previous.sourceId]) !== null && _a !== void 0 ? _a : {}));
|
|
98
|
+
delete previousSourceInstances[previous.id];
|
|
99
|
+
sourceInstanceIdsById[previous.sourceId] = Object.keys(previousSourceInstances).length > 0
|
|
100
|
+
? previousSourceInstances
|
|
101
|
+
: undefined;
|
|
102
|
+
}
|
|
103
|
+
if (instance.sourceId !== instance.id) {
|
|
104
|
+
sourceInstanceIdsById[instance.sourceId] = Object.assign(Object.assign({}, ((_b = sourceInstanceIdsById[instance.sourceId]) !== null && _b !== void 0 ? _b : {})), { [instance.id]: true });
|
|
105
|
+
}
|
|
106
|
+
return { instancesById, sourceInstanceIdsById };
|
|
107
|
+
});
|
|
108
|
+
},
|
|
109
|
+
removeInstance: (id) => {
|
|
110
|
+
const previous = get().instancesById[id];
|
|
111
|
+
if (!previous)
|
|
112
|
+
return;
|
|
113
|
+
set(state => {
|
|
114
|
+
var _a;
|
|
115
|
+
const instancesById = Object.assign({}, state.instancesById);
|
|
116
|
+
const sourceInstanceIdsById = Object.assign({}, state.sourceInstanceIdsById);
|
|
117
|
+
delete instancesById[id];
|
|
118
|
+
if (previous.sourceId !== previous.id) {
|
|
119
|
+
const sourceInstances = Object.assign({}, ((_a = sourceInstanceIdsById[previous.sourceId]) !== null && _a !== void 0 ? _a : {}));
|
|
120
|
+
delete sourceInstances[id];
|
|
121
|
+
sourceInstanceIdsById[previous.sourceId] = Object.keys(sourceInstances).length > 0
|
|
122
|
+
? sourceInstances
|
|
123
|
+
: undefined;
|
|
124
|
+
}
|
|
125
|
+
return { instancesById, sourceInstanceIdsById };
|
|
126
|
+
});
|
|
127
|
+
},
|
|
128
|
+
}));
|
|
129
|
+
}
|
|
130
|
+
const GameInstanceContext = createContext(null);
|
|
131
|
+
export function GameInstanceProvider({ children, models, onSelect, onClick, registerRef, selectedId, editMode }) {
|
|
132
|
+
const [instanceStore] = useState(createInstanceRegistryStore);
|
|
133
|
+
const instancesById = useStore(instanceStore, state => state.instancesById);
|
|
144
134
|
// Flatten all model meshes once (models → flat mesh parts)
|
|
145
135
|
// Note: Geometry is cloned with baked transforms for instancing
|
|
146
136
|
const { flatMeshes, modelParts } = useMemo(() => {
|
|
@@ -170,13 +160,14 @@ export function GameInstanceProvider({ children, models, onSelect, registerRef,
|
|
|
170
160
|
Object.values(flatMeshes).forEach(mesh => mesh.geometry.dispose());
|
|
171
161
|
};
|
|
172
162
|
}, [flatMeshes]);
|
|
173
|
-
|
|
163
|
+
const instances = useMemo(() => Object.values(instancesById), [instancesById]);
|
|
164
|
+
// Group instances by meshPath for batched rendering.
|
|
174
165
|
const grouped = useMemo(() => {
|
|
175
166
|
const groups = {};
|
|
176
167
|
for (const inst of instances) {
|
|
177
|
-
const key =
|
|
168
|
+
const key = inst.meshPath;
|
|
178
169
|
if (!groups[key])
|
|
179
|
-
groups[key] = {
|
|
170
|
+
groups[key] = { instances: [] };
|
|
180
171
|
groups[key].instances.push(inst);
|
|
181
172
|
}
|
|
182
173
|
Object.values(groups).forEach(group => {
|
|
@@ -184,146 +175,45 @@ export function GameInstanceProvider({ children, models, onSelect, registerRef,
|
|
|
184
175
|
});
|
|
185
176
|
return groups;
|
|
186
177
|
}, [instances]);
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
hasInstance
|
|
194
|
-
}, children: [children, Object.entries(grouped).map(([key, group]) => {
|
|
195
|
-
if (!group.hasPhysics)
|
|
196
|
-
return null;
|
|
178
|
+
const contextValue = useMemo(() => ({
|
|
179
|
+
store: instanceStore,
|
|
180
|
+
meshes: flatMeshes,
|
|
181
|
+
modelParts,
|
|
182
|
+
}), [instanceStore, flatMeshes, modelParts]);
|
|
183
|
+
return (_jsxs(GameInstanceContext.Provider, { value: contextValue, children: [children, Object.entries(grouped).map(([key, group]) => {
|
|
197
184
|
const modelKey = group.instances[0].meshPath;
|
|
198
185
|
const partCount = modelParts[modelKey] || 0;
|
|
199
186
|
if (partCount === 0)
|
|
200
187
|
return null;
|
|
201
|
-
return (_jsx(InstancedRigidGroup, { group: group, modelKey: modelKey, partCount: partCount, flatMeshes: flatMeshes, onSelect: onSelect, editMode: editMode }, key));
|
|
202
|
-
}), Object.entries(grouped).map(([key, group]) => {
|
|
203
|
-
if (group.hasPhysics)
|
|
204
|
-
return null;
|
|
205
|
-
const modelKey = group.instances[0].meshPath;
|
|
206
|
-
const partCount = modelParts[modelKey] || 0;
|
|
207
|
-
if (partCount === 0)
|
|
208
|
-
return null;
|
|
209
|
-
// Create mesh subset for this specific model
|
|
210
188
|
const meshesForModel = {};
|
|
211
189
|
for (let i = 0; i < partCount; i++) {
|
|
212
190
|
const partKey = `${modelKey}__${i}`;
|
|
213
191
|
meshesForModel[partKey] = flatMeshes[partKey];
|
|
214
192
|
}
|
|
215
|
-
return (_jsx(Merged, { meshes: meshesForModel, castShadow: true, receiveShadow: true, children: (instancesMap) => (_jsx(
|
|
193
|
+
return (_jsx(Merged, { meshes: meshesForModel, castShadow: true, receiveShadow: true, children: (instancesMap) => (_jsx(InstancedGroup, { modelKey: modelKey, group: group, partCount: partCount, instancesMap: instancesMap, onSelect: onSelect, onClick: onClick, registerRef: registerRef, selectedId: selectedId, editMode: editMode })) }, key));
|
|
216
194
|
})] }));
|
|
217
195
|
}
|
|
218
|
-
|
|
219
|
-
function InstancedRigidGroup({ group, modelKey, partCount, flatMeshes, onSelect, editMode }) {
|
|
220
|
-
const meshRefs = useRef([]);
|
|
221
|
-
const rigidBodiesRef = useRef(null);
|
|
222
|
-
const instances = useMemo(() => group.instances.filter(hasPhysics).map(inst => {
|
|
223
|
-
const _a = inst.physics, { activeCollisionTypes: _activeCollisionTypes, colliders: _colliders, userData } = _a, rigidBodyProps = __rest(_a, ["activeCollisionTypes", "colliders", "userData"]);
|
|
224
|
-
return Object.assign(Object.assign({ key: inst.id, position: inst.position, rotation: inst.rotation, scale: inst.scale }, rigidBodyProps), { colliders: getColliderType(inst.physics), userData: Object.assign(Object.assign({}, userData), { entityId: inst.sourceId }) });
|
|
225
|
-
}), [group.instances]);
|
|
226
|
-
// Apply scale to visual meshes (InstancedRigidBodies only scales colliders, not visuals)
|
|
227
|
-
useEffect(() => {
|
|
228
|
-
const matrix = new Matrix4();
|
|
229
|
-
const pos = new Vector3();
|
|
230
|
-
const quat = new Quaternion();
|
|
231
|
-
const euler = new Euler();
|
|
232
|
-
const scl = new Vector3();
|
|
233
|
-
meshRefs.current.forEach(mesh => {
|
|
234
|
-
if (!mesh)
|
|
235
|
-
return;
|
|
236
|
-
group.instances.forEach((inst, i) => {
|
|
237
|
-
pos.set(...inst.position);
|
|
238
|
-
euler.set(...inst.rotation);
|
|
239
|
-
quat.setFromEuler(euler);
|
|
240
|
-
scl.set(...inst.scale);
|
|
241
|
-
matrix.compose(pos, quat, scl);
|
|
242
|
-
mesh.setMatrixAt(i, matrix);
|
|
243
|
-
});
|
|
244
|
-
mesh.instanceMatrix.needsUpdate = true;
|
|
245
|
-
});
|
|
246
|
-
// Update rigid body positions when instances change
|
|
247
|
-
if (rigidBodiesRef.current) {
|
|
248
|
-
try {
|
|
249
|
-
group.instances.forEach((inst, i) => {
|
|
250
|
-
var _a;
|
|
251
|
-
const body = (_a = rigidBodiesRef.current) === null || _a === void 0 ? void 0 : _a[i];
|
|
252
|
-
if (body && body.setTranslation && body.setRotation) {
|
|
253
|
-
pos.set(...inst.position);
|
|
254
|
-
euler.set(...inst.rotation);
|
|
255
|
-
quat.setFromEuler(euler);
|
|
256
|
-
body.setTranslation(pos, false);
|
|
257
|
-
body.setRotation(quat, false);
|
|
258
|
-
}
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
catch (error) {
|
|
262
|
-
// Ignore errors when switching between instanced/non-instanced states
|
|
263
|
-
console.warn('Failed to update rigidbody positions:', error);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
}, [group.instances]);
|
|
267
|
-
useEffect(() => {
|
|
268
|
-
group.instances.forEach((inst, i) => {
|
|
269
|
-
var _a, _b;
|
|
270
|
-
if (!inst.physics || inst.physics.activeCollisionTypes !== 'all')
|
|
271
|
-
return;
|
|
272
|
-
const body = (_a = rigidBodiesRef.current) === null || _a === void 0 ? void 0 : _a[i];
|
|
273
|
-
if (!body || !body.numColliders || !body.collider)
|
|
274
|
-
return;
|
|
275
|
-
for (let colliderIndex = 0; colliderIndex < body.numColliders(); colliderIndex++) {
|
|
276
|
-
const collider = body.collider(colliderIndex);
|
|
277
|
-
(_b = collider.setActiveCollisionTypes) === null || _b === void 0 ? void 0 : _b.call(collider, ActiveCollisionTypes.DEFAULT |
|
|
278
|
-
ActiveCollisionTypes.KINEMATIC_FIXED |
|
|
279
|
-
ActiveCollisionTypes.KINEMATIC_KINEMATIC);
|
|
280
|
-
}
|
|
281
|
-
});
|
|
282
|
-
}, [group.instances]);
|
|
283
|
-
// Handle click on instanced mesh in edit mode
|
|
284
|
-
const handleClick = (e) => {
|
|
285
|
-
const instanceId = e.instanceId;
|
|
286
|
-
const instance = instanceId !== undefined ? group.instances[instanceId] : undefined;
|
|
287
|
-
if (!instance)
|
|
288
|
-
return;
|
|
289
|
-
if (editMode) {
|
|
290
|
-
if (!onSelect || instance.locked)
|
|
291
|
-
return;
|
|
292
|
-
e.stopPropagation();
|
|
293
|
-
onSelect(instance.sourceId);
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
|
-
};
|
|
297
|
-
const shouldHandleClick = editMode;
|
|
298
|
-
// Add key to force remount when instance count changes significantly (helps with cleanup)
|
|
299
|
-
const rigidBodyKey = `rb_${modelKey}_${group.instances.map(inst => `${inst.id}:${getPhysicsSignature(inst.physics)}`).join('|')}`;
|
|
300
|
-
return (_jsx(InstancedRigidBodies, { ref: rigidBodiesRef, instances: instances, children: Array.from({ length: partCount }).map((_, i) => {
|
|
301
|
-
const mesh = flatMeshes[`${modelKey}__${i}`];
|
|
302
|
-
if (!mesh)
|
|
303
|
-
return null;
|
|
304
|
-
return (_jsx("instancedMesh", { ref: el => { meshRefs.current[i] = el; }, args: [mesh.geometry, mesh.material, group.instances.length], castShadow: true, receiveShadow: true, frustumCulled: false, onClick: shouldHandleClick ? handleClick : undefined }, i));
|
|
305
|
-
}) }, rigidBodyKey));
|
|
306
|
-
}
|
|
307
|
-
// Render non-physics instances using Merged (instancing without rigid bodies)
|
|
308
|
-
function NonPhysicsInstancedGroup({ modelKey, group, partCount, instancesMap, onSelect, registerRef, selectedId, editMode }) {
|
|
309
|
-
// Pre-compute which Instance components exist for this model
|
|
196
|
+
function InstancedGroup({ modelKey, group, partCount, instancesMap, onSelect, onClick, registerRef, selectedId, editMode }) {
|
|
310
197
|
const InstanceComponents = useMemo(() => Array.from({ length: partCount }, (_, i) => instancesMap[`${modelKey}__${i}`]).filter(Boolean), [instancesMap, modelKey, partCount]);
|
|
311
|
-
|
|
198
|
+
const visibleInstances = useMemo(() => group.instances.filter(instance => instance.visible !== false), [group.instances]);
|
|
199
|
+
return (_jsx(_Fragment, { children: visibleInstances.map(inst => (_jsx(InstanceGroupItem, { instance: inst, InstanceComponents: InstanceComponents, onSelect: onSelect, onClick: onClick, registerRef: registerRef, selectedId: selectedId, editMode: editMode }, inst.id))) }));
|
|
312
200
|
}
|
|
313
201
|
// Individual instance item with its own click state
|
|
314
|
-
function InstanceGroupItem({ instance, InstanceComponents, onSelect, registerRef, selectedId, editMode }) {
|
|
202
|
+
function InstanceGroupItem({ instance, InstanceComponents, onSelect, onClick, registerRef, selectedId, editMode }) {
|
|
315
203
|
const groupRef = useRef(null);
|
|
316
204
|
const isLocked = Boolean(instance.locked);
|
|
317
205
|
const isSelected = selectedId === instance.id || selectedId === instance.sourceId;
|
|
318
206
|
const canSelect = editMode && !isLocked;
|
|
319
|
-
const canClick =
|
|
207
|
+
const canClick = !editMode && Boolean(onClick);
|
|
320
208
|
const pointerHandlers = usePointerEvents({
|
|
321
209
|
enabled: canSelect || canClick,
|
|
322
|
-
|
|
323
|
-
onClick: () => {
|
|
210
|
+
node: instance,
|
|
211
|
+
onClick: (event) => {
|
|
324
212
|
if (editMode) {
|
|
325
213
|
onSelect === null || onSelect === void 0 ? void 0 : onSelect(instance.sourceId);
|
|
214
|
+
return;
|
|
326
215
|
}
|
|
216
|
+
onClick === null || onClick === void 0 ? void 0 : onClick(event, instance.sourceId, groupRef.current);
|
|
327
217
|
},
|
|
328
218
|
});
|
|
329
219
|
// Use BoxHelper when object is selected in edit mode
|
|
@@ -336,34 +226,29 @@ function InstanceGroupItem({ instance, InstanceComponents, onSelect, registerRef
|
|
|
336
226
|
}, [editMode, instance.id, registerRef]);
|
|
337
227
|
return (_jsx("group", Object.assign({ ref: groupRef, position: instance.position, rotation: instance.rotation, scale: instance.scale }, pointerHandlers, { children: InstanceComponents.map((Instance, i) => _jsx(Instance, {}, i)) })));
|
|
338
228
|
}
|
|
339
|
-
// Hook to check if an instance exists
|
|
340
229
|
export function useInstanceCheck(id) {
|
|
341
|
-
var _a;
|
|
342
230
|
const ctx = useContext(GameInstanceContext);
|
|
343
|
-
return
|
|
231
|
+
return ctx ? useStore(ctx.store, state => Boolean(state.instancesById[id] || state.sourceInstanceIdsById[id])) : false;
|
|
344
232
|
}
|
|
345
|
-
|
|
346
|
-
export const GameInstance = React.forwardRef(({ id, sourceId, modelUrl, locked = false, position, rotation, scale, physics = undefined, }, ref) => {
|
|
233
|
+
export const GameInstance = React.forwardRef(({ id, sourceId, modelUrl, locked = false, position, rotation, scale, visible = true, onClick: _onClick, }, ref) => {
|
|
347
234
|
const ctx = useContext(GameInstanceContext);
|
|
348
|
-
const addInstance = ctx === null || ctx === void 0 ? void 0 : ctx.addInstance;
|
|
349
|
-
const removeInstance = ctx === null || ctx === void 0 ? void 0 : ctx.removeInstance;
|
|
350
235
|
const [positionX, positionY, positionZ] = position;
|
|
351
236
|
const [rotationX, rotationY, rotationZ] = rotation;
|
|
352
237
|
const [scaleX, scaleY, scaleZ] = scale;
|
|
353
|
-
const physicsSignature = getPhysicsSignature(physics);
|
|
354
238
|
const instance = useMemo(() => ({
|
|
355
239
|
id,
|
|
356
240
|
sourceId: sourceId !== null && sourceId !== void 0 ? sourceId : id,
|
|
357
241
|
locked,
|
|
242
|
+
visible,
|
|
358
243
|
meshPath: modelUrl,
|
|
359
244
|
position,
|
|
360
245
|
rotation,
|
|
361
246
|
scale,
|
|
362
|
-
physics,
|
|
363
247
|
}), [
|
|
364
248
|
id,
|
|
365
249
|
sourceId,
|
|
366
250
|
locked,
|
|
251
|
+
visible,
|
|
367
252
|
modelUrl,
|
|
368
253
|
positionX,
|
|
369
254
|
positionY,
|
|
@@ -374,16 +259,16 @@ export const GameInstance = React.forwardRef(({ id, sourceId, modelUrl, locked =
|
|
|
374
259
|
scaleX,
|
|
375
260
|
scaleY,
|
|
376
261
|
scaleZ,
|
|
377
|
-
physicsSignature,
|
|
378
262
|
]);
|
|
379
263
|
useEffect(() => {
|
|
380
|
-
if (!
|
|
264
|
+
if (!ctx)
|
|
381
265
|
return;
|
|
266
|
+
const store = ctx.store;
|
|
267
|
+
const { addInstance, removeInstance } = store.getState();
|
|
382
268
|
addInstance(instance);
|
|
383
269
|
return () => {
|
|
384
270
|
removeInstance(instance.id);
|
|
385
271
|
};
|
|
386
|
-
}, [
|
|
387
|
-
// No visual rendering - provider handles all instanced visuals
|
|
272
|
+
}, [ctx === null || ctx === void 0 ? void 0 : ctx.store, instance]);
|
|
388
273
|
return null;
|
|
389
274
|
});
|
|
@@ -1,31 +1,21 @@
|
|
|
1
1
|
import GameCanvas from "../../shared/GameCanvas";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { Prefab } from "./types";
|
|
3
|
+
import { PrefabEditorMode, type Scene } from "./PrefabRoot";
|
|
4
4
|
import type { ExportGLBOptions } from "./utils";
|
|
5
|
-
|
|
6
|
-
import type { SpawnOptions } from "./scene";
|
|
7
|
-
export interface PrefabEditorRef {
|
|
8
|
-
root: Object3D | null;
|
|
9
|
-
store: PrefabStoreApi;
|
|
10
|
-
getObject: (nodeId: string) => Object3D | null;
|
|
11
|
-
getRigidBody: (nodeId: string) => any;
|
|
12
|
-
screenshot: () => void;
|
|
13
|
-
exportGLB: (options?: ExportGLBOptions) => Promise<ArrayBuffer | undefined>;
|
|
14
|
-
exportGLBData: () => Promise<ArrayBuffer | undefined>;
|
|
15
|
-
clearSelection: () => Promise<void>;
|
|
5
|
+
export interface PrefabEditorRef extends Scene {
|
|
16
6
|
save: () => Prefab;
|
|
17
7
|
load: (prefab: Prefab, options?: {
|
|
18
8
|
resetHistory?: boolean;
|
|
19
9
|
notifyChange?: boolean;
|
|
20
10
|
}) => void;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
Play = "play"
|
|
11
|
+
undo: () => void;
|
|
12
|
+
redo: () => void;
|
|
13
|
+
screenshot: () => void;
|
|
14
|
+
exportGLB: (options?: ExportGLBOptions) => Promise<ArrayBuffer | undefined>;
|
|
15
|
+
exportGLBData: () => Promise<ArrayBuffer | undefined>;
|
|
16
|
+
clearSelection: () => Promise<void>;
|
|
28
17
|
}
|
|
18
|
+
export type { PrefabNode } from "./PrefabRoot";
|
|
29
19
|
export interface EditorContextType {
|
|
30
20
|
mode: PrefabEditorMode;
|
|
31
21
|
setMode: (mode: PrefabEditorMode) => void;
|
|
@@ -42,11 +32,12 @@ export interface EditorContextType {
|
|
|
42
32
|
onExportGLB?: () => void;
|
|
43
33
|
}
|
|
44
34
|
export declare const EditorContext: import("react").Context<EditorContextType | null>;
|
|
35
|
+
export declare const EditorRefContext: import("react").Context<PrefabEditorRef | null>;
|
|
45
36
|
export declare function useEditorContext(): EditorContextType;
|
|
37
|
+
export declare function useEditorRef(): PrefabEditorRef;
|
|
46
38
|
export interface PrefabEditorProps {
|
|
47
39
|
basePath?: string;
|
|
48
40
|
initialPrefab?: Prefab;
|
|
49
|
-
physics?: boolean;
|
|
50
41
|
mode?: PrefabEditorMode;
|
|
51
42
|
onChange?: (prefab: Prefab) => void;
|
|
52
43
|
showUI?: boolean;
|