react-three-game 0.0.104 → 0.0.106
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,8 +8,9 @@ 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 FIXED_PHYSICS_STEP_DELTA = 1 / 60;
|
|
12
12
|
const MAX_PHYSICS_CATCH_UP_DELTA = 1 / 10;
|
|
13
|
+
const MAX_PHYSICS_STEPS_PER_FRAME = 4;
|
|
13
14
|
let didRegisterCrashcat = false;
|
|
14
15
|
function ensureCrashcatRegistered() {
|
|
15
16
|
if (didRegisterCrashcat)
|
|
@@ -68,6 +69,7 @@ export function CrashcatRuntime({ debug = false, children }) {
|
|
|
68
69
|
const bodiesRef = useRef(new Map());
|
|
69
70
|
const bodyByIdRef = useRef(new Map());
|
|
70
71
|
const apiRef = useRef(null);
|
|
72
|
+
const accumulatedDeltaRef = useRef(0);
|
|
71
73
|
const [debugState] = useState(() => debug ? createDebugState() : null);
|
|
72
74
|
const listener = useMemo(() => ({
|
|
73
75
|
onContactAdded: (bodyA, bodyB, manifold) => {
|
|
@@ -131,8 +133,10 @@ export function CrashcatRuntime({ debug = false, children }) {
|
|
|
131
133
|
getBody: (nodeId) => { var _a, _b; return (_b = (_a = bodies.get(nodeId)) === null || _a === void 0 ? void 0 : _a.body) !== null && _b !== void 0 ? _b : null; },
|
|
132
134
|
};
|
|
133
135
|
apiRef.current = runtimeApi;
|
|
136
|
+
accumulatedDeltaRef.current = 0;
|
|
134
137
|
setCrashcatApi(runtimeApi);
|
|
135
138
|
return () => {
|
|
139
|
+
accumulatedDeltaRef.current = 0;
|
|
136
140
|
for (const entry of bodies.values()) {
|
|
137
141
|
rigidBody.remove(world, entry.body);
|
|
138
142
|
}
|
|
@@ -152,11 +156,20 @@ export function CrashcatRuntime({ debug = false, children }) {
|
|
|
152
156
|
const { world } = runtimeApi;
|
|
153
157
|
const frameDelta = Math.min(delta, MAX_PHYSICS_CATCH_UP_DELTA);
|
|
154
158
|
if (mode === PrefabEditorMode.Play) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
+
accumulatedDeltaRef.current += frameDelta;
|
|
160
|
+
let stepCount = 0;
|
|
161
|
+
while (accumulatedDeltaRef.current >= FIXED_PHYSICS_STEP_DELTA &&
|
|
162
|
+
stepCount < MAX_PHYSICS_STEPS_PER_FRAME) {
|
|
163
|
+
updateWorld(world, listener, FIXED_PHYSICS_STEP_DELTA);
|
|
164
|
+
accumulatedDeltaRef.current -= FIXED_PHYSICS_STEP_DELTA;
|
|
165
|
+
stepCount += 1;
|
|
159
166
|
}
|
|
167
|
+
if (stepCount === MAX_PHYSICS_STEPS_PER_FRAME) {
|
|
168
|
+
accumulatedDeltaRef.current = Math.min(accumulatedDeltaRef.current, FIXED_PHYSICS_STEP_DELTA);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
accumulatedDeltaRef.current = 0;
|
|
160
173
|
}
|
|
161
174
|
if (debugState)
|
|
162
175
|
debugRenderer.update(debugState, world);
|
|
@@ -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 : [])
|