react-three-game 0.0.69 → 0.0.70
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/helpers/SoundManager.d.ts +2 -0
- package/dist/helpers/SoundManager.js +6 -0
- package/dist/index.d.ts +10 -8
- package/dist/index.js +7 -5
- package/dist/shared/GameCanvas.js +0 -2
- package/dist/tools/assetviewer/page.d.ts +5 -0
- package/dist/tools/assetviewer/page.js +3 -0
- package/dist/tools/dragdrop/DragDropLoader.d.ts +3 -2
- package/dist/tools/dragdrop/DragDropLoader.js +18 -3
- package/dist/tools/dragdrop/index.d.ts +2 -2
- package/dist/tools/dragdrop/index.js +1 -1
- package/dist/tools/dragdrop/modelLoader.d.ts +10 -0
- package/dist/tools/dragdrop/modelLoader.js +60 -0
- package/dist/tools/prefabeditor/EditorTree.js +6 -30
- package/dist/tools/prefabeditor/EditorTreeMenus.js +3 -3
- package/dist/tools/prefabeditor/EditorUI.js +6 -4
- package/dist/tools/prefabeditor/InstanceProvider.d.ts +2 -0
- package/dist/tools/prefabeditor/InstanceProvider.js +54 -52
- package/dist/tools/prefabeditor/PrefabEditor.d.ts +22 -0
- package/dist/tools/prefabeditor/PrefabEditor.js +68 -27
- package/dist/tools/prefabeditor/PrefabRoot.d.ts +5 -1
- package/dist/tools/prefabeditor/PrefabRoot.js +148 -145
- package/dist/tools/prefabeditor/components/ClickComponent.js +10 -7
- package/dist/tools/prefabeditor/components/ComponentRegistry.d.ts +10 -4
- package/dist/tools/prefabeditor/components/ComponentRegistry.js +6 -6
- package/dist/tools/prefabeditor/components/GeometryComponent.js +1 -1
- package/dist/tools/prefabeditor/components/Input.d.ts +16 -0
- package/dist/tools/prefabeditor/components/Input.js +33 -0
- package/dist/tools/prefabeditor/components/MaterialComponent.js +10 -2
- package/dist/tools/prefabeditor/components/ModelComponent.js +35 -43
- package/dist/tools/prefabeditor/components/PhysicsComponent.d.ts +10 -1
- package/dist/tools/prefabeditor/components/PhysicsComponent.js +122 -28
- package/dist/tools/prefabeditor/components/SoundComponent.d.ts +3 -0
- package/dist/tools/prefabeditor/components/SoundComponent.js +240 -0
- package/dist/tools/prefabeditor/components/SpotLightComponent.js +6 -1
- package/dist/tools/prefabeditor/components/TransformComponent.js +2 -2
- package/dist/tools/prefabeditor/components/index.js +2 -0
- package/dist/tools/prefabeditor/prefabStore.d.ts +1 -0
- package/dist/tools/prefabeditor/prefabStore.js +11 -13
- package/dist/tools/prefabeditor/sceneApi.d.ts +15 -1
- package/dist/tools/prefabeditor/sceneApi.js +77 -32
- package/dist/tools/prefabeditor/styles.d.ts +1 -0
- package/dist/tools/prefabeditor/styles.js +9 -0
- package/dist/tools/prefabeditor/types.d.ts +13 -0
- package/dist/tools/prefabeditor/types.js +28 -1
- package/dist/tools/prefabeditor/useClickValid.d.ts +13 -0
- package/dist/tools/prefabeditor/useClickValid.js +21 -0
- package/dist/tools/prefabeditor/utils.d.ts +2 -0
- package/dist/tools/prefabeditor/utils.js +34 -35
- package/package.json +1 -1
- package/dist/tools/prefabeditor/EditorContext.d.ts +0 -16
- package/dist/tools/prefabeditor/EditorContext.js +0 -9
|
@@ -51,6 +51,11 @@ const SpotLightComponent = {
|
|
|
51
51
|
name: 'SpotLight',
|
|
52
52
|
Editor: SpotLightComponentEditor,
|
|
53
53
|
View: SpotLightView,
|
|
54
|
-
defaultProperties: spotLightDefaults
|
|
54
|
+
defaultProperties: spotLightDefaults,
|
|
55
|
+
getAssetRefs: (properties) => {
|
|
56
|
+
if (properties.map)
|
|
57
|
+
return [{ type: 'texture', path: properties.map }];
|
|
58
|
+
return [];
|
|
59
|
+
},
|
|
55
60
|
};
|
|
56
61
|
export default SpotLightComponent;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Label, Vector3Input } from "./Input";
|
|
3
|
-
import { useEditorContext } from "../
|
|
3
|
+
import { useEditorContext } from "../PrefabEditor";
|
|
4
4
|
import { colors } from "../styles";
|
|
5
5
|
const buttonStyle = {
|
|
6
6
|
padding: '4px 8px',
|
|
@@ -48,7 +48,7 @@ function TransformComponentEditor({ component, onUpdate }) {
|
|
|
48
48
|
const TransformComponent = {
|
|
49
49
|
name: 'Transform',
|
|
50
50
|
Editor: TransformComponentEditor,
|
|
51
|
-
|
|
51
|
+
isWrapper: true,
|
|
52
52
|
defaultProperties: {
|
|
53
53
|
position: [0, 0, 0],
|
|
54
54
|
rotation: [0, 0, 0],
|
|
@@ -10,6 +10,7 @@ import TextComponent from './TextComponent';
|
|
|
10
10
|
import EnvironmentComponent from './EnvironmentComponent';
|
|
11
11
|
import CameraComponent from './CameraComponent';
|
|
12
12
|
import ClickComponent from './ClickComponent';
|
|
13
|
+
import SoundComponent from './SoundComponent';
|
|
13
14
|
export default [
|
|
14
15
|
GeometryComponent,
|
|
15
16
|
TransformComponent,
|
|
@@ -23,4 +24,5 @@ export default [
|
|
|
23
24
|
EnvironmentComponent,
|
|
24
25
|
CameraComponent,
|
|
25
26
|
ClickComponent,
|
|
27
|
+
SoundComponent,
|
|
26
28
|
];
|
|
@@ -33,6 +33,7 @@ export declare function PrefabStoreProvider({ store, children, }: {
|
|
|
33
33
|
children: ReactNode;
|
|
34
34
|
}): import("react").FunctionComponentElement<import("react").ProviderProps<PrefabStoreApi | null>>;
|
|
35
35
|
export declare function usePrefabStoreApi(): PrefabStoreApi;
|
|
36
|
+
export declare function useOptionalPrefabStoreApi(): PrefabStoreApi | null;
|
|
36
37
|
export declare function usePrefabStore<T>(selector: (state: PrefabStoreState) => T): T;
|
|
37
38
|
export declare function usePrefabRootId(): string;
|
|
38
39
|
export declare function usePrefabNode(nodeId: string | null | undefined): PrefabNodeRecord | null;
|
|
@@ -13,6 +13,7 @@ import { createContext, createElement, useContext } from "react";
|
|
|
13
13
|
import { subscribeWithSelector } from "zustand/middleware";
|
|
14
14
|
import { useStore } from "zustand";
|
|
15
15
|
import { createStore } from "zustand/vanilla";
|
|
16
|
+
import { getComponentAssetRefs } from "./components/ComponentRegistry";
|
|
16
17
|
const PrefabStoreContext = createContext(null);
|
|
17
18
|
const EMPTY_CHILD_IDS = [];
|
|
18
19
|
export function PrefabStoreProvider({ store, children, }) {
|
|
@@ -25,6 +26,9 @@ export function usePrefabStoreApi() {
|
|
|
25
26
|
}
|
|
26
27
|
return store;
|
|
27
28
|
}
|
|
29
|
+
export function useOptionalPrefabStoreApi() {
|
|
30
|
+
return useContext(PrefabStoreContext);
|
|
31
|
+
}
|
|
28
32
|
export function usePrefabStore(selector) {
|
|
29
33
|
return useStore(usePrefabStoreApi(), selector);
|
|
30
34
|
}
|
|
@@ -296,26 +300,20 @@ function collectSubtreeAssetRefs(node) {
|
|
|
296
300
|
return refs;
|
|
297
301
|
}
|
|
298
302
|
function collectAssetRefsForIds(ids, nodesById) {
|
|
299
|
-
return ids.
|
|
303
|
+
return ids.reduce((refs, id) => {
|
|
304
|
+
refs.push(...getAssetRefs(nodesById[id]));
|
|
305
|
+
return refs;
|
|
306
|
+
}, []);
|
|
300
307
|
}
|
|
301
308
|
function getAssetRefs(node) {
|
|
302
309
|
var _a;
|
|
303
310
|
const refs = [];
|
|
304
311
|
Object.values((_a = node === null || node === void 0 ? void 0 : node.components) !== null && _a !== void 0 ? _a : {}).forEach(component => {
|
|
305
|
-
var _a
|
|
312
|
+
var _a;
|
|
306
313
|
if (!(component === null || component === void 0 ? void 0 : component.type))
|
|
307
314
|
return;
|
|
308
|
-
|
|
309
|
-
refs.push(
|
|
310
|
-
}
|
|
311
|
-
if (component.type === "Material") {
|
|
312
|
-
if ((_b = component.properties) === null || _b === void 0 ? void 0 : _b.texture)
|
|
313
|
-
refs.push(`texture:${component.properties.texture}`);
|
|
314
|
-
if ((_c = component.properties) === null || _c === void 0 ? void 0 : _c.normalMapTexture)
|
|
315
|
-
refs.push(`texture:${component.properties.normalMapTexture}`);
|
|
316
|
-
}
|
|
317
|
-
if (component.type === "SpotLight" && ((_d = component.properties) === null || _d === void 0 ? void 0 : _d.map)) {
|
|
318
|
-
refs.push(`texture:${component.properties.map}`);
|
|
315
|
+
for (const ref of getComponentAssetRefs(component.type, (_a = component.properties) !== null && _a !== void 0 ? _a : {})) {
|
|
316
|
+
refs.push(`${ref.type}:${ref.path}`);
|
|
319
317
|
}
|
|
320
318
|
});
|
|
321
319
|
return refs.sort();
|
|
@@ -15,16 +15,27 @@ export interface EntityComponent<TProperties = Record<string, any>> {
|
|
|
15
15
|
}
|
|
16
16
|
export interface Entity {
|
|
17
17
|
readonly id: string;
|
|
18
|
+
readonly name: string | undefined;
|
|
19
|
+
readonly enabled: boolean;
|
|
20
|
+
readonly parent: Entity | null;
|
|
21
|
+
readonly children: Entity[];
|
|
18
22
|
set: (data: EntityData) => void;
|
|
19
23
|
update: (update: (node: EntityData) => EntityData) => void;
|
|
20
24
|
getComponent: <TProperties = Record<string, any>>(name: string) => EntityComponent<TProperties> | null;
|
|
25
|
+
addComponent: (type: string, properties?: Record<string, any>) => EntityComponent | null;
|
|
26
|
+
removeComponent: (name: string) => void;
|
|
27
|
+
destroy: () => void;
|
|
21
28
|
}
|
|
22
29
|
export type EntityUpdate = (node: EntityData) => EntityData;
|
|
23
30
|
export type SceneUpdates = Record<string, EntityUpdate>;
|
|
24
31
|
export interface Scene {
|
|
25
32
|
readonly rootId: string;
|
|
26
|
-
find: (
|
|
33
|
+
find: (nameOrId: string) => Entity | null;
|
|
27
34
|
get: (id: string) => Entity;
|
|
35
|
+
create: (name: string, components?: Record<string, {
|
|
36
|
+
type: string;
|
|
37
|
+
properties?: Record<string, any>;
|
|
38
|
+
}>, options?: SpawnOptions) => Entity;
|
|
28
39
|
update: {
|
|
29
40
|
(id: string, update: EntityUpdate): void;
|
|
30
41
|
(updates: SceneUpdates): void;
|
|
@@ -35,6 +46,9 @@ export interface Scene {
|
|
|
35
46
|
interface SceneAdapter {
|
|
36
47
|
getRootId: () => string;
|
|
37
48
|
getNode: (id: string) => EntityData | null;
|
|
49
|
+
getChildIds: (id: string) => string[];
|
|
50
|
+
getParentId: (id: string) => string | null;
|
|
51
|
+
findByName: (name: string) => string | null;
|
|
38
52
|
updateNode: (id: string, update: (node: EntityData) => EntityData) => void;
|
|
39
53
|
updateNodes: (updates: Record<string, (node: EntityData) => EntityData>) => void;
|
|
40
54
|
addNode: (node: GameObject, options?: SpawnOptions) => string;
|
|
@@ -1,14 +1,23 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { findComponentEntry } from "./types";
|
|
13
|
+
import { getComponentDef } from "./components/ComponentRegistry";
|
|
1
14
|
function missingNode(id) {
|
|
2
15
|
throw new Error(`Scene node not found: ${id}`);
|
|
3
16
|
}
|
|
4
17
|
function normalizePath(path) {
|
|
5
|
-
if (path
|
|
18
|
+
if (!path)
|
|
6
19
|
return [];
|
|
7
|
-
|
|
8
|
-
if (Array.isArray(path)) {
|
|
9
|
-
return path;
|
|
10
|
-
}
|
|
11
|
-
return path.split(".").filter(Boolean);
|
|
20
|
+
return Array.isArray(path) ? path : path.split(".").filter(Boolean);
|
|
12
21
|
}
|
|
13
22
|
function getValueAtPath(value, path) {
|
|
14
23
|
const segments = normalizePath(path);
|
|
@@ -47,28 +56,12 @@ function setValueAtPath(value, path, nextValue) {
|
|
|
47
56
|
};
|
|
48
57
|
return cloneBranch(value, 0);
|
|
49
58
|
}
|
|
50
|
-
function findComponentEntry(node, name) {
|
|
51
|
-
if (!node.components) {
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
54
|
-
const direct = node.components[name];
|
|
55
|
-
if (direct) {
|
|
56
|
-
return [name, direct];
|
|
57
|
-
}
|
|
58
|
-
const normalizedName = name.toLowerCase();
|
|
59
|
-
for (const [key, component] of Object.entries(node.components)) {
|
|
60
|
-
if (!component) {
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
if (key.toLowerCase() === normalizedName || component.type.toLowerCase() === normalizedName) {
|
|
64
|
-
return [key, component];
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return null;
|
|
68
|
-
}
|
|
69
59
|
export function createScene(adapter) {
|
|
70
|
-
const
|
|
71
|
-
|
|
60
|
+
const getNode = (id) => {
|
|
61
|
+
if (!adapter.getNode(id))
|
|
62
|
+
missingNode(id);
|
|
63
|
+
return createNode(id);
|
|
64
|
+
};
|
|
72
65
|
function createComponent(entityId, componentKey, componentType) {
|
|
73
66
|
return {
|
|
74
67
|
key: componentKey,
|
|
@@ -111,6 +104,21 @@ export function createScene(adapter) {
|
|
|
111
104
|
function createNode(id) {
|
|
112
105
|
return {
|
|
113
106
|
id,
|
|
107
|
+
get name() {
|
|
108
|
+
var _a;
|
|
109
|
+
return (_a = adapter.getNode(id)) === null || _a === void 0 ? void 0 : _a.name;
|
|
110
|
+
},
|
|
111
|
+
get enabled() {
|
|
112
|
+
var _a;
|
|
113
|
+
return !((_a = adapter.getNode(id)) === null || _a === void 0 ? void 0 : _a.disabled);
|
|
114
|
+
},
|
|
115
|
+
get parent() {
|
|
116
|
+
const parentId = adapter.getParentId(id);
|
|
117
|
+
return parentId ? createNode(parentId) : null;
|
|
118
|
+
},
|
|
119
|
+
get children() {
|
|
120
|
+
return adapter.getChildIds(id).map(createNode);
|
|
121
|
+
},
|
|
114
122
|
set(data) {
|
|
115
123
|
adapter.updateNode(id, () => data);
|
|
116
124
|
},
|
|
@@ -119,16 +127,36 @@ export function createScene(adapter) {
|
|
|
119
127
|
},
|
|
120
128
|
getComponent(name) {
|
|
121
129
|
const node = adapter.getNode(id);
|
|
122
|
-
if (!node)
|
|
130
|
+
if (!node)
|
|
123
131
|
return null;
|
|
124
|
-
}
|
|
125
132
|
const entry = findComponentEntry(node, name);
|
|
126
|
-
if (!entry)
|
|
133
|
+
if (!entry)
|
|
127
134
|
return null;
|
|
128
|
-
}
|
|
129
135
|
const [componentKey, component] = entry;
|
|
130
136
|
return createComponent(id, componentKey, component.type);
|
|
131
137
|
},
|
|
138
|
+
addComponent(type, properties) {
|
|
139
|
+
var _a;
|
|
140
|
+
const def = getComponentDef(type);
|
|
141
|
+
const key = type.toLowerCase();
|
|
142
|
+
const props = (_a = properties !== null && properties !== void 0 ? properties : def === null || def === void 0 ? void 0 : def.defaultProperties) !== null && _a !== void 0 ? _a : {};
|
|
143
|
+
adapter.updateNode(id, node => (Object.assign(Object.assign({}, node), { components: Object.assign(Object.assign({}, node.components), { [key]: { type, properties: props } }) })));
|
|
144
|
+
return createComponent(id, key, type);
|
|
145
|
+
},
|
|
146
|
+
removeComponent(name) {
|
|
147
|
+
adapter.updateNode(id, node => {
|
|
148
|
+
var _a;
|
|
149
|
+
const entry = findComponentEntry(node, name);
|
|
150
|
+
if (!entry)
|
|
151
|
+
return node;
|
|
152
|
+
const [key] = entry;
|
|
153
|
+
const _b = (_a = node.components) !== null && _a !== void 0 ? _a : {}, _c = key, _ = _b[_c], rest = __rest(_b, [typeof _c === "symbol" ? _c : _c + ""]);
|
|
154
|
+
return Object.assign(Object.assign({}, node), { components: rest });
|
|
155
|
+
});
|
|
156
|
+
},
|
|
157
|
+
destroy() {
|
|
158
|
+
adapter.removeNode(id);
|
|
159
|
+
},
|
|
132
160
|
};
|
|
133
161
|
}
|
|
134
162
|
function update(idOrUpdates, mutate) {
|
|
@@ -148,8 +176,25 @@ export function createScene(adapter) {
|
|
|
148
176
|
get rootId() {
|
|
149
177
|
return adapter.getRootId();
|
|
150
178
|
},
|
|
151
|
-
find
|
|
179
|
+
find(nameOrId) {
|
|
180
|
+
// Try by ID first, then by name
|
|
181
|
+
if (adapter.getNode(nameOrId))
|
|
182
|
+
return createNode(nameOrId);
|
|
183
|
+
const foundId = adapter.findByName(nameOrId);
|
|
184
|
+
return foundId ? createNode(foundId) : null;
|
|
185
|
+
},
|
|
152
186
|
get: getNode,
|
|
187
|
+
create(name, components, options) {
|
|
188
|
+
const node = {
|
|
189
|
+
id: crypto.randomUUID(),
|
|
190
|
+
name,
|
|
191
|
+
components: Object.assign({ transform: {
|
|
192
|
+
type: "Transform",
|
|
193
|
+
properties: { position: [0, 0, 0], rotation: [0, 0, 0], scale: [1, 1, 1] },
|
|
194
|
+
} }, components),
|
|
195
|
+
};
|
|
196
|
+
return createNode(adapter.addNode(node, options));
|
|
197
|
+
},
|
|
153
198
|
update,
|
|
154
199
|
add(node, options) {
|
|
155
200
|
return createNode(adapter.addNode(node, options));
|
|
@@ -129,6 +129,15 @@ export const tree = {
|
|
|
129
129
|
background: colors.accentBg,
|
|
130
130
|
borderBottomColor: colors.accentBorder,
|
|
131
131
|
},
|
|
132
|
+
iconButton: {
|
|
133
|
+
background: 'none',
|
|
134
|
+
border: 'none',
|
|
135
|
+
cursor: 'pointer',
|
|
136
|
+
padding: '0 4px',
|
|
137
|
+
fontSize: 14,
|
|
138
|
+
opacity: 0.7,
|
|
139
|
+
color: 'inherit',
|
|
140
|
+
},
|
|
132
141
|
};
|
|
133
142
|
export const menu = {
|
|
134
143
|
container: {
|
|
@@ -17,3 +17,16 @@ export interface ComponentData {
|
|
|
17
17
|
type: string;
|
|
18
18
|
properties: Record<string, any>;
|
|
19
19
|
}
|
|
20
|
+
type ComponentHost = {
|
|
21
|
+
components?: Record<string, {
|
|
22
|
+
type?: string;
|
|
23
|
+
properties?: Record<string, any>;
|
|
24
|
+
} | undefined>;
|
|
25
|
+
};
|
|
26
|
+
/** Find a component on a node by type name or key (e.g. "Model", "transform"). */
|
|
27
|
+
export declare function findComponent(node: ComponentHost | null | undefined, name: string): ComponentData | undefined;
|
|
28
|
+
/** Find a component entry [key, data] by type name or key — use when you need the key for mutations. */
|
|
29
|
+
export declare function findComponentEntry(node: ComponentHost | null | undefined, name: string): [string, ComponentData] | undefined;
|
|
30
|
+
/** Check if a node has a component of the given type. */
|
|
31
|
+
export declare function hasComponent(node: ComponentHost | null | undefined, typeName: string): boolean;
|
|
32
|
+
export {};
|
|
@@ -1 +1,28 @@
|
|
|
1
|
-
|
|
1
|
+
/** Find a component on a node by type name or key (e.g. "Model", "transform"). */
|
|
2
|
+
export function findComponent(node, name) {
|
|
3
|
+
var _a;
|
|
4
|
+
return (_a = findComponentEntry(node, name)) === null || _a === void 0 ? void 0 : _a[1];
|
|
5
|
+
}
|
|
6
|
+
/** Find a component entry [key, data] by type name or key — use when you need the key for mutations. */
|
|
7
|
+
export function findComponentEntry(node, name) {
|
|
8
|
+
if (!(node === null || node === void 0 ? void 0 : node.components))
|
|
9
|
+
return undefined;
|
|
10
|
+
// Direct key match
|
|
11
|
+
const direct = node.components[name];
|
|
12
|
+
if (direct === null || direct === void 0 ? void 0 : direct.type)
|
|
13
|
+
return [name, direct];
|
|
14
|
+
// Case-insensitive key + type scan
|
|
15
|
+
const normalized = name.toLowerCase();
|
|
16
|
+
for (const [key, comp] of Object.entries(node.components)) {
|
|
17
|
+
if (!(comp === null || comp === void 0 ? void 0 : comp.type))
|
|
18
|
+
continue;
|
|
19
|
+
if (key.toLowerCase() === normalized || comp.type.toLowerCase() === normalized) {
|
|
20
|
+
return [key, comp];
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
/** Check if a node has a component of the given type. */
|
|
26
|
+
export function hasComponent(node, typeName) {
|
|
27
|
+
return findComponentEntry(node, typeName) !== undefined;
|
|
28
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared click-valid pattern: tracks pointer down → move → up
|
|
3
|
+
* to distinguish clicks from drags. Returns handlers to spread onto a group.
|
|
4
|
+
*/
|
|
5
|
+
export declare function useClickValid(enabled: boolean, onValidClick: (e: any) => void): {
|
|
6
|
+
onPointerDown: undefined;
|
|
7
|
+
onPointerMove: undefined;
|
|
8
|
+
onPointerUp: undefined;
|
|
9
|
+
} | {
|
|
10
|
+
onPointerDown: (e: any) => void;
|
|
11
|
+
onPointerMove: () => void;
|
|
12
|
+
onPointerUp: (e: any) => void;
|
|
13
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useRef } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Shared click-valid pattern: tracks pointer down → move → up
|
|
4
|
+
* to distinguish clicks from drags. Returns handlers to spread onto a group.
|
|
5
|
+
*/
|
|
6
|
+
export function useClickValid(enabled, onValidClick) {
|
|
7
|
+
const clickValid = useRef(false);
|
|
8
|
+
if (!enabled)
|
|
9
|
+
return { onPointerDown: undefined, onPointerMove: undefined, onPointerUp: undefined };
|
|
10
|
+
return {
|
|
11
|
+
onPointerDown: (e) => { e.stopPropagation(); clickValid.current = true; },
|
|
12
|
+
onPointerMove: () => { clickValid.current = false; },
|
|
13
|
+
onPointerUp: (e) => {
|
|
14
|
+
if (clickValid.current) {
|
|
15
|
+
e.stopPropagation();
|
|
16
|
+
onValidClick(e);
|
|
17
|
+
}
|
|
18
|
+
clickValid.current = false;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -21,6 +21,8 @@ export declare function decompose(m: Matrix4): {
|
|
|
21
21
|
rotation: [number, number, number];
|
|
22
22
|
scale: [number, number, number];
|
|
23
23
|
};
|
|
24
|
+
/** Build a local Matrix4 from position/rotation/scale arrays. */
|
|
25
|
+
export declare function composeTransform(position?: [number, number, number], rotation?: [number, number, number], scale?: [number, number, number]): Matrix4;
|
|
24
26
|
/** Compute the parent world matrix for a node using the normalized store data */
|
|
25
27
|
export declare function computeParentWorldMatrix(state: {
|
|
26
28
|
nodesById: Record<string, {
|
|
@@ -7,6 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
+
import { findComponent } from "./types";
|
|
10
11
|
import { GLTFExporter } from 'three/examples/jsm/exporters/GLTFExporter.js';
|
|
11
12
|
import { Box3, Euler, Matrix4, PerspectiveCamera, Quaternion, Vector3 } from 'three';
|
|
12
13
|
/** Save scene JSON, showing a Save As dialog when supported */
|
|
@@ -129,9 +130,13 @@ export function decompose(m) {
|
|
|
129
130
|
scale: [s.x, s.y, s.z],
|
|
130
131
|
};
|
|
131
132
|
}
|
|
133
|
+
/** Build a local Matrix4 from position/rotation/scale arrays. */
|
|
134
|
+
export function composeTransform(position = [0, 0, 0], rotation = [0, 0, 0], scale = [1, 1, 1]) {
|
|
135
|
+
return new Matrix4().compose(new Vector3(...position), new Quaternion().setFromEuler(new Euler(...rotation)), new Vector3(...scale));
|
|
136
|
+
}
|
|
132
137
|
/** Compute the parent world matrix for a node using the normalized store data */
|
|
133
138
|
export function computeParentWorldMatrix(state, targetId) {
|
|
134
|
-
var _a
|
|
139
|
+
var _a;
|
|
135
140
|
const parentWorld = new Matrix4();
|
|
136
141
|
const chain = [];
|
|
137
142
|
let currentId = state.parentIdById[targetId];
|
|
@@ -140,8 +145,8 @@ export function computeParentWorldMatrix(state, targetId) {
|
|
|
140
145
|
currentId = state.parentIdById[currentId];
|
|
141
146
|
}
|
|
142
147
|
for (const nodeId of chain) {
|
|
143
|
-
const transform = (
|
|
144
|
-
parentWorld.multiply(
|
|
148
|
+
const transform = (_a = findComponent(state.nodesById[nodeId], "Transform")) === null || _a === void 0 ? void 0 : _a.properties;
|
|
149
|
+
parentWorld.multiply(composeTransform(transform === null || transform === void 0 ? void 0 : transform.position, transform === null || transform === void 0 ? void 0 : transform.rotation, transform === null || transform === void 0 ? void 0 : transform.scale));
|
|
145
150
|
}
|
|
146
151
|
return parentWorld;
|
|
147
152
|
}
|
|
@@ -150,46 +155,40 @@ export function regenerateIds(node) {
|
|
|
150
155
|
var _a;
|
|
151
156
|
return Object.assign(Object.assign({}, node), { id: crypto.randomUUID(), children: (_a = node.children) === null || _a === void 0 ? void 0 : _a.map(regenerateIds) });
|
|
152
157
|
}
|
|
153
|
-
|
|
154
|
-
export function createModelNode(filename, name) {
|
|
158
|
+
function createNode(path, name, extraComponents) {
|
|
155
159
|
return {
|
|
156
160
|
id: crypto.randomUUID(),
|
|
157
|
-
name: name !== null && name !== void 0 ? name :
|
|
158
|
-
components: {
|
|
159
|
-
transform: {
|
|
161
|
+
name: name !== null && name !== void 0 ? name : path.replace(/^.*[\/]/, '').replace(/\.[^.]+$/, ''),
|
|
162
|
+
components: Object.assign({ transform: {
|
|
160
163
|
type: 'Transform',
|
|
161
164
|
properties: { position: [0, 0, 0], rotation: [0, 0, 0], scale: [1, 1, 1] }
|
|
162
|
-
},
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
165
|
+
} }, extraComponents)
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/** Create a GameObject node for a 3D model file */
|
|
169
|
+
export function createModelNode(filename, name) {
|
|
170
|
+
return createNode(filename, name, {
|
|
171
|
+
model: {
|
|
172
|
+
type: 'Model',
|
|
173
|
+
properties: {
|
|
174
|
+
filename,
|
|
175
|
+
instanced: false,
|
|
176
|
+
repeat: false,
|
|
177
|
+
repeatAxes: [{ axis: 'x', count: 1, offset: 1 }]
|
|
171
178
|
}
|
|
172
179
|
}
|
|
173
|
-
};
|
|
180
|
+
});
|
|
174
181
|
}
|
|
175
182
|
/** Create a GameObject node for an image as a textured plane */
|
|
176
183
|
export function createImageNode(texturePath, name) {
|
|
177
|
-
return {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
185
|
-
geometry: {
|
|
186
|
-
type: 'Geometry',
|
|
187
|
-
properties: { geometryType: 'plane', args: [1, 1] }
|
|
188
|
-
},
|
|
189
|
-
material: {
|
|
190
|
-
type: 'Material',
|
|
191
|
-
properties: { color: '#ffffff', texture: texturePath }
|
|
192
|
-
}
|
|
184
|
+
return createNode(texturePath, name, {
|
|
185
|
+
geometry: {
|
|
186
|
+
type: 'Geometry',
|
|
187
|
+
properties: { geometryType: 'plane', args: [1, 1] }
|
|
188
|
+
},
|
|
189
|
+
material: {
|
|
190
|
+
type: 'Material',
|
|
191
|
+
properties: { color: '#ffffff', texture: texturePath }
|
|
193
192
|
}
|
|
194
|
-
};
|
|
193
|
+
});
|
|
195
194
|
}
|
package/package.json
CHANGED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export interface EditorContextType {
|
|
2
|
-
editMode: boolean;
|
|
3
|
-
transformMode: "translate" | "rotate" | "scale";
|
|
4
|
-
setTransformMode: (mode: "translate" | "rotate" | "scale") => void;
|
|
5
|
-
scaleSnap: number;
|
|
6
|
-
setScaleSnap: (resolution: number) => void;
|
|
7
|
-
positionSnap: number;
|
|
8
|
-
setPositionSnap: (resolution: number) => void;
|
|
9
|
-
rotationSnap: number;
|
|
10
|
-
setRotationSnap: (resolution: number) => void;
|
|
11
|
-
onFocusNode?: (nodeId: string) => void;
|
|
12
|
-
onScreenshot?: () => void;
|
|
13
|
-
onExportGLB?: () => void;
|
|
14
|
-
}
|
|
15
|
-
export declare const EditorContext: import("react").Context<EditorContextType | null>;
|
|
16
|
-
export declare function useEditorContext(): EditorContextType;
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { createContext, useContext } from "react";
|
|
2
|
-
export const EditorContext = createContext(null);
|
|
3
|
-
export function useEditorContext() {
|
|
4
|
-
const context = useContext(EditorContext);
|
|
5
|
-
if (!context) {
|
|
6
|
-
throw new Error("useEditorContext must be used within EditorContext.Provider");
|
|
7
|
-
}
|
|
8
|
-
return context;
|
|
9
|
-
}
|