react-three-game 0.0.95 → 0.0.96
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 +7 -7
- package/dist/tools/prefabeditor/InstanceProvider.js +29 -34
- package/dist/tools/prefabeditor/PrefabEditor.d.ts +1 -1
- package/dist/tools/prefabeditor/PrefabEditor.js +25 -13
- package/dist/tools/prefabeditor/PrefabRoot.d.ts +6 -5
- package/dist/tools/prefabeditor/PrefabRoot.js +59 -34
- package/dist/tools/prefabeditor/components/MaterialComponent.d.ts +9 -3
- package/dist/tools/prefabeditor/components/MaterialComponent.js +88 -66
- package/dist/tools/prefabeditor/components/ModelComponent.d.ts +1 -1
- package/dist/tools/prefabeditor/components/ModelComponent.js +5 -7
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { ThreeEvent } from '@react-three/fiber';
|
|
3
|
-
import { Object3D
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { ThreeEvent } from '@react-three/fiber';
|
|
3
|
+
import type { Object3D } from "three";
|
|
4
4
|
export type RepeatAxisConfig = {
|
|
5
5
|
axis: 'x' | 'y' | 'z';
|
|
6
6
|
count: number;
|
|
@@ -8,7 +8,7 @@ export type RepeatAxisConfig = {
|
|
|
8
8
|
};
|
|
9
9
|
export declare const DEFAULT_REPEAT_AXES: RepeatAxisConfig[];
|
|
10
10
|
export declare function normalizeRepeatAxes(value: unknown): RepeatAxisConfig[];
|
|
11
|
-
export declare function getRepeatAxesFromModelProperties(properties: Record<string,
|
|
11
|
+
export declare function getRepeatAxesFromModelProperties(properties: Record<string, unknown>): RepeatAxisConfig[];
|
|
12
12
|
export type InstanceData = {
|
|
13
13
|
id: string;
|
|
14
14
|
sourceId: string;
|
|
@@ -20,7 +20,7 @@ export type InstanceData = {
|
|
|
20
20
|
meshPath: string;
|
|
21
21
|
};
|
|
22
22
|
export declare function GameInstanceProvider({ children, models, onSelect, onClick, registerRef, selectedId, editMode }: {
|
|
23
|
-
children:
|
|
23
|
+
children: ReactNode;
|
|
24
24
|
models: {
|
|
25
25
|
[filename: string]: Object3D;
|
|
26
26
|
};
|
|
@@ -31,7 +31,7 @@ export declare function GameInstanceProvider({ children, models, onSelect, onCli
|
|
|
31
31
|
editMode?: boolean;
|
|
32
32
|
}): import("react/jsx-runtime").JSX.Element;
|
|
33
33
|
export declare function useInstanceCheck(id: string): boolean;
|
|
34
|
-
export declare
|
|
34
|
+
export declare function GameInstance({ id, sourceId, modelUrl, locked, position, rotation, scale, visible, onClick: _onClick, }: {
|
|
35
35
|
id: string;
|
|
36
36
|
sourceId?: string;
|
|
37
37
|
modelUrl: string;
|
|
@@ -41,4 +41,4 @@ export declare const GameInstance: React.ForwardRefExoticComponent<{
|
|
|
41
41
|
scale: [number, number, number];
|
|
42
42
|
visible?: boolean;
|
|
43
43
|
onClick?: (event: ThreeEvent<PointerEvent>, nodeId: string, object: Object3D | null) => void;
|
|
44
|
-
}
|
|
44
|
+
}): null;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import
|
|
2
|
+
import { createContext, useContext, useMemo, useRef, useState, useEffect } from "react";
|
|
3
3
|
import { Merged, useHelper } from '@react-three/drei';
|
|
4
4
|
import { Mesh, Matrix4, BoxHelper } from "three";
|
|
5
5
|
import { useStore } from "zustand";
|
|
6
6
|
import { createStore } from "zustand/vanilla";
|
|
7
7
|
import { usePointerEvents } from "./usePointerEvents";
|
|
8
8
|
export const DEFAULT_REPEAT_AXES = [{ axis: 'x', count: 1, offset: 1 }];
|
|
9
|
+
const EMPTY_INSTANCE_STORE = createInstanceRegistryStore();
|
|
9
10
|
export function normalizeRepeatAxes(value) {
|
|
10
11
|
if (!Array.isArray(value)) {
|
|
11
12
|
return DEFAULT_REPEAT_AXES;
|
|
@@ -14,14 +15,15 @@ export function normalizeRepeatAxes(value) {
|
|
|
14
15
|
const normalized = value.reduce((result, entry) => {
|
|
15
16
|
if (!entry || typeof entry !== 'object')
|
|
16
17
|
return result;
|
|
17
|
-
const
|
|
18
|
+
const record = entry;
|
|
19
|
+
const axisValue = record.axis;
|
|
18
20
|
if (axisValue !== 'x' && axisValue !== 'y' && axisValue !== 'z')
|
|
19
21
|
return result;
|
|
20
22
|
if (seen.has(axisValue))
|
|
21
23
|
return result;
|
|
22
24
|
seen.add(axisValue);
|
|
23
|
-
const countValue = Number(
|
|
24
|
-
const offsetValue = Number(
|
|
25
|
+
const countValue = Number(record.count);
|
|
26
|
+
const offsetValue = Number(record.offset);
|
|
25
27
|
result.push({
|
|
26
28
|
axis: axisValue,
|
|
27
29
|
count: Number.isFinite(countValue) ? Math.max(1, Math.floor(countValue)) : 1,
|
|
@@ -141,7 +143,7 @@ export function GameInstanceProvider({ children, models, onSelect, onClick, regi
|
|
|
141
143
|
const rootInverse = new Matrix4().copy(model.matrixWorld).invert();
|
|
142
144
|
let partIndex = 0;
|
|
143
145
|
model.traverse((obj) => {
|
|
144
|
-
if (obj
|
|
146
|
+
if (obj instanceof Mesh) {
|
|
145
147
|
// Clone geometry and bake relative transform
|
|
146
148
|
const geom = obj.geometry.clone();
|
|
147
149
|
geom.applyMatrix4(obj.matrixWorld.clone().premultiply(rootInverse));
|
|
@@ -157,7 +159,9 @@ export function GameInstanceProvider({ children, models, onSelect, onClick, regi
|
|
|
157
159
|
// Cleanup geometries when models change
|
|
158
160
|
useEffect(() => {
|
|
159
161
|
return () => {
|
|
160
|
-
Object.values(flatMeshes).forEach(mesh =>
|
|
162
|
+
Object.values(flatMeshes).forEach(mesh => {
|
|
163
|
+
mesh.geometry.dispose();
|
|
164
|
+
});
|
|
161
165
|
};
|
|
162
166
|
}, [flatMeshes]);
|
|
163
167
|
const instances = useMemo(() => Object.values(instancesById), [instancesById]);
|
|
@@ -194,12 +198,16 @@ export function GameInstanceProvider({ children, models, onSelect, onClick, regi
|
|
|
194
198
|
})] }));
|
|
195
199
|
}
|
|
196
200
|
function InstancedGroup({ modelKey, group, partCount, instancesMap, onSelect, onClick, registerRef, selectedId, editMode }) {
|
|
197
|
-
const
|
|
201
|
+
const instanceEntries = useMemo(() => Array.from({ length: partCount }, (_, i) => {
|
|
202
|
+
const partKey = `${modelKey}__${i}`;
|
|
203
|
+
const Component = instancesMap[partKey];
|
|
204
|
+
return Component ? { partKey, Component } : null;
|
|
205
|
+
}).filter((entry) => Boolean(entry)), [instancesMap, modelKey, partCount]);
|
|
198
206
|
const visibleInstances = useMemo(() => group.instances.filter(instance => instance.visible !== false), [group.instances]);
|
|
199
|
-
return (_jsx(_Fragment, { children: visibleInstances.map(inst => (_jsx(InstanceGroupItem, { instance: inst,
|
|
207
|
+
return (_jsx(_Fragment, { children: visibleInstances.map(inst => (_jsx(InstanceGroupItem, { instance: inst, instanceEntries: instanceEntries, onSelect: onSelect, onClick: onClick, registerRef: registerRef, selectedId: selectedId, editMode: editMode }, inst.id))) }));
|
|
200
208
|
}
|
|
201
209
|
// Individual instance item with its own click state
|
|
202
|
-
function InstanceGroupItem({ instance,
|
|
210
|
+
function InstanceGroupItem({ instance, instanceEntries, onSelect, onClick, registerRef, selectedId, editMode }) {
|
|
203
211
|
const groupRef = useRef(null);
|
|
204
212
|
const isLocked = Boolean(instance.locked);
|
|
205
213
|
const isSelected = selectedId === instance.id || selectedId === instance.sourceId;
|
|
@@ -217,24 +225,26 @@ function InstanceGroupItem({ instance, InstanceComponents, onSelect, onClick, re
|
|
|
217
225
|
},
|
|
218
226
|
});
|
|
219
227
|
// Use BoxHelper when object is selected in edit mode
|
|
220
|
-
|
|
228
|
+
const helperTarget = editMode && isSelected && groupRef.current
|
|
229
|
+
? { current: groupRef.current }
|
|
230
|
+
: null;
|
|
231
|
+
useHelper(helperTarget, BoxHelper, 'cyan');
|
|
221
232
|
useEffect(() => {
|
|
222
233
|
if (editMode)
|
|
223
234
|
return;
|
|
224
235
|
registerRef === null || registerRef === void 0 ? void 0 : registerRef(instance.id, groupRef.current);
|
|
225
236
|
return () => registerRef === null || registerRef === void 0 ? void 0 : registerRef(instance.id, null);
|
|
226
237
|
}, [editMode, instance.id, registerRef]);
|
|
227
|
-
return (_jsx("group", Object.assign({ ref: groupRef, position: instance.position, rotation: instance.rotation, scale: instance.scale }, pointerHandlers, { children:
|
|
238
|
+
return (_jsx("group", Object.assign({ ref: groupRef, position: instance.position, rotation: instance.rotation, scale: instance.scale }, pointerHandlers, { children: instanceEntries.map(({ partKey, Component }) => _jsx(Component, {}, partKey)) })));
|
|
228
239
|
}
|
|
229
240
|
export function useInstanceCheck(id) {
|
|
241
|
+
var _a;
|
|
230
242
|
const ctx = useContext(GameInstanceContext);
|
|
231
|
-
|
|
243
|
+
const store = (_a = ctx === null || ctx === void 0 ? void 0 : ctx.store) !== null && _a !== void 0 ? _a : EMPTY_INSTANCE_STORE;
|
|
244
|
+
return useStore(store, state => Boolean(state.instancesById[id] || state.sourceInstanceIdsById[id]));
|
|
232
245
|
}
|
|
233
|
-
export
|
|
246
|
+
export function GameInstance({ id, sourceId, modelUrl, locked = false, position, rotation, scale, visible = true, onClick: _onClick, }) {
|
|
234
247
|
const ctx = useContext(GameInstanceContext);
|
|
235
|
-
const [positionX, positionY, positionZ] = position;
|
|
236
|
-
const [rotationX, rotationY, rotationZ] = rotation;
|
|
237
|
-
const [scaleX, scaleY, scaleZ] = scale;
|
|
238
248
|
const instance = useMemo(() => ({
|
|
239
249
|
id,
|
|
240
250
|
sourceId: sourceId !== null && sourceId !== void 0 ? sourceId : id,
|
|
@@ -244,22 +254,7 @@ export const GameInstance = React.forwardRef(({ id, sourceId, modelUrl, locked =
|
|
|
244
254
|
position,
|
|
245
255
|
rotation,
|
|
246
256
|
scale,
|
|
247
|
-
}), [
|
|
248
|
-
id,
|
|
249
|
-
sourceId,
|
|
250
|
-
locked,
|
|
251
|
-
visible,
|
|
252
|
-
modelUrl,
|
|
253
|
-
positionX,
|
|
254
|
-
positionY,
|
|
255
|
-
positionZ,
|
|
256
|
-
rotationX,
|
|
257
|
-
rotationY,
|
|
258
|
-
rotationZ,
|
|
259
|
-
scaleX,
|
|
260
|
-
scaleY,
|
|
261
|
-
scaleZ,
|
|
262
|
-
]);
|
|
257
|
+
}), [id, sourceId, locked, visible, modelUrl, position, rotation, scale]);
|
|
263
258
|
useEffect(() => {
|
|
264
259
|
if (!ctx)
|
|
265
260
|
return;
|
|
@@ -269,6 +264,6 @@ export const GameInstance = React.forwardRef(({ id, sourceId, modelUrl, locked =
|
|
|
269
264
|
return () => {
|
|
270
265
|
removeInstance(instance.id);
|
|
271
266
|
};
|
|
272
|
-
}, [ctx
|
|
267
|
+
}, [ctx, instance]);
|
|
273
268
|
return null;
|
|
274
|
-
}
|
|
269
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import GameCanvas from "../../shared/GameCanvas";
|
|
2
|
-
import { Prefab } from "./types";
|
|
2
|
+
import type { Prefab } from "./types";
|
|
3
3
|
import { PrefabEditorMode, type Scene } from "./PrefabRoot";
|
|
4
4
|
import type { ExportGLBOptions } from "./utils";
|
|
5
5
|
export interface PrefabEditorRef extends Scene {
|
|
@@ -79,6 +79,10 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, mode: initialMode =
|
|
|
79
79
|
const transformControlsRef = useRef(null);
|
|
80
80
|
const onChangeRef = useRef(onChange);
|
|
81
81
|
const isEditMode = mode === PrefabEditorMode.Edit;
|
|
82
|
+
const detachTransformControls = useCallback(() => {
|
|
83
|
+
var _a;
|
|
84
|
+
(_a = transformControlsRef.current) === null || _a === void 0 ? void 0 : _a.detach();
|
|
85
|
+
}, []);
|
|
82
86
|
const getPrefab = useCallback(() => denormalizePrefab(prefabStore.getState()), [prefabStore]);
|
|
83
87
|
const getNode = useCallback((nodeId) => { var _a; return (_a = prefabStore.getState().nodesById[nodeId]) !== null && _a !== void 0 ? _a : null; }, [prefabStore]);
|
|
84
88
|
const getRoot = useCallback(() => { var _a, _b; return (_b = (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.root) !== null && _b !== void 0 ? _b : null; }, []);
|
|
@@ -158,13 +162,13 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, mode: initialMode =
|
|
|
158
162
|
updateMode(initialMode);
|
|
159
163
|
}, [initialMode, updateMode]);
|
|
160
164
|
const loadPrefab = useCallback((prefab, options) => {
|
|
161
|
-
var _a
|
|
162
|
-
(
|
|
165
|
+
var _a;
|
|
166
|
+
detachTransformControls();
|
|
163
167
|
const before = prefabStore.getState();
|
|
164
168
|
prefabStore.getState().replacePrefab(prefab);
|
|
165
169
|
const after = prefabStore.getState();
|
|
166
170
|
if (after !== before && (options === null || options === void 0 ? void 0 : options.notifyChange) !== false) {
|
|
167
|
-
(
|
|
171
|
+
(_a = onChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(onChangeRef, prefab);
|
|
168
172
|
}
|
|
169
173
|
if (options === null || options === void 0 ? void 0 : options.resetHistory) {
|
|
170
174
|
setSelectedId(null);
|
|
@@ -175,7 +179,7 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, mode: initialMode =
|
|
|
175
179
|
else {
|
|
176
180
|
setSelectedId(prev => prev && prefabStore.getState().nodesById[prev] ? prev : null);
|
|
177
181
|
}
|
|
178
|
-
}, [prefabStore]);
|
|
182
|
+
}, [detachTransformControls, prefabStore]);
|
|
179
183
|
useEffect(() => {
|
|
180
184
|
if (initialPrefab)
|
|
181
185
|
loadPrefab(initialPrefab, { resetHistory: true, notifyChange: false });
|
|
@@ -205,17 +209,25 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, mode: initialMode =
|
|
|
205
209
|
const importPrefab = useCallback((prefab) => {
|
|
206
210
|
add(regenerateIds(prefab.root));
|
|
207
211
|
}, [add]);
|
|
208
|
-
const applyHistory = (index) => {
|
|
209
|
-
var _a
|
|
210
|
-
(
|
|
212
|
+
const applyHistory = useCallback((index) => {
|
|
213
|
+
var _a;
|
|
214
|
+
detachTransformControls();
|
|
211
215
|
prefabStore.getState().replacePrefab(history[index]);
|
|
212
|
-
(
|
|
216
|
+
(_a = onChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(onChangeRef, history[index]);
|
|
213
217
|
historyIndexRef.current = index;
|
|
214
218
|
setHistoryIndex(index);
|
|
215
219
|
setSelectedId(prev => prev && prefabStore.getState().nodesById[prev] ? prev : null);
|
|
216
|
-
};
|
|
217
|
-
const undo = () =>
|
|
218
|
-
|
|
220
|
+
}, [detachTransformControls, history, prefabStore]);
|
|
221
|
+
const undo = useCallback(() => {
|
|
222
|
+
if (historyIndex > 0) {
|
|
223
|
+
applyHistory(historyIndex - 1);
|
|
224
|
+
}
|
|
225
|
+
}, [applyHistory, historyIndex]);
|
|
226
|
+
const redo = useCallback(() => {
|
|
227
|
+
if (historyIndex < history.length - 1) {
|
|
228
|
+
applyHistory(historyIndex + 1);
|
|
229
|
+
}
|
|
230
|
+
}, [applyHistory, history.length, historyIndex]);
|
|
219
231
|
useEffect(() => {
|
|
220
232
|
if (!isEditMode)
|
|
221
233
|
return;
|
|
@@ -233,7 +245,7 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, mode: initialMode =
|
|
|
233
245
|
};
|
|
234
246
|
window.addEventListener('keydown', handleKeyDown);
|
|
235
247
|
return () => window.removeEventListener('keydown', handleKeyDown);
|
|
236
|
-
}, [isEditMode,
|
|
248
|
+
}, [isEditMode, redo, undo]);
|
|
237
249
|
const handleScreenshot = useCallback(() => {
|
|
238
250
|
const canvas = canvasRef.current;
|
|
239
251
|
if (!canvas)
|
|
@@ -383,7 +395,7 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, mode: initialMode =
|
|
|
383
395
|
}
|
|
384
396
|
(_d = canvasProps === null || canvasProps === void 0 ? void 0 : canvasProps.onPointerMissed) === null || _d === void 0 ? void 0 : _d.call(canvasProps, event);
|
|
385
397
|
}
|
|
386
|
-
: canvasProps === null || canvasProps === void 0 ? void 0 : canvasProps.onPointerMissed, children: [content, isEditMode ? _jsx(SelectionHelper, { object: transformObject }) : null, isEditMode && (_jsxs(_Fragment, { children: [_jsx(MapControls, { ref: controlsRef, enableDamping: false, makeDefault: true }), transformObject && (_jsx(TransformControls, { ref: transformControlsRef, object: transformObject, mode: transformMode, space: "local", onObjectChange: handleTransformChange, translationSnap: positionSnap > 0 ? positionSnap : undefined, rotationSnap: rotationSnap > 0 ? rotationSnap : undefined, scaleSnap: scaleSnap > 0 ? scaleSnap : undefined }, `transform-${selectedId}-${transformMode}-${positionSnap}-${rotationSnap}-${scaleSnap}`))] }))] })), showUI && (_jsxs(_Fragment, { children: [_jsxs("div", { style: toolbar.panel, children: [_jsx("button", { style: base.btn, onClick: toggleMode, children: isEditMode ? "▶" : "⏸" }), uiPlugins] }), isEditMode && (_jsx(EditorUI, { selectedId: selectedId, setSelectedId: setSelection, getPrefab: getPrefab, onReplacePrefab: (prefab) => loadPrefab(prefab, { resetHistory: true }), onImportPrefab: importPrefab, basePath: basePath, onUndo: undo, onRedo: redo, canUndo: historyIndex > 0, canRedo: historyIndex < history.length - 1 }))] }))] }) }) });
|
|
398
|
+
: canvasProps === null || canvasProps === void 0 ? void 0 : canvasProps.onPointerMissed, children: [content, isEditMode ? _jsx(SelectionHelper, { object: transformObject }) : null, isEditMode && (_jsxs(_Fragment, { children: [_jsx(MapControls, { ref: controlsRef, enableDamping: false, makeDefault: true }), transformObject && (_jsx(TransformControls, { ref: transformControlsRef, object: transformObject, mode: transformMode, space: transformMode === "translate" ? "world" : "local", onObjectChange: handleTransformChange, translationSnap: positionSnap > 0 ? positionSnap : undefined, rotationSnap: rotationSnap > 0 ? rotationSnap : undefined, scaleSnap: scaleSnap > 0 ? scaleSnap : undefined }, `transform-${selectedId}-${transformMode}-${positionSnap}-${rotationSnap}-${scaleSnap}`))] }))] })), showUI && (_jsxs(_Fragment, { children: [_jsxs("div", { style: toolbar.panel, children: [_jsx("button", { type: "button", style: base.btn, onClick: toggleMode, children: isEditMode ? "▶" : "⏸" }), uiPlugins] }), isEditMode && (_jsx(EditorUI, { selectedId: selectedId, setSelectedId: setSelection, getPrefab: getPrefab, onReplacePrefab: (prefab) => loadPrefab(prefab, { resetHistory: true }), onImportPrefab: importPrefab, basePath: basePath, onUndo: undo, onRedo: redo, canUndo: historyIndex > 0, canRedo: historyIndex < history.length - 1 }))] }))] }) }) });
|
|
387
399
|
});
|
|
388
400
|
PrefabEditor.displayName = "PrefabEditor";
|
|
389
401
|
export default PrefabEditor;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { Matrix4
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { Matrix4 } from "three";
|
|
2
|
+
import type { Object3D, Texture } from "three";
|
|
3
|
+
import type { ThreeEvent } from "@react-three/fiber";
|
|
4
|
+
import type { GameObject as GameObjectType, Prefab } from "./types";
|
|
5
|
+
import type { LoadedModels } from "../dragdrop";
|
|
6
|
+
import type { PrefabStoreApi } from "./prefabStore";
|
|
6
7
|
export declare enum PrefabEditorMode {
|
|
7
8
|
Edit = "edit",
|
|
8
9
|
Play = "play"
|
|
@@ -11,14 +11,14 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
11
11
|
};
|
|
12
12
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
13
13
|
import { createContext, forwardRef, useCallback, useContext, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
|
|
14
|
-
import { Euler, Matrix4
|
|
14
|
+
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
18
|
import { getComponentDef, getComponentAssetRefs, registerComponent } from "./components/ComponentRegistry";
|
|
19
19
|
import { builtinComponents } from "./components";
|
|
20
20
|
import { loadModel, loadSound, loadTexture } from "../dragdrop";
|
|
21
|
-
import { GameInstance, GameInstanceProvider, getRepeatAxesFromModelProperties
|
|
21
|
+
import { GameInstance, GameInstanceProvider, getRepeatAxesFromModelProperties } from "./InstanceProvider";
|
|
22
22
|
import { composeTransform, decompose } from "./utils";
|
|
23
23
|
import { createPrefabStore, PrefabStoreProvider, usePrefabChildIds, usePrefabNode, usePrefabRootId } from "./prefabStore";
|
|
24
24
|
import { AssetRuntimeContext, NodeScope } from "./assetRuntime";
|
|
@@ -29,6 +29,14 @@ const IDENTITY = new Matrix4();
|
|
|
29
29
|
const EMPTY_MODELS = {};
|
|
30
30
|
const EMPTY_TEXTURES = {};
|
|
31
31
|
const EMPTY_SOUNDS = {};
|
|
32
|
+
const EMPTY_NODE_COMPONENTS = {
|
|
33
|
+
geometry: undefined,
|
|
34
|
+
material: undefined,
|
|
35
|
+
model: undefined,
|
|
36
|
+
sprite: undefined,
|
|
37
|
+
clickEventName: null,
|
|
38
|
+
composition: [],
|
|
39
|
+
};
|
|
32
40
|
/** Resolve a relative or absolute asset file path against a base path. */
|
|
33
41
|
function resolveAssetPath(basePath, file) {
|
|
34
42
|
if (file.startsWith("http://") || file.startsWith("https://"))
|
|
@@ -84,6 +92,9 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
|
|
|
84
92
|
throw new Error("PrefabRoot requires either a `data` or `store` prop");
|
|
85
93
|
});
|
|
86
94
|
const resolvedStore = store !== null && store !== void 0 ? store : ownedStore;
|
|
95
|
+
if (!resolvedStore) {
|
|
96
|
+
throw new Error("PrefabRoot requires either a `data` or `store` prop");
|
|
97
|
+
}
|
|
87
98
|
const usesOwnedStore = resolvedStore === ownedStore;
|
|
88
99
|
const rootId = useStore(resolvedStore, state => state.rootId);
|
|
89
100
|
const assetManifestKey = useStore(resolvedStore, state => state.assetManifestKey);
|
|
@@ -155,6 +166,7 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
|
|
|
155
166
|
}
|
|
156
167
|
}, [data, resolvedStore, usesOwnedStore]);
|
|
157
168
|
useEffect(() => {
|
|
169
|
+
void assetManifestKey;
|
|
158
170
|
const syncAssets = (snapshot = resolvedStore.getState()) => {
|
|
159
171
|
const modelsToLoad = new Set();
|
|
160
172
|
const texturesToLoad = new Set();
|
|
@@ -187,23 +199,34 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
|
|
|
187
199
|
}
|
|
188
200
|
});
|
|
189
201
|
};
|
|
190
|
-
modelsToLoad.forEach(file =>
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
202
|
+
modelsToLoad.forEach(file => {
|
|
203
|
+
loadAsset(file, models, injectedModels, failedModels.current, path => loadModel(path).then(result => {
|
|
204
|
+
const loadedModel = result.model;
|
|
205
|
+
if (result.success && loadedModel) {
|
|
206
|
+
setModels(currentModels => (Object.assign(Object.assign({}, currentModels), { [file]: loadedModel })));
|
|
207
|
+
}
|
|
208
|
+
return result;
|
|
209
|
+
}));
|
|
210
|
+
});
|
|
211
|
+
texturesToLoad.forEach(file => {
|
|
212
|
+
loadAsset(file, textures, injectedTextures, failedTextures.current, path => loadTexture(path).then(result => {
|
|
213
|
+
const loadedTexture = result.texture;
|
|
214
|
+
if (result.success && loadedTexture) {
|
|
215
|
+
setTextures(currentTextures => (Object.assign(Object.assign({}, currentTextures), { [file]: loadedTexture })));
|
|
216
|
+
}
|
|
217
|
+
return result;
|
|
218
|
+
}));
|
|
219
|
+
});
|
|
220
|
+
soundsToLoad.forEach(file => {
|
|
221
|
+
loadAsset(file, sounds, injectedSounds, failedSounds.current, path => loadSound(path).then(result => {
|
|
222
|
+
const loadedSound = result.sound;
|
|
223
|
+
if (result.success && loadedSound) {
|
|
224
|
+
soundManager.setBuffer(file, loadedSound);
|
|
225
|
+
setSounds(currentSounds => (Object.assign(Object.assign({}, currentSounds), { [file]: loadedSound })));
|
|
226
|
+
}
|
|
227
|
+
return result;
|
|
228
|
+
}));
|
|
229
|
+
});
|
|
207
230
|
};
|
|
208
231
|
syncAssets();
|
|
209
232
|
}, [resolvedStore, assetManifestKey, basePath, injectedModels, injectedSounds, injectedTextures, models, sounds, textures]);
|
|
@@ -335,13 +358,11 @@ export function GameObjectRenderer(props) {
|
|
|
335
358
|
function InstancedNode({ nodeId, parentMatrix = IDENTITY, editMode, registerRef, onSelect, onEditNodeClick, onClick, isVisible = true }) {
|
|
336
359
|
var _a, _b;
|
|
337
360
|
const gameObject = usePrefabNode(nodeId);
|
|
338
|
-
|
|
339
|
-
return null;
|
|
340
|
-
const analyzedComponents = useMemo(() => analyzeNodeComponents(gameObject), [gameObject]);
|
|
361
|
+
const analyzedComponents = useMemo(() => gameObject ? analyzeNodeComponents(gameObject) : EMPTY_NODE_COMPONENTS, [gameObject]);
|
|
341
362
|
const localTransform = getNodeTransformProps(gameObject);
|
|
342
|
-
const isLocked = Boolean(gameObject.locked);
|
|
343
|
-
const nodeVisible = isVisible && !gameObject.hidden;
|
|
344
|
-
const metadataProps = getNodeMetadataProps(gameObject);
|
|
363
|
+
const isLocked = Boolean(gameObject === null || gameObject === void 0 ? void 0 : gameObject.locked);
|
|
364
|
+
const nodeVisible = isVisible && !(gameObject === null || gameObject === void 0 ? void 0 : gameObject.hidden);
|
|
365
|
+
const metadataProps = gameObject ? getNodeMetadataProps(gameObject) : { name: '', userData: {} };
|
|
345
366
|
const groupProps = Object.assign(Object.assign({}, metadataProps), { visible: nodeVisible, position: localTransform.position, rotation: localTransform.rotation, scale: localTransform.scale });
|
|
346
367
|
const modelUrl = (_b = (_a = analyzedComponents.model) === null || _a === void 0 ? void 0 : _a.properties) === null || _b === void 0 ? void 0 : _b.filename;
|
|
347
368
|
const instances = useMemo(() => buildRepeatedInstances(gameObject, parentMatrix, modelUrl), [gameObject, modelUrl, parentMatrix]);
|
|
@@ -353,9 +374,13 @@ function InstancedNode({ nodeId, parentMatrix = IDENTITY, editMode, registerRef,
|
|
|
353
374
|
}
|
|
354
375
|
}, [editMode, nodeId, registerRef]);
|
|
355
376
|
const editClickHandlers = useClickValid(!!editMode && !isLocked, (event) => {
|
|
377
|
+
if (!gameObject)
|
|
378
|
+
return;
|
|
356
379
|
onSelect === null || onSelect === void 0 ? void 0 : onSelect(nodeId);
|
|
357
380
|
onEditNodeClick === null || onEditNodeClick === void 0 ? void 0 : onEditNodeClick(event, gameObject);
|
|
358
381
|
});
|
|
382
|
+
if (!gameObject)
|
|
383
|
+
return null;
|
|
359
384
|
const renderedInstances = instances.map(instance => (_jsx(GameInstance, { id: instance.id, sourceId: gameObject.id, modelUrl: instance.modelUrl, position: instance.position, rotation: instance.rotation, scale: instance.scale, visible: nodeVisible, locked: isLocked, onClick: onClick }, instance.id)));
|
|
360
385
|
if (editMode) {
|
|
361
386
|
return (_jsxs(_Fragment, { children: [_jsx("group", Object.assign({ ref: handleGroupRef }, groupProps, editClickHandlers, { children: _jsx("mesh", { visible: false, children: _jsx("boxGeometry", { args: [0.01, 0.01, 0.01] }) }) })), renderedInstances] }));
|
|
@@ -365,20 +390,19 @@ function InstancedNode({ nodeId, parentMatrix = IDENTITY, editMode, registerRef,
|
|
|
365
390
|
function StandardNode({ nodeId, selectedId, onSelect, onClick, onEditNodeClick, registerRef, loadedModels, editMode, parentMatrix = IDENTITY, isVisible = true, }) {
|
|
366
391
|
const gameObject = usePrefabNode(nodeId);
|
|
367
392
|
const childIds = usePrefabChildIds(nodeId);
|
|
368
|
-
|
|
369
|
-
return null;
|
|
370
|
-
const analyzedComponents = useMemo(() => analyzeNodeComponents(gameObject), [gameObject]);
|
|
393
|
+
const analyzedComponents = useMemo(() => gameObject ? analyzeNodeComponents(gameObject) : EMPTY_NODE_COMPONENTS, [gameObject]);
|
|
371
394
|
const isSelected = selectedId === nodeId;
|
|
372
|
-
const isLocked = Boolean(gameObject.locked);
|
|
373
|
-
const nodeVisible = isVisible && !gameObject.hidden;
|
|
374
|
-
const
|
|
375
|
-
const metadataProps = getNodeMetadataProps(gameObject);
|
|
395
|
+
const isLocked = Boolean(gameObject === null || gameObject === void 0 ? void 0 : gameObject.locked);
|
|
396
|
+
const nodeVisible = isVisible && !(gameObject === null || gameObject === void 0 ? void 0 : gameObject.hidden);
|
|
397
|
+
const metadataProps = gameObject ? getNodeMetadataProps(gameObject) : { name: '', userData: {} };
|
|
376
398
|
const groupRef = useRef(null);
|
|
377
399
|
const handleGroupRef = useCallback((object) => {
|
|
378
400
|
groupRef.current = object;
|
|
379
401
|
registerRef(nodeId, object);
|
|
380
402
|
}, [nodeId, registerRef]);
|
|
381
403
|
const editClickHandlers = useClickValid(!!editMode && !isLocked, (event) => {
|
|
404
|
+
if (!gameObject)
|
|
405
|
+
return;
|
|
382
406
|
onSelect === null || onSelect === void 0 ? void 0 : onSelect(nodeId);
|
|
383
407
|
onEditNodeClick === null || onEditNodeClick === void 0 ? void 0 : onEditNodeClick(event, gameObject);
|
|
384
408
|
});
|
|
@@ -391,7 +415,6 @@ function StandardNode({ nodeId, selectedId, onSelect, onClick, onEditNodeClick,
|
|
|
391
415
|
}
|
|
392
416
|
: undefined;
|
|
393
417
|
const world = parentMatrix.clone().multiply(compose(gameObject));
|
|
394
|
-
const ready = isNodeReady(analyzedComponents.model, loadedModels);
|
|
395
418
|
const transform = getNodeTransformProps(gameObject);
|
|
396
419
|
const transformProps = {
|
|
397
420
|
position: transform.position,
|
|
@@ -400,6 +423,8 @@ function StandardNode({ nodeId, selectedId, onSelect, onClick, onEditNodeClick,
|
|
|
400
423
|
};
|
|
401
424
|
const groupProps = Object.assign(Object.assign({}, metadataProps), transformProps);
|
|
402
425
|
const childNodes = _jsx(ChildNodes, { childIds: childIds, parentMatrix: world, selectedId: selectedId, onSelect: onSelect, onClick: onClick, onEditNodeClick: onEditNodeClick, registerRef: registerRef, loadedModels: loadedModels, editMode: editMode, isVisible: nodeVisible });
|
|
426
|
+
if (!gameObject)
|
|
427
|
+
return null;
|
|
403
428
|
const inner = renderNodeContent(analyzedComponents, loadedModels, primaryClickHandlers, childNodes);
|
|
404
429
|
const editAnchor = editMode ? (_jsx("mesh", { visible: false, children: _jsx("boxGeometry", { args: [0.01, 0.01, 0.01] }) })) : null;
|
|
405
430
|
const standardNode = (_jsxs("group", Object.assign({ ref: handleGroupRef }, groupProps, { visible: nodeVisible }, (editMode ? editClickHandlers : undefined), { children: [editAnchor, inner] })));
|
|
@@ -422,7 +447,7 @@ function getModelRepeatSettings(node) {
|
|
|
422
447
|
};
|
|
423
448
|
}
|
|
424
449
|
function buildRepeatedInstances(gameObject, parentMatrix, modelUrl) {
|
|
425
|
-
if (!modelUrl)
|
|
450
|
+
if (!gameObject || !modelUrl)
|
|
426
451
|
return [];
|
|
427
452
|
const transform = getNodeTransformProps(gameObject);
|
|
428
453
|
const repeat = getModelRepeatSettings(gameObject);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
2
|
import type { ThreeElement } from '@react-three/fiber';
|
|
3
|
-
import { Component } from './ComponentRegistry';
|
|
3
|
+
import type { Component } from './ComponentRegistry';
|
|
4
4
|
import { MeshBasicNodeMaterial, MeshStandardNodeMaterial, SpriteNodeMaterial } from 'three/webgpu';
|
|
5
|
-
import { MeshBasicMaterialProperties, MeshStandardMaterialProperties } from 'three';
|
|
5
|
+
import type { MeshBasicMaterialProperties, MeshStandardMaterialProperties } from 'three';
|
|
6
6
|
declare module '@react-three/fiber' {
|
|
7
7
|
interface ThreeElements {
|
|
8
8
|
meshBasicNodeMaterial: ThreeElement<typeof MeshBasicNodeMaterial>;
|
|
@@ -10,7 +10,7 @@ declare module '@react-three/fiber' {
|
|
|
10
10
|
spriteNodeMaterial: ThreeElement<typeof SpriteNodeMaterial>;
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
|
-
export interface MaterialProps extends Omit<MeshStandardMaterialProperties & MeshBasicMaterialProperties, 'args' | 'normalScale'> {
|
|
13
|
+
export interface MaterialProps extends Omit<MeshStandardMaterialProperties & MeshBasicMaterialProperties, 'args' | 'normalScale' | 'side'> {
|
|
14
14
|
materialType?: 'standard' | 'basic' | 'sprite';
|
|
15
15
|
transmission?: number;
|
|
16
16
|
thickness?: number;
|
|
@@ -28,8 +28,14 @@ export interface MaterialProps extends Omit<MeshStandardMaterialProperties & Mes
|
|
|
28
28
|
magFilter?: string;
|
|
29
29
|
normalMapTexture?: string;
|
|
30
30
|
normalScale?: [number, number];
|
|
31
|
+
side?: keyof typeof SIDE_MAP;
|
|
31
32
|
}
|
|
32
33
|
export type MaterialOverrides = Record<string, unknown>;
|
|
34
|
+
declare const SIDE_MAP: {
|
|
35
|
+
readonly FrontSide: 0;
|
|
36
|
+
readonly BackSide: 1;
|
|
37
|
+
readonly DoubleSide: 2;
|
|
38
|
+
};
|
|
33
39
|
export declare function useMaterialOverrides(): MaterialOverrides;
|
|
34
40
|
export declare function MaterialOverridesProvider({ overrides, children, }: {
|
|
35
41
|
overrides: MaterialOverrides;
|
|
@@ -24,6 +24,39 @@ function Vector2Editor({ label, value, onChange, min, max, step, }) {
|
|
|
24
24
|
}
|
|
25
25
|
const EMPTY_MATERIAL_OVERRIDES = Object.freeze({});
|
|
26
26
|
const MaterialOverridesContext = createContext(EMPTY_MATERIAL_OVERRIDES);
|
|
27
|
+
const SIDE_MAP = { FrontSide, BackSide, DoubleSide };
|
|
28
|
+
const MIN_FILTER_MAP = {
|
|
29
|
+
NearestFilter,
|
|
30
|
+
LinearFilter,
|
|
31
|
+
NearestMipmapNearestFilter,
|
|
32
|
+
NearestMipmapLinearFilter,
|
|
33
|
+
LinearMipmapNearestFilter,
|
|
34
|
+
LinearMipmapLinearFilter,
|
|
35
|
+
};
|
|
36
|
+
const MAG_FILTER_MAP = {
|
|
37
|
+
NearestFilter,
|
|
38
|
+
LinearFilter,
|
|
39
|
+
};
|
|
40
|
+
function cloneConfiguredTexture({ texture, repeat, repeatCount, offset, colorSpace, generateMipmaps, minFilter, magFilter, }) {
|
|
41
|
+
var _a, _b;
|
|
42
|
+
const clonedTexture = texture.clone();
|
|
43
|
+
if (repeat) {
|
|
44
|
+
clonedTexture.wrapS = clonedTexture.wrapT = RepeatWrapping;
|
|
45
|
+
if (repeatCount)
|
|
46
|
+
clonedTexture.repeat.set(repeatCount[0], repeatCount[1]);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
clonedTexture.wrapS = clonedTexture.wrapT = ClampToEdgeWrapping;
|
|
50
|
+
clonedTexture.repeat.set(1, 1);
|
|
51
|
+
}
|
|
52
|
+
clonedTexture.offset.set((_a = offset === null || offset === void 0 ? void 0 : offset[0]) !== null && _a !== void 0 ? _a : 0, (_b = offset === null || offset === void 0 ? void 0 : offset[1]) !== null && _b !== void 0 ? _b : 0);
|
|
53
|
+
clonedTexture.colorSpace = colorSpace;
|
|
54
|
+
clonedTexture.generateMipmaps = generateMipmaps;
|
|
55
|
+
clonedTexture.minFilter = minFilter;
|
|
56
|
+
clonedTexture.magFilter = magFilter;
|
|
57
|
+
clonedTexture.needsUpdate = true;
|
|
58
|
+
return clonedTexture;
|
|
59
|
+
}
|
|
27
60
|
export function useMaterialOverrides() {
|
|
28
61
|
return useContext(MaterialOverridesContext);
|
|
29
62
|
}
|
|
@@ -37,7 +70,7 @@ extend({
|
|
|
37
70
|
MeshStandardNodeMaterial,
|
|
38
71
|
SpriteNodeMaterial,
|
|
39
72
|
});
|
|
40
|
-
function MaterialComponentEditor({ component, onUpdate, basePath = "" }) {
|
|
73
|
+
function MaterialComponentEditor({ component, onUpdate, basePath = "", }) {
|
|
41
74
|
var _a;
|
|
42
75
|
const materialType = (_a = component.properties.materialType) !== null && _a !== void 0 ? _a : 'standard';
|
|
43
76
|
const hasTexture = !!component.properties.texture;
|
|
@@ -155,94 +188,83 @@ function MaterialComponentEditor({ component, onUpdate, basePath = "" }) {
|
|
|
155
188
|
}
|
|
156
189
|
// View for Material component
|
|
157
190
|
function MaterialComponentView({ properties: rawProps }) {
|
|
158
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
|
|
191
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
|
|
159
192
|
const { getTexture } = useAssetRuntime();
|
|
160
193
|
const properties = rawProps;
|
|
161
|
-
const materialType = (_a = properties === null || properties === void 0 ? void 0 : properties.materialType) !== null && _a !== void 0 ? _a : 'standard';
|
|
162
|
-
const textureName = properties === null || properties === void 0 ? void 0 : properties.texture;
|
|
163
|
-
const offset = properties === null || properties === void 0 ? void 0 : properties.offset;
|
|
164
|
-
const repeat = properties === null || properties === void 0 ? void 0 : properties.repeat;
|
|
165
|
-
const repeatCount = properties === null || properties === void 0 ? void 0 : properties.repeatCount;
|
|
166
|
-
const animateOffset = properties === null || properties === void 0 ? void 0 : properties.animateOffset;
|
|
167
|
-
const offsetSpeed = properties === null || properties === void 0 ? void 0 : properties.offsetSpeed;
|
|
168
|
-
const generateMipmaps = (properties === null || properties === void 0 ? void 0 : properties.generateMipmaps) !== false;
|
|
169
|
-
const minFilter = (properties === null || properties === void 0 ? void 0 : properties.minFilter) || 'LinearMipmapLinearFilter';
|
|
170
|
-
const magFilter = (properties === null || properties === void 0 ? void 0 : properties.magFilter) || 'LinearFilter';
|
|
171
|
-
const texture = textureName ? (_b = getTexture(textureName)) !== null && _b !== void 0 ? _b : undefined : undefined;
|
|
172
|
-
const normalMapTextureName = properties === null || properties === void 0 ? void 0 : properties.normalMapTexture;
|
|
173
|
-
const normalScaleProp = properties === null || properties === void 0 ? void 0 : properties.normalScale;
|
|
174
|
-
const normalMapTexture = normalMapTextureName ? (_c = getTexture(normalMapTextureName)) !== null && _c !== void 0 ? _c : undefined : undefined;
|
|
175
194
|
const materialSource = properties !== null && properties !== void 0 ? properties : {};
|
|
195
|
+
const materialType = (_a = materialSource.materialType) !== null && _a !== void 0 ? _a : 'standard';
|
|
196
|
+
const textureName = materialSource.texture;
|
|
197
|
+
const normalMapTextureName = materialSource.normalMapTexture;
|
|
198
|
+
const offset = materialSource.offset;
|
|
199
|
+
const repeat = materialSource.repeat;
|
|
200
|
+
const repeatCount = materialSource.repeatCount;
|
|
201
|
+
const animateOffset = materialSource.animateOffset;
|
|
202
|
+
const offsetSpeed = materialSource.offsetSpeed;
|
|
203
|
+
const generateMipmaps = materialSource.generateMipmaps !== false;
|
|
204
|
+
const minFilter = (_b = materialSource.minFilter) !== null && _b !== void 0 ? _b : 'LinearMipmapLinearFilter';
|
|
205
|
+
const magFilter = (_c = materialSource.magFilter) !== null && _c !== void 0 ? _c : 'LinearFilter';
|
|
206
|
+
const texture = textureName ? getTexture(textureName) : undefined;
|
|
207
|
+
const normalScaleProp = materialSource.normalScale;
|
|
208
|
+
const normalMapTexture = normalMapTextureName ? getTexture(normalMapTextureName) : undefined;
|
|
176
209
|
// Destructure all material props and separate custom texture handling props
|
|
177
210
|
const { texture: _texture, offset: _offset, repeat: _repeat, repeatCount: _repeatCount, animateOffset: _animateOffset, offsetSpeed: _offsetSpeed, generateMipmaps: _generateMipmaps, minFilter: _minFilter, magFilter: _magFilter, map: _map, materialType: _materialType, normalMapTexture: _normalMapTexture, normalScale: _normalScale, normalMap: _normalMap, rotation, sizeAttenuation, side: sideProp } = materialSource, materialProps = __rest(materialSource, ["texture", "offset", "repeat", "repeatCount", "animateOffset", "offsetSpeed", "generateMipmaps", "minFilter", "magFilter", "map", "materialType", "normalMapTexture", "normalScale", "normalMap", "rotation", "sizeAttenuation", "side"]);
|
|
178
|
-
const
|
|
179
|
-
const
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
LinearFilter,
|
|
183
|
-
NearestMipmapNearestFilter,
|
|
184
|
-
NearestMipmapLinearFilter,
|
|
185
|
-
LinearMipmapNearestFilter,
|
|
186
|
-
LinearMipmapLinearFilter
|
|
187
|
-
};
|
|
188
|
-
const magFilterMap = {
|
|
189
|
-
NearestFilter,
|
|
190
|
-
LinearFilter
|
|
191
|
-
};
|
|
192
|
-
const animatedOffsetRef = useRef([(_e = offset === null || offset === void 0 ? void 0 : offset[0]) !== null && _e !== void 0 ? _e : 0, (_f = offset === null || offset === void 0 ? void 0 : offset[1]) !== null && _f !== void 0 ? _f : 0]);
|
|
211
|
+
const resolvedSide = sideProp ? (_d = SIDE_MAP[sideProp]) !== null && _d !== void 0 ? _d : FrontSide : FrontSide;
|
|
212
|
+
const resolvedMinFilter = (_e = MIN_FILTER_MAP[minFilter]) !== null && _e !== void 0 ? _e : LinearMipmapLinearFilter;
|
|
213
|
+
const resolvedMagFilter = (_f = MAG_FILTER_MAP[magFilter]) !== null && _f !== void 0 ? _f : LinearFilter;
|
|
214
|
+
const animatedOffsetRef = useRef([(_g = offset === null || offset === void 0 ? void 0 : offset[0]) !== null && _g !== void 0 ? _g : 0, (_h = offset === null || offset === void 0 ? void 0 : offset[1]) !== null && _h !== void 0 ? _h : 0]);
|
|
193
215
|
const finalTexture = useMemo(() => {
|
|
194
|
-
var _a, _b, _c, _d;
|
|
195
216
|
if (!texture)
|
|
196
217
|
return undefined;
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
218
|
+
return cloneConfiguredTexture({
|
|
219
|
+
texture,
|
|
220
|
+
repeat,
|
|
221
|
+
repeatCount,
|
|
222
|
+
offset,
|
|
223
|
+
colorSpace: SRGBColorSpace,
|
|
224
|
+
generateMipmaps,
|
|
225
|
+
minFilter: resolvedMinFilter,
|
|
226
|
+
magFilter: resolvedMagFilter,
|
|
227
|
+
});
|
|
228
|
+
}, [texture, repeat, repeatCount === null || repeatCount === void 0 ? void 0 : repeatCount[0], repeatCount === null || repeatCount === void 0 ? void 0 : repeatCount[1], offset === null || offset === void 0 ? void 0 : offset[0], offset === null || offset === void 0 ? void 0 : offset[1], generateMipmaps, resolvedMinFilter, resolvedMagFilter]);
|
|
229
|
+
const finalNormalMap = useMemo(() => {
|
|
230
|
+
if (!normalMapTexture)
|
|
231
|
+
return undefined;
|
|
232
|
+
return cloneConfiguredTexture({
|
|
233
|
+
texture: normalMapTexture,
|
|
234
|
+
repeat,
|
|
235
|
+
repeatCount,
|
|
236
|
+
offset,
|
|
237
|
+
colorSpace: LinearSRGBColorSpace,
|
|
238
|
+
generateMipmaps,
|
|
239
|
+
minFilter: resolvedMinFilter,
|
|
240
|
+
magFilter: resolvedMagFilter,
|
|
241
|
+
});
|
|
242
|
+
}, [normalMapTexture, repeat, repeatCount === null || repeatCount === void 0 ? void 0 : repeatCount[0], repeatCount === null || repeatCount === void 0 ? void 0 : repeatCount[1], offset === null || offset === void 0 ? void 0 : offset[0], offset === null || offset === void 0 ? void 0 : offset[1], generateMipmaps, resolvedMinFilter, resolvedMagFilter]);
|
|
243
|
+
animatedOffsetRef.current = [(_j = offset === null || offset === void 0 ? void 0 : offset[0]) !== null && _j !== void 0 ? _j : 0, (_k = offset === null || offset === void 0 ? void 0 : offset[1]) !== null && _k !== void 0 ? _k : 0];
|
|
216
244
|
useFrame((_, delta) => {
|
|
217
245
|
var _a, _b;
|
|
218
|
-
if (!finalTexture || !animateOffset)
|
|
246
|
+
if ((!finalTexture && !finalNormalMap) || !animateOffset)
|
|
219
247
|
return;
|
|
220
248
|
const nextX = animatedOffsetRef.current[0] + ((_a = offsetSpeed === null || offsetSpeed === void 0 ? void 0 : offsetSpeed[0]) !== null && _a !== void 0 ? _a : 0) * delta;
|
|
221
249
|
const nextY = animatedOffsetRef.current[1] + ((_b = offsetSpeed === null || offsetSpeed === void 0 ? void 0 : offsetSpeed[1]) !== null && _b !== void 0 ? _b : 0) * delta;
|
|
222
250
|
animatedOffsetRef.current = [nextX, nextY];
|
|
223
|
-
finalTexture.offset.set(nextX, nextY);
|
|
251
|
+
finalTexture === null || finalTexture === void 0 ? void 0 : finalTexture.offset.set(nextX, nextY);
|
|
252
|
+
finalNormalMap === null || finalNormalMap === void 0 ? void 0 : finalNormalMap.offset.set(nextX, nextY);
|
|
224
253
|
});
|
|
225
|
-
const
|
|
226
|
-
if (!normalMapTexture)
|
|
227
|
-
return undefined;
|
|
228
|
-
const t = normalMapTexture.clone();
|
|
229
|
-
t.colorSpace = LinearSRGBColorSpace;
|
|
230
|
-
t.needsUpdate = true;
|
|
231
|
-
return t;
|
|
232
|
-
}, [normalMapTexture]);
|
|
254
|
+
const overrides = useMaterialOverrides();
|
|
233
255
|
if (!properties) {
|
|
234
256
|
return _jsx("meshStandardNodeMaterial", { attach: "material", color: "red", wireframe: true });
|
|
235
257
|
}
|
|
236
|
-
const
|
|
258
|
+
const materialKey = `${materialType}:${textureName !== null && textureName !== void 0 ? textureName : 'none'}:${normalMapTextureName !== null && normalMapTextureName !== void 0 ? normalMapTextureName : 'none'}`;
|
|
237
259
|
const sharedProps = Object.assign(Object.assign({ map: finalTexture, side: resolvedSide }, materialProps), overrides);
|
|
238
260
|
if (materialType === 'basic') {
|
|
239
|
-
return _jsx("meshBasicNodeMaterial", Object.assign({ attach: "material" }, sharedProps));
|
|
261
|
+
return _jsx("meshBasicNodeMaterial", Object.assign({ attach: "material" }, sharedProps), materialKey);
|
|
240
262
|
}
|
|
241
263
|
if (materialType === 'sprite') {
|
|
242
|
-
const spriteTransparent =
|
|
243
|
-
return (_jsx("spriteNodeMaterial", Object.assign({ attach: "material", map: finalTexture, color: (
|
|
264
|
+
const spriteTransparent = materialSource.transparent !== false;
|
|
265
|
+
return (_jsx("spriteNodeMaterial", Object.assign({ attach: "material", map: finalTexture, color: (_l = materialSource.color) !== null && _l !== void 0 ? _l : '#ffffff', opacity: (_m = materialSource.opacity) !== null && _m !== void 0 ? _m : 1, transparent: spriteTransparent, alphaTest: (_o = materialSource.alphaTest) !== null && _o !== void 0 ? _o : 0, depthTest: (_p = materialSource.depthTest) !== null && _p !== void 0 ? _p : false, depthWrite: (_q = materialSource.depthWrite) !== null && _q !== void 0 ? _q : false, toneMapped: (_r = materialSource.toneMapped) !== null && _r !== void 0 ? _r : true }, overrides, { rotation: rotation !== null && rotation !== void 0 ? rotation : 0, sizeAttenuation: sizeAttenuation !== null && sizeAttenuation !== void 0 ? sizeAttenuation : true }), materialKey));
|
|
244
266
|
}
|
|
245
|
-
return (_jsx("meshStandardNodeMaterial", Object.assign({ attach: "material" }, sharedProps, { normalMap: finalNormalMap, normalScale: finalNormalMap ? [(
|
|
267
|
+
return (_jsx("meshStandardNodeMaterial", Object.assign({ attach: "material" }, sharedProps, { normalMap: finalNormalMap, normalScale: finalNormalMap ? [(_s = normalScaleProp === null || normalScaleProp === void 0 ? void 0 : normalScaleProp[0]) !== null && _s !== void 0 ? _s : 1, (_t = normalScaleProp === null || normalScaleProp === void 0 ? void 0 : normalScaleProp[1]) !== null && _t !== void 0 ? _t : 1] : undefined }), materialKey));
|
|
246
268
|
}
|
|
247
269
|
const MaterialComponent = {
|
|
248
270
|
name: 'Material',
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { ModelPicker } from '../../assetviewer/page';
|
|
3
3
|
import { useMemo } from 'react';
|
|
4
|
+
import { Mesh } from 'three';
|
|
4
5
|
import { BooleanField, FieldGroup, Label, ListEditor, NumberInput, SelectInput, StringField } from './Input';
|
|
5
6
|
import { useAssetRuntime } from '../assetRuntime';
|
|
6
7
|
import { useEditorContext } from '../PrefabEditor';
|
|
@@ -46,23 +47,20 @@ function ModelComponentEditor({ component, node, onUpdate, basePath = "" }) {
|
|
|
46
47
|
// View for Model component
|
|
47
48
|
function ModelComponentView({ properties, children }) {
|
|
48
49
|
const { getModel } = useAssetRuntime();
|
|
49
|
-
|
|
50
|
-
if (!properties.filename || properties.instanced)
|
|
51
|
-
return _jsx(_Fragment, { children: children });
|
|
52
|
-
const sourceModel = getModel(properties.filename);
|
|
50
|
+
const sourceModel = properties.filename ? getModel(properties.filename) : null;
|
|
53
51
|
// Clone model once and set up shadows - memoized to avoid cloning on every render
|
|
54
52
|
const clonedModel = useMemo(() => {
|
|
55
|
-
if (!sourceModel)
|
|
53
|
+
if (!sourceModel || !properties.filename || properties.instanced)
|
|
56
54
|
return null;
|
|
57
55
|
const clone = sourceModel.clone();
|
|
58
56
|
clone.traverse((obj) => {
|
|
59
|
-
if (obj
|
|
57
|
+
if (obj instanceof Mesh) {
|
|
60
58
|
obj.castShadow = true;
|
|
61
59
|
obj.receiveShadow = true;
|
|
62
60
|
}
|
|
63
61
|
});
|
|
64
62
|
return clone;
|
|
65
|
-
}, [sourceModel]);
|
|
63
|
+
}, [properties.filename, properties.instanced, sourceModel]);
|
|
66
64
|
if (!clonedModel)
|
|
67
65
|
return _jsx(_Fragment, { children: children });
|
|
68
66
|
return _jsx("primitive", { object: clonedModel, children: children });
|