react-three-game 0.0.99 → 0.0.101
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/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/tools/assetviewer/page.js +70 -58
- package/dist/tools/dragdrop/DragDropLoader.d.ts +3 -0
- package/dist/tools/dragdrop/DragDropLoader.js +183 -44
- package/dist/tools/dragdrop/index.d.ts +1 -1
- package/dist/tools/dragdrop/index.js +1 -1
- package/dist/tools/dragdrop/modelLoader.js +2 -0
- package/dist/tools/prefabeditor/EditorUI.js +7 -8
- package/dist/tools/prefabeditor/PrefabEditor.d.ts +3 -0
- package/dist/tools/prefabeditor/PrefabEditor.js +28 -11
- package/dist/tools/prefabeditor/PrefabRoot.d.ts +2 -0
- package/dist/tools/prefabeditor/PrefabRoot.js +51 -35
- package/dist/tools/prefabeditor/components/BufferGeometryComponent.js +20 -0
- package/dist/tools/prefabeditor/components/CameraComponent.js +1 -14
- package/dist/tools/prefabeditor/components/ComponentRegistry.d.ts +5 -0
- package/dist/tools/prefabeditor/components/ComponentRegistry.js +31 -0
- package/dist/tools/prefabeditor/components/DataComponent.js +1 -0
- package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +31 -119
- package/dist/tools/prefabeditor/components/EnvironmentComponent.js +1 -0
- package/dist/tools/prefabeditor/components/GeometryComponent.js +1 -0
- package/dist/tools/prefabeditor/components/Input.d.ts +1 -0
- package/dist/tools/prefabeditor/components/MaterialComponent.d.ts +1 -0
- package/dist/tools/prefabeditor/components/MaterialComponent.js +89 -52
- package/dist/tools/prefabeditor/components/ModelComponent.js +45 -3
- package/dist/tools/prefabeditor/components/PointLightComponent.js +19 -25
- package/dist/tools/prefabeditor/components/SpotLightComponent.js +27 -42
- package/dist/tools/prefabeditor/components/SpriteComponent.js +1 -0
- package/dist/tools/prefabeditor/components/TransformComponent.js +1 -0
- package/dist/tools/prefabeditor/modelPrefab.d.ts +16 -0
- package/dist/tools/prefabeditor/modelPrefab.js +180 -0
- package/dist/tools/prefabeditor/prefabStore.d.ts +1 -0
- package/dist/tools/prefabeditor/prefabStore.js +75 -42
- package/package.json +1 -1
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useHelper } from "@react-three/drei";
|
|
3
|
-
import {
|
|
4
|
-
import { useFrame } from "@react-three/fiber";
|
|
3
|
+
import { useEffect, useRef } from "react";
|
|
5
4
|
import { CameraHelper } from "three";
|
|
6
5
|
import { useNode } from "../assetRuntime";
|
|
7
6
|
import { BooleanField, ColorField, NumberField, NumberInput, Vector3Input } from "./Input";
|
|
@@ -23,141 +22,54 @@ const directionalLightDefaults = {
|
|
|
23
22
|
shadowCameraRight: 5,
|
|
24
23
|
targetOffset: [0, -5, 0],
|
|
25
24
|
};
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
textTransform: 'uppercase',
|
|
29
|
-
letterSpacing: '0.06em',
|
|
30
|
-
color: colors.textMuted,
|
|
31
|
-
textAlign: 'center',
|
|
32
|
-
};
|
|
33
|
-
const frustumCellStyle = {
|
|
34
|
-
display: 'flex',
|
|
35
|
-
alignItems: 'center',
|
|
36
|
-
justifyContent: 'center',
|
|
37
|
-
};
|
|
38
|
-
const frustumInputStyle = {
|
|
39
|
-
width: 62,
|
|
40
|
-
minWidth: 62,
|
|
41
|
-
textAlign: 'center',
|
|
42
|
-
};
|
|
43
|
-
const centerLockButtonStyle = {
|
|
44
|
-
width: 34,
|
|
45
|
-
height: 34,
|
|
46
|
-
borderRadius: 0,
|
|
47
|
-
border: `1px solid ${colors.border}`,
|
|
48
|
-
background: colors.bgInput,
|
|
49
|
-
color: colors.textMuted,
|
|
50
|
-
cursor: 'pointer',
|
|
51
|
-
fontSize: 14,
|
|
52
|
-
lineHeight: 1,
|
|
53
|
-
padding: 0,
|
|
54
|
-
};
|
|
55
|
-
function areFrustumSidesLocked(values) {
|
|
56
|
-
const top = Math.abs(values.shadowCameraTop);
|
|
57
|
-
const bottom = Math.abs(values.shadowCameraBottom);
|
|
58
|
-
const left = Math.abs(values.shadowCameraLeft);
|
|
59
|
-
const right = Math.abs(values.shadowCameraRight);
|
|
60
|
-
return top === bottom && top === left && top === right;
|
|
61
|
-
}
|
|
62
|
-
function ShadowFrustumField({ values, onChange, }) {
|
|
63
|
-
const [locked, setLocked] = useState(() => areFrustumSidesLocked(values));
|
|
64
|
-
const updateSide = (side, nextValue) => {
|
|
65
|
-
if (!locked) {
|
|
66
|
-
onChange({ [side]: nextValue });
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
const magnitude = Math.abs(nextValue);
|
|
70
|
-
onChange({
|
|
71
|
-
shadowCameraTop: magnitude,
|
|
72
|
-
shadowCameraBottom: -magnitude,
|
|
73
|
-
shadowCameraLeft: -magnitude,
|
|
74
|
-
shadowCameraRight: magnitude,
|
|
75
|
-
});
|
|
76
|
-
};
|
|
77
|
-
const toggleLocked = () => {
|
|
78
|
-
setLocked(current => {
|
|
79
|
-
const nextLocked = !current;
|
|
80
|
-
if (nextLocked) {
|
|
81
|
-
const magnitude = Math.max(Math.abs(values.shadowCameraTop), Math.abs(values.shadowCameraBottom), Math.abs(values.shadowCameraLeft), Math.abs(values.shadowCameraRight));
|
|
82
|
-
onChange({
|
|
83
|
-
shadowCameraTop: magnitude,
|
|
84
|
-
shadowCameraBottom: -magnitude,
|
|
85
|
-
shadowCameraLeft: -magnitude,
|
|
86
|
-
shadowCameraRight: magnitude,
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
return nextLocked;
|
|
90
|
-
});
|
|
91
|
-
};
|
|
92
|
-
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 6 }, children: [_jsx("div", { style: Object.assign(Object.assign({}, frustumLabelStyle), { textAlign: 'left' }), children: "Shadow Frustum" }), _jsxs("div", { style: {
|
|
93
|
-
display: 'grid',
|
|
94
|
-
gridTemplateColumns: '1fr auto 1fr',
|
|
95
|
-
gridTemplateRows: 'auto auto auto',
|
|
96
|
-
gap: 8,
|
|
97
|
-
alignItems: 'center',
|
|
98
|
-
}, children: [_jsx("div", {}), _jsx("div", { style: frustumCellStyle, children: _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 4, alignItems: 'center' }, children: [_jsx("div", { style: frustumLabelStyle, children: "Top" }), _jsx(NumberInput, { value: values.shadowCameraTop, onChange: nextValue => updateSide('shadowCameraTop', nextValue), step: 0.5, style: frustumInputStyle })] }) }), _jsx("div", {}), _jsx("div", { style: frustumCellStyle, children: _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 4, alignItems: 'center' }, children: [_jsx("div", { style: frustumLabelStyle, children: "Left" }), _jsx(NumberInput, { value: values.shadowCameraLeft, onChange: nextValue => updateSide('shadowCameraLeft', nextValue), step: 0.5, style: frustumInputStyle })] }) }), _jsx("div", { style: frustumCellStyle, children: _jsx("button", { type: "button", onClick: toggleLocked, style: Object.assign(Object.assign({}, centerLockButtonStyle), { color: locked ? colors.accent : colors.textMuted, borderColor: locked ? colors.accentBorder : colors.border, background: locked ? colors.accentBg : colors.bgInput }), title: locked ? 'Frustum sides locked' : 'Frustum sides unlocked', children: locked ? '🔒' : '🔓' }) }), _jsx("div", { style: frustumCellStyle, children: _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 4, alignItems: 'center' }, children: [_jsx("div", { style: frustumLabelStyle, children: "Right" }), _jsx(NumberInput, { value: values.shadowCameraRight, onChange: nextValue => updateSide('shadowCameraRight', nextValue), step: 0.5, style: frustumInputStyle })] }) }), _jsx("div", {}), _jsx("div", { style: frustumCellStyle, children: _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 4, alignItems: 'center' }, children: [_jsx("div", { style: frustumLabelStyle, children: "Bottom" }), _jsx(NumberInput, { value: values.shadowCameraBottom, onChange: nextValue => updateSide('shadowCameraBottom', nextValue), step: 0.5, style: frustumInputStyle })] }) }), _jsx("div", {})] })] }));
|
|
25
|
+
function ShadowFrustumField({ values, onChange }) {
|
|
26
|
+
// Minimal, no lock UI for simplicity (can add back if needed)
|
|
27
|
+
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 6 }, children: [_jsx("div", { style: { fontSize: 10, textTransform: 'uppercase', letterSpacing: '0.06em', color: colors.textMuted, textAlign: 'left' }, children: "Shadow Frustum" }), _jsxs("div", { style: { display: 'flex', gap: 8 }, children: [_jsx(NumberInput, { value: values.shadowCameraTop, onChange: v => onChange({ shadowCameraTop: v }), step: 0.5, style: { width: 62, minWidth: 62, textAlign: 'center' }, label: "Top" }), _jsx(NumberInput, { value: values.shadowCameraBottom, onChange: v => onChange({ shadowCameraBottom: v }), step: 0.5, style: { width: 62, minWidth: 62, textAlign: 'center' }, label: "Bottom" }), _jsx(NumberInput, { value: values.shadowCameraLeft, onChange: v => onChange({ shadowCameraLeft: v }), step: 0.5, style: { width: 62, minWidth: 62, textAlign: 'center' }, label: "Left" }), _jsx(NumberInput, { value: values.shadowCameraRight, onChange: v => onChange({ shadowCameraRight: v }), step: 0.5, style: { width: 62, minWidth: 62, textAlign: 'center' }, label: "Right" })] })] }));
|
|
99
28
|
}
|
|
100
29
|
function DirectionalLightComponentEditor({ component, onUpdate }) {
|
|
101
30
|
const values = mergeWithDefaults(directionalLightDefaults, component.properties);
|
|
102
31
|
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: [_jsxs(LightSection, { title: "Light", children: [_jsx(ColorField, { name: "color", label: "Color", values: values, onChange: onUpdate }), _jsx(NumberField, { name: "intensity", label: "Intensity", values: values, onChange: onUpdate, min: 0, step: 0.1, fallback: 1 }), _jsx(Vector3Input, { label: "Target Offset", value: values.targetOffset, onChange: targetOffset => onUpdate({ targetOffset }), snap: 0.5 })] }), _jsxs(LightSection, { title: "Shadow", children: [_jsx(BooleanField, { name: "castShadow", label: "Cast Shadow", values: values, onChange: onUpdate, fallback: false }), values.castShadow ? (_jsxs(_Fragment, { children: [_jsx(BooleanField, { name: "shadowAutoUpdate", label: "Auto Update", values: values, onChange: onUpdate, fallback: true }), _jsx(NumberField, { name: "shadowMapSize", label: "Map Size", values: values, onChange: onUpdate, min: 128, step: 128, fallback: 512 }), _jsx(ShadowBiasField, { name: "shadowBias", label: "Bias", values: values, onChange: onUpdate, fallback: 0 }), _jsx(ShadowBiasField, { name: "shadowNormalBias", label: "Normal Bias", values: values, onChange: onUpdate, fallback: 0 }), _jsx(NumberField, { name: "shadowCameraNear", label: "Near", values: values, onChange: onUpdate, min: 0.001, step: 0.1, fallback: 0.5 }), _jsx(NumberField, { name: "shadowCameraFar", label: "Far", values: values, onChange: onUpdate, min: 0.1, step: 1, fallback: 500 }), _jsx(ShadowFrustumField, { values: values, onChange: onUpdate })] })) : null] })] }));
|
|
103
32
|
}
|
|
104
33
|
function DirectionalLightView({ properties, children }) {
|
|
34
|
+
var _a;
|
|
105
35
|
const { editMode, isSelected } = useNode();
|
|
106
36
|
const merged = mergeWithDefaults(directionalLightDefaults, properties);
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
37
|
+
const lightProps = {
|
|
38
|
+
color: merged.color,
|
|
39
|
+
intensity: merged.intensity,
|
|
40
|
+
castShadow: merged.castShadow,
|
|
41
|
+
"shadow-mapSize-width": merged.shadowMapSize,
|
|
42
|
+
"shadow-mapSize-height": merged.shadowMapSize,
|
|
43
|
+
"shadow-bias": merged.shadowBias,
|
|
44
|
+
"shadow-normalBias": merged.shadowNormalBias,
|
|
45
|
+
"shadow-autoUpdate": merged.shadowAutoUpdate,
|
|
46
|
+
"shadow-camera-near": merged.shadowCameraNear,
|
|
47
|
+
"shadow-camera-far": merged.shadowCameraFar,
|
|
48
|
+
"shadow-camera-top": merged.shadowCameraTop,
|
|
49
|
+
"shadow-camera-bottom": merged.shadowCameraBottom,
|
|
50
|
+
"shadow-camera-left": merged.shadowCameraLeft,
|
|
51
|
+
"shadow-camera-right": merged.shadowCameraRight,
|
|
52
|
+
};
|
|
121
53
|
const directionalLightRef = useRef(null);
|
|
122
54
|
const targetRef = useRef(null);
|
|
123
55
|
const shadowCameraRef = useRef(null);
|
|
124
|
-
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
: null;
|
|
56
|
+
// Show CameraHelper only in edit mode, selected, and castShadow
|
|
57
|
+
const showHelper = editMode && isSelected && merged.castShadow;
|
|
58
|
+
const helperTarget = showHelper && shadowCameraRef.current ? { current: shadowCameraRef.current } : null;
|
|
128
59
|
useHelper(helperTarget, CameraHelper);
|
|
129
|
-
// Use a local target object so node transforms rotate the light direction naturally.
|
|
130
|
-
useEffect(() => {
|
|
131
|
-
if (directionalLightRef.current && targetRef.current) {
|
|
132
|
-
directionalLightRef.current.target = targetRef.current;
|
|
133
|
-
const nextShadowCamera = directionalLightRef.current.shadow.camera;
|
|
134
|
-
shadowCameraRef.current = nextShadowCamera;
|
|
135
|
-
setShadowCamera(castShadow ? nextShadowCamera : null);
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
60
|
useEffect(() => {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if (!shadow)
|
|
142
|
-
return;
|
|
143
|
-
shadow.needsUpdate = true;
|
|
144
|
-
shadow.camera.updateProjectionMatrix();
|
|
145
|
-
});
|
|
146
|
-
useFrame(() => {
|
|
147
|
-
if (!directionalLightRef.current || !targetRef.current)
|
|
148
|
-
return;
|
|
149
|
-
directionalLightRef.current.target.updateMatrixWorld();
|
|
150
|
-
if (shadowCamera && castShadow) {
|
|
151
|
-
shadowCamera.updateProjectionMatrix();
|
|
152
|
-
shadowCamera.updateMatrixWorld();
|
|
61
|
+
if (directionalLightRef.current) {
|
|
62
|
+
shadowCameraRef.current = directionalLightRef.current.shadow.camera;
|
|
153
63
|
}
|
|
154
|
-
});
|
|
155
|
-
return (_jsxs(
|
|
64
|
+
}, []);
|
|
65
|
+
return (_jsxs("group", { children: [_jsxs("directionalLight", Object.assign({ ref: directionalLightRef }, lightProps, {
|
|
66
|
+
// Attach the target object
|
|
67
|
+
target: (_a = targetRef.current) !== null && _a !== void 0 ? _a : undefined, children: [children, editMode && isSelected && (_jsxs(_Fragment, { children: [_jsxs("mesh", { children: [_jsx("sphereGeometry", { args: [0.3, 8, 6] }), _jsx("meshBasicMaterial", { color: merged.color, wireframe: true })] }), _jsxs("mesh", { position: merged.targetOffset, children: [_jsx("sphereGeometry", { args: [0.2, 8, 6] }), _jsx("meshBasicMaterial", { color: merged.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, merged.targetOffset[0], merged.targetOffset[1], merged.targetOffset[2]]), 3] }) }), _jsx("lineBasicMaterial", { color: merged.color, opacity: 0.6, transparent: true })] })] }))] })), _jsx("object3D", { ref: targetRef, position: merged.targetOffset })] }));
|
|
156
68
|
}
|
|
157
69
|
const DirectionalLightComponent = {
|
|
158
70
|
name: 'DirectionalLight',
|
|
159
71
|
Editor: DirectionalLightComponentEditor,
|
|
160
72
|
View: DirectionalLightView,
|
|
161
|
-
defaultProperties: {}
|
|
73
|
+
defaultProperties: {},
|
|
162
74
|
};
|
|
163
75
|
export default DirectionalLightComponent;
|
|
@@ -10,6 +10,7 @@ function EnvironmentView({ properties, children, }) {
|
|
|
10
10
|
}
|
|
11
11
|
const EnvironmentComponent = {
|
|
12
12
|
name: 'Environment',
|
|
13
|
+
disableSiblingComposition: true,
|
|
13
14
|
Editor: ({ component, onUpdate }) => (_jsxs(FieldGroup, { children: [_jsx(NumberField, { name: "intensity", label: "Intensity", values: component.properties, onChange: onUpdate, min: 0, step: 0.1, fallback: 1 }), _jsx(NumberField, { name: "resolution", label: "Resolution", values: component.properties, onChange: onUpdate, min: 64, step: 64, fallback: 256 })] })),
|
|
14
15
|
View: EnvironmentView,
|
|
15
16
|
defaultProperties: {},
|
|
@@ -57,6 +57,7 @@ interface InputProps {
|
|
|
57
57
|
min?: number;
|
|
58
58
|
max?: number;
|
|
59
59
|
style?: React.CSSProperties;
|
|
60
|
+
label?: string;
|
|
60
61
|
}
|
|
61
62
|
export declare function NumberInput({ value, onChange, step, min, max, style }: InputProps): import("react/jsx-runtime").JSX.Element;
|
|
62
63
|
export declare function Label({ children }: {
|
|
@@ -11,6 +11,7 @@ declare module '@react-three/fiber' {
|
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
export interface MaterialProps extends Omit<MeshStandardMaterialProperties & MeshBasicMaterialProperties, 'args' | 'normalScale' | 'side'> {
|
|
14
|
+
attach?: string;
|
|
14
15
|
materialType?: 'standard' | 'basic' | 'sprite';
|
|
15
16
|
transmission?: number;
|
|
16
17
|
thickness?: number;
|
|
@@ -10,7 +10,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
10
10
|
return t;
|
|
11
11
|
};
|
|
12
12
|
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
13
|
-
import { createContext, useContext, useMemo, useRef } from 'react';
|
|
13
|
+
import { createContext, useContext, useEffect, useMemo, useRef } from 'react';
|
|
14
14
|
import { extend } from '@react-three/fiber';
|
|
15
15
|
import { useFrame } from '@react-three/fiber';
|
|
16
16
|
import { assetRef, assetRefs } from './ComponentRegistry';
|
|
@@ -38,26 +38,6 @@ const MAG_FILTER_MAP = {
|
|
|
38
38
|
NearestFilter,
|
|
39
39
|
LinearFilter,
|
|
40
40
|
};
|
|
41
|
-
function cloneConfiguredTexture({ texture, repeat, repeatCount, offset, colorSpace, generateMipmaps, minFilter, magFilter, }) {
|
|
42
|
-
var _a, _b;
|
|
43
|
-
const clonedTexture = texture.clone();
|
|
44
|
-
if (repeat) {
|
|
45
|
-
clonedTexture.wrapS = clonedTexture.wrapT = RepeatWrapping;
|
|
46
|
-
if (repeatCount)
|
|
47
|
-
clonedTexture.repeat.set(repeatCount[0], repeatCount[1]);
|
|
48
|
-
}
|
|
49
|
-
else {
|
|
50
|
-
clonedTexture.wrapS = clonedTexture.wrapT = ClampToEdgeWrapping;
|
|
51
|
-
clonedTexture.repeat.set(1, 1);
|
|
52
|
-
}
|
|
53
|
-
clonedTexture.offset.set((_a = offset === null || offset === void 0 ? void 0 : offset[0]) !== null && _a !== void 0 ? _a : 0, (_b = offset === null || offset === void 0 ? void 0 : offset[1]) !== null && _b !== void 0 ? _b : 0);
|
|
54
|
-
clonedTexture.colorSpace = colorSpace;
|
|
55
|
-
clonedTexture.generateMipmaps = generateMipmaps;
|
|
56
|
-
clonedTexture.minFilter = minFilter;
|
|
57
|
-
clonedTexture.magFilter = magFilter;
|
|
58
|
-
clonedTexture.needsUpdate = true;
|
|
59
|
-
return clonedTexture;
|
|
60
|
-
}
|
|
61
41
|
export function useMaterialOverrides() {
|
|
62
42
|
return useContext(MaterialOverridesContext);
|
|
63
43
|
}
|
|
@@ -80,6 +60,11 @@ function MaterialComponentEditor({ component, onUpdate, basePath = "", }) {
|
|
|
80
60
|
const isStandardMaterial = materialType === 'standard';
|
|
81
61
|
const isSpriteMaterial = materialType === 'sprite';
|
|
82
62
|
const fields = [
|
|
63
|
+
{
|
|
64
|
+
name: 'attach',
|
|
65
|
+
type: 'string',
|
|
66
|
+
label: 'Attach',
|
|
67
|
+
},
|
|
83
68
|
{
|
|
84
69
|
name: 'materialType',
|
|
85
70
|
type: 'select',
|
|
@@ -208,39 +193,85 @@ function MaterialComponentView({ properties: rawProps }) {
|
|
|
208
193
|
const normalScaleProp = materialSource.normalScale;
|
|
209
194
|
const normalMapTexture = normalMapTextureName ? getTexture(normalMapTextureName) : undefined;
|
|
210
195
|
// Destructure all material props and separate custom texture handling props
|
|
211
|
-
const { texture: _texture, offset: _offset, repeat: _repeat, repeatCount: _repeatCount, animateOffset: _animateOffset, offsetSpeed: _offsetSpeed, generateMipmaps: _generateMipmaps, minFilter: _minFilter, magFilter: _magFilter, map: _map, materialType: _materialType, normalMapTexture: _normalMapTexture, normalScale: _normalScale, normalMap: _normalMap, rotation, sizeAttenuation, side: sideProp } = materialSource, materialProps = __rest(materialSource, ["texture", "offset", "repeat", "repeatCount", "animateOffset", "offsetSpeed", "generateMipmaps", "minFilter", "magFilter", "map", "materialType", "normalMapTexture", "normalScale", "normalMap", "rotation", "sizeAttenuation", "side"]);
|
|
196
|
+
const { texture: _texture, offset: _offset, repeat: _repeat, repeatCount: _repeatCount, animateOffset: _animateOffset, offsetSpeed: _offsetSpeed, generateMipmaps: _generateMipmaps, minFilter: _minFilter, magFilter: _magFilter, map: _map, materialType: _materialType, attach, normalMapTexture: _normalMapTexture, normalScale: _normalScale, normalMap: _normalMap, rotation, sizeAttenuation, side: sideProp } = materialSource, materialProps = __rest(materialSource, ["texture", "offset", "repeat", "repeatCount", "animateOffset", "offsetSpeed", "generateMipmaps", "minFilter", "magFilter", "map", "materialType", "attach", "normalMapTexture", "normalScale", "normalMap", "rotation", "sizeAttenuation", "side"]);
|
|
212
197
|
const resolvedSide = sideProp ? (_d = SIDE_MAP[sideProp]) !== null && _d !== void 0 ? _d : FrontSide : FrontSide;
|
|
213
198
|
const resolvedMinFilter = (_e = MIN_FILTER_MAP[minFilter]) !== null && _e !== void 0 ? _e : LinearMipmapLinearFilter;
|
|
214
199
|
const resolvedMagFilter = (_f = MAG_FILTER_MAP[magFilter]) !== null && _f !== void 0 ? _f : LinearFilter;
|
|
215
200
|
const animatedOffsetRef = useRef([(_g = offset === null || offset === void 0 ? void 0 : offset[0]) !== null && _g !== void 0 ? _g : 0, (_h = offset === null || offset === void 0 ? void 0 : offset[1]) !== null && _h !== void 0 ? _h : 0]);
|
|
216
201
|
const finalTexture = useMemo(() => {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
202
|
+
return texture ? texture.clone() : undefined;
|
|
203
|
+
}, [texture]);
|
|
204
|
+
useEffect(() => {
|
|
205
|
+
return () => finalTexture === null || finalTexture === void 0 ? void 0 : finalTexture.dispose();
|
|
206
|
+
}, [finalTexture]);
|
|
207
|
+
useEffect(() => {
|
|
208
|
+
var _a, _b;
|
|
209
|
+
if (!finalTexture)
|
|
210
|
+
return;
|
|
211
|
+
if (repeat) {
|
|
212
|
+
finalTexture.wrapS = finalTexture.wrapT = RepeatWrapping;
|
|
213
|
+
if (repeatCount) {
|
|
214
|
+
finalTexture.repeat.set(repeatCount[0], repeatCount[1]);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
finalTexture.wrapS = finalTexture.wrapT = ClampToEdgeWrapping;
|
|
219
|
+
finalTexture.repeat.set(1, 1);
|
|
220
|
+
}
|
|
221
|
+
finalTexture.offset.set((_a = offset === null || offset === void 0 ? void 0 : offset[0]) !== null && _a !== void 0 ? _a : 0, (_b = offset === null || offset === void 0 ? void 0 : offset[1]) !== null && _b !== void 0 ? _b : 0);
|
|
222
|
+
finalTexture.colorSpace = SRGBColorSpace;
|
|
223
|
+
finalTexture.generateMipmaps = generateMipmaps;
|
|
224
|
+
finalTexture.minFilter = resolvedMinFilter;
|
|
225
|
+
finalTexture.magFilter = resolvedMagFilter;
|
|
226
|
+
finalTexture.needsUpdate = true;
|
|
227
|
+
}, [
|
|
228
|
+
finalTexture,
|
|
229
|
+
repeat,
|
|
230
|
+
repeatCount === null || repeatCount === void 0 ? void 0 : repeatCount[0],
|
|
231
|
+
repeatCount === null || repeatCount === void 0 ? void 0 : repeatCount[1],
|
|
232
|
+
offset === null || offset === void 0 ? void 0 : offset[0],
|
|
233
|
+
offset === null || offset === void 0 ? void 0 : offset[1],
|
|
234
|
+
generateMipmaps,
|
|
235
|
+
resolvedMinFilter,
|
|
236
|
+
resolvedMagFilter
|
|
237
|
+
]);
|
|
230
238
|
const finalNormalMap = useMemo(() => {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
239
|
+
return normalMapTexture ? normalMapTexture.clone() : undefined;
|
|
240
|
+
}, [normalMapTexture]);
|
|
241
|
+
useEffect(() => {
|
|
242
|
+
return () => finalNormalMap === null || finalNormalMap === void 0 ? void 0 : finalNormalMap.dispose();
|
|
243
|
+
}, [finalNormalMap]);
|
|
244
|
+
useEffect(() => {
|
|
245
|
+
var _a, _b;
|
|
246
|
+
if (!finalNormalMap)
|
|
247
|
+
return;
|
|
248
|
+
if (repeat) {
|
|
249
|
+
finalNormalMap.wrapS = finalNormalMap.wrapT = RepeatWrapping;
|
|
250
|
+
if (repeatCount) {
|
|
251
|
+
finalNormalMap.repeat.set(repeatCount[0], repeatCount[1]);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
finalNormalMap.wrapS = finalNormalMap.wrapT = ClampToEdgeWrapping;
|
|
256
|
+
finalNormalMap.repeat.set(1, 1);
|
|
257
|
+
}
|
|
258
|
+
finalNormalMap.offset.set((_a = offset === null || offset === void 0 ? void 0 : offset[0]) !== null && _a !== void 0 ? _a : 0, (_b = offset === null || offset === void 0 ? void 0 : offset[1]) !== null && _b !== void 0 ? _b : 0);
|
|
259
|
+
finalNormalMap.colorSpace = NoColorSpace;
|
|
260
|
+
finalNormalMap.generateMipmaps = generateMipmaps;
|
|
261
|
+
finalNormalMap.minFilter = resolvedMinFilter;
|
|
262
|
+
finalNormalMap.magFilter = resolvedMagFilter;
|
|
263
|
+
finalNormalMap.needsUpdate = true;
|
|
264
|
+
}, [
|
|
265
|
+
finalNormalMap,
|
|
266
|
+
repeat,
|
|
267
|
+
repeatCount === null || repeatCount === void 0 ? void 0 : repeatCount[0],
|
|
268
|
+
repeatCount === null || repeatCount === void 0 ? void 0 : repeatCount[1],
|
|
269
|
+
offset === null || offset === void 0 ? void 0 : offset[0],
|
|
270
|
+
offset === null || offset === void 0 ? void 0 : offset[1],
|
|
271
|
+
generateMipmaps,
|
|
272
|
+
resolvedMinFilter,
|
|
273
|
+
resolvedMagFilter
|
|
274
|
+
]);
|
|
244
275
|
animatedOffsetRef.current = [(_j = offset === null || offset === void 0 ? void 0 : offset[0]) !== null && _j !== void 0 ? _j : 0, (_k = offset === null || offset === void 0 ? void 0 : offset[1]) !== null && _k !== void 0 ? _k : 0];
|
|
245
276
|
useFrame((_, delta) => {
|
|
246
277
|
var _a, _b;
|
|
@@ -259,22 +290,28 @@ function MaterialComponentView({ properties: rawProps }) {
|
|
|
259
290
|
const sharedProps = Object.assign(Object.assign({ map: finalTexture !== null && finalTexture !== void 0 ? finalTexture : null, side: resolvedSide, onUpdate: (material) => {
|
|
260
291
|
material.needsUpdate = true;
|
|
261
292
|
} }, materialProps), overrides);
|
|
293
|
+
const materialKey = [
|
|
294
|
+
materialType,
|
|
295
|
+
textureName !== null && textureName !== void 0 ? textureName : 'no-texture',
|
|
296
|
+
normalMapTextureName !== null && normalMapTextureName !== void 0 ? normalMapTextureName : 'no-normal',
|
|
297
|
+
].join('|');
|
|
262
298
|
if (materialType === 'basic') {
|
|
263
|
-
return _jsx("meshBasicNodeMaterial", Object.assign({ attach:
|
|
299
|
+
return _jsx("meshBasicNodeMaterial", Object.assign({ attach: attach !== null && attach !== void 0 ? attach : 'material' }, sharedProps), materialKey);
|
|
264
300
|
}
|
|
265
301
|
if (materialType === 'sprite') {
|
|
266
302
|
const spriteTransparent = materialSource.transparent !== false;
|
|
267
|
-
return (_jsx("spriteNodeMaterial", Object.assign({ attach:
|
|
303
|
+
return (_jsx("spriteNodeMaterial", Object.assign({ attach: attach !== null && attach !== void 0 ? attach : 'material', map: finalTexture !== null && finalTexture !== void 0 ? finalTexture : null, color: (_l = materialSource.color) !== null && _l !== void 0 ? _l : '#ffffff', opacity: (_m = materialSource.opacity) !== null && _m !== void 0 ? _m : 1, transparent: spriteTransparent, alphaTest: (_o = materialSource.alphaTest) !== null && _o !== void 0 ? _o : 0, depthTest: (_p = materialSource.depthTest) !== null && _p !== void 0 ? _p : false, depthWrite: (_q = materialSource.depthWrite) !== null && _q !== void 0 ? _q : false, toneMapped: (_r = materialSource.toneMapped) !== null && _r !== void 0 ? _r : true, onUpdate: material => {
|
|
268
304
|
material.needsUpdate = true;
|
|
269
|
-
} }, overrides, { rotation: rotation !== null && rotation !== void 0 ? rotation : 0, sizeAttenuation: sizeAttenuation !== null && sizeAttenuation !== void 0 ? sizeAttenuation : true })));
|
|
305
|
+
} }, overrides, { rotation: rotation !== null && rotation !== void 0 ? rotation : 0, sizeAttenuation: sizeAttenuation !== null && sizeAttenuation !== void 0 ? sizeAttenuation : true }), materialKey));
|
|
270
306
|
}
|
|
271
|
-
return (_jsx("meshStandardNodeMaterial", Object.assign({ attach:
|
|
307
|
+
return (_jsx("meshStandardNodeMaterial", Object.assign({ attach: attach !== null && attach !== void 0 ? attach : 'material' }, sharedProps, { normalMap: finalNormalMap !== null && finalNormalMap !== void 0 ? finalNormalMap : null, normalScale: finalNormalMap ? [(_s = normalScaleProp === null || normalScaleProp === void 0 ? void 0 : normalScaleProp[0]) !== null && _s !== void 0 ? _s : 1, (_t = normalScaleProp === null || normalScaleProp === void 0 ? void 0 : normalScaleProp[1]) !== null && _t !== void 0 ? _t : 1] : undefined }), materialKey));
|
|
272
308
|
}
|
|
273
309
|
const MaterialComponent = {
|
|
274
310
|
name: 'Material',
|
|
275
311
|
Editor: MaterialComponentEditor,
|
|
276
312
|
View: MaterialComponentView,
|
|
277
313
|
defaultProperties: {
|
|
314
|
+
attach: 'material',
|
|
278
315
|
materialType: 'standard',
|
|
279
316
|
color: '#ffffff',
|
|
280
317
|
toneMapped: true,
|
|
@@ -5,9 +5,10 @@ import { Mesh } from 'three';
|
|
|
5
5
|
import { assetRef, assetRefs } from './ComponentRegistry';
|
|
6
6
|
import { BooleanField, FieldGroup, Label, ListEditor, NumberInput, SelectInput, StringField } from './Input';
|
|
7
7
|
import { useAssetRuntime } from '../assetRuntime';
|
|
8
|
-
import { useEditorContext } from '../PrefabEditor';
|
|
8
|
+
import { useEditorContext, useEditorRef } from '../PrefabEditor';
|
|
9
9
|
import { getRepeatAxesFromModelProperties, normalizeRepeatAxes } from '../InstanceProvider';
|
|
10
|
-
import { colors, ui } from '../styles';
|
|
10
|
+
import { base, colors, ui } from '../styles';
|
|
11
|
+
import { decomposeModelToPrefabNodes } from '../modelPrefab';
|
|
11
12
|
const AXIS_OPTIONS = [
|
|
12
13
|
{ value: 'x', label: 'X' },
|
|
13
14
|
{ value: 'y', label: 'Y' },
|
|
@@ -42,8 +43,49 @@ function RepeatAxisEditor({ axes, onChange, positionSnap, }) {
|
|
|
42
43
|
}
|
|
43
44
|
function ModelComponentEditor({ component, node, onUpdate, basePath = "" }) {
|
|
44
45
|
const { positionSnap } = useEditorContext();
|
|
46
|
+
const editor = useEditorRef();
|
|
45
47
|
const repeatAxes = getRepeatAxesFromModelProperties(component.properties);
|
|
46
|
-
|
|
48
|
+
const filename = component.properties.filename;
|
|
49
|
+
const canDecompose = Boolean(node && filename);
|
|
50
|
+
const handleDecompose = () => {
|
|
51
|
+
var _a, _b, _c, _d;
|
|
52
|
+
if (!node || !filename)
|
|
53
|
+
return;
|
|
54
|
+
const model = editor.getModel(filename);
|
|
55
|
+
if (!model) {
|
|
56
|
+
console.warn(`Model is not loaded yet: ${filename}`);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const textureRefs = new Map();
|
|
60
|
+
const decomposed = decomposeModelToPrefabNodes(model, {
|
|
61
|
+
idPrefix: node.id,
|
|
62
|
+
getTexturePath: (texture, usage) => {
|
|
63
|
+
const key = `embedded/${node.id}/${usage}/${texture.uuid}`;
|
|
64
|
+
textureRefs.set(key, texture);
|
|
65
|
+
return key;
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
textureRefs.forEach((texture, path) => {
|
|
69
|
+
editor.addTexture(path, texture);
|
|
70
|
+
});
|
|
71
|
+
const preservedComponents = Object.entries((_a = node.components) !== null && _a !== void 0 ? _a : {}).reduce((result, [key, entry]) => {
|
|
72
|
+
if (!(entry === null || entry === void 0 ? void 0 : entry.type))
|
|
73
|
+
return result;
|
|
74
|
+
if (entry.type === 'Model' || entry.type === 'Geometry' || entry.type === 'BufferGeometry' || entry.type === 'Material') {
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
result[key] = entry;
|
|
78
|
+
return result;
|
|
79
|
+
}, {});
|
|
80
|
+
const decomposedComponents = Object.entries((_b = decomposed.components) !== null && _b !== void 0 ? _b : {}).reduce((result, [key, entry]) => {
|
|
81
|
+
if (!(entry === null || entry === void 0 ? void 0 : entry.type) || entry.type === 'Transform')
|
|
82
|
+
return result;
|
|
83
|
+
result[key] = entry;
|
|
84
|
+
return result;
|
|
85
|
+
}, {});
|
|
86
|
+
editor.replaceNode(node.id, Object.assign(Object.assign({}, node), { name: (_c = node.name) !== null && _c !== void 0 ? _c : decomposed.name, components: Object.assign(Object.assign({}, preservedComponents), decomposedComponents), children: (_d = decomposed.children) !== null && _d !== void 0 ? _d : [] }));
|
|
87
|
+
};
|
|
88
|
+
return (_jsxs(FieldGroup, { children: [_jsx(ModelPicker, { value: filename, onChange: (filename) => onUpdate({ filename }), basePath: basePath, pickerKey: node === null || node === void 0 ? void 0 : node.id }), _jsx("button", { type: "button", style: Object.assign(Object.assign({}, base.btn), { width: '100%' }), onClick: handleDecompose, disabled: !canDecompose, title: canDecompose ? 'Replace this model node with editable geometry and material nodes' : 'Choose a model before decomposing', children: "Decompose Model" }), _jsx(BooleanField, { name: "instanced", label: "Instanced", values: component.properties, onChange: onUpdate, fallback: false }), !component.properties.instanced ? (_jsxs(_Fragment, { children: [_jsx(BooleanField, { name: "emitClickEvent", label: "Emit Click Event", values: component.properties, onChange: onUpdate, fallback: false }), component.properties.emitClickEvent ? (_jsx(StringField, { name: "clickEventName", label: "Click Event Name", values: component.properties, onChange: onUpdate, placeholder: "node:click" })) : null] })) : null, component.properties.instanced && (_jsxs(_Fragment, { children: [_jsx(BooleanField, { name: "repeat", label: "Repeat", values: component.properties, onChange: onUpdate, fallback: false }), component.properties.repeat && (_jsx(RepeatAxisEditor, { axes: repeatAxes, onChange: (nextAxes) => onUpdate({ repeatAxes: nextAxes }), positionSnap: positionSnap }))] }))] }));
|
|
47
89
|
}
|
|
48
90
|
// View for Model component
|
|
49
91
|
function ModelComponentView({ properties, children }) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { useRef } from 'react';
|
|
3
3
|
import { useHelper } from '@react-three/drei';
|
|
4
4
|
import { PointLightHelper } from 'three';
|
|
5
5
|
import { useNode } from '../assetRuntime';
|
|
@@ -25,31 +25,25 @@ function PointLightComponentEditor({ component, onUpdate }) {
|
|
|
25
25
|
function PointLightView({ properties, children }) {
|
|
26
26
|
const { editMode, isSelected } = useNode();
|
|
27
27
|
const merged = mergeWithDefaults(pointLightDefaults, properties);
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
28
|
+
const lightProps = {
|
|
29
|
+
color: merged.color,
|
|
30
|
+
intensity: merged.intensity,
|
|
31
|
+
distance: merged.distance,
|
|
32
|
+
decay: merged.decay,
|
|
33
|
+
castShadow: merged.castShadow,
|
|
34
|
+
"shadow-mapSize-width": merged.shadowMapSize,
|
|
35
|
+
"shadow-mapSize-height": merged.shadowMapSize,
|
|
36
|
+
"shadow-bias": merged.shadowBias,
|
|
37
|
+
"shadow-normalBias": merged.shadowNormalBias,
|
|
38
|
+
"shadow-autoUpdate": merged.shadowAutoUpdate,
|
|
39
|
+
"shadow-camera-near": merged.shadowCameraNear,
|
|
40
|
+
"shadow-camera-far": merged.shadowCameraFar,
|
|
41
|
+
};
|
|
39
42
|
const lightRef = useRef(null);
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
useEffect(() => {
|
|
45
|
-
var _a;
|
|
46
|
-
const shadow = (_a = lightRef.current) === null || _a === void 0 ? void 0 : _a.shadow;
|
|
47
|
-
if (!shadow)
|
|
48
|
-
return;
|
|
49
|
-
shadow.needsUpdate = true;
|
|
50
|
-
shadow.camera.updateProjectionMatrix();
|
|
51
|
-
});
|
|
52
|
-
return (_jsxs(_Fragment, { children: [_jsx("pointLight", { ref: lightRef, color: color, intensity: intensity, distance: distance, decay: decay, castShadow: castShadow, "shadow-mapSize-width": shadowMapSize, "shadow-mapSize-height": shadowMapSize, "shadow-bias": shadowBias, "shadow-normalBias": shadowNormalBias, "shadow-autoUpdate": shadowAutoUpdate, "shadow-camera-near": shadowCameraNear, "shadow-camera-far": shadowCameraFar }), editMode && isSelected ? (_jsxs("mesh", { children: [_jsx("sphereGeometry", { args: [0.2, 10, 8] }), _jsx("meshBasicMaterial", { color: color, wireframe: true })] })) : null, children] }));
|
|
43
|
+
const showHelper = editMode && isSelected && lightRef.current;
|
|
44
|
+
const helperTarget = showHelper && lightRef.current ? { current: lightRef.current } : null;
|
|
45
|
+
useHelper(helperTarget, PointLightHelper, 0.5);
|
|
46
|
+
return (_jsx("group", { children: _jsxs("pointLight", Object.assign({ ref: lightRef }, lightProps, { children: [children, editMode && isSelected && (_jsxs("mesh", { children: [_jsx("sphereGeometry", { args: [0.2, 10, 8] }), _jsx("meshBasicMaterial", { color: merged.color, wireframe: true })] }))] })) }));
|
|
53
47
|
}
|
|
54
48
|
const PointLightComponent = {
|
|
55
49
|
name: 'PointLight',
|