react-three-game 0.0.39 → 0.0.41
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/.claude/settings.local.json +9 -0
- package/README.md +17 -0
- package/dist/index.umd.js +438 -0
- package/package.json +6 -3
- package/src/shared/GameCanvas.tsx +0 -2
- package/src/tools/assetviewer/page.tsx +31 -26
- package/src/tools/dragdrop/DragDropLoader.tsx +0 -1
- package/src/tools/prefabeditor/PrefabEditor.tsx +0 -2
- package/src/tools/prefabeditor/PrefabRoot.tsx +0 -2
- package/src/tools/prefabeditor/components/MaterialComponent.tsx +25 -9
- package/src/tools/prefabeditor/components/ModelComponent.tsx +25 -10
- package/src/tools/prefabeditor/styles.ts +0 -1
- package/vite.config.ts +34 -0
- package/dist/helpers/SoundManager.d.ts +0 -35
- package/dist/helpers/SoundManager.js +0 -93
- package/dist/helpers/index.d.ts +0 -35
- package/dist/helpers/index.js +0 -44
- package/dist/index.d.ts +0 -14
- package/dist/index.js +0 -14
- package/dist/shared/GameCanvas.d.ts +0 -9
- package/dist/shared/GameCanvas.js +0 -48
- package/dist/shared/extend-three.d.ts +0 -1
- package/dist/shared/extend-three.js +0 -13
- package/dist/tools/assetviewer/page.d.ts +0 -23
- package/dist/tools/assetviewer/page.js +0 -159
- package/dist/tools/dragdrop/DragDropLoader.d.ts +0 -9
- package/dist/tools/dragdrop/DragDropLoader.js +0 -79
- package/dist/tools/dragdrop/modelLoader.d.ts +0 -7
- package/dist/tools/dragdrop/modelLoader.js +0 -52
- package/dist/tools/dragdrop/page.d.ts +0 -1
- package/dist/tools/dragdrop/page.js +0 -11
- package/dist/tools/loading/GameWithLoader.d.ts +0 -6
- package/dist/tools/loading/GameWithLoader.js +0 -8
- package/dist/tools/loading/loading.d.ts +0 -2
- package/dist/tools/loading/loading.js +0 -38
- package/dist/tools/prefabeditor/EditorContext.d.ts +0 -11
- package/dist/tools/prefabeditor/EditorContext.js +0 -9
- package/dist/tools/prefabeditor/EditorTree.d.ts +0 -12
- package/dist/tools/prefabeditor/EditorTree.js +0 -150
- package/dist/tools/prefabeditor/EditorUI.d.ts +0 -14
- package/dist/tools/prefabeditor/EditorUI.js +0 -71
- package/dist/tools/prefabeditor/EventSystem.d.ts +0 -7
- package/dist/tools/prefabeditor/EventSystem.js +0 -23
- package/dist/tools/prefabeditor/ExportHelper.d.ts +0 -7
- package/dist/tools/prefabeditor/ExportHelper.js +0 -55
- package/dist/tools/prefabeditor/InstanceProvider.d.ts +0 -30
- package/dist/tools/prefabeditor/InstanceProvider.js +0 -254
- package/dist/tools/prefabeditor/PrefabEditor.d.ts +0 -16
- package/dist/tools/prefabeditor/PrefabEditor.js +0 -141
- package/dist/tools/prefabeditor/PrefabRoot.d.ts +0 -28
- package/dist/tools/prefabeditor/PrefabRoot.js +0 -294
- package/dist/tools/prefabeditor/components/ComponentRegistry.d.ts +0 -18
- package/dist/tools/prefabeditor/components/ComponentRegistry.js +0 -13
- package/dist/tools/prefabeditor/components/DirectionalLightComponent.d.ts +0 -3
- package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +0 -78
- package/dist/tools/prefabeditor/components/GeometryComponent.d.ts +0 -3
- package/dist/tools/prefabeditor/components/GeometryComponent.js +0 -66
- package/dist/tools/prefabeditor/components/Input.d.ts +0 -20
- package/dist/tools/prefabeditor/components/Input.js +0 -129
- package/dist/tools/prefabeditor/components/MaterialComponent.d.ts +0 -3
- package/dist/tools/prefabeditor/components/MaterialComponent.js +0 -96
- package/dist/tools/prefabeditor/components/ModelComponent.d.ts +0 -3
- package/dist/tools/prefabeditor/components/ModelComponent.js +0 -53
- package/dist/tools/prefabeditor/components/PhysicsComponent.d.ts +0 -10
- package/dist/tools/prefabeditor/components/PhysicsComponent.js +0 -33
- package/dist/tools/prefabeditor/components/RotatorComponent.d.ts +0 -3
- package/dist/tools/prefabeditor/components/RotatorComponent.js +0 -42
- package/dist/tools/prefabeditor/components/SpotLightComponent.d.ts +0 -3
- package/dist/tools/prefabeditor/components/SpotLightComponent.js +0 -49
- package/dist/tools/prefabeditor/components/TransformComponent.d.ts +0 -3
- package/dist/tools/prefabeditor/components/TransformComponent.js +0 -42
- package/dist/tools/prefabeditor/components/index.d.ts +0 -2
- package/dist/tools/prefabeditor/components/index.js +0 -16
- package/dist/tools/prefabeditor/hooks/useModelLoader.d.ts +0 -10
- package/dist/tools/prefabeditor/hooks/useModelLoader.js +0 -40
- package/dist/tools/prefabeditor/page.d.ts +0 -1
- package/dist/tools/prefabeditor/page.js +0 -5
- package/dist/tools/prefabeditor/styles.d.ts +0 -1809
- package/dist/tools/prefabeditor/styles.js +0 -168
- package/dist/tools/prefabeditor/types.d.ts +0 -19
- package/dist/tools/prefabeditor/types.js +0 -1
- package/dist/tools/prefabeditor/utils.d.ts +0 -26
- package/dist/tools/prefabeditor/utils.js +0 -131
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { TextureListViewer } from '../../assetviewer/page';
|
|
3
|
-
import { useEffect, useState } from 'react';
|
|
4
|
-
import { Input, Label } from './Input';
|
|
5
|
-
import { useMemo } from 'react';
|
|
6
|
-
import { DoubleSide, RepeatWrapping, ClampToEdgeWrapping, SRGBColorSpace, NearestFilter, LinearFilter, NearestMipmapNearestFilter, NearestMipmapLinearFilter, LinearMipmapNearestFilter, LinearMipmapLinearFilter } from 'three';
|
|
7
|
-
function MaterialComponentEditor({ component, onUpdate, basePath = "" }) {
|
|
8
|
-
var _a, _b, _c, _d;
|
|
9
|
-
const [textureFiles, setTextureFiles] = useState([]);
|
|
10
|
-
useEffect(() => {
|
|
11
|
-
const base = basePath ? `${basePath}/` : '';
|
|
12
|
-
fetch(`/${base}textures/manifest.json`)
|
|
13
|
-
.then(r => r.json())
|
|
14
|
-
.then(data => setTextureFiles(Array.isArray(data) ? data : data.files || []))
|
|
15
|
-
.catch(console.error);
|
|
16
|
-
}, [basePath]);
|
|
17
|
-
const textInputStyle = {
|
|
18
|
-
flex: 1,
|
|
19
|
-
backgroundColor: 'rgba(0, 0, 0, 0.4)',
|
|
20
|
-
border: '1px solid rgba(34, 211, 238, 0.3)',
|
|
21
|
-
padding: '2px 4px',
|
|
22
|
-
fontSize: '10px',
|
|
23
|
-
color: 'rgba(165, 243, 252, 1)',
|
|
24
|
-
fontFamily: 'monospace',
|
|
25
|
-
outline: 'none',
|
|
26
|
-
};
|
|
27
|
-
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 4 }, children: [_jsxs("div", { children: [_jsx(Label, { children: "Color" }), _jsxs("div", { style: { display: 'flex', gap: 2 }, children: [_jsx("input", { type: "color", style: { height: 20, width: 20, backgroundColor: 'transparent', border: 'none', cursor: 'pointer' }, value: component.properties.color, onChange: e => onUpdate({ color: e.target.value }) }), _jsx("input", { type: "text", style: textInputStyle, value: component.properties.color, onChange: e => onUpdate({ color: e.target.value }) })] })] }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 4 }, children: [_jsx("input", { type: "checkbox", style: { width: 12, height: 12 }, checked: component.properties.wireframe || false, onChange: e => onUpdate({ wireframe: e.target.checked }) }), _jsx("label", { style: { fontSize: '9px', color: 'rgba(34, 211, 238, 0.6)' }, children: "Wireframe" })] }), _jsxs("div", { children: [_jsx(Label, { children: "Texture" }), _jsx("div", { style: { maxHeight: 128, overflowY: 'auto' }, children: _jsx(TextureListViewer, { files: textureFiles, selected: component.properties.texture || undefined, onSelect: file => onUpdate({ texture: file }), basePath: basePath }) })] }), component.properties.texture && (_jsxs("div", { style: { borderTop: '1px solid rgba(34, 211, 238, 0.2)', paddingTop: 4, marginTop: 4 }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 4, marginBottom: 4 }, children: [_jsx("input", { type: "checkbox", style: { width: 12, height: 12 }, checked: component.properties.repeat || false, onChange: e => onUpdate({ repeat: e.target.checked }) }), _jsx("label", { style: { fontSize: '9px', color: 'rgba(34, 211, 238, 0.6)' }, children: "Repeat Texture" })] }), component.properties.repeat && (_jsxs("div", { children: [_jsx(Label, { children: "Repeat (X, Y)" }), _jsxs("div", { style: { display: 'flex', gap: 2 }, children: [_jsx(Input, { value: (_b = (_a = component.properties.repeatCount) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : 1, onChange: value => {
|
|
28
|
-
var _a, _b;
|
|
29
|
-
const y = (_b = (_a = component.properties.repeatCount) === null || _a === void 0 ? void 0 : _a[1]) !== null && _b !== void 0 ? _b : 1;
|
|
30
|
-
onUpdate({ repeatCount: [value, y] });
|
|
31
|
-
} }), _jsx(Input, { value: (_d = (_c = component.properties.repeatCount) === null || _c === void 0 ? void 0 : _c[1]) !== null && _d !== void 0 ? _d : 1, onChange: value => {
|
|
32
|
-
var _a, _b;
|
|
33
|
-
const x = (_b = (_a = component.properties.repeatCount) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : 1;
|
|
34
|
-
onUpdate({ repeatCount: [x, value] });
|
|
35
|
-
} })] })] })), _jsxs("div", { style: { marginTop: 4 }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 4, marginBottom: 4 }, children: [_jsx("input", { type: "checkbox", style: { width: 12, height: 12 }, checked: component.properties.generateMipmaps !== false, onChange: e => onUpdate({ generateMipmaps: e.target.checked }) }), _jsx("label", { style: { fontSize: '9px', color: 'rgba(34, 211, 238, 0.6)' }, children: "Generate Mipmaps" })] }), _jsxs("div", { children: [_jsx(Label, { children: "Min Filter" }), _jsxs("select", { style: Object.assign(Object.assign({}, textInputStyle), { width: '100%', cursor: 'pointer' }), value: component.properties.minFilter || 'LinearMipmapLinearFilter', onChange: e => onUpdate({ minFilter: e.target.value }), children: [_jsx("option", { value: "NearestFilter", children: "Nearest" }), _jsx("option", { value: "NearestMipmapNearestFilter", children: "Nearest Mipmap Nearest" }), _jsx("option", { value: "NearestMipmapLinearFilter", children: "Nearest Mipmap Linear" }), _jsx("option", { value: "LinearFilter", children: "Linear" }), _jsx("option", { value: "LinearMipmapNearestFilter", children: "Linear Mipmap Nearest" }), _jsx("option", { value: "LinearMipmapLinearFilter", children: "Linear Mipmap Linear (Default)" })] })] }), _jsxs("div", { style: { marginTop: 4 }, children: [_jsx(Label, { children: "Mag Filter" }), _jsxs("select", { style: Object.assign(Object.assign({}, textInputStyle), { width: '100%', cursor: 'pointer' }), value: component.properties.magFilter || 'LinearFilter', onChange: e => onUpdate({ magFilter: e.target.value }), children: [_jsx("option", { value: "NearestFilter", children: "Nearest" }), _jsx("option", { value: "LinearFilter", children: "Linear (Default)" })] })] })] })] }))] }));
|
|
36
|
-
}
|
|
37
|
-
;
|
|
38
|
-
// View for Material component
|
|
39
|
-
function MaterialComponentView({ properties, loadedTextures }) {
|
|
40
|
-
var _a;
|
|
41
|
-
const textureName = properties === null || properties === void 0 ? void 0 : properties.texture;
|
|
42
|
-
const repeat = properties === null || properties === void 0 ? void 0 : properties.repeat;
|
|
43
|
-
const repeatCount = properties === null || properties === void 0 ? void 0 : properties.repeatCount;
|
|
44
|
-
const generateMipmaps = (properties === null || properties === void 0 ? void 0 : properties.generateMipmaps) !== false;
|
|
45
|
-
const minFilter = (properties === null || properties === void 0 ? void 0 : properties.minFilter) || 'LinearMipmapLinearFilter';
|
|
46
|
-
const magFilter = (properties === null || properties === void 0 ? void 0 : properties.magFilter) || 'LinearFilter';
|
|
47
|
-
const texture = textureName && loadedTextures ? loadedTextures[textureName] : undefined;
|
|
48
|
-
const minFilterMap = {
|
|
49
|
-
NearestFilter,
|
|
50
|
-
LinearFilter,
|
|
51
|
-
NearestMipmapNearestFilter,
|
|
52
|
-
NearestMipmapLinearFilter,
|
|
53
|
-
LinearMipmapNearestFilter,
|
|
54
|
-
LinearMipmapLinearFilter
|
|
55
|
-
};
|
|
56
|
-
const magFilterMap = {
|
|
57
|
-
NearestFilter,
|
|
58
|
-
LinearFilter
|
|
59
|
-
};
|
|
60
|
-
const finalTexture = useMemo(() => {
|
|
61
|
-
var _a, _b;
|
|
62
|
-
if (!texture)
|
|
63
|
-
return undefined;
|
|
64
|
-
const t = texture.clone();
|
|
65
|
-
if (repeat) {
|
|
66
|
-
t.wrapS = t.wrapT = RepeatWrapping;
|
|
67
|
-
if (repeatCount)
|
|
68
|
-
t.repeat.set(repeatCount[0], repeatCount[1]);
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
t.wrapS = t.wrapT = ClampToEdgeWrapping;
|
|
72
|
-
t.repeat.set(1, 1);
|
|
73
|
-
}
|
|
74
|
-
t.colorSpace = SRGBColorSpace;
|
|
75
|
-
t.generateMipmaps = generateMipmaps;
|
|
76
|
-
t.minFilter = (_a = minFilterMap[minFilter]) !== null && _a !== void 0 ? _a : LinearMipmapLinearFilter;
|
|
77
|
-
t.magFilter = (_b = magFilterMap[magFilter]) !== null && _b !== void 0 ? _b : LinearFilter;
|
|
78
|
-
t.needsUpdate = true;
|
|
79
|
-
return t;
|
|
80
|
-
}, [texture, repeat, repeatCount === null || repeatCount === void 0 ? void 0 : repeatCount[0], repeatCount === null || repeatCount === void 0 ? void 0 : repeatCount[1], generateMipmaps, minFilter, magFilter]);
|
|
81
|
-
if (!properties) {
|
|
82
|
-
return _jsx("meshStandardMaterial", { color: "red", wireframe: true });
|
|
83
|
-
}
|
|
84
|
-
const { color, wireframe = false } = properties;
|
|
85
|
-
return (_jsx("meshStandardMaterial", { color: color, wireframe: wireframe, map: finalTexture, transparent: !!finalTexture, side: DoubleSide }, (_a = finalTexture === null || finalTexture === void 0 ? void 0 : finalTexture.uuid) !== null && _a !== void 0 ? _a : 'no-texture'));
|
|
86
|
-
}
|
|
87
|
-
const MaterialComponent = {
|
|
88
|
-
name: 'Material',
|
|
89
|
-
Editor: MaterialComponentEditor,
|
|
90
|
-
View: MaterialComponentView,
|
|
91
|
-
defaultProperties: {
|
|
92
|
-
color: '#ffffff',
|
|
93
|
-
wireframe: false
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
export default MaterialComponent;
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { ModelListViewer } from '../../assetviewer/page';
|
|
3
|
-
import { useEffect, useState, useMemo } from 'react';
|
|
4
|
-
import { Label } from './Input';
|
|
5
|
-
function ModelComponentEditor({ component, node, onUpdate, basePath = "" }) {
|
|
6
|
-
const [modelFiles, setModelFiles] = useState([]);
|
|
7
|
-
useEffect(() => {
|
|
8
|
-
const base = basePath ? `${basePath}/` : '';
|
|
9
|
-
fetch(`/${base}models/manifest.json`)
|
|
10
|
-
.then(r => r.json())
|
|
11
|
-
.then(data => setModelFiles(Array.isArray(data) ? data : data.files || []))
|
|
12
|
-
.catch(console.error);
|
|
13
|
-
}, [basePath]);
|
|
14
|
-
const handleModelSelect = (file) => {
|
|
15
|
-
// Remove leading slash for prefab compatibility
|
|
16
|
-
const filename = file.startsWith('/') ? file.slice(1) : file;
|
|
17
|
-
onUpdate({ 'filename': filename });
|
|
18
|
-
};
|
|
19
|
-
return _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 4 }, children: [_jsxs("div", { children: [_jsx(Label, { children: "Model" }), _jsx("div", { style: { maxHeight: 128, overflowY: 'auto' }, children: _jsx(ModelListViewer, { files: modelFiles, selected: component.properties.filename ? `/${component.properties.filename}` : undefined, onSelect: handleModelSelect, basePath: basePath }, node === null || node === void 0 ? void 0 : node.id) })] }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 4 }, children: [_jsx("input", { type: "checkbox", id: "instanced-checkbox", checked: component.properties.instanced || false, onChange: e => onUpdate({ instanced: e.target.checked }), style: { width: 12, height: 12 } }), _jsx("label", { htmlFor: "instanced-checkbox", style: { fontSize: '9px', color: 'rgba(34, 211, 238, 0.6)' }, children: "Instanced" })] })] });
|
|
20
|
-
}
|
|
21
|
-
// View for Model component
|
|
22
|
-
function ModelComponentView({ properties, loadedModels, children }) {
|
|
23
|
-
// Instanced models are handled elsewhere (GameInstance), so only render non-instanced here
|
|
24
|
-
if (!properties.filename || properties.instanced)
|
|
25
|
-
return _jsx(_Fragment, { children: children });
|
|
26
|
-
const sourceModel = loadedModels === null || loadedModels === void 0 ? void 0 : loadedModels[properties.filename];
|
|
27
|
-
// Clone model once and set up shadows - memoized to avoid cloning on every render
|
|
28
|
-
const clonedModel = useMemo(() => {
|
|
29
|
-
if (!sourceModel)
|
|
30
|
-
return null;
|
|
31
|
-
const clone = sourceModel.clone();
|
|
32
|
-
clone.traverse((obj) => {
|
|
33
|
-
if (obj.isMesh) {
|
|
34
|
-
obj.castShadow = true;
|
|
35
|
-
obj.receiveShadow = true;
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
return clone;
|
|
39
|
-
}, [sourceModel]);
|
|
40
|
-
if (!clonedModel)
|
|
41
|
-
return _jsx(_Fragment, { children: children });
|
|
42
|
-
return _jsx("primitive", { object: clonedModel, children: children });
|
|
43
|
-
}
|
|
44
|
-
const ModelComponent = {
|
|
45
|
-
name: 'Model',
|
|
46
|
-
Editor: ModelComponentEditor,
|
|
47
|
-
View: ModelComponentView,
|
|
48
|
-
defaultProperties: {
|
|
49
|
-
filename: '',
|
|
50
|
-
instanced: false
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
export default ModelComponent;
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { Component } from "./ComponentRegistry";
|
|
2
|
-
export interface PhysicsProps {
|
|
3
|
-
type: "fixed" | "dynamic";
|
|
4
|
-
collider?: string;
|
|
5
|
-
mass?: number;
|
|
6
|
-
restitution?: number;
|
|
7
|
-
friction?: number;
|
|
8
|
-
}
|
|
9
|
-
declare const PhysicsComponent: Component;
|
|
10
|
-
export default PhysicsComponent;
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { RigidBody } from "@react-three/rapier";
|
|
3
|
-
import { Label } from "./Input";
|
|
4
|
-
function PhysicsComponentEditor({ component, onUpdate }) {
|
|
5
|
-
const { type = 'dynamic', collider = 'hull' } = component.properties;
|
|
6
|
-
const selectStyle = {
|
|
7
|
-
width: '100%',
|
|
8
|
-
backgroundColor: 'rgba(0, 0, 0, 0.4)',
|
|
9
|
-
border: '1px solid rgba(34, 211, 238, 0.3)',
|
|
10
|
-
padding: '2px 4px',
|
|
11
|
-
fontSize: '10px',
|
|
12
|
-
color: 'rgba(165, 243, 252, 1)',
|
|
13
|
-
fontFamily: 'monospace',
|
|
14
|
-
outline: 'none',
|
|
15
|
-
};
|
|
16
|
-
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: [_jsxs("div", { children: [_jsx(Label, { children: "Type" }), _jsxs("select", { style: selectStyle, value: type, onChange: e => onUpdate({ type: e.target.value }), children: [_jsx("option", { value: "dynamic", children: "Dynamic" }), _jsx("option", { value: "fixed", children: "Fixed" })] })] }), _jsxs("div", { children: [_jsx(Label, { children: "Collider" }), _jsxs("select", { style: selectStyle, value: collider, onChange: e => onUpdate({ collider: e.target.value }), children: [_jsx("option", { value: "hull", children: "Hull (convex)" }), _jsx("option", { value: "trimesh", children: "Trimesh (exact)" }), _jsx("option", { value: "cuboid", children: "Cuboid (box)" }), _jsx("option", { value: "ball", children: "Ball (sphere)" })] })] })] }));
|
|
17
|
-
}
|
|
18
|
-
function PhysicsComponentView({ properties, children, position, rotation, scale, editMode }) {
|
|
19
|
-
const colliders = properties.collider || (properties.type === 'fixed' ? 'trimesh' : 'hull');
|
|
20
|
-
// In edit mode, include position/rotation in key to force remount when transform changes
|
|
21
|
-
// This ensures the RigidBody debug visualization updates even when physics is paused
|
|
22
|
-
const rbKey = editMode
|
|
23
|
-
? `${properties.type || 'dynamic'}_${colliders}_${position === null || position === void 0 ? void 0 : position.join(',')}_${rotation === null || rotation === void 0 ? void 0 : rotation.join(',')}`
|
|
24
|
-
: `${properties.type || 'dynamic'}_${colliders}`;
|
|
25
|
-
return (_jsx(RigidBody, { type: properties.type, colliders: colliders, position: position, rotation: rotation, scale: scale, children: children }, rbKey));
|
|
26
|
-
}
|
|
27
|
-
const PhysicsComponent = {
|
|
28
|
-
name: 'Physics',
|
|
29
|
-
Editor: PhysicsComponentEditor,
|
|
30
|
-
View: PhysicsComponentView,
|
|
31
|
-
defaultProperties: { type: 'dynamic', collider: 'hull' }
|
|
32
|
-
};
|
|
33
|
-
export default PhysicsComponent;
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useFrame } from "@react-three/fiber";
|
|
3
|
-
import { useRef } from "react";
|
|
4
|
-
function RotatorComponentEditor({ component, onUpdate }) {
|
|
5
|
-
var _a, _b;
|
|
6
|
-
const props = {
|
|
7
|
-
speed: (_a = component.properties.speed) !== null && _a !== void 0 ? _a : 1.0,
|
|
8
|
-
axis: (_b = component.properties.axis) !== null && _b !== void 0 ? _b : 'y'
|
|
9
|
-
};
|
|
10
|
-
return _jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("div", { children: [_jsx("label", { className: "block text-[9px] text-cyan-400/60 uppercase tracking-wider mb-0.5", children: "Rotation Speed" }), _jsx("input", { type: "number", step: "0.1", className: "w-full bg-black/40 border border-cyan-500/30 px-1 py-0.5 text-[10px] text-cyan-300 font-mono focus:outline-none focus:border-cyan-400/50", value: props.speed, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { speed: parseFloat(e.target.value) })) })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-[9px] text-cyan-400/60 uppercase tracking-wider mb-0.5", children: "Rotation Axis" }), _jsxs("select", { className: "w-full bg-black/40 border border-cyan-500/30 px-1 py-0.5 text-[10px] text-cyan-300 font-mono focus:outline-none focus:border-cyan-400/50", value: props.axis, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { axis: e.target.value })), children: [_jsx("option", { value: "x", children: "X" }), _jsx("option", { value: "y", children: "Y" }), _jsx("option", { value: "z", children: "Z" })] })] })] });
|
|
11
|
-
}
|
|
12
|
-
// The view component for Rotator
|
|
13
|
-
function RotatorView({ properties, children }) {
|
|
14
|
-
var _a, _b;
|
|
15
|
-
const groupRef = useRef(null);
|
|
16
|
-
const speed = (_a = properties.speed) !== null && _a !== void 0 ? _a : 1.0;
|
|
17
|
-
const axis = (_b = properties.axis) !== null && _b !== void 0 ? _b : 'y';
|
|
18
|
-
useFrame((state, delta) => {
|
|
19
|
-
if (groupRef.current) {
|
|
20
|
-
if (axis === 'x') {
|
|
21
|
-
groupRef.current.rotation.x += delta * speed;
|
|
22
|
-
}
|
|
23
|
-
else if (axis === 'y') {
|
|
24
|
-
groupRef.current.rotation.y += delta * speed;
|
|
25
|
-
}
|
|
26
|
-
else if (axis === 'z') {
|
|
27
|
-
groupRef.current.rotation.z += delta * speed;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
return (_jsx("group", { ref: groupRef, children: children }));
|
|
32
|
-
}
|
|
33
|
-
const RotatorComponent = {
|
|
34
|
-
name: 'Rotator',
|
|
35
|
-
Editor: RotatorComponentEditor,
|
|
36
|
-
View: RotatorView,
|
|
37
|
-
defaultProperties: {
|
|
38
|
-
speed: 1.0,
|
|
39
|
-
axis: 'y'
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
export default RotatorComponent;
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useRef, useEffect } from "react";
|
|
3
|
-
import { Input, Label } from "./Input";
|
|
4
|
-
function SpotLightComponentEditor({ component, onUpdate }) {
|
|
5
|
-
var _a, _b, _c, _d, _e, _f;
|
|
6
|
-
const props = {
|
|
7
|
-
color: (_a = component.properties.color) !== null && _a !== void 0 ? _a : '#ffffff',
|
|
8
|
-
intensity: (_b = component.properties.intensity) !== null && _b !== void 0 ? _b : 1.0,
|
|
9
|
-
angle: (_c = component.properties.angle) !== null && _c !== void 0 ? _c : Math.PI / 6,
|
|
10
|
-
penumbra: (_d = component.properties.penumbra) !== null && _d !== void 0 ? _d : 0.5,
|
|
11
|
-
distance: (_e = component.properties.distance) !== null && _e !== void 0 ? _e : 100,
|
|
12
|
-
castShadow: (_f = component.properties.castShadow) !== null && _f !== void 0 ? _f : true
|
|
13
|
-
};
|
|
14
|
-
const textInputStyle = {
|
|
15
|
-
flex: 1,
|
|
16
|
-
backgroundColor: 'rgba(0, 0, 0, 0.4)',
|
|
17
|
-
border: '1px solid rgba(34, 211, 238, 0.3)',
|
|
18
|
-
padding: '2px 4px',
|
|
19
|
-
fontSize: '10px',
|
|
20
|
-
color: 'rgba(165, 243, 252, 1)',
|
|
21
|
-
fontFamily: 'monospace',
|
|
22
|
-
outline: 'none',
|
|
23
|
-
};
|
|
24
|
-
return _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: [_jsxs("div", { children: [_jsx(Label, { children: "Color" }), _jsxs("div", { style: { display: 'flex', gap: 2 }, children: [_jsx("input", { type: "color", style: { height: 20, width: 20, backgroundColor: 'transparent', border: 'none', cursor: 'pointer' }, value: props.color, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { color: e.target.value })) }), _jsx("input", { type: "text", style: textInputStyle, value: props.color, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { color: e.target.value })) })] })] }), _jsxs("div", { children: [_jsx(Label, { children: "Intensity" }), _jsx(Input, { step: "0.1", value: props.intensity, onChange: value => onUpdate(Object.assign(Object.assign({}, component.properties), { intensity: value })) })] }), _jsxs("div", { children: [_jsx(Label, { children: "Angle" }), _jsx(Input, { step: "0.1", min: 0, max: Math.PI, value: props.angle, onChange: value => onUpdate(Object.assign(Object.assign({}, component.properties), { angle: value })) })] }), _jsxs("div", { children: [_jsx(Label, { children: "Penumbra" }), _jsx(Input, { step: "0.1", min: 0, max: 1, value: props.penumbra, onChange: value => onUpdate(Object.assign(Object.assign({}, component.properties), { penumbra: value })) })] }), _jsxs("div", { children: [_jsx(Label, { children: "Distance" }), _jsx(Input, { step: "1", min: 0, value: props.distance, onChange: value => onUpdate(Object.assign(Object.assign({}, component.properties), { distance: value })) })] }), _jsxs("div", { children: [_jsx(Label, { children: "Cast Shadow" }), _jsx("input", { type: "checkbox", style: { height: 16, width: 16, backgroundColor: 'rgba(0, 0, 0, 0.4)', border: '1px solid rgba(34, 211, 238, 0.3)', cursor: 'pointer' }, checked: props.castShadow, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { castShadow: e.target.checked })) })] })] });
|
|
25
|
-
}
|
|
26
|
-
function SpotLightView({ properties, editMode }) {
|
|
27
|
-
var _a, _b, _c, _d, _e, _f;
|
|
28
|
-
const color = (_a = properties.color) !== null && _a !== void 0 ? _a : '#ffffff';
|
|
29
|
-
const intensity = (_b = properties.intensity) !== null && _b !== void 0 ? _b : 1.0;
|
|
30
|
-
const angle = (_c = properties.angle) !== null && _c !== void 0 ? _c : Math.PI / 6;
|
|
31
|
-
const penumbra = (_d = properties.penumbra) !== null && _d !== void 0 ? _d : 0.5;
|
|
32
|
-
const distance = (_e = properties.distance) !== null && _e !== void 0 ? _e : 100;
|
|
33
|
-
const castShadow = (_f = properties.castShadow) !== null && _f !== void 0 ? _f : true;
|
|
34
|
-
const spotLightRef = useRef(null);
|
|
35
|
-
const targetRef = useRef(null);
|
|
36
|
-
useEffect(() => {
|
|
37
|
-
if (spotLightRef.current && targetRef.current) {
|
|
38
|
-
spotLightRef.current.target = targetRef.current;
|
|
39
|
-
}
|
|
40
|
-
}, []);
|
|
41
|
-
return (_jsxs(_Fragment, { children: [_jsx("spotLight", { ref: spotLightRef, color: color, intensity: intensity, angle: angle, penumbra: penumbra, distance: distance, castShadow: castShadow, "shadow-mapSize-width": 1024, "shadow-mapSize-height": 1024, "shadow-bias": -0.0001, "shadow-normalBias": 0.02 }), _jsx("object3D", { ref: targetRef, position: [0, -5, 0] }), editMode && (_jsxs(_Fragment, { children: [_jsxs("mesh", { children: [_jsx("sphereGeometry", { args: [0.2, 8, 6] }), _jsx("meshBasicMaterial", { color: color, wireframe: true })] }), _jsxs("mesh", { position: [0, -5, 0], children: [_jsx("sphereGeometry", { args: [0.15, 8, 6] }), _jsx("meshBasicMaterial", { color: color, wireframe: true, opacity: 0.5, transparent: true })] })] }))] }));
|
|
42
|
-
}
|
|
43
|
-
const SpotLightComponent = {
|
|
44
|
-
name: 'SpotLight',
|
|
45
|
-
Editor: SpotLightComponentEditor,
|
|
46
|
-
View: SpotLightView,
|
|
47
|
-
defaultProperties: {}
|
|
48
|
-
};
|
|
49
|
-
export default SpotLightComponent;
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { Vector3Input, Label } from "./Input";
|
|
3
|
-
import { useEditorContext } from "../EditorContext";
|
|
4
|
-
const buttonStyle = {
|
|
5
|
-
padding: '2px 6px',
|
|
6
|
-
background: 'transparent',
|
|
7
|
-
color: 'rgba(255,255,255,0.9)',
|
|
8
|
-
border: '1px solid rgba(255,255,255,0.14)',
|
|
9
|
-
borderRadius: 4,
|
|
10
|
-
cursor: 'pointer',
|
|
11
|
-
font: 'inherit',
|
|
12
|
-
flex: 1,
|
|
13
|
-
};
|
|
14
|
-
function TransformComponentEditor({ component, onUpdate, transformMode, setTransformMode }) {
|
|
15
|
-
const { snapResolution, setSnapResolution } = useEditorContext();
|
|
16
|
-
return _jsxs("div", { style: { display: 'flex', flexDirection: 'column' }, children: [transformMode && setTransformMode && (_jsxs("div", { style: { marginBottom: 8 }, children: [_jsxs(Label, { children: ["Transform Mode ", snapResolution > 0 && `(Snap: ${snapResolution})`] }), _jsx("div", { style: { display: 'flex', gap: 6 }, children: ["translate", "rotate", "scale"].map(mode => {
|
|
17
|
-
const isActive = transformMode === mode;
|
|
18
|
-
return (_jsx("button", { onClick: () => setTransformMode(mode), style: Object.assign(Object.assign({}, buttonStyle), { background: isActive ? 'rgba(255,255,255,0.10)' : 'transparent' }), onPointerEnter: (e) => {
|
|
19
|
-
if (!isActive)
|
|
20
|
-
e.currentTarget.style.background = 'rgba(255,255,255,0.08)';
|
|
21
|
-
}, onPointerLeave: (e) => {
|
|
22
|
-
if (!isActive)
|
|
23
|
-
e.currentTarget.style.background = 'transparent';
|
|
24
|
-
}, children: mode }, mode));
|
|
25
|
-
}) }), _jsx("div", { style: { marginTop: 6 }, children: _jsxs("button", { onClick: () => setSnapResolution(snapResolution > 0 ? 0 : 0.1), style: Object.assign(Object.assign({}, buttonStyle), { background: snapResolution > 0 ? 'rgba(255,255,255,0.10)' : 'transparent', width: '100%' }), onPointerEnter: (e) => {
|
|
26
|
-
if (snapResolution === 0)
|
|
27
|
-
e.currentTarget.style.background = 'rgba(255,255,255,0.08)';
|
|
28
|
-
}, onPointerLeave: (e) => {
|
|
29
|
-
if (snapResolution === 0)
|
|
30
|
-
e.currentTarget.style.background = 'transparent';
|
|
31
|
-
}, children: ["Snap: ", snapResolution > 0 ? `ON (${snapResolution})` : 'OFF'] }) })] })), _jsx(Vector3Input, { label: "Position", value: component.properties.position, onChange: v => onUpdate({ position: v }), snap: snapResolution }), _jsx(Vector3Input, { label: "Rotation", value: component.properties.rotation, onChange: v => onUpdate({ rotation: v }), snap: snapResolution }), _jsx(Vector3Input, { label: "Scale", value: component.properties.scale, onChange: v => onUpdate({ scale: v }), snap: snapResolution })] });
|
|
32
|
-
}
|
|
33
|
-
const TransformComponent = {
|
|
34
|
-
name: 'Transform',
|
|
35
|
-
Editor: TransformComponentEditor,
|
|
36
|
-
defaultProperties: {
|
|
37
|
-
position: [0, 0, 0],
|
|
38
|
-
rotation: [0, 0, 0],
|
|
39
|
-
scale: [1, 1, 1]
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
export default TransformComponent;
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import GeometryComponent from './GeometryComponent';
|
|
2
|
-
import TransformComponent from './TransformComponent';
|
|
3
|
-
import MaterialComponent from './MaterialComponent';
|
|
4
|
-
import PhysicsComponent from './PhysicsComponent';
|
|
5
|
-
import SpotLightComponent from './SpotLightComponent';
|
|
6
|
-
import DirectionalLightComponent from './DirectionalLightComponent';
|
|
7
|
-
import ModelComponent from './ModelComponent';
|
|
8
|
-
export default [
|
|
9
|
-
GeometryComponent,
|
|
10
|
-
TransformComponent,
|
|
11
|
-
MaterialComponent,
|
|
12
|
-
PhysicsComponent,
|
|
13
|
-
SpotLightComponent,
|
|
14
|
-
DirectionalLightComponent,
|
|
15
|
-
ModelComponent
|
|
16
|
-
];
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { Object3D } from 'three';
|
|
2
|
-
/**
|
|
3
|
-
* Hook to load a model (GLB/GLTF/FBX) using drei's optimized loaders
|
|
4
|
-
* Returns the loaded model with proper caching and suspense support
|
|
5
|
-
*/
|
|
6
|
-
export declare function useModel(filename: string | undefined): Object3D | null;
|
|
7
|
-
/**
|
|
8
|
-
* Preload a model to avoid suspense boundaries during runtime
|
|
9
|
-
*/
|
|
10
|
-
export declare function preloadModel(filename: string): void;
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { useGLTF, useFBX } from '@react-three/drei';
|
|
2
|
-
import { useMemo } from 'react';
|
|
3
|
-
/**
|
|
4
|
-
* Hook to load a model (GLB/GLTF/FBX) using drei's optimized loaders
|
|
5
|
-
* Returns the loaded model with proper caching and suspense support
|
|
6
|
-
*/
|
|
7
|
-
export function useModel(filename) {
|
|
8
|
-
const isFBX = filename === null || filename === void 0 ? void 0 : filename.toLowerCase().endsWith('.fbx');
|
|
9
|
-
const isGLTF = (filename === null || filename === void 0 ? void 0 : filename.toLowerCase().endsWith('.glb')) || (filename === null || filename === void 0 ? void 0 : filename.toLowerCase().endsWith('.gltf'));
|
|
10
|
-
// Normalize path (ensure leading slash)
|
|
11
|
-
const normalizedPath = useMemo(() => {
|
|
12
|
-
if (!filename)
|
|
13
|
-
return '';
|
|
14
|
-
return filename.startsWith('/') ? filename : `/${filename}`;
|
|
15
|
-
}, [filename]);
|
|
16
|
-
// Load models using drei hooks (these handle caching automatically)
|
|
17
|
-
const gltf = useGLTF(isGLTF && normalizedPath ? normalizedPath : '', true);
|
|
18
|
-
const fbx = useFBX(isFBX && normalizedPath ? normalizedPath : '');
|
|
19
|
-
// Return the appropriate model
|
|
20
|
-
if (!filename)
|
|
21
|
-
return null;
|
|
22
|
-
if (isGLTF)
|
|
23
|
-
return gltf.scene;
|
|
24
|
-
if (isFBX)
|
|
25
|
-
return fbx;
|
|
26
|
-
return null;
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Preload a model to avoid suspense boundaries during runtime
|
|
30
|
-
*/
|
|
31
|
-
export function preloadModel(filename) {
|
|
32
|
-
const normalizedPath = filename.startsWith('/') ? filename : `/${filename}`;
|
|
33
|
-
const isFBX = filename.toLowerCase().endsWith('.fbx');
|
|
34
|
-
if (isFBX) {
|
|
35
|
-
useFBX.preload(normalizedPath);
|
|
36
|
-
}
|
|
37
|
-
else {
|
|
38
|
-
useGLTF.preload(normalizedPath);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default function PrefabEditorPage(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import PrefabEditor from "./PrefabEditor";
|
|
3
|
-
export default function PrefabEditorPage() {
|
|
4
|
-
return _jsx("div", { className: "w-screen h-screen", children: _jsx(PrefabEditor, { children: _jsx("directionalLight", { position: [5, 10, 7.5], intensity: 1, castShadow: true }) }) });
|
|
5
|
-
}
|