react-three-game 0.0.103 → 0.0.105
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.
|
@@ -8,7 +8,8 @@ import { gameEvents } from "../../tools/prefabeditor/GameEvents";
|
|
|
8
8
|
import { PrefabEditorMode, useScene } from "../../tools/prefabeditor/PrefabRoot";
|
|
9
9
|
const SLEEP_TIME_BEFORE_REST = 0.1;
|
|
10
10
|
const SLEEP_POINT_VELOCITY_THRESHOLD = 0.06;
|
|
11
|
-
const
|
|
11
|
+
const MAX_PHYSICS_STEP_DELTA = 1 / 60;
|
|
12
|
+
const MAX_PHYSICS_CATCH_UP_DELTA = 1 / 10;
|
|
12
13
|
let didRegisterCrashcat = false;
|
|
13
14
|
function ensureCrashcatRegistered() {
|
|
14
15
|
if (didRegisterCrashcat)
|
|
@@ -19,11 +20,18 @@ function ensureCrashcatRegistered() {
|
|
|
19
20
|
const crashcatListeners = new Set();
|
|
20
21
|
let crashcatApi = null;
|
|
21
22
|
export function useCrashcat() {
|
|
22
|
-
return useSyncExternalStore((listener) =>
|
|
23
|
+
return useSyncExternalStore((listener) => {
|
|
24
|
+
crashcatListeners.add(listener);
|
|
25
|
+
return () => {
|
|
26
|
+
crashcatListeners.delete(listener);
|
|
27
|
+
};
|
|
28
|
+
}, () => crashcatApi, () => crashcatApi);
|
|
23
29
|
}
|
|
24
30
|
function setCrashcatApi(api) {
|
|
25
31
|
crashcatApi = api;
|
|
26
|
-
crashcatListeners.forEach((listener) =>
|
|
32
|
+
crashcatListeners.forEach((listener) => {
|
|
33
|
+
listener();
|
|
34
|
+
});
|
|
27
35
|
}
|
|
28
36
|
function emitConfiguredEvent(eventName, sourceNodeId, targetNodeId, collisionNormal) {
|
|
29
37
|
const trimmed = eventName === null || eventName === void 0 ? void 0 : eventName.trim();
|
|
@@ -142,9 +150,14 @@ export function CrashcatRuntime({ debug = false, children }) {
|
|
|
142
150
|
if (!runtimeApi)
|
|
143
151
|
return;
|
|
144
152
|
const { world } = runtimeApi;
|
|
145
|
-
const
|
|
146
|
-
if (mode === PrefabEditorMode.Play)
|
|
147
|
-
|
|
153
|
+
const frameDelta = Math.min(delta, MAX_PHYSICS_CATCH_UP_DELTA);
|
|
154
|
+
if (mode === PrefabEditorMode.Play) {
|
|
155
|
+
const stepCount = Math.max(1, Math.ceil(frameDelta / MAX_PHYSICS_STEP_DELTA));
|
|
156
|
+
const stepDelta = frameDelta / stepCount;
|
|
157
|
+
for (let stepIndex = 0; stepIndex < stepCount; stepIndex += 1) {
|
|
158
|
+
updateWorld(world, listener, stepDelta);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
148
161
|
if (debugState)
|
|
149
162
|
debugRenderer.update(debugState, world);
|
|
150
163
|
}, -1);
|
|
@@ -15,7 +15,7 @@ import { Euler, Matrix4 } from "three";
|
|
|
15
15
|
import { useStore } from "zustand";
|
|
16
16
|
import { useClickValid } from "./useClickValid";
|
|
17
17
|
import { findComponent, getNodeUserData } from "./types";
|
|
18
|
-
import { getComponentDef,
|
|
18
|
+
import { getComponentDef, registerComponent } from "./components/ComponentRegistry";
|
|
19
19
|
import { builtinComponents } from "./components";
|
|
20
20
|
import { loadModel, loadSound, loadTexture } from "../dragdrop";
|
|
21
21
|
import { GameInstance, GameInstanceProvider, getRepeatAxesFromModelProperties } from "./InstanceProvider";
|
|
@@ -99,13 +99,12 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
|
|
|
99
99
|
}
|
|
100
100
|
const usesOwnedStore = resolvedStore === ownedStore;
|
|
101
101
|
const rootId = useStore(resolvedStore, state => state.rootId);
|
|
102
|
-
const
|
|
102
|
+
const assetRefCounts = useStore(resolvedStore, state => state.assetRefCounts);
|
|
103
103
|
const availableModels = useMemo(() => (Object.assign(Object.assign({}, models), injectedModels)), [models, injectedModels]);
|
|
104
|
-
const availableTextures = useMemo(() => (Object.assign(Object.assign({}, textures), injectedTextures)), [textures, injectedTextures]);
|
|
105
|
-
const availableSounds = useMemo(() => (Object.assign(Object.assign({}, sounds), injectedSounds)), [sounds, injectedSounds]);
|
|
106
104
|
const getModel = useCallback((path) => { var _a; return (_a = availableModels[path]) !== null && _a !== void 0 ? _a : null; }, [availableModels]);
|
|
107
|
-
const getTexture = useCallback((path) => { var _a; return (_a =
|
|
108
|
-
const getSound = useCallback((path) => { var _a; return (_a =
|
|
105
|
+
const getTexture = useCallback((path) => { var _a, _b; return (_b = (_a = injectedTextures[path]) !== null && _a !== void 0 ? _a : textures[path]) !== null && _b !== void 0 ? _b : null; }, [injectedTextures, textures]);
|
|
106
|
+
const getSound = useCallback((path) => { var _a, _b; return (_b = (_a = injectedSounds[path]) !== null && _a !== void 0 ? _a : sounds[path]) !== null && _b !== void 0 ? _b : null; }, [injectedSounds, sounds]);
|
|
107
|
+
const assetRevision = useMemo(() => `${Object.keys(textures).concat(Object.keys(injectedTextures)).sort().join('|')}::${Object.keys(availableModels).sort().join('|')}`, [availableModels, injectedTextures, textures]);
|
|
109
108
|
const getObject = useCallback((id) => {
|
|
110
109
|
var _a;
|
|
111
110
|
return (_a = objectRefs.current[id]) !== null && _a !== void 0 ? _a : null;
|
|
@@ -173,40 +172,23 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
|
|
|
173
172
|
}
|
|
174
173
|
}, [data, resolvedStore, usesOwnedStore]);
|
|
175
174
|
useEffect(() => {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
return;
|
|
187
|
-
for (const ref of getComponentAssetRefs(component.type, (_a = component.properties) !== null && _a !== void 0 ? _a : {})) {
|
|
188
|
-
if (ref.type === 'model')
|
|
189
|
-
modelsToLoad.add(ref.path);
|
|
190
|
-
else if (ref.type === 'texture')
|
|
191
|
-
texturesToLoad.add(ref.path);
|
|
192
|
-
else if (ref.type === 'sound')
|
|
193
|
-
soundsToLoad.add(ref.path);
|
|
194
|
-
}
|
|
195
|
-
});
|
|
175
|
+
const loadAsset = (file, loaded, injected, failed, loader) => {
|
|
176
|
+
if (loaded[file] || injected[file] || loading.current.has(file) || failed.has(file))
|
|
177
|
+
return;
|
|
178
|
+
loading.current.add(file);
|
|
179
|
+
void loader(resolveAssetPath(basePath, file)).then(result => {
|
|
180
|
+
loading.current.delete(file);
|
|
181
|
+
if (!result.success) {
|
|
182
|
+
console.warn(`Failed to load asset: ${file}`, result.error);
|
|
183
|
+
failed.add(file);
|
|
184
|
+
}
|
|
196
185
|
});
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
if (!result.success) {
|
|
204
|
-
console.warn(`Failed to load asset: ${file}`, result.error);
|
|
205
|
-
failed.add(file);
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
};
|
|
209
|
-
modelsToLoad.forEach(file => {
|
|
186
|
+
};
|
|
187
|
+
Object.keys(assetRefCounts).forEach(entry => {
|
|
188
|
+
const separator = entry.indexOf(':');
|
|
189
|
+
const type = entry.slice(0, separator);
|
|
190
|
+
const file = entry.slice(separator + 1);
|
|
191
|
+
if (type === 'model') {
|
|
210
192
|
loadAsset(file, models, injectedModels, failedModels.current, path => loadModel(path).then(result => {
|
|
211
193
|
const loadedModel = result.model;
|
|
212
194
|
if (result.success && loadedModel) {
|
|
@@ -214,8 +196,8 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
|
|
|
214
196
|
}
|
|
215
197
|
return result;
|
|
216
198
|
}));
|
|
217
|
-
}
|
|
218
|
-
|
|
199
|
+
}
|
|
200
|
+
else if (type === 'texture') {
|
|
219
201
|
loadAsset(file, textures, injectedTextures, failedTextures.current, path => loadTexture(path).then(result => {
|
|
220
202
|
const loadedTexture = result.texture;
|
|
221
203
|
if (result.success && loadedTexture) {
|
|
@@ -223,8 +205,8 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
|
|
|
223
205
|
}
|
|
224
206
|
return result;
|
|
225
207
|
}));
|
|
226
|
-
}
|
|
227
|
-
|
|
208
|
+
}
|
|
209
|
+
else if (type === 'sound') {
|
|
228
210
|
loadAsset(file, sounds, injectedSounds, failedSounds.current, path => loadSound(path).then(result => {
|
|
229
211
|
const loadedSound = result.sound;
|
|
230
212
|
if (result.success && loadedSound) {
|
|
@@ -233,10 +215,9 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
|
|
|
233
215
|
}
|
|
234
216
|
return result;
|
|
235
217
|
}));
|
|
236
|
-
}
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
}, [resolvedStore, assetManifestKey, basePath, injectedModels, injectedSounds, injectedTextures, models, sounds, textures]);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
}, [assetRefCounts, basePath, injectedModels, injectedSounds, injectedTextures, models, sounds, textures]);
|
|
240
221
|
const assetRuntime = useMemo(() => ({
|
|
241
222
|
registerHandle,
|
|
242
223
|
getHandle,
|
|
@@ -244,8 +225,8 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
|
|
|
244
225
|
getModel,
|
|
245
226
|
getTexture,
|
|
246
227
|
getSound,
|
|
247
|
-
getAssetRevision: () =>
|
|
248
|
-
}), [registerHandle, getHandle, getObject, getModel, getTexture, getSound,
|
|
228
|
+
getAssetRevision: () => assetRevision,
|
|
229
|
+
}), [registerHandle, getHandle, getObject, getModel, getTexture, getSound, assetRevision]);
|
|
249
230
|
const handleNodeClick = useCallback((event, nodeId, fallbackObject) => {
|
|
250
231
|
const node = resolvedStore.getState().nodesById[nodeId];
|
|
251
232
|
if (!node)
|
|
@@ -66,18 +66,19 @@ function GeometryComponentEditor({ component, onUpdate, }) {
|
|
|
66
66
|
// View for Geometry component
|
|
67
67
|
function GeometryComponentView({ properties, children }) {
|
|
68
68
|
const { geometryType, args = [] } = properties;
|
|
69
|
+
const geometryKey = `${geometryType !== null && geometryType !== void 0 ? geometryType : 'box'}:${JSON.stringify(args)}`;
|
|
69
70
|
// Only return the geometry node, do not wrap in mesh or group
|
|
70
71
|
switch (geometryType) {
|
|
71
72
|
case "box":
|
|
72
|
-
return _jsx("boxGeometry", { args: args });
|
|
73
|
+
return _jsx("boxGeometry", { args: args }, geometryKey);
|
|
73
74
|
case "sphere":
|
|
74
|
-
return _jsx("sphereGeometry", { args: args });
|
|
75
|
+
return _jsx("sphereGeometry", { args: args }, geometryKey);
|
|
75
76
|
case "plane":
|
|
76
|
-
return _jsx("planeGeometry", { args: args });
|
|
77
|
+
return _jsx("planeGeometry", { args: args }, geometryKey);
|
|
77
78
|
case "cylinder":
|
|
78
|
-
return _jsx("cylinderGeometry", { args: args });
|
|
79
|
+
return _jsx("cylinderGeometry", { args: args }, geometryKey);
|
|
79
80
|
default:
|
|
80
|
-
return _jsx("boxGeometry", { args: [1, 1, 1] });
|
|
81
|
+
return _jsx("boxGeometry", { args: [1, 1, 1] }, "box:[1,1,1]");
|
|
81
82
|
}
|
|
82
83
|
}
|
|
83
84
|
const GeometryComponent = {
|
|
@@ -185,7 +185,7 @@ export function cloneSubtree(id, parentId, source, nodesById, childIdsById, pare
|
|
|
185
185
|
if (!originalNode)
|
|
186
186
|
return null;
|
|
187
187
|
const clonedId = crypto.randomUUID();
|
|
188
|
-
const clonedNode = Object.assign(Object.assign({}, originalNode), { id: clonedId, name: `${(_a = originalNode.name) !== null && _a !== void 0 ? _a : originalNode.id} Copy` });
|
|
188
|
+
const clonedNode = Object.assign(Object.assign({}, originalNode), { components: clonePrefabValue(originalNode.components), id: clonedId, name: `${(_a = originalNode.name) !== null && _a !== void 0 ? _a : originalNode.id} Copy` });
|
|
189
189
|
nodesById[clonedId] = clonedNode;
|
|
190
190
|
parentIdById[clonedId] = parentId;
|
|
191
191
|
const clonedChildIds = ((_b = source.childIdsById[id]) !== null && _b !== void 0 ? _b : [])
|