react-three-game 0.0.83 → 0.0.85
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 +72 -2
- package/dist/index.d.ts +6 -2
- package/dist/index.js +3 -1
- package/dist/tools/prefabeditor/PrefabRoot.js +64 -54
- package/dist/tools/prefabeditor/components/BufferGeometryComponent.d.ts +3 -0
- package/dist/tools/prefabeditor/components/BufferGeometryComponent.js +96 -0
- package/dist/tools/prefabeditor/components/CameraComponent.js +1 -2
- package/dist/tools/prefabeditor/components/ClickComponent.js +1 -1
- package/dist/tools/prefabeditor/components/ComponentRegistry.d.ts +3 -5
- package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +3 -9
- package/dist/tools/prefabeditor/components/EnvironmentComponent.js +1 -1
- package/dist/tools/prefabeditor/components/MaterialComponent.d.ts +10 -0
- package/dist/tools/prefabeditor/components/MaterialComponent.js +63 -28
- package/dist/tools/prefabeditor/components/ModelComponent.js +1 -1
- package/dist/tools/prefabeditor/components/PhysicsComponent.js +1 -1
- package/dist/tools/prefabeditor/components/PointLightComponent.js +1 -1
- package/dist/tools/prefabeditor/components/SoundComponent.js +1 -2
- package/dist/tools/prefabeditor/components/SpotLightComponent.js +1 -1
- package/dist/tools/prefabeditor/components/index.js +8 -6
- package/dist/tools/prefabeditor/runtime.d.ts +61 -0
- package/dist/tools/prefabeditor/runtime.js +184 -0
- package/dist/tools/prefabeditor/scene.d.ts +8 -0
- package/dist/tools/prefabeditor/scene.js +45 -8
- package/package.json +2 -2
- package/dist/tools/prefabeditor/runtimeContext.d.ts +0 -40
- package/dist/tools/prefabeditor/runtimeContext.js +0 -45
package/README.md
CHANGED
|
@@ -110,6 +110,38 @@ That means a saved scene is just a prefab, and the same prefab can be:
|
|
|
110
110
|
* rendered directly with `PrefabRoot`
|
|
111
111
|
* loaded inside another scene as reusable content
|
|
112
112
|
|
|
113
|
+
`PrefabRoot` keeps the rendering model narrow and compositional:
|
|
114
|
+
|
|
115
|
+
* `Transform` is the renderer-owned outer transform
|
|
116
|
+
* `Geometry` or `BufferGeometry` + `Material` become the primary mesh content
|
|
117
|
+
* non-instanced `Model` becomes the node's primary content
|
|
118
|
+
* `Physics` is a renderer-owned outer wrapper
|
|
119
|
+
* every other component `View` wraps the current subtree
|
|
120
|
+
|
|
121
|
+
Custom component `View`s use normal React Three Fiber composition with `children`.
|
|
122
|
+
|
|
123
|
+
For agent-authored custom meshes, use `BufferGeometry` with flat numeric arrays:
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"id": "triangle",
|
|
128
|
+
"components": {
|
|
129
|
+
"bufferGeometry": {
|
|
130
|
+
"type": "BufferGeometry",
|
|
131
|
+
"properties": {
|
|
132
|
+
"positions": [0, 0, 0, 1, 0, 0, 0, 1, 0],
|
|
133
|
+
"indices": [0, 1, 2],
|
|
134
|
+
"computeVertexNormals": true
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
"material": {
|
|
138
|
+
"type": "Material",
|
|
139
|
+
"properties": { "color": "#ff8844" }
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
113
145
|
## Prefab Format
|
|
114
146
|
|
|
115
147
|
```ts
|
|
@@ -131,7 +163,7 @@ interface GameObject {
|
|
|
131
163
|
|
|
132
164
|
## Runtime Mutation
|
|
133
165
|
|
|
134
|
-
|
|
166
|
+
Use the `Scene` API from `PrefabEditorRef` for authored state changes that should stay in prefab data.
|
|
135
167
|
|
|
136
168
|
```tsx
|
|
137
169
|
import { useEffect, useRef } from "react";
|
|
@@ -153,21 +185,59 @@ function RaiseBall() {
|
|
|
153
185
|
}
|
|
154
186
|
```
|
|
155
187
|
|
|
188
|
+
Batch related entity changes so they flush as one store revision:
|
|
189
|
+
|
|
190
|
+
```tsx
|
|
191
|
+
scene.batch(() => {
|
|
192
|
+
scene.find("orb1")?.getComponent("Transform")?.set("position", [1, 0, 0]);
|
|
193
|
+
scene.find("orb2")?.getComponent("Transform")?.set("position", [-1, 0, 0]);
|
|
194
|
+
});
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Define a component runtime factory with `create(ctx)` for hot runtime behavior that mutates the live Three.js object directly:
|
|
198
|
+
|
|
199
|
+
```tsx
|
|
200
|
+
import type { Component } from "react-three-game";
|
|
201
|
+
|
|
202
|
+
const Rotator: Component = {
|
|
203
|
+
name: "Rotator",
|
|
204
|
+
Editor: () => null,
|
|
205
|
+
defaultProperties: { speed: 1 },
|
|
206
|
+
create(ctx) {
|
|
207
|
+
return {
|
|
208
|
+
update(dt) {
|
|
209
|
+
const speed = ctx.component.get<number>("speed") ?? 1;
|
|
210
|
+
ctx.object.rotation.y += dt * speed;
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
`View` handles composition and rendering. `create(ctx)` handles imperative runtime behavior.
|
|
218
|
+
|
|
156
219
|
## Useful Exports
|
|
157
220
|
|
|
158
221
|
* `GameCanvas`
|
|
159
222
|
* `PrefabRoot`
|
|
160
223
|
* `PrefabEditor`
|
|
224
|
+
* `PrefabEditorMode`
|
|
161
225
|
* `Prefab`
|
|
162
226
|
* `GameObject`
|
|
163
227
|
* `Scene`
|
|
164
228
|
* `Entity`
|
|
165
229
|
* `EntityComponent`
|
|
166
230
|
* `registerComponent`
|
|
231
|
+
* `createPrefabStore`
|
|
232
|
+
* `usePrefabStoreApi`
|
|
233
|
+
* `useAssetRuntime()` / `useEntityRuntime()`
|
|
234
|
+
* `useEntityObjectRef()` / `useEntityRigidBodyRef()`
|
|
167
235
|
* `ground(...)`
|
|
168
236
|
* `loadJson()` / `saveJson()`
|
|
169
237
|
* `loadModel()` / `loadTexture()`
|
|
170
|
-
* `
|
|
238
|
+
* `loadSound()` / `loadFiles()`
|
|
239
|
+
* `exportGLB()`
|
|
240
|
+
* `computeParentWorldMatrix()`
|
|
171
241
|
|
|
172
242
|
## Development
|
|
173
243
|
|
package/dist/index.d.ts
CHANGED
|
@@ -20,12 +20,16 @@ export { createModelNode, createImageNode, } from './tools/prefabeditor/prefab';
|
|
|
20
20
|
export type { PrefabEditorProps, PrefabEditorRef, } from './tools/prefabeditor/PrefabEditor';
|
|
21
21
|
export type { SpawnOptions, Scene, Entity, EntityComponent, EntityData, EntityUpdate, PropertyPath, SceneUpdates, } from './tools/prefabeditor/scene';
|
|
22
22
|
export type { PrefabRootProps } from './tools/prefabeditor/PrefabRoot';
|
|
23
|
-
export type { AssetRuntime, EntityRuntime, LiveObjectRef, LiveRigidBodyRef } from './tools/prefabeditor/
|
|
24
|
-
export { useAssetRuntime, useEntityRuntime, useEntityObjectRef, useEntityRigidBodyRef } from './tools/prefabeditor/
|
|
23
|
+
export type { AssetRuntime, EntityRuntime, LiveObjectRef, LiveRigidBodyRef } from './tools/prefabeditor/runtime';
|
|
24
|
+
export { useAssetRuntime, useEntityRuntime, useEntityObjectRef, useEntityRigidBodyRef } from './tools/prefabeditor/runtime';
|
|
25
|
+
export type { ComponentInstance, ComponentRuntimeContext } from './tools/prefabeditor/runtime';
|
|
25
26
|
export type { Component, ComponentViewProps } from './tools/prefabeditor/components/ComponentRegistry';
|
|
26
27
|
export type { FieldDefinition, FieldType } from './tools/prefabeditor/components/Input';
|
|
28
|
+
export { MaterialOverridesProvider, useMaterialOverrides } from './tools/prefabeditor/components/MaterialComponent';
|
|
29
|
+
export type { MaterialOverrides } from './tools/prefabeditor/components/MaterialComponent';
|
|
27
30
|
export type { Prefab, GameObject, ComponentData } from './tools/prefabeditor/types';
|
|
28
31
|
export { findComponent, findComponentEntry, hasComponent } from './tools/prefabeditor/types';
|
|
32
|
+
export { float, positionLocal, sin, time, uniform, vec3, } from 'three/tsl';
|
|
29
33
|
export { gameEvents, useGameEvent, usePhysicsEvent, useClickEvent, getEntityIdFromRigidBody } from './tools/prefabeditor/GameEvents';
|
|
30
34
|
export type { GameEventType, GameEventMap, GameEventPayload, PhysicsEventType, InteractionEventType, PhysicsEventPayload, ClickEventPayload } from './tools/prefabeditor/GameEvents';
|
|
31
35
|
export { loadFiles } from './tools/dragdrop/DragDropLoader';
|
package/dist/index.js
CHANGED
|
@@ -19,8 +19,10 @@ export { FieldRenderer, FieldGroup, ListEditor, Label, Vector3Input, Vector3Fiel
|
|
|
19
19
|
// Prefab Editor - Utils
|
|
20
20
|
export { loadJson, saveJson, exportGLB, exportGLBData, regenerateIds, computeParentWorldMatrix, } from './tools/prefabeditor/utils';
|
|
21
21
|
export { createModelNode, createImageNode, } from './tools/prefabeditor/prefab';
|
|
22
|
-
export { useAssetRuntime, useEntityRuntime, useEntityObjectRef, useEntityRigidBodyRef } from './tools/prefabeditor/
|
|
22
|
+
export { useAssetRuntime, useEntityRuntime, useEntityObjectRef, useEntityRigidBodyRef } from './tools/prefabeditor/runtime';
|
|
23
|
+
export { MaterialOverridesProvider, useMaterialOverrides } from './tools/prefabeditor/components/MaterialComponent';
|
|
23
24
|
export { findComponent, findComponentEntry, hasComponent } from './tools/prefabeditor/types';
|
|
25
|
+
export { float, positionLocal, sin, time, uniform, vec3, } from 'three/tsl';
|
|
24
26
|
// Game Events (physics + custom events)
|
|
25
27
|
export { gameEvents, useGameEvent, usePhysicsEvent, useClickEvent, getEntityIdFromRigidBody } from './tools/prefabeditor/GameEvents';
|
|
26
28
|
// Asset Loading
|
|
@@ -13,6 +13,7 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
13
13
|
import { useHelper } from "@react-three/drei";
|
|
14
14
|
import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
|
|
15
15
|
import { BoxHelper, Euler, Matrix4, } from "three";
|
|
16
|
+
import { useFrame } from "@react-three/fiber";
|
|
16
17
|
import { useStore } from "zustand";
|
|
17
18
|
import { useClickValid } from "./useClickValid";
|
|
18
19
|
import { findComponent } from "./types";
|
|
@@ -22,9 +23,8 @@ import { loadModel, loadSound, loadTexture } from "../dragdrop";
|
|
|
22
23
|
import { GameInstance, GameInstanceProvider, getRepeatAxesFromModelProperties, useInstanceCheck } from "./InstanceProvider";
|
|
23
24
|
import { composeTransform, decompose } from "./utils";
|
|
24
25
|
import { isPhysicsProps } from "./components/PhysicsComponent";
|
|
25
|
-
import { denormalizePrefab } from "./prefab";
|
|
26
26
|
import { createPrefabStore, PrefabStoreProvider, useOptionalPrefabStoreApi, usePrefabChildIds, usePrefabNode, usePrefabRootId } from "./prefabStore";
|
|
27
|
-
import { AssetRuntimeContext, EntityRuntimeScope } from "./
|
|
27
|
+
import { AssetRuntimeContext, EntityRuntimeScope, createRuntimeEngine } from "./runtime";
|
|
28
28
|
import { sound as soundManager } from "../../helpers/SoundManager";
|
|
29
29
|
builtinComponents.forEach(registerComponent);
|
|
30
30
|
const IDENTITY = new Matrix4();
|
|
@@ -61,7 +61,13 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
|
|
|
61
61
|
const rigidBodyRefs = useRef(new Map());
|
|
62
62
|
const rootRef = useRef(null);
|
|
63
63
|
const parentStore = useOptionalPrefabStoreApi();
|
|
64
|
-
const [ownedStore] = useState(() => {
|
|
64
|
+
const [ownedStore] = useState(() => {
|
|
65
|
+
if (data)
|
|
66
|
+
return createPrefabStore(data);
|
|
67
|
+
if (store || parentStore)
|
|
68
|
+
return null;
|
|
69
|
+
throw new Error("PrefabRoot requires either a `data` or `store` prop");
|
|
70
|
+
});
|
|
65
71
|
const resolvedStore = (_a = store !== null && store !== void 0 ? store : parentStore) !== null && _a !== void 0 ? _a : ownedStore;
|
|
66
72
|
const usesOwnedStore = resolvedStore === ownedStore;
|
|
67
73
|
const shouldProvideStoreContext = !parentStore || parentStore !== resolvedStore;
|
|
@@ -73,6 +79,10 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
|
|
|
73
79
|
var _a;
|
|
74
80
|
return (_a = objectRefs.current[id]) !== null && _a !== void 0 ? _a : null;
|
|
75
81
|
}, []);
|
|
82
|
+
const getRigidBody = useCallback((id) => {
|
|
83
|
+
var _a;
|
|
84
|
+
return (_a = rigidBodyRefs.current.get(id)) !== null && _a !== void 0 ? _a : null;
|
|
85
|
+
}, []);
|
|
76
86
|
useImperativeHandle(ref, () => ({
|
|
77
87
|
root: rootRef.current,
|
|
78
88
|
getObject,
|
|
@@ -84,17 +94,29 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
|
|
|
84
94
|
setInjectedSounds(prev => (Object.assign(Object.assign({}, prev), { [path]: sound })));
|
|
85
95
|
},
|
|
86
96
|
}), [getObject]);
|
|
97
|
+
const runtimeEngine = useMemo(() => createRuntimeEngine({
|
|
98
|
+
store: resolvedStore,
|
|
99
|
+
getObject,
|
|
100
|
+
getRigidBody,
|
|
101
|
+
}), [resolvedStore, getObject, getRigidBody]);
|
|
87
102
|
const registerRef = useCallback((id, obj) => {
|
|
88
103
|
objectRefs.current[id] = obj;
|
|
104
|
+
runtimeEngine.invalidate();
|
|
89
105
|
onObjectRefChange === null || onObjectRefChange === void 0 ? void 0 : onObjectRefChange(id, obj);
|
|
90
|
-
}, [onObjectRefChange]);
|
|
106
|
+
}, [onObjectRefChange, runtimeEngine]);
|
|
91
107
|
const registerRigidBodyRef = useCallback((id, rb) => {
|
|
92
108
|
rigidBodyRefs.current.set(id, rb);
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}, []);
|
|
109
|
+
runtimeEngine.invalidate();
|
|
110
|
+
}, [runtimeEngine]);
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
runtimeEngine.setActive(!editMode);
|
|
113
|
+
}, [editMode, runtimeEngine]);
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
return () => runtimeEngine.dispose();
|
|
116
|
+
}, [runtimeEngine]);
|
|
117
|
+
useFrame((_, dt) => {
|
|
118
|
+
runtimeEngine.tick(dt);
|
|
119
|
+
});
|
|
98
120
|
useEffect(() => {
|
|
99
121
|
if (usesOwnedStore && data) {
|
|
100
122
|
resolvedStore.getState().replacePrefab(data);
|
|
@@ -157,26 +179,15 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
|
|
|
157
179
|
};
|
|
158
180
|
syncAssets();
|
|
159
181
|
}, [resolvedStore, assetManifestKey, basePath, injectedModels, injectedSounds, injectedTextures, models, sounds, textures]);
|
|
160
|
-
// Keep refs current so context getters are always fresh without changing context identity
|
|
161
|
-
const availableModelsRef = useRef(availableModels);
|
|
162
|
-
availableModelsRef.current = availableModels;
|
|
163
|
-
const availableTexturesRef = useRef(availableTextures);
|
|
164
|
-
availableTexturesRef.current = availableTextures;
|
|
165
|
-
const availableSoundsRef = useRef(availableSounds);
|
|
166
|
-
availableSoundsRef.current = availableSounds;
|
|
167
182
|
const assetRuntime = useMemo(() => ({
|
|
168
183
|
getObject,
|
|
169
184
|
getRigidBody,
|
|
170
185
|
registerRigidBodyRef,
|
|
171
|
-
getModel: (path) => { var _a; return (_a =
|
|
172
|
-
getTexture: (path) => { var _a; return (_a =
|
|
173
|
-
getSound: (path) => { var _a; return (_a =
|
|
174
|
-
getAssetRevision: () => {
|
|
175
|
-
|
|
176
|
-
const textureKeys = Object.keys(availableTexturesRef.current).sort().join('|');
|
|
177
|
-
return `${textureKeys}::${modelKeys}`;
|
|
178
|
-
},
|
|
179
|
-
}), [getObject, getRigidBody, registerRigidBodyRef]);
|
|
186
|
+
getModel: (path) => { var _a; return (_a = availableModels[path]) !== null && _a !== void 0 ? _a : null; },
|
|
187
|
+
getTexture: (path) => { var _a; return (_a = availableTextures[path]) !== null && _a !== void 0 ? _a : null; },
|
|
188
|
+
getSound: (path) => { var _a; return (_a = availableSounds[path]) !== null && _a !== void 0 ? _a : null; },
|
|
189
|
+
getAssetRevision: () => `${Object.keys(availableTextures).sort().join('|')}::${Object.keys(availableModels).sort().join('|')}`,
|
|
190
|
+
}), [getObject, getRigidBody, registerRigidBodyRef, availableModels, availableTextures, availableSounds]);
|
|
180
191
|
const content = (_jsx("group", { ref: rootRef, children: _jsx(GameInstanceProvider, { models: availableModels, selectedId: selectedId, editMode: editMode, onSelect: editMode ? onSelect : undefined, registerRef: registerRef, children: _jsx(StoreRootNode, { selectedId: selectedId, onSelect: editMode ? onSelect : undefined, onClick: onClick, registerRef: registerRef, loadedModels: availableModels, editMode: editMode, parentMatrix: IDENTITY }) }) }));
|
|
181
192
|
if (!shouldProvideStoreContext) {
|
|
182
193
|
return _jsx(AssetRuntimeContext.Provider, { value: assetRuntime, children: content });
|
|
@@ -197,6 +208,7 @@ export function GameObjectRenderer(props) {
|
|
|
197
208
|
if (prevInstancedRef.current !== undefined && prevInstancedRef.current !== isInstanced) {
|
|
198
209
|
setIsTransitioning(true);
|
|
199
210
|
const timer = setTimeout(() => setIsTransitioning(false), 100);
|
|
211
|
+
prevInstancedRef.current = isInstanced;
|
|
200
212
|
return () => clearTimeout(timer);
|
|
201
213
|
}
|
|
202
214
|
prevInstancedRef.current = isInstanced;
|
|
@@ -225,18 +237,18 @@ function InstancedNode({ nodeId, parentMatrix = IDENTITY, editMode, registerRef,
|
|
|
225
237
|
const modelUrl = (_c = (_b = findComponent(gameObject, "Model")) === null || _b === void 0 ? void 0 : _b.properties) === null || _c === void 0 ? void 0 : _c.filename;
|
|
226
238
|
const instances = useMemo(() => buildRepeatedInstances(gameObject, parentMatrix, modelUrl, physicsProps), [gameObject, modelUrl, parentMatrix, physicsProps]);
|
|
227
239
|
const groupRef = useRef(null);
|
|
240
|
+
const handleGroupRef = useCallback((object) => {
|
|
241
|
+
groupRef.current = object;
|
|
242
|
+
if (editMode) {
|
|
243
|
+
registerRef(nodeId, object);
|
|
244
|
+
}
|
|
245
|
+
}, [editMode, nodeId, registerRef]);
|
|
228
246
|
const editClickHandlers = useClickValid(!!editMode && !isLocked, (e) => {
|
|
229
247
|
onSelect === null || onSelect === void 0 ? void 0 : onSelect(nodeId);
|
|
230
248
|
onClick === null || onClick === void 0 ? void 0 : onClick(e, gameObject);
|
|
231
249
|
});
|
|
232
|
-
useEffect(() => {
|
|
233
|
-
if (editMode) {
|
|
234
|
-
registerRef(nodeId, groupRef.current);
|
|
235
|
-
return () => registerRef(nodeId, null);
|
|
236
|
-
}
|
|
237
|
-
}, [nodeId, registerRef, editMode]);
|
|
238
250
|
if (editMode) {
|
|
239
|
-
return (_jsxs(_Fragment, { children: [_jsx("group", Object.assign({ ref:
|
|
251
|
+
return (_jsxs(_Fragment, { children: [_jsx("group", Object.assign({ ref: handleGroupRef, position: localTransform.position, rotation: localTransform.rotation, scale: localTransform.scale }, editClickHandlers, { children: _jsx("mesh", { visible: false, children: _jsx("boxGeometry", { args: [0.01, 0.01, 0.01] }) }) })), instances.map(instance => (_jsx(GameInstance, { id: instance.id, sourceId: gameObject.id, clickable: clickable, clickEventName: clickEventName, modelUrl: instance.modelUrl, position: instance.position, rotation: instance.rotation, scale: instance.scale, locked: isLocked, physics: instance.physics }, instance.id)))] }));
|
|
240
252
|
}
|
|
241
253
|
return (_jsx(_Fragment, { children: instances.map(instance => (_jsx(GameInstance, { id: instance.id, sourceId: gameObject.id, clickable: clickable, clickEventName: clickEventName, modelUrl: instance.modelUrl, position: instance.position, rotation: instance.rotation, scale: instance.scale, locked: isLocked, physics: instance.physics }, instance.id))) }));
|
|
242
254
|
}
|
|
@@ -245,21 +257,25 @@ function StandardNode({ nodeId, selectedId, onSelect, onClick, registerRef, load
|
|
|
245
257
|
const gameObject = usePrefabNode(nodeId);
|
|
246
258
|
const childIds = usePrefabChildIds(nodeId);
|
|
247
259
|
const isSelected = selectedId === nodeId;
|
|
248
|
-
|
|
249
|
-
|
|
260
|
+
const isLocked = Boolean(gameObject === null || gameObject === void 0 ? void 0 : gameObject.locked);
|
|
261
|
+
const stillInstanced = useInstanceCheck(nodeId);
|
|
250
262
|
const groupRef = useRef(null);
|
|
251
263
|
const helperRef = useRef(null);
|
|
252
|
-
const
|
|
253
|
-
|
|
264
|
+
const handleGroupRef = useCallback((object) => {
|
|
265
|
+
groupRef.current = object;
|
|
266
|
+
registerRef(nodeId, object);
|
|
267
|
+
}, [nodeId, registerRef]);
|
|
268
|
+
const handleHelperRef = useCallback((object) => {
|
|
269
|
+
helperRef.current = object;
|
|
270
|
+
}, []);
|
|
254
271
|
const clickHandlers = useClickValid(!!editMode && !isLocked, (e) => {
|
|
255
272
|
onSelect === null || onSelect === void 0 ? void 0 : onSelect(nodeId);
|
|
256
|
-
|
|
273
|
+
if (gameObject)
|
|
274
|
+
onClick === null || onClick === void 0 ? void 0 : onClick(e, gameObject);
|
|
257
275
|
});
|
|
258
276
|
useHelper(editMode && isSelected ? helperRef : null, BoxHelper, "cyan");
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
return () => registerRef(nodeId, null);
|
|
262
|
-
}, [nodeId, registerRef]);
|
|
277
|
+
if (!gameObject)
|
|
278
|
+
return null;
|
|
263
279
|
const world = parentMatrix.clone().multiply(compose(gameObject));
|
|
264
280
|
const physics = findComponent(gameObject, "Physics");
|
|
265
281
|
const ready = isNodeReady(gameObject, loadedModels);
|
|
@@ -272,10 +288,11 @@ function StandardNode({ nodeId, selectedId, onSelect, onClick, registerRef, load
|
|
|
272
288
|
const childNodes = _jsx(ChildNodes, { childIds: childIds, parentMatrix: world, selectedId: selectedId, onSelect: onSelect, onClick: onClick, registerRef: registerRef, loadedModels: loadedModels, editMode: editMode });
|
|
273
289
|
const inner = (_jsx("group", Object.assign({}, clickHandlers, { children: renderCompositionNode(gameObject, renderCtx, childNodes) })));
|
|
274
290
|
const physicsInner = editMode ? _jsx("group", { visible: false, children: inner }) : inner;
|
|
275
|
-
return (_jsx(EntityRuntimeScope, { nodeId: nodeId, editMode: editMode, isSelected: isSelected, children: editMode ? (_jsxs(_Fragment, { children: [_jsx("group", { ref:
|
|
291
|
+
return (_jsx(EntityRuntimeScope, { nodeId: nodeId, editMode: editMode, isSelected: isSelected, children: editMode ? (_jsxs(_Fragment, { children: [_jsx("group", { ref: handleGroupRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: _jsx("mesh", { visible: false, children: _jsx("boxGeometry", { args: [0.01, 0.01, 0.01] }) }) }), _jsx("group", { ref: handleHelperRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: inner }), hasPhysics && (physicsDef === null || physicsDef === void 0 ? void 0 : physicsDef.View) ? (_jsx(physicsDef.View, { properties: physics.properties, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: physicsInner }, physicsKey)) : null] })) : hasPhysics && (physicsDef === null || physicsDef === void 0 ? void 0 : physicsDef.View) ? (_jsx(physicsDef.View, { properties: physics.properties, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: inner }, physicsKey)) : (_jsx("group", { ref: handleGroupRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: inner })) }));
|
|
276
292
|
}
|
|
277
293
|
function isRendererHandledComponent(componentType) {
|
|
278
294
|
return componentType === "Transform"
|
|
295
|
+
|| componentType === "BufferGeometry"
|
|
279
296
|
|| componentType === "Geometry"
|
|
280
297
|
|| componentType === "Material"
|
|
281
298
|
|| componentType === "Physics"
|
|
@@ -284,7 +301,6 @@ function isRendererHandledComponent(componentType) {
|
|
|
284
301
|
function getCompositionComponents(gameObject) {
|
|
285
302
|
var _a;
|
|
286
303
|
return Object.entries((_a = gameObject.components) !== null && _a !== void 0 ? _a : {}).reduce((result, [key, comp]) => {
|
|
287
|
-
var _a;
|
|
288
304
|
if (!(comp === null || comp === void 0 ? void 0 : comp.type) || isRendererHandledComponent(comp.type))
|
|
289
305
|
return result;
|
|
290
306
|
const def = getComponentDef(comp.type);
|
|
@@ -294,7 +310,6 @@ function getCompositionComponents(gameObject) {
|
|
|
294
310
|
key,
|
|
295
311
|
View: def.View,
|
|
296
312
|
properties: comp.properties,
|
|
297
|
-
composition: (_a = def.composition) !== null && _a !== void 0 ? _a : "wrap",
|
|
298
313
|
});
|
|
299
314
|
return result;
|
|
300
315
|
}, []);
|
|
@@ -373,32 +388,27 @@ function renderCompositionNode(gameObject, ctx, childNodes) {
|
|
|
373
388
|
return applyNodeComposition(gameObject, _jsxs(_Fragment, { children: [primaryContent, childNodes] }));
|
|
374
389
|
}
|
|
375
390
|
function renderNodePrimaryContent(gameObject, ctx) {
|
|
376
|
-
var _a, _b;
|
|
377
|
-
const geometry = findComponent(gameObject, "Geometry");
|
|
391
|
+
var _a, _b, _c;
|
|
392
|
+
const geometry = (_a = findComponent(gameObject, "BufferGeometry")) !== null && _a !== void 0 ? _a : findComponent(gameObject, "Geometry");
|
|
378
393
|
const material = findComponent(gameObject, "Material");
|
|
379
394
|
const model = findComponent(gameObject, "Model");
|
|
380
395
|
const geometryDef = geometry && getComponentDef(geometry.type);
|
|
381
396
|
const materialDef = material && getComponentDef(material.type);
|
|
382
397
|
const modelDef = model && getComponentDef(model.type);
|
|
383
|
-
const geometryProperties = (
|
|
398
|
+
const geometryProperties = (_b = geometry === null || geometry === void 0 ? void 0 : geometry.properties) !== null && _b !== void 0 ? _b : {};
|
|
384
399
|
const meshVisible = geometryProperties.visible !== false;
|
|
385
400
|
const meshCastShadow = meshVisible && geometryProperties.castShadow !== false;
|
|
386
401
|
const meshReceiveShadow = meshVisible && geometryProperties.receiveShadow !== false;
|
|
387
402
|
if ((geometry === null || geometry === void 0 ? void 0 : geometry.type) && (geometryDef === null || geometryDef === void 0 ? void 0 : geometryDef.View)) {
|
|
388
403
|
return (_jsxs("mesh", { visible: meshVisible, castShadow: meshCastShadow, receiveShadow: meshReceiveShadow, children: [_jsx(geometryDef.View, { properties: geometry.properties }), material && (materialDef === null || materialDef === void 0 ? void 0 : materialDef.View) && (_jsx(materialDef.View, { properties: material.properties }, "material"))] }));
|
|
389
404
|
}
|
|
390
|
-
if ((model === null || model === void 0 ? void 0 : model.type) && (modelDef === null || modelDef === void 0 ? void 0 : modelDef.View) && !((
|
|
405
|
+
if ((model === null || model === void 0 ? void 0 : model.type) && (modelDef === null || modelDef === void 0 ? void 0 : modelDef.View) && !((_c = model.properties) === null || _c === void 0 ? void 0 : _c.instanced) && isNodeReady(gameObject, ctx.loadedModels)) {
|
|
391
406
|
return _jsx(modelDef.View, { properties: model.properties });
|
|
392
407
|
}
|
|
393
408
|
return null;
|
|
394
409
|
}
|
|
395
410
|
function applyNodeComposition(gameObject, subtree) {
|
|
396
411
|
const components = getCompositionComponents(gameObject);
|
|
397
|
-
return components.reduce((acc, { key, View, properties
|
|
398
|
-
? (_jsxs(_Fragment, { children: [_jsx(View, { properties: properties }, key), acc] }))
|
|
399
|
-
: (_jsx(View, { properties: properties, children: acc }, key)), subtree);
|
|
412
|
+
return components.reduce((acc, { key, View, properties }) => (_jsx(View, { properties: properties, children: acc }, key)), subtree);
|
|
400
413
|
}
|
|
401
414
|
export default PrefabRoot;
|
|
402
|
-
function missingStoreState() {
|
|
403
|
-
throw new Error("PrefabRoot requires either data or store");
|
|
404
|
-
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { BooleanField, FieldGroup } from "./Input";
|
|
3
|
+
const DEFAULT_TRIANGLE_POSITIONS = [
|
|
4
|
+
0, 0, 0,
|
|
5
|
+
1, 0, 0,
|
|
6
|
+
0, 1, 0,
|
|
7
|
+
];
|
|
8
|
+
const DEFAULT_TRIANGLE_INDICES = [0, 1, 2];
|
|
9
|
+
const DEFAULT_TRIANGLE_UVS = [
|
|
10
|
+
0, 0,
|
|
11
|
+
1, 0,
|
|
12
|
+
0, 1,
|
|
13
|
+
];
|
|
14
|
+
function isFiniteNumberArray(value) {
|
|
15
|
+
return Array.isArray(value) && value.every(entry => typeof entry === 'number' && Number.isFinite(entry));
|
|
16
|
+
}
|
|
17
|
+
function normalizeNumberArray(value, fallback) {
|
|
18
|
+
return isFiniteNumberArray(value) ? value : fallback;
|
|
19
|
+
}
|
|
20
|
+
function toAttributeText(value, fallback) {
|
|
21
|
+
return JSON.stringify(normalizeNumberArray(value, fallback));
|
|
22
|
+
}
|
|
23
|
+
function parseArrayInput(raw) {
|
|
24
|
+
const trimmed = raw.trim();
|
|
25
|
+
if (!trimmed)
|
|
26
|
+
return [];
|
|
27
|
+
const parsed = JSON.parse(trimmed);
|
|
28
|
+
if (!isFiniteNumberArray(parsed)) {
|
|
29
|
+
throw new Error('Expected a JSON array of numbers');
|
|
30
|
+
}
|
|
31
|
+
return parsed;
|
|
32
|
+
}
|
|
33
|
+
function getIndexArray(indices) {
|
|
34
|
+
if (indices.length === 0)
|
|
35
|
+
return null;
|
|
36
|
+
const maxIndex = Math.max(...indices);
|
|
37
|
+
return maxIndex > 65535 ? new Uint32Array(indices) : new Uint16Array(indices);
|
|
38
|
+
}
|
|
39
|
+
function BufferArrayField({ label, value, fallback, onChange, rows = 4, }) {
|
|
40
|
+
return (_jsxs("label", { style: { display: 'grid', gap: 4 }, children: [_jsx("span", { style: { fontSize: '10px', color: '#888', textTransform: 'uppercase', letterSpacing: '0.05em', fontWeight: 500 }, children: label }), _jsx("textarea", { rows: rows, spellCheck: false, defaultValue: toAttributeText(value, fallback), onBlur: (event) => {
|
|
41
|
+
try {
|
|
42
|
+
onChange(parseArrayInput(event.target.value));
|
|
43
|
+
event.target.setCustomValidity('');
|
|
44
|
+
}
|
|
45
|
+
catch (_a) {
|
|
46
|
+
event.target.setCustomValidity('Expected a JSON array of numbers');
|
|
47
|
+
event.target.reportValidity();
|
|
48
|
+
}
|
|
49
|
+
}, style: {
|
|
50
|
+
width: '100%',
|
|
51
|
+
backgroundColor: '#171717',
|
|
52
|
+
border: '1px solid #333',
|
|
53
|
+
padding: '6px 8px',
|
|
54
|
+
fontSize: '11px',
|
|
55
|
+
color: '#eee',
|
|
56
|
+
fontFamily: 'monospace',
|
|
57
|
+
outline: 'none',
|
|
58
|
+
borderRadius: 3,
|
|
59
|
+
resize: 'vertical',
|
|
60
|
+
boxSizing: 'border-box',
|
|
61
|
+
} })] }));
|
|
62
|
+
}
|
|
63
|
+
function BufferGeometryComponentEditor({ component, onUpdate, }) {
|
|
64
|
+
var _a;
|
|
65
|
+
const properties = (_a = component.properties) !== null && _a !== void 0 ? _a : {};
|
|
66
|
+
return (_jsxs(FieldGroup, { children: [_jsx(BufferArrayField, { label: "Positions", value: properties.positions, fallback: DEFAULT_TRIANGLE_POSITIONS, rows: 5, onChange: (positions) => onUpdate({ positions }) }), _jsx(BufferArrayField, { label: "Indices", value: properties.indices, fallback: DEFAULT_TRIANGLE_INDICES, onChange: (indices) => onUpdate({ indices }) }), _jsx(BufferArrayField, { label: "Normals", value: properties.normals, fallback: [], onChange: (normals) => onUpdate({ normals }) }), _jsx(BufferArrayField, { label: "UVs", value: properties.uvs, fallback: DEFAULT_TRIANGLE_UVS, onChange: (uvs) => onUpdate({ uvs }) }), _jsx(BooleanField, { name: "computeVertexNormals", label: "Compute Normals", values: properties, onChange: onUpdate, fallback: true }), _jsx(BooleanField, { name: "visible", label: "Visible", values: properties, onChange: onUpdate, fallback: true }), _jsx(BooleanField, { name: "castShadow", label: "Cast Shadow", values: properties, onChange: onUpdate, fallback: true }), _jsx(BooleanField, { name: "receiveShadow", label: "Receive Shadow", values: properties, onChange: onUpdate, fallback: true })] }));
|
|
67
|
+
}
|
|
68
|
+
function BufferGeometryComponentView({ properties }) {
|
|
69
|
+
const positions = normalizeNumberArray(properties.positions, DEFAULT_TRIANGLE_POSITIONS);
|
|
70
|
+
const indices = normalizeNumberArray(properties.indices, DEFAULT_TRIANGLE_INDICES);
|
|
71
|
+
const normals = normalizeNumberArray(properties.normals, []);
|
|
72
|
+
const uvs = normalizeNumberArray(properties.uvs, DEFAULT_TRIANGLE_UVS);
|
|
73
|
+
const indexArray = getIndexArray(indices);
|
|
74
|
+
const hasNormals = normals.length >= 3 && normals.length % 3 === 0;
|
|
75
|
+
const hasUvs = uvs.length >= 2 && uvs.length % 2 === 0;
|
|
76
|
+
return (_jsxs("bufferGeometry", { onUpdate: (geometry) => {
|
|
77
|
+
if (properties.computeVertexNormals !== false && !hasNormals) {
|
|
78
|
+
geometry.computeVertexNormals();
|
|
79
|
+
}
|
|
80
|
+
geometry.computeBoundingBox();
|
|
81
|
+
geometry.computeBoundingSphere();
|
|
82
|
+
}, children: [_jsx("bufferAttribute", { attach: "attributes-position", args: [new Float32Array(positions), 3] }), indexArray ? (_jsx("bufferAttribute", { attach: "index", args: [indexArray, 1] })) : null, hasNormals ? (_jsx("bufferAttribute", { attach: "attributes-normal", args: [new Float32Array(normals), 3] })) : null, hasUvs ? (_jsx("bufferAttribute", { attach: "attributes-uv", args: [new Float32Array(uvs), 2] })) : null] }));
|
|
83
|
+
}
|
|
84
|
+
const BufferGeometryComponent = {
|
|
85
|
+
name: 'BufferGeometry',
|
|
86
|
+
Editor: BufferGeometryComponentEditor,
|
|
87
|
+
View: BufferGeometryComponentView,
|
|
88
|
+
defaultProperties: {
|
|
89
|
+
positions: DEFAULT_TRIANGLE_POSITIONS,
|
|
90
|
+
indices: DEFAULT_TRIANGLE_INDICES,
|
|
91
|
+
normals: [],
|
|
92
|
+
uvs: DEFAULT_TRIANGLE_UVS,
|
|
93
|
+
computeVertexNormals: true,
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
export default BufferGeometryComponent;
|
|
@@ -3,7 +3,7 @@ import { OrthographicCamera, PerspectiveCamera, useHelper } from '@react-three/d
|
|
|
3
3
|
import { useRef } from 'react';
|
|
4
4
|
import { CameraHelper } from 'three';
|
|
5
5
|
import { useFrame, useThree } from '@react-three/fiber';
|
|
6
|
-
import { useEntityRuntime } from '../
|
|
6
|
+
import { useEntityRuntime } from '../runtime';
|
|
7
7
|
import { FieldGroup, NumberField, SelectField } from './Input';
|
|
8
8
|
const CAMERA_PROJECTION_OPTIONS = [
|
|
9
9
|
{ value: 'perspective', label: 'Perspective' },
|
|
@@ -66,7 +66,6 @@ const CameraComponent = {
|
|
|
66
66
|
name: 'Camera',
|
|
67
67
|
Editor: CameraComponentEditor,
|
|
68
68
|
View: CameraComponentView,
|
|
69
|
-
composition: 'wrap',
|
|
70
69
|
defaultProperties: cameraDefaults,
|
|
71
70
|
};
|
|
72
71
|
export default CameraComponent;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useRef } from 'react';
|
|
3
3
|
import { gameEvents } from '../GameEvents';
|
|
4
|
-
import { useEntityRuntime } from '../
|
|
4
|
+
import { useEntityRuntime } from '../runtime';
|
|
5
5
|
import { EventField, FieldGroup } from './Input';
|
|
6
6
|
function ClickComponentEditor({ component, onUpdate }) {
|
|
7
7
|
return (_jsxs(FieldGroup, { children: [_jsx("div", { style: { fontSize: 12, opacity: 0.8 }, children: "Emits a game event in play mode when this entity is clicked." }), _jsx(EventField, { name: "eventName", label: "Emit Event", values: component.properties, onChange: onUpdate, placeholder: "click" })] }));
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { FC } from "react";
|
|
2
2
|
import { ComponentData, GameObject } from "../types";
|
|
3
|
+
import type { ComponentInstance, ComponentRuntimeContext } from "../runtime";
|
|
3
4
|
export type AssetRef = {
|
|
4
5
|
type: "model" | "texture" | "sound";
|
|
5
6
|
path: string;
|
|
@@ -27,11 +28,8 @@ export interface Component {
|
|
|
27
28
|
}>;
|
|
28
29
|
defaultProperties: any;
|
|
29
30
|
View?: FC<ComponentViewProps>;
|
|
30
|
-
/**
|
|
31
|
-
|
|
32
|
-
* Defaults to `wrap`; `sibling` renders next to the current subtree.
|
|
33
|
-
*/
|
|
34
|
-
composition?: "wrap" | "sibling";
|
|
31
|
+
/** Optional runtime factory for the non-React game loop. */
|
|
32
|
+
create?: (ctx: ComponentRuntimeContext) => ComponentInstance | void;
|
|
35
33
|
/** Declare which asset paths this component references (for asset loading). */
|
|
36
34
|
getAssetRefs?: (properties: Record<string, any>) => AssetRef[];
|
|
37
35
|
}
|
|
@@ -2,8 +2,8 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { useHelper } from "@react-three/drei";
|
|
3
3
|
import { useRef, useEffect, useState } from "react";
|
|
4
4
|
import { useFrame } from "@react-three/fiber";
|
|
5
|
-
import { CameraHelper
|
|
6
|
-
import { useEntityRuntime } from "../
|
|
5
|
+
import { CameraHelper } from "three";
|
|
6
|
+
import { useEntityRuntime } from "../runtime";
|
|
7
7
|
import { BooleanField, ColorField, NumberField, NumberInput, Vector3Input } from "./Input";
|
|
8
8
|
import { LightSection, ShadowBiasField, mergeWithDefaults } from "./lightUtils";
|
|
9
9
|
import { colors } from "../styles";
|
|
@@ -161,13 +161,7 @@ function DirectionalLightView({ properties, children }) {
|
|
|
161
161
|
shadowCamera.updateMatrixWorld();
|
|
162
162
|
}
|
|
163
163
|
});
|
|
164
|
-
return (_jsxs(_Fragment, { children: [_jsx("directionalLight", { ref: directionalLightRef, color: color, intensity: intensity, castShadow: castShadow, "shadow-mapSize-width": shadowMapSize, "shadow-mapSize-height": shadowMapSize, "shadow-camera-near": shadowCameraNear, "shadow-camera-far": shadowCameraFar, "shadow-camera-top": shadowCameraTop, "shadow-camera-bottom": shadowCameraBottom, "shadow-camera-left": shadowCameraLeft, "shadow-camera-right": shadowCameraRight, "shadow-bias": shadowBias, "shadow-normalBias": shadowNormalBias, "shadow-autoUpdate": shadowAutoUpdate }), _jsx("object3D", { ref: targetRef, position: targetOffset }), editMode && isSelected && (_jsxs(_Fragment, { children: [_jsxs("mesh", { children: [_jsx("sphereGeometry", { args: [0.3, 8, 6] }), _jsx("meshBasicMaterial", { color: color, wireframe: true })] }), _jsxs("mesh", { position: targetOffset, children: [_jsx("sphereGeometry", { args: [0.2, 8, 6] }), _jsx("meshBasicMaterial", { color: color, wireframe: true, opacity: 0.5, transparent: true })] }), _jsxs("line", { children: [_jsx("bufferGeometry", {
|
|
165
|
-
const points = [
|
|
166
|
-
new Vector3(0, 0, 0),
|
|
167
|
-
new Vector3(targetOffset[0], targetOffset[1], targetOffset[2])
|
|
168
|
-
];
|
|
169
|
-
geo.setFromPoints(points);
|
|
170
|
-
} }), _jsx("lineBasicMaterial", { color: color, opacity: 0.6, transparent: true })] })] })), children] }));
|
|
164
|
+
return (_jsxs(_Fragment, { children: [_jsx("directionalLight", { ref: directionalLightRef, color: color, intensity: intensity, castShadow: castShadow, "shadow-mapSize-width": shadowMapSize, "shadow-mapSize-height": shadowMapSize, "shadow-camera-near": shadowCameraNear, "shadow-camera-far": shadowCameraFar, "shadow-camera-top": shadowCameraTop, "shadow-camera-bottom": shadowCameraBottom, "shadow-camera-left": shadowCameraLeft, "shadow-camera-right": shadowCameraRight, "shadow-bias": shadowBias, "shadow-normalBias": shadowNormalBias, "shadow-autoUpdate": shadowAutoUpdate }), _jsx("object3D", { ref: targetRef, position: targetOffset }), editMode && isSelected && (_jsxs(_Fragment, { children: [_jsxs("mesh", { children: [_jsx("sphereGeometry", { args: [0.3, 8, 6] }), _jsx("meshBasicMaterial", { color: color, wireframe: true })] }), _jsxs("mesh", { position: targetOffset, children: [_jsx("sphereGeometry", { args: [0.2, 8, 6] }), _jsx("meshBasicMaterial", { color: color, wireframe: true, opacity: 0.5, transparent: true })] }), _jsxs("line", { children: [_jsx("bufferGeometry", { children: _jsx("bufferAttribute", { attach: "attributes-position", args: [new Float32Array([0, 0, 0, targetOffset[0], targetOffset[1], targetOffset[2]]), 3] }) }), _jsx("lineBasicMaterial", { color: color, opacity: 0.6, transparent: true })] })] })), children] }));
|
|
171
165
|
}
|
|
172
166
|
const DirectionalLightComponent = {
|
|
173
167
|
name: 'DirectionalLight',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Environment } from '@react-three/drei';
|
|
3
3
|
import { FieldGroup, NumberField } from './Input';
|
|
4
|
-
import { useAssetRuntime } from '../
|
|
4
|
+
import { useAssetRuntime } from '../runtime';
|
|
5
5
|
function EnvironmentView({ properties, children, }) {
|
|
6
6
|
const { getAssetRevision } = useAssetRuntime();
|
|
7
7
|
const { intensity = 1, resolution = 256 } = properties;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
1
2
|
import type { ThreeElement } from '@react-three/fiber';
|
|
2
3
|
import { Component } from './ComponentRegistry';
|
|
3
4
|
import { MeshBasicNodeMaterial, MeshStandardNodeMaterial } from 'three/webgpu';
|
|
@@ -14,13 +15,22 @@ export interface MaterialProps extends Omit<MeshStandardMaterialProperties & Mes
|
|
|
14
15
|
thickness?: number;
|
|
15
16
|
ior?: number;
|
|
16
17
|
texture?: string;
|
|
18
|
+
offset?: [number, number];
|
|
17
19
|
repeat?: boolean;
|
|
18
20
|
repeatCount?: [number, number];
|
|
21
|
+
animateOffset?: boolean;
|
|
22
|
+
offsetSpeed?: [number, number];
|
|
19
23
|
generateMipmaps?: boolean;
|
|
20
24
|
minFilter?: string;
|
|
21
25
|
magFilter?: string;
|
|
22
26
|
normalMapTexture?: string;
|
|
23
27
|
normalScale?: [number, number];
|
|
24
28
|
}
|
|
29
|
+
export type MaterialOverrides = Record<string, unknown>;
|
|
30
|
+
export declare function useMaterialOverrides(): MaterialOverrides;
|
|
31
|
+
export declare function MaterialOverridesProvider({ overrides, children, }: {
|
|
32
|
+
overrides: MaterialOverrides;
|
|
33
|
+
children: ReactNode;
|
|
34
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
25
35
|
declare const MaterialComponent: Component;
|
|
26
36
|
export default MaterialComponent;
|