react-three-game 0.0.70 → 0.0.72
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 +9 -5
- package/dist/index.js +5 -2
- package/dist/tools/prefabeditor/EditorTree.js +2 -12
- package/dist/tools/prefabeditor/EditorTreeMenus.js +1 -19
- package/dist/tools/prefabeditor/EditorUI.js +2 -1
- package/dist/tools/prefabeditor/PrefabEditor.d.ts +1 -3
- package/dist/tools/prefabeditor/PrefabEditor.js +23 -42
- package/dist/tools/prefabeditor/PrefabRoot.d.ts +39 -13
- package/dist/tools/prefabeditor/PrefabRoot.js +105 -49
- package/dist/tools/prefabeditor/components/AmbientLightComponent.js +10 -7
- package/dist/tools/prefabeditor/components/CameraComponent.js +11 -15
- package/dist/tools/prefabeditor/components/ClickComponent.js +5 -1
- package/dist/tools/prefabeditor/components/ComponentRegistry.d.ts +15 -1
- package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +127 -53
- package/dist/tools/prefabeditor/components/EnvironmentComponent.js +5 -3
- package/dist/tools/prefabeditor/components/MaterialComponent.js +9 -6
- package/dist/tools/prefabeditor/components/ModelComponent.js +4 -2
- package/dist/tools/prefabeditor/components/PhysicsComponent.js +6 -3
- package/dist/tools/prefabeditor/components/PointLightComponent.d.ts +3 -0
- package/dist/tools/prefabeditor/components/PointLightComponent.js +57 -0
- package/dist/tools/prefabeditor/components/SoundComponent.js +21 -16
- package/dist/tools/prefabeditor/components/SpotLightComponent.js +49 -24
- package/dist/tools/prefabeditor/components/index.js +2 -0
- package/dist/tools/prefabeditor/components/lightUtils.d.ts +13 -0
- package/dist/tools/prefabeditor/components/lightUtils.js +64 -0
- package/dist/tools/prefabeditor/prefab.d.ts +37 -0
- package/dist/tools/prefabeditor/prefab.js +229 -0
- package/dist/tools/prefabeditor/prefabStore.d.ts +3 -16
- package/dist/tools/prefabeditor/prefabStore.js +29 -168
- package/dist/tools/prefabeditor/{sceneApi.d.ts → scene.d.ts} +6 -2
- package/dist/tools/prefabeditor/{sceneApi.js → scene.js} +13 -19
- package/dist/tools/prefabeditor/utils.d.ts +0 -4
- package/dist/tools/prefabeditor/utils.js +0 -37
- package/package.json +1 -1
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Environment } from '@react-three/drei';
|
|
3
3
|
import { FieldGroup, NumberField } from './Input';
|
|
4
|
-
|
|
4
|
+
import { useAssetRuntime } from '../PrefabRoot';
|
|
5
|
+
function EnvironmentView({ properties, children, }) {
|
|
6
|
+
const { getAssetRevision } = useAssetRuntime();
|
|
5
7
|
const { intensity = 1, resolution = 256 } = properties;
|
|
6
|
-
const
|
|
7
|
-
return (_jsx(Environment, { background: true, environmentIntensity: intensity, resolution: resolution, frames:
|
|
8
|
+
const environmentRevision = `${getAssetRevision()}::${intensity}::${resolution}`;
|
|
9
|
+
return (_jsx(Environment, { background: true, environmentIntensity: intensity, resolution: resolution, frames: 1, children: children }, environmentRevision));
|
|
8
10
|
}
|
|
9
11
|
const EnvironmentComponent = {
|
|
10
12
|
name: 'Environment',
|
|
@@ -12,6 +12,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
12
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
13
|
import { extend } from '@react-three/fiber';
|
|
14
14
|
import { FieldRenderer, Label, NumberInput } from './Input';
|
|
15
|
+
import { useAssetRuntime } from '../PrefabRoot';
|
|
15
16
|
import { useMemo } from 'react';
|
|
16
17
|
import { MeshBasicNodeMaterial, MeshStandardNodeMaterial } from 'three/webgpu';
|
|
17
18
|
import { TexturePicker } from '../../assetviewer/page';
|
|
@@ -119,8 +120,10 @@ function MaterialComponentEditor({ component, onUpdate, basePath = "" }) {
|
|
|
119
120
|
return (_jsx(FieldRenderer, { fields: fields, values: component.properties, onChange: onUpdate }));
|
|
120
121
|
}
|
|
121
122
|
// View for Material component
|
|
122
|
-
function MaterialComponentView({ properties
|
|
123
|
-
var _a, _b, _c;
|
|
123
|
+
function MaterialComponentView({ properties: rawProps }) {
|
|
124
|
+
var _a, _b, _c, _d, _e;
|
|
125
|
+
const { getTexture } = useAssetRuntime();
|
|
126
|
+
const properties = rawProps;
|
|
124
127
|
const materialType = (_a = properties === null || properties === void 0 ? void 0 : properties.materialType) !== null && _a !== void 0 ? _a : 'standard';
|
|
125
128
|
const textureName = properties === null || properties === void 0 ? void 0 : properties.texture;
|
|
126
129
|
const repeat = properties === null || properties === void 0 ? void 0 : properties.repeat;
|
|
@@ -128,15 +131,15 @@ function MaterialComponentView({ properties, loadedTextures }) {
|
|
|
128
131
|
const generateMipmaps = (properties === null || properties === void 0 ? void 0 : properties.generateMipmaps) !== false;
|
|
129
132
|
const minFilter = (properties === null || properties === void 0 ? void 0 : properties.minFilter) || 'LinearMipmapLinearFilter';
|
|
130
133
|
const magFilter = (properties === null || properties === void 0 ? void 0 : properties.magFilter) || 'LinearFilter';
|
|
131
|
-
const texture = textureName &&
|
|
134
|
+
const texture = textureName ? (_b = getTexture(textureName)) !== null && _b !== void 0 ? _b : undefined : undefined;
|
|
132
135
|
const normalMapTextureName = properties === null || properties === void 0 ? void 0 : properties.normalMapTexture;
|
|
133
136
|
const normalScaleProp = properties === null || properties === void 0 ? void 0 : properties.normalScale;
|
|
134
|
-
const normalMapTexture = normalMapTextureName &&
|
|
137
|
+
const normalMapTexture = normalMapTextureName ? (_c = getTexture(normalMapTextureName)) !== null && _c !== void 0 ? _c : undefined : undefined;
|
|
135
138
|
const materialSource = properties !== null && properties !== void 0 ? properties : {};
|
|
136
139
|
// Destructure all material props and separate custom texture handling props
|
|
137
140
|
const { texture: _texture, repeat: _repeat, repeatCount: _repeatCount, generateMipmaps: _generateMipmaps, minFilter: _minFilter, magFilter: _magFilter, map: _map, materialType: _materialType, normalMapTexture: _normalMapTexture, normalScale: _normalScale, normalMap: _normalMap, side: sideProp } = materialSource, materialProps = __rest(materialSource, ["texture", "repeat", "repeatCount", "generateMipmaps", "minFilter", "magFilter", "map", "materialType", "normalMapTexture", "normalScale", "normalMap", "side"]);
|
|
138
141
|
const sideMap = { FrontSide, BackSide, DoubleSide };
|
|
139
|
-
const resolvedSide = sideProp ? ((
|
|
142
|
+
const resolvedSide = sideProp ? ((_d = sideMap[sideProp]) !== null && _d !== void 0 ? _d : FrontSide) : FrontSide;
|
|
140
143
|
const minFilterMap = {
|
|
141
144
|
NearestFilter,
|
|
142
145
|
LinearFilter,
|
|
@@ -187,7 +190,7 @@ function MaterialComponentView({ properties, loadedTextures }) {
|
|
|
187
190
|
if (!properties) {
|
|
188
191
|
return _jsx("meshStandardNodeMaterial", { color: "red", wireframe: true });
|
|
189
192
|
}
|
|
190
|
-
const materialKey = `${(
|
|
193
|
+
const materialKey = `${(_e = finalTexture === null || finalTexture === void 0 ? void 0 : finalTexture.uuid) !== null && _e !== void 0 ? _e : 'no-texture'}:${materialProps.transparent ? 'transparent' : 'opaque'}`;
|
|
191
194
|
const sharedProps = Object.assign({ map: finalTexture, side: resolvedSide }, materialProps);
|
|
192
195
|
if (materialType === 'basic') {
|
|
193
196
|
return _jsx("meshBasicNodeMaterial", Object.assign({}, sharedProps), materialKey);
|
|
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { ModelPicker } from '../../assetviewer/page';
|
|
3
3
|
import { useContext, useMemo } from 'react';
|
|
4
4
|
import { BooleanField, FieldGroup, Label, ListEditor, NumberInput, SelectInput } from './Input';
|
|
5
|
+
import { useAssetRuntime } from '../PrefabRoot';
|
|
5
6
|
import { EditorContext } from '../PrefabEditor';
|
|
6
7
|
import { DEFAULT_REPEAT_AXES, getRepeatAxesFromModelProperties, normalizeRepeatAxes } from '../InstanceProvider';
|
|
7
8
|
import { colors } from '../styles';
|
|
@@ -63,11 +64,12 @@ function ModelComponentEditor({ component, node, onUpdate, basePath = "" }) {
|
|
|
63
64
|
return (_jsxs(FieldGroup, { children: [_jsx(ModelPicker, { value: component.properties.filename, onChange: (filename) => onUpdate({ filename }), basePath: basePath, pickerKey: node === null || node === void 0 ? void 0 : node.id }), _jsx(BooleanField, { name: "instanced", label: "Instanced", values: component.properties, onChange: onUpdate, fallback: false }), 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 }))] }))] }));
|
|
64
65
|
}
|
|
65
66
|
// View for Model component
|
|
66
|
-
function ModelComponentView({ properties,
|
|
67
|
+
function ModelComponentView({ properties, children }) {
|
|
68
|
+
const { getModel } = useAssetRuntime();
|
|
67
69
|
// Instanced models are handled elsewhere (GameInstance), so only render non-instanced here
|
|
68
70
|
if (!properties.filename || properties.instanced)
|
|
69
71
|
return _jsx(_Fragment, { children: children });
|
|
70
|
-
const sourceModel =
|
|
72
|
+
const sourceModel = getModel(properties.filename);
|
|
71
73
|
// Clone model once and set up shadows - memoized to avoid cloning on every render
|
|
72
74
|
const clonedModel = useMemo(() => {
|
|
73
75
|
if (!sourceModel)
|
|
@@ -12,6 +12,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
12
12
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
13
13
|
import { CapsuleCollider, RigidBody, useRapier } from "@react-three/rapier";
|
|
14
14
|
import { useRef, useEffect, useCallback } from 'react';
|
|
15
|
+
import { useAssetRuntime, useEntityRuntime } from "../PrefabRoot";
|
|
15
16
|
import { BooleanField, FieldGroup, ListEditor, NumberField, SelectField, SelectInput, StringInput, Vector3Field } from "./Input";
|
|
16
17
|
import { gameEvents, getEntityIdFromRigidBody } from "../GameEvents";
|
|
17
18
|
import { colors } from "../styles";
|
|
@@ -162,7 +163,9 @@ function PhysicsComponentEditor({ component, onUpdate }) {
|
|
|
162
163
|
{ value: 'all', label: 'All (includes kinematic & fixed)' },
|
|
163
164
|
] })] }));
|
|
164
165
|
}
|
|
165
|
-
function PhysicsComponentView({ properties, children, position, rotation, scale
|
|
166
|
+
function PhysicsComponentView({ properties, children, position, rotation, scale }) {
|
|
167
|
+
const { registerRigidBodyRef } = useAssetRuntime();
|
|
168
|
+
const { editMode, nodeId } = useEntityRuntime();
|
|
166
169
|
const { type, colliders, sensor, activeCollisionTypes, linearVelocity = [0, 0, 0], angularVelocity = [0, 0, 0], capsuleRadius = capsuleRadiusFallback, capsuleHalfHeight = capsuleHalfHeightFallback, sensorEnterEventName, sensorExitEventName, collisionEnterEventName, collisionExitEventName, enabledTranslations = enabledAxesFallback, enabledRotations = enabledAxesFallback } = properties, otherProps = __rest(properties, ["type", "colliders", "sensor", "activeCollisionTypes", "linearVelocity", "angularVelocity", "capsuleRadius", "capsuleHalfHeight", "sensorEnterEventName", "sensorExitEventName", "collisionEnterEventName", "collisionExitEventName", "enabledTranslations", "enabledRotations"]);
|
|
167
170
|
const colliderType = colliders || (type === 'fixed' ? 'trimesh' : 'hull');
|
|
168
171
|
const usesManualCapsuleCollider = colliderType === 'capsule';
|
|
@@ -183,11 +186,11 @@ function PhysicsComponentView({ properties, children, position, rotation, scale,
|
|
|
183
186
|
}
|
|
184
187
|
// Register RigidBody ref when it's available
|
|
185
188
|
useEffect(() => {
|
|
186
|
-
if (nodeId &&
|
|
189
|
+
if (nodeId && rigidBodyRef.current) {
|
|
187
190
|
registerRigidBodyRef(nodeId, rigidBodyRef.current);
|
|
188
191
|
}
|
|
189
192
|
return () => {
|
|
190
|
-
if (nodeId
|
|
193
|
+
if (nodeId) {
|
|
191
194
|
registerRigidBodyRef(nodeId, null);
|
|
192
195
|
}
|
|
193
196
|
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
3
|
+
import { useHelper } from '@react-three/drei';
|
|
4
|
+
import { PointLightHelper } from 'three';
|
|
5
|
+
import { useEntityRuntime } from '../PrefabRoot';
|
|
6
|
+
import { BooleanField, ColorField, NumberField } from './Input';
|
|
7
|
+
import { LightSection, ShadowBiasField, mergeWithDefaults } from './lightUtils';
|
|
8
|
+
const pointLightDefaults = {
|
|
9
|
+
color: '#ffffff',
|
|
10
|
+
intensity: 1,
|
|
11
|
+
distance: 0,
|
|
12
|
+
decay: 2,
|
|
13
|
+
castShadow: false,
|
|
14
|
+
shadowMapSize: 512,
|
|
15
|
+
shadowBias: 0,
|
|
16
|
+
shadowNormalBias: 0,
|
|
17
|
+
shadowAutoUpdate: true,
|
|
18
|
+
shadowCameraNear: 0.5,
|
|
19
|
+
shadowCameraFar: 500,
|
|
20
|
+
};
|
|
21
|
+
function PointLightComponentEditor({ component, onUpdate }) {
|
|
22
|
+
const values = mergeWithDefaults(pointLightDefaults, component.properties);
|
|
23
|
+
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(NumberField, { name: "distance", label: "Distance", values: values, onChange: onUpdate, min: 0, step: 1, fallback: 0 }), _jsx(NumberField, { name: "decay", label: "Decay", values: values, onChange: onUpdate, min: 0, step: 0.1, fallback: 2 })] }), _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 })] })) : null] })] }));
|
|
24
|
+
}
|
|
25
|
+
function PointLightView({ properties, children }) {
|
|
26
|
+
const { editMode, isSelected } = useEntityRuntime();
|
|
27
|
+
const merged = mergeWithDefaults(pointLightDefaults, properties);
|
|
28
|
+
const color = merged.color;
|
|
29
|
+
const intensity = merged.intensity;
|
|
30
|
+
const distance = merged.distance;
|
|
31
|
+
const decay = merged.decay;
|
|
32
|
+
const castShadow = merged.castShadow;
|
|
33
|
+
const shadowMapSize = merged.shadowMapSize;
|
|
34
|
+
const shadowBias = merged.shadowBias;
|
|
35
|
+
const shadowNormalBias = merged.shadowNormalBias;
|
|
36
|
+
const shadowAutoUpdate = merged.shadowAutoUpdate;
|
|
37
|
+
const shadowCameraNear = merged.shadowCameraNear;
|
|
38
|
+
const shadowCameraFar = merged.shadowCameraFar;
|
|
39
|
+
const lightRef = useRef(null);
|
|
40
|
+
useHelper(editMode && isSelected ? lightRef : null, PointLightHelper, 0.5, color);
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
var _a;
|
|
43
|
+
const shadow = (_a = lightRef.current) === null || _a === void 0 ? void 0 : _a.shadow;
|
|
44
|
+
if (!shadow)
|
|
45
|
+
return;
|
|
46
|
+
shadow.needsUpdate = true;
|
|
47
|
+
shadow.camera.updateProjectionMatrix();
|
|
48
|
+
}, [castShadow, shadowMapSize, shadowBias, shadowNormalBias, shadowAutoUpdate, shadowCameraNear, shadowCameraFar]);
|
|
49
|
+
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] }));
|
|
50
|
+
}
|
|
51
|
+
const PointLightComponent = {
|
|
52
|
+
name: 'PointLight',
|
|
53
|
+
Editor: PointLightComponentEditor,
|
|
54
|
+
View: PointLightView,
|
|
55
|
+
defaultProperties: {},
|
|
56
|
+
};
|
|
57
|
+
export default PointLightComponent;
|
|
@@ -4,6 +4,7 @@ import { useThree } from '@react-three/fiber';
|
|
|
4
4
|
import { SoundPicker } from '../../assetviewer/page';
|
|
5
5
|
import { sound as soundManager } from '../../../helpers/SoundManager';
|
|
6
6
|
import { gameEvents } from '../GameEvents';
|
|
7
|
+
import { useAssetRuntime, useEntityRuntime } from '../PrefabRoot';
|
|
7
8
|
import { BooleanField, FieldGroup, FieldRenderer, ListEditor, NumberField, SelectField, StringField } from './Input';
|
|
8
9
|
import { colors } from '../styles';
|
|
9
10
|
import { AudioListener } from 'three';
|
|
@@ -44,12 +45,12 @@ function getVolumeValue(properties) {
|
|
|
44
45
|
}
|
|
45
46
|
return Number.isFinite(properties.volume) ? Number(properties.volume) : 1;
|
|
46
47
|
}
|
|
47
|
-
function resolveClipPaths({
|
|
48
|
+
function resolveClipPaths({ clips, clipMode }) {
|
|
48
49
|
const normalizedClips = normalizeClips(clips);
|
|
49
50
|
if (normalizedClips.length > 0) {
|
|
50
51
|
return { paths: normalizedClips, mode: clipMode !== null && clipMode !== void 0 ? clipMode : 'random' };
|
|
51
52
|
}
|
|
52
|
-
return
|
|
53
|
+
return { paths: [], mode: 'single' };
|
|
53
54
|
}
|
|
54
55
|
function pickClip(paths, mode, sequenceIndexRef) {
|
|
55
56
|
if (paths.length <= 1 || mode === 'single') {
|
|
@@ -63,22 +64,27 @@ function pickClip(paths, mode, sequenceIndexRef) {
|
|
|
63
64
|
return paths[Math.floor(Math.random() * paths.length)];
|
|
64
65
|
}
|
|
65
66
|
function SoundComponentEditor({ component, onUpdate, basePath = '' }) {
|
|
66
|
-
const clips =
|
|
67
|
+
const clips = Array.isArray(component.properties.clips)
|
|
68
|
+
? component.properties.clips.map((clip) => typeof clip === 'string' ? clip : '')
|
|
69
|
+
: [];
|
|
67
70
|
const randomizePitch = Boolean(component.properties.randomizePitch);
|
|
68
71
|
const randomizeVolume = Boolean(component.properties.randomizeVolume);
|
|
69
72
|
const positional = Boolean(component.properties.positional);
|
|
73
|
+
const setClips = (nextClips) => {
|
|
74
|
+
onUpdate({ clips: nextClips });
|
|
75
|
+
};
|
|
70
76
|
const addClip = () => {
|
|
71
|
-
|
|
77
|
+
setClips([...clips, '']);
|
|
72
78
|
};
|
|
73
79
|
const updateClip = (index, nextPath) => {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
80
|
+
const nextClips = [...clips];
|
|
81
|
+
nextClips[index] = nextPath;
|
|
82
|
+
setClips(nextClips);
|
|
77
83
|
};
|
|
78
84
|
const removeClip = (index) => {
|
|
79
|
-
|
|
85
|
+
setClips(clips.filter((_, clipIndex) => clipIndex !== index));
|
|
80
86
|
};
|
|
81
|
-
return (_jsxs(FieldGroup, { children: [_jsx(
|
|
87
|
+
return (_jsxs(FieldGroup, { children: [_jsx(StringField, { name: "eventName", label: "Listen Event", values: component.properties, onChange: onUpdate, placeholder: "click" }), _jsx(FieldRenderer, { fields: [
|
|
82
88
|
{
|
|
83
89
|
name: 'clipMode',
|
|
84
90
|
label: 'Clip Mode',
|
|
@@ -117,7 +123,9 @@ function payloadMatchesNode(nodeId, payload) {
|
|
|
117
123
|
const hasEntityIds = ids.some(id => typeof id === 'string');
|
|
118
124
|
return hasEntityIds ? ids.includes(nodeId) : true;
|
|
119
125
|
}
|
|
120
|
-
function SoundComponentView({ properties,
|
|
126
|
+
function SoundComponentView({ properties, children }) {
|
|
127
|
+
const { getSound } = useAssetRuntime();
|
|
128
|
+
const { editMode, nodeId } = useEntityRuntime();
|
|
121
129
|
const { camera } = useThree();
|
|
122
130
|
const { eventName, positional = false, refDistance = 1, maxDistance = 24, rolloffFactor = 1, distanceModel = 'inverse' } = properties;
|
|
123
131
|
const sequenceIndexRef = useRef(0);
|
|
@@ -170,7 +178,7 @@ function SoundComponentView({ properties, editMode, nodeId, children, loadedSoun
|
|
|
170
178
|
const pitch = getPitchValue(properties);
|
|
171
179
|
const volume = getVolumeValue(properties);
|
|
172
180
|
if (!positional) {
|
|
173
|
-
const loadedBuffer =
|
|
181
|
+
const loadedBuffer = getSound(clip);
|
|
174
182
|
if (loadedBuffer && !soundManager.hasBuffer(clip)) {
|
|
175
183
|
soundManager.setBuffer(clip, loadedBuffer);
|
|
176
184
|
}
|
|
@@ -183,7 +191,7 @@ function SoundComponentView({ properties, editMode, nodeId, children, loadedSoun
|
|
|
183
191
|
}
|
|
184
192
|
const audio = positionalAudioRef.current;
|
|
185
193
|
const listener = listenerRef.current;
|
|
186
|
-
const buffer =
|
|
194
|
+
const buffer = getSound(clip);
|
|
187
195
|
if (!audio || !listener || !buffer) {
|
|
188
196
|
return;
|
|
189
197
|
}
|
|
@@ -197,7 +205,7 @@ function SoundComponentView({ properties, editMode, nodeId, children, loadedSoun
|
|
|
197
205
|
audio.setVolume(volume);
|
|
198
206
|
audio.play();
|
|
199
207
|
});
|
|
200
|
-
}, [editMode, eventName,
|
|
208
|
+
}, [editMode, eventName, getSound, mode, nodeId, paths, positional, properties]);
|
|
201
209
|
return (_jsxs(_Fragment, { children: [positional && listenerRef.current ? _jsx("positionalAudio", { ref: positionalAudioRef, args: [listenerRef.current] }) : null, children] }));
|
|
202
210
|
}
|
|
203
211
|
const SoundComponent = {
|
|
@@ -205,7 +213,6 @@ const SoundComponent = {
|
|
|
205
213
|
Editor: SoundComponentEditor,
|
|
206
214
|
View: SoundComponentView,
|
|
207
215
|
defaultProperties: {
|
|
208
|
-
path: '',
|
|
209
216
|
eventName: '',
|
|
210
217
|
clips: [],
|
|
211
218
|
clipMode: 'single',
|
|
@@ -225,8 +232,6 @@ const SoundComponent = {
|
|
|
225
232
|
},
|
|
226
233
|
getAssetRefs: (properties) => {
|
|
227
234
|
const refs = [];
|
|
228
|
-
if (properties.path)
|
|
229
|
-
refs.push({ type: 'sound', path: properties.path });
|
|
230
235
|
if (Array.isArray(properties.clips)) {
|
|
231
236
|
properties.clips.forEach((clip) => {
|
|
232
237
|
if (typeof clip === 'string' && clip.trim().length > 0) {
|
|
@@ -1,57 +1,82 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { useHelper } from "@react-three/drei";
|
|
3
|
+
import { useRef, useEffect } from "react";
|
|
4
|
+
import { BooleanField, ColorField, Label, NumberField, Vector3Input } from "./Input";
|
|
4
5
|
import { SpotLightHelper } from "three";
|
|
6
|
+
import { useAssetRuntime, useEntityRuntime } from "../PrefabRoot";
|
|
5
7
|
import { useFrame } from "@react-three/fiber";
|
|
6
8
|
import { TexturePicker } from "../../assetviewer/page";
|
|
9
|
+
import { LightSection, ShadowBiasField, mergeWithDefaults } from "./lightUtils";
|
|
7
10
|
const spotLightDefaults = {
|
|
8
11
|
color: '#ffffff',
|
|
9
|
-
intensity:
|
|
10
|
-
angle:
|
|
11
|
-
penumbra: 0
|
|
12
|
-
distance:
|
|
13
|
-
|
|
12
|
+
intensity: 1,
|
|
13
|
+
angle: Math.PI / 3,
|
|
14
|
+
penumbra: 0,
|
|
15
|
+
distance: 0,
|
|
16
|
+
decay: 2,
|
|
17
|
+
castShadow: false,
|
|
18
|
+
shadowMapSize: 512,
|
|
19
|
+
shadowBias: 0,
|
|
20
|
+
shadowNormalBias: 0,
|
|
21
|
+
shadowAutoUpdate: true,
|
|
22
|
+
shadowCameraNear: 0.5,
|
|
23
|
+
shadowCameraFar: 500,
|
|
24
|
+
targetOffset: [0, -5, 0],
|
|
25
|
+
map: undefined,
|
|
14
26
|
};
|
|
15
27
|
function SpotLightComponentEditor({ component, onUpdate, basePath = "" }) {
|
|
16
|
-
const values =
|
|
17
|
-
return (_jsxs(
|
|
28
|
+
const values = mergeWithDefaults(spotLightDefaults, component.properties);
|
|
29
|
+
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(NumberField, { name: "angle", label: "Angle", values: values, onChange: onUpdate, min: 0, max: Math.PI / 2, step: 0.05, fallback: Math.PI / 3 }), _jsx(NumberField, { name: "penumbra", label: "Penumbra", values: values, onChange: onUpdate, min: 0, max: 1, step: 0.05, fallback: 0 }), _jsx(NumberField, { name: "distance", label: "Distance", values: values, onChange: onUpdate, min: 0, step: 1, fallback: 0 }), _jsx(NumberField, { name: "decay", label: "Decay", values: values, onChange: onUpdate, min: 0, step: 0.1, fallback: 2 }), _jsx(Vector3Input, { label: "Target Offset", value: values.targetOffset, onChange: targetOffset => onUpdate({ targetOffset }), snap: 0.5 }), _jsxs("div", { children: [_jsx(Label, { children: "Texture Map" }), _jsx(TexturePicker, { value: values.map, onChange: (map) => onUpdate({ map }), basePath: basePath })] })] }), _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 })] })) : null] })] }));
|
|
18
30
|
}
|
|
19
|
-
function SpotLightView({ properties, children
|
|
20
|
-
|
|
31
|
+
function SpotLightView({ properties, children }) {
|
|
32
|
+
var _a;
|
|
33
|
+
const { getTexture } = useAssetRuntime();
|
|
34
|
+
const { editMode, isSelected } = useEntityRuntime();
|
|
35
|
+
const merged = mergeWithDefaults(spotLightDefaults, properties);
|
|
21
36
|
const color = merged.color;
|
|
22
37
|
const intensity = merged.intensity;
|
|
23
38
|
const angle = merged.angle;
|
|
24
39
|
const penumbra = merged.penumbra;
|
|
25
40
|
const distance = merged.distance;
|
|
41
|
+
const decay = merged.decay;
|
|
26
42
|
const castShadow = merged.castShadow;
|
|
27
|
-
const
|
|
43
|
+
const shadowMapSize = merged.shadowMapSize;
|
|
44
|
+
const shadowBias = merged.shadowBias;
|
|
45
|
+
const shadowNormalBias = merged.shadowNormalBias;
|
|
46
|
+
const shadowAutoUpdate = merged.shadowAutoUpdate;
|
|
47
|
+
const shadowCameraNear = merged.shadowCameraNear;
|
|
48
|
+
const shadowCameraFar = merged.shadowCameraFar;
|
|
49
|
+
const targetOffset = merged.targetOffset;
|
|
50
|
+
const textureMap = merged.map ? (_a = getTexture(merged.map)) !== null && _a !== void 0 ? _a : undefined : undefined;
|
|
28
51
|
const spotLightRef = useRef(null);
|
|
29
52
|
const targetRef = useRef(null);
|
|
30
|
-
|
|
31
|
-
const spotLightHelper = useMemo(() => spotLight ? new SpotLightHelper(spotLight, color) : null, [spotLight, color]);
|
|
32
|
-
useEffect(() => {
|
|
33
|
-
return () => {
|
|
34
|
-
spotLightHelper === null || spotLightHelper === void 0 ? void 0 : spotLightHelper.dispose();
|
|
35
|
-
};
|
|
36
|
-
}, [spotLightHelper]);
|
|
53
|
+
useHelper(editMode && isSelected ? spotLightRef : null, SpotLightHelper, color);
|
|
37
54
|
useEffect(() => {
|
|
38
55
|
if (spotLightRef.current && targetRef.current) {
|
|
39
56
|
spotLightRef.current.target = targetRef.current;
|
|
40
|
-
setSpotLight(spotLightRef.current);
|
|
41
57
|
}
|
|
42
58
|
}, []);
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
var _a;
|
|
61
|
+
const shadow = (_a = spotLightRef.current) === null || _a === void 0 ? void 0 : _a.shadow;
|
|
62
|
+
if (!shadow)
|
|
63
|
+
return;
|
|
64
|
+
shadow.needsUpdate = true;
|
|
65
|
+
shadow.camera.updateProjectionMatrix();
|
|
66
|
+
}, [castShadow, shadowMapSize, shadowBias, shadowNormalBias, shadowAutoUpdate, shadowCameraNear, shadowCameraFar]);
|
|
43
67
|
useFrame(() => {
|
|
44
|
-
|
|
45
|
-
|
|
68
|
+
var _a;
|
|
69
|
+
if ((_a = spotLightRef.current) === null || _a === void 0 ? void 0 : _a.target) {
|
|
70
|
+
spotLightRef.current.target.updateMatrixWorld();
|
|
46
71
|
}
|
|
47
72
|
});
|
|
48
|
-
return (_jsxs(_Fragment, { children: [_jsx("spotLight", { ref: spotLightRef, color: color, intensity: intensity, angle: angle, penumbra: penumbra, distance: distance, map: textureMap, castShadow: castShadow, "shadow-mapSize-width":
|
|
73
|
+
return (_jsxs(_Fragment, { children: [_jsx("spotLight", { ref: spotLightRef, color: color, intensity: intensity, angle: angle, penumbra: penumbra, distance: distance, decay: decay, map: textureMap, 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 }), _jsx("object3D", { ref: targetRef, position: targetOffset }), editMode && isSelected && (_jsxs(_Fragment, { children: [_jsxs("mesh", { children: [_jsx("sphereGeometry", { args: [0.2, 8, 6] }), _jsx("meshBasicMaterial", { color: color, wireframe: true })] }), _jsxs("mesh", { position: targetOffset, children: [_jsx("sphereGeometry", { args: [0.15, 8, 6] }), _jsx("meshBasicMaterial", { color: color, wireframe: true, opacity: 0.5, transparent: true })] })] })), children] }));
|
|
49
74
|
}
|
|
50
75
|
const SpotLightComponent = {
|
|
51
76
|
name: 'SpotLight',
|
|
52
77
|
Editor: SpotLightComponentEditor,
|
|
53
78
|
View: SpotLightView,
|
|
54
|
-
defaultProperties:
|
|
79
|
+
defaultProperties: {},
|
|
55
80
|
getAssetRefs: (properties) => {
|
|
56
81
|
if (properties.map)
|
|
57
82
|
return [{ type: 'texture', path: properties.map }];
|
|
@@ -3,6 +3,7 @@ import TransformComponent from './TransformComponent';
|
|
|
3
3
|
import MaterialComponent from './MaterialComponent';
|
|
4
4
|
import PhysicsComponent from './PhysicsComponent';
|
|
5
5
|
import SpotLightComponent from './SpotLightComponent';
|
|
6
|
+
import PointLightComponent from './PointLightComponent';
|
|
6
7
|
import DirectionalLightComponent from './DirectionalLightComponent';
|
|
7
8
|
import AmbientLightComponent from './AmbientLightComponent';
|
|
8
9
|
import ModelComponent from './ModelComponent';
|
|
@@ -17,6 +18,7 @@ export default [
|
|
|
17
18
|
MaterialComponent,
|
|
18
19
|
PhysicsComponent,
|
|
19
20
|
SpotLightComponent,
|
|
21
|
+
PointLightComponent,
|
|
20
22
|
DirectionalLightComponent,
|
|
21
23
|
AmbientLightComponent,
|
|
22
24
|
ModelComponent,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
export declare function mergeWithDefaults<T extends Record<string, any>>(defaults: T, properties?: Record<string, any> | null): T;
|
|
3
|
+
export declare function LightSection({ title, children }: {
|
|
4
|
+
title: string;
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export declare function ShadowBiasField({ name, label, values, onChange, fallback, }: {
|
|
8
|
+
name: string;
|
|
9
|
+
label: string;
|
|
10
|
+
values: Record<string, any>;
|
|
11
|
+
onChange: (values: Record<string, any>) => void;
|
|
12
|
+
fallback?: number;
|
|
13
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { colors } from '../styles';
|
|
4
|
+
import { FieldGroup, FieldRow, NumberInput } from './Input';
|
|
5
|
+
export function mergeWithDefaults(defaults, properties) {
|
|
6
|
+
const merged = Object.assign({}, defaults);
|
|
7
|
+
if (!properties) {
|
|
8
|
+
return merged;
|
|
9
|
+
}
|
|
10
|
+
for (const [key, value] of Object.entries(properties)) {
|
|
11
|
+
if (value !== undefined) {
|
|
12
|
+
merged[key] = value;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return merged;
|
|
16
|
+
}
|
|
17
|
+
export function LightSection({ title, children }) {
|
|
18
|
+
return (_jsxs("div", { style: {
|
|
19
|
+
display: 'flex',
|
|
20
|
+
flexDirection: 'column',
|
|
21
|
+
gap: 8,
|
|
22
|
+
padding: '8px 10px',
|
|
23
|
+
border: `1px solid ${colors.border}`,
|
|
24
|
+
borderRadius: 6,
|
|
25
|
+
background: colors.bgSurface,
|
|
26
|
+
}, children: [_jsx("div", { style: {
|
|
27
|
+
fontSize: 10,
|
|
28
|
+
textTransform: 'uppercase',
|
|
29
|
+
letterSpacing: '0.08em',
|
|
30
|
+
color: colors.textMuted,
|
|
31
|
+
fontWeight: 600,
|
|
32
|
+
}, children: title }), _jsx(FieldGroup, { children: children })] }));
|
|
33
|
+
}
|
|
34
|
+
const shadowBiasSteps = [0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001];
|
|
35
|
+
function getBiasStep(value) {
|
|
36
|
+
var _a;
|
|
37
|
+
const absValue = Math.abs(value);
|
|
38
|
+
if (absValue === 0) {
|
|
39
|
+
return 0.001;
|
|
40
|
+
}
|
|
41
|
+
return (_a = shadowBiasSteps.find(step => absValue >= step)) !== null && _a !== void 0 ? _a : shadowBiasSteps[shadowBiasSteps.length - 1];
|
|
42
|
+
}
|
|
43
|
+
function formatBiasStep(step) {
|
|
44
|
+
return step.toLocaleString('en-US', {
|
|
45
|
+
minimumFractionDigits: 0,
|
|
46
|
+
maximumFractionDigits: 6,
|
|
47
|
+
useGrouping: false,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
export function ShadowBiasField({ name, label, values, onChange, fallback = 0, }) {
|
|
51
|
+
var _a;
|
|
52
|
+
const value = (_a = values[name]) !== null && _a !== void 0 ? _a : fallback;
|
|
53
|
+
const [step, setStep] = useState(() => getBiasStep(value));
|
|
54
|
+
return (_jsx(FieldRow, { label: label, children: _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 6 }, children: [_jsx(NumberInput, { value: value, onChange: nextValue => onChange({ [name]: nextValue }), step: step, min: -0.1, max: 0.1, style: { width: 92 } }), _jsx("select", { value: step.toString(), onChange: event => setStep(Number(event.target.value)), style: {
|
|
55
|
+
width: 78,
|
|
56
|
+
backgroundColor: colors.bgInput,
|
|
57
|
+
border: `1px solid ${colors.border}`,
|
|
58
|
+
color: colors.text,
|
|
59
|
+
borderRadius: 3,
|
|
60
|
+
fontSize: 11,
|
|
61
|
+
padding: '3px 6px',
|
|
62
|
+
fontFamily: 'monospace',
|
|
63
|
+
}, title: "Bias scrub step", children: shadowBiasSteps.map(option => (_jsx("option", { value: option, children: formatBiasStep(option) }, option))) })] }) }));
|
|
64
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ComponentData, GameObject, Prefab } from './types';
|
|
2
|
+
export type PrefabNodeRecord = Omit<GameObject, 'children'>;
|
|
3
|
+
export type PrefabAssetRefCounts = Record<string, number>;
|
|
4
|
+
export interface PrefabState {
|
|
5
|
+
prefabId?: string;
|
|
6
|
+
prefabName?: string;
|
|
7
|
+
rootId: string;
|
|
8
|
+
nodesById: Record<string, PrefabNodeRecord>;
|
|
9
|
+
childIdsById: Record<string, string[]>;
|
|
10
|
+
parentIdById: Record<string, string | null>;
|
|
11
|
+
revision: number;
|
|
12
|
+
assetManifestKey: string;
|
|
13
|
+
assetRefCounts: PrefabAssetRefCounts;
|
|
14
|
+
}
|
|
15
|
+
export declare function createDefaultComponentProperties(type: string): Record<string, any>;
|
|
16
|
+
export declare function createComponentData(type: string, properties?: Record<string, any>): ComponentData;
|
|
17
|
+
export declare function createNode(name: string, components?: Record<string, {
|
|
18
|
+
type: string;
|
|
19
|
+
properties?: Record<string, any>;
|
|
20
|
+
}>, options?: {
|
|
21
|
+
id?: string;
|
|
22
|
+
children?: GameObject[];
|
|
23
|
+
}): GameObject;
|
|
24
|
+
export declare function createEmptyNode(name?: string): GameObject;
|
|
25
|
+
export declare function createEmptyPrefab(): Prefab;
|
|
26
|
+
export declare function createModelNode(filename: string, name?: string): GameObject;
|
|
27
|
+
export declare function createImageNode(texturePath: string, name?: string): GameObject;
|
|
28
|
+
export declare function normalizePrefab(prefab: Prefab, revision?: number): PrefabState;
|
|
29
|
+
export declare function createPrefabPatch(state: PrefabState, patch: Partial<PrefabState>, nextAssetRefCounts?: PrefabAssetRefCounts): Partial<PrefabState>;
|
|
30
|
+
export declare function denormalizePrefab(state: Pick<PrefabState, 'prefabId' | 'prefabName' | 'rootId' | 'nodesById' | 'childIdsById'>): Prefab;
|
|
31
|
+
export declare function collectSubtreeIds(id: string, childIdsById: Record<string, string[]>): string[];
|
|
32
|
+
export declare function insertSubtree(node: GameObject, parentId: string | null, nodesById: Record<string, PrefabNodeRecord>, childIdsById: Record<string, string[]>, parentIdById: Record<string, string | null>): void;
|
|
33
|
+
export declare function cloneSubtree(id: string, parentId: string | null, source: Pick<PrefabState, 'nodesById' | 'childIdsById'>, nodesById: Record<string, PrefabNodeRecord>, childIdsById: Record<string, string[]>, parentIdById: Record<string, string | null>): string | null;
|
|
34
|
+
export declare function isDescendant(id: string, potentialAncestorId: string, parentIdById: Record<string, string | null>): boolean;
|
|
35
|
+
export declare function updateAssetRefsForNodeChange(assetRefCounts: PrefabAssetRefCounts, currentNode: PrefabNodeRecord, nextNode: PrefabNodeRecord): PrefabAssetRefCounts;
|
|
36
|
+
export declare function collectSubtreeAssetRefs(node: GameObject): string[];
|
|
37
|
+
export declare function collectAssetRefsForIds(ids: string[], nodesById: Record<string, PrefabNodeRecord>): string[];
|