react-three-game 0.0.60 → 0.0.61
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/package.json +9 -3
- package/.gitattributes +0 -2
- package/.github/copilot-instructions.md +0 -83
- package/.github/workflows/nextjs.yml +0 -99
- package/.gitmodules +0 -3
- package/assets/architecture.png +0 -0
- package/assets/editor.gif +0 -0
- package/assets/favicon.ico +0 -0
- package/assets/react-three-game-logo.png +0 -0
- package/dist/tools/dragdrop/page.d.ts +0 -1
- package/dist/tools/dragdrop/page.js +0 -11
- package/dist/tools/prefabeditor/EntityEvents.d.ts +0 -54
- package/dist/tools/prefabeditor/EntityEvents.js +0 -85
- package/dist/tools/prefabeditor/page.d.ts +0 -1
- package/dist/tools/prefabeditor/page.js +0 -5
- package/react-three-game-skill/.gitattributes +0 -2
- package/react-three-game-skill/README.md +0 -7
- package/react-three-game-skill/react-three-game/SKILL.md +0 -514
- package/react-three-game-skill/react-three-game/rules/ADVANCED_PHYSICS.md +0 -472
- package/react-three-game-skill/react-three-game/rules/LIGHTING.md +0 -6
- package/src/helpers/SoundManager.ts +0 -130
- package/src/helpers/index.ts +0 -91
- package/src/index.ts +0 -59
- package/src/shared/ContactShadow.tsx +0 -74
- package/src/shared/GameCanvas.tsx +0 -52
- package/src/tools/assetviewer/page.tsx +0 -425
- package/src/tools/dragdrop/DragDropLoader.tsx +0 -159
- package/src/tools/dragdrop/index.ts +0 -4
- package/src/tools/dragdrop/modelLoader.ts +0 -204
- package/src/tools/dragdrop/page.tsx +0 -45
- package/src/tools/prefabeditor/Dropdown.tsx +0 -112
- package/src/tools/prefabeditor/EditorContext.tsx +0 -25
- package/src/tools/prefabeditor/EditorTree.tsx +0 -452
- package/src/tools/prefabeditor/EditorTreeMenus.tsx +0 -307
- package/src/tools/prefabeditor/EditorUI.tsx +0 -204
- package/src/tools/prefabeditor/EventSystem.tsx +0 -36
- package/src/tools/prefabeditor/GameEvents.ts +0 -191
- package/src/tools/prefabeditor/InstanceProvider.tsx +0 -466
- package/src/tools/prefabeditor/PrefabEditor.tsx +0 -256
- package/src/tools/prefabeditor/PrefabRoot.tsx +0 -767
- package/src/tools/prefabeditor/components/AmbientLightComponent.tsx +0 -34
- package/src/tools/prefabeditor/components/CameraComponent.tsx +0 -117
- package/src/tools/prefabeditor/components/ComponentRegistry.ts +0 -40
- package/src/tools/prefabeditor/components/DirectionalLightComponent.tsx +0 -210
- package/src/tools/prefabeditor/components/EnvironmentComponent.tsx +0 -47
- package/src/tools/prefabeditor/components/GeometryComponent.tsx +0 -133
- package/src/tools/prefabeditor/components/Input.tsx +0 -820
- package/src/tools/prefabeditor/components/MaterialComponent.tsx +0 -431
- package/src/tools/prefabeditor/components/ModelComponent.tsx +0 -176
- package/src/tools/prefabeditor/components/PhysicsComponent.tsx +0 -188
- package/src/tools/prefabeditor/components/SpotLightComponent.tsx +0 -109
- package/src/tools/prefabeditor/components/TextComponent.tsx +0 -137
- package/src/tools/prefabeditor/components/TransformComponent.tsx +0 -173
- package/src/tools/prefabeditor/components/index.ts +0 -26
- package/src/tools/prefabeditor/page.tsx +0 -10
- package/src/tools/prefabeditor/styles.ts +0 -235
- package/src/tools/prefabeditor/types.ts +0 -20
- package/src/tools/prefabeditor/utils.ts +0 -312
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { Component } from "./ComponentRegistry";
|
|
2
|
-
import { ColorField, FieldGroup, NumberField } from "./Input";
|
|
3
|
-
|
|
4
|
-
function AmbientLightComponentEditor({
|
|
5
|
-
component,
|
|
6
|
-
onUpdate,
|
|
7
|
-
}: {
|
|
8
|
-
component: any;
|
|
9
|
-
onUpdate: (newProps: any) => void;
|
|
10
|
-
}) {
|
|
11
|
-
return (
|
|
12
|
-
<FieldGroup>
|
|
13
|
-
<ColorField name="color" label="Color" values={component.properties} onChange={onUpdate} />
|
|
14
|
-
<NumberField name="intensity" label="Intensity" values={component.properties} onChange={onUpdate} min={0} step={0.1} fallback={1} />
|
|
15
|
-
</FieldGroup>
|
|
16
|
-
);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function AmbientLightComponentView({ properties }: { properties: any }) {
|
|
20
|
-
const { color = '#ffffff', intensity = 1 } = properties;
|
|
21
|
-
return <ambientLight color={color} intensity={intensity} />;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const AmbientLightComponent: Component = {
|
|
25
|
-
name: 'AmbientLight',
|
|
26
|
-
Editor: AmbientLightComponentEditor,
|
|
27
|
-
View: AmbientLightComponentView,
|
|
28
|
-
defaultProperties: {
|
|
29
|
-
color: '#ffffff',
|
|
30
|
-
intensity: 1,
|
|
31
|
-
},
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export default AmbientLightComponent;
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import { PerspectiveCamera } from '@react-three/drei';
|
|
2
|
-
import { useEffect, useMemo, useState } from 'react';
|
|
3
|
-
import { CameraHelper, PerspectiveCamera as ThreePerspectiveCamera } from 'three';
|
|
4
|
-
import { useFrame } from '@react-three/fiber';
|
|
5
|
-
import { Component } from './ComponentRegistry';
|
|
6
|
-
import { FieldGroup, NumberField } from './Input';
|
|
7
|
-
|
|
8
|
-
const cameraDefaults = {
|
|
9
|
-
fov: 50,
|
|
10
|
-
near: 0.1,
|
|
11
|
-
zoom: 1,
|
|
12
|
-
far: 1000,
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
function CameraComponentEditor({ component, onUpdate }: { component: any; onUpdate: (newComp: any) => void }) {
|
|
16
|
-
const values = { ...cameraDefaults, ...component.properties };
|
|
17
|
-
|
|
18
|
-
return (
|
|
19
|
-
<FieldGroup>
|
|
20
|
-
<NumberField
|
|
21
|
-
name="fov"
|
|
22
|
-
label="FOV"
|
|
23
|
-
values={values}
|
|
24
|
-
onChange={onUpdate}
|
|
25
|
-
fallback={50}
|
|
26
|
-
min={1}
|
|
27
|
-
max={179}
|
|
28
|
-
step={1}
|
|
29
|
-
/>
|
|
30
|
-
<NumberField
|
|
31
|
-
name="near"
|
|
32
|
-
label="Near"
|
|
33
|
-
values={values}
|
|
34
|
-
onChange={onUpdate}
|
|
35
|
-
fallback={0.1}
|
|
36
|
-
min={0.001}
|
|
37
|
-
step={0.1}
|
|
38
|
-
/>
|
|
39
|
-
<NumberField
|
|
40
|
-
name="zoom"
|
|
41
|
-
label="Zoom"
|
|
42
|
-
values={values}
|
|
43
|
-
onChange={onUpdate}
|
|
44
|
-
fallback={1}
|
|
45
|
-
min={0.01}
|
|
46
|
-
step={0.1}
|
|
47
|
-
/>
|
|
48
|
-
<NumberField
|
|
49
|
-
name="far"
|
|
50
|
-
label="Far"
|
|
51
|
-
values={values}
|
|
52
|
-
onChange={onUpdate}
|
|
53
|
-
fallback={1000}
|
|
54
|
-
min={0.1}
|
|
55
|
-
step={1}
|
|
56
|
-
/>
|
|
57
|
-
</FieldGroup>
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function CameraComponentView({ properties, editMode, isSelected }: { properties: any; editMode?: boolean; isSelected?: boolean }) {
|
|
62
|
-
const merged = { ...cameraDefaults, ...properties };
|
|
63
|
-
const fov = merged.fov;
|
|
64
|
-
const near = merged.near;
|
|
65
|
-
const zoom = merged.zoom;
|
|
66
|
-
const far = merged.far;
|
|
67
|
-
const [camera, setCamera] = useState<ThreePerspectiveCamera | null>(null);
|
|
68
|
-
const cameraHelper = useMemo(
|
|
69
|
-
() => camera ? new CameraHelper(camera) : null,
|
|
70
|
-
[camera]
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
useEffect(() => {
|
|
74
|
-
return () => {
|
|
75
|
-
cameraHelper?.dispose();
|
|
76
|
-
};
|
|
77
|
-
}, [cameraHelper]);
|
|
78
|
-
|
|
79
|
-
useFrame(() => {
|
|
80
|
-
if (camera && cameraHelper && editMode && isSelected) {
|
|
81
|
-
camera.updateProjectionMatrix();
|
|
82
|
-
camera.updateMatrixWorld();
|
|
83
|
-
cameraHelper.update();
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
return (
|
|
88
|
-
<>
|
|
89
|
-
<PerspectiveCamera
|
|
90
|
-
ref={(instance) => setCamera(instance)}
|
|
91
|
-
makeDefault={!editMode}
|
|
92
|
-
fov={fov}
|
|
93
|
-
near={near}
|
|
94
|
-
zoom={zoom}
|
|
95
|
-
far={far}
|
|
96
|
-
/>
|
|
97
|
-
{editMode && isSelected && cameraHelper && (
|
|
98
|
-
<primitive object={cameraHelper} />
|
|
99
|
-
)}
|
|
100
|
-
{editMode && !isSelected ? (
|
|
101
|
-
<mesh>
|
|
102
|
-
<boxGeometry args={[0.34, 0.22, 0.18]} />
|
|
103
|
-
<meshBasicMaterial color="#22d3ee" wireframe />
|
|
104
|
-
</mesh>
|
|
105
|
-
) : null}
|
|
106
|
-
</>
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const CameraComponent: Component = {
|
|
111
|
-
name: 'Camera',
|
|
112
|
-
Editor: CameraComponentEditor,
|
|
113
|
-
View: CameraComponentView,
|
|
114
|
-
defaultProperties: cameraDefaults,
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
export default CameraComponent;
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { FC } from "react";
|
|
2
|
-
import { ComponentData, GameObject } from "../types";
|
|
3
|
-
|
|
4
|
-
export interface Component {
|
|
5
|
-
name: string;
|
|
6
|
-
Editor: FC<{
|
|
7
|
-
node?: GameObject;
|
|
8
|
-
component: ComponentData;
|
|
9
|
-
onUpdate: (newComp: any) => void;
|
|
10
|
-
basePath?: string;
|
|
11
|
-
}>;
|
|
12
|
-
defaultProperties: any;
|
|
13
|
-
// Allow View to accept extra props for special cases (like material)
|
|
14
|
-
View?: FC<any>;
|
|
15
|
-
// Non-composable components have special rendering logic in PrefabRoot
|
|
16
|
-
nonComposable?: boolean;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const REGISTRY: Record<string, Component> = {};
|
|
20
|
-
|
|
21
|
-
export function registerComponent(component: Component) {
|
|
22
|
-
if (REGISTRY[component.name]) {
|
|
23
|
-
throw new Error(`Component with name ${component.name} already registered.`);
|
|
24
|
-
}
|
|
25
|
-
REGISTRY[component.name] = component;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function getComponent(name: string): Component | undefined {
|
|
29
|
-
return REGISTRY[name];
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function getAllComponents(): Record<string, Component> {
|
|
33
|
-
return { ...REGISTRY };
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function getNonComposableKeys(): string[] {
|
|
37
|
-
return Object.values(REGISTRY)
|
|
38
|
-
.filter(c => c.nonComposable)
|
|
39
|
-
.map(c => c.name.toLowerCase());
|
|
40
|
-
}
|
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
import { Component } from "./ComponentRegistry";
|
|
2
|
-
import { useRef, useEffect, useMemo, useState } from "react";
|
|
3
|
-
import { useFrame } from "@react-three/fiber";
|
|
4
|
-
import { CameraHelper, DirectionalLight, Object3D, OrthographicCamera, Vector3 } from "three";
|
|
5
|
-
import { FieldRenderer, FieldDefinition, Input } from "./Input";
|
|
6
|
-
|
|
7
|
-
const smallLabel = { display: 'block', fontSize: '8px', color: 'rgba(34, 211, 238, 0.5)', marginBottom: 2 } as const;
|
|
8
|
-
|
|
9
|
-
const directionalLightDefaults = {
|
|
10
|
-
color: '#ffffff',
|
|
11
|
-
intensity: 1,
|
|
12
|
-
castShadow: true,
|
|
13
|
-
shadowMapSize: 1024,
|
|
14
|
-
shadowCameraNear: 0.1,
|
|
15
|
-
shadowCameraFar: 100,
|
|
16
|
-
shadowCameraTop: 30,
|
|
17
|
-
shadowCameraBottom: -30,
|
|
18
|
-
shadowCameraLeft: -30,
|
|
19
|
-
shadowCameraRight: 30,
|
|
20
|
-
targetOffset: [0, -5, 0],
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const directionalLightFields: FieldDefinition[] = [
|
|
24
|
-
{ name: 'color', type: 'color', label: 'Color' },
|
|
25
|
-
{ name: 'intensity', type: 'number', label: 'Intensity', step: 0.1, min: 0 },
|
|
26
|
-
{ name: 'castShadow', type: 'boolean', label: 'Cast Shadow' },
|
|
27
|
-
{ name: 'shadowMapSize', type: 'number', label: 'Shadow Map Size', step: 256, min: 256 },
|
|
28
|
-
{
|
|
29
|
-
name: '_shadowCamera',
|
|
30
|
-
type: 'custom',
|
|
31
|
-
label: 'Shadow Camera',
|
|
32
|
-
render: ({ values, onChangeMultiple }) => (
|
|
33
|
-
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 4 }}>
|
|
34
|
-
<div>
|
|
35
|
-
<label style={smallLabel}>Near</label>
|
|
36
|
-
<Input step={0.1} value={values.shadowCameraNear ?? 0.1} onChange={v => onChangeMultiple({ shadowCameraNear: v })} />
|
|
37
|
-
</div>
|
|
38
|
-
<div>
|
|
39
|
-
<label style={smallLabel}>Far</label>
|
|
40
|
-
<Input step={1} value={values.shadowCameraFar ?? 100} onChange={v => onChangeMultiple({ shadowCameraFar: v })} />
|
|
41
|
-
</div>
|
|
42
|
-
<div>
|
|
43
|
-
<label style={smallLabel}>Top</label>
|
|
44
|
-
<Input step={1} value={values.shadowCameraTop ?? 30} onChange={v => onChangeMultiple({ shadowCameraTop: v })} />
|
|
45
|
-
</div>
|
|
46
|
-
<div>
|
|
47
|
-
<label style={smallLabel}>Bottom</label>
|
|
48
|
-
<Input step={1} value={values.shadowCameraBottom ?? -30} onChange={v => onChangeMultiple({ shadowCameraBottom: v })} />
|
|
49
|
-
</div>
|
|
50
|
-
<div>
|
|
51
|
-
<label style={smallLabel}>Left</label>
|
|
52
|
-
<Input step={1} value={values.shadowCameraLeft ?? -30} onChange={v => onChangeMultiple({ shadowCameraLeft: v })} />
|
|
53
|
-
</div>
|
|
54
|
-
<div>
|
|
55
|
-
<label style={smallLabel}>Right</label>
|
|
56
|
-
<Input step={1} value={values.shadowCameraRight ?? 30} onChange={v => onChangeMultiple({ shadowCameraRight: v })} />
|
|
57
|
-
</div>
|
|
58
|
-
</div>
|
|
59
|
-
),
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
name: 'targetOffset',
|
|
63
|
-
type: 'custom',
|
|
64
|
-
label: 'Target Offset',
|
|
65
|
-
render: ({ value, onChange }) => {
|
|
66
|
-
const offset = value ?? [0, -5, 0];
|
|
67
|
-
return (
|
|
68
|
-
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 4 }}>
|
|
69
|
-
<div>
|
|
70
|
-
<label style={smallLabel}>X</label>
|
|
71
|
-
<Input step={0.5} value={offset[0]} onChange={v => onChange([v, offset[1], offset[2]])} />
|
|
72
|
-
</div>
|
|
73
|
-
<div>
|
|
74
|
-
<label style={smallLabel}>Y</label>
|
|
75
|
-
<Input step={0.5} value={offset[1]} onChange={v => onChange([offset[0], v, offset[2]])} />
|
|
76
|
-
</div>
|
|
77
|
-
<div>
|
|
78
|
-
<label style={smallLabel}>Z</label>
|
|
79
|
-
<Input step={0.5} value={offset[2]} onChange={v => onChange([offset[0], offset[1], v])} />
|
|
80
|
-
</div>
|
|
81
|
-
</div>
|
|
82
|
-
);
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
|
-
];
|
|
86
|
-
|
|
87
|
-
function DirectionalLightComponentEditor({ component, onUpdate }: { component: any; onUpdate: (newComp: any) => void }) {
|
|
88
|
-
const values = { ...directionalLightDefaults, ...component.properties };
|
|
89
|
-
const fields = values.castShadow
|
|
90
|
-
? directionalLightFields
|
|
91
|
-
: directionalLightFields.filter(field => field.name !== '_shadowCamera');
|
|
92
|
-
|
|
93
|
-
return (
|
|
94
|
-
<FieldRenderer
|
|
95
|
-
fields={fields}
|
|
96
|
-
values={values}
|
|
97
|
-
onChange={onUpdate}
|
|
98
|
-
/>
|
|
99
|
-
);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function DirectionalLightView({ properties, editMode, isSelected }: { properties: any; editMode?: boolean; isSelected?: boolean }) {
|
|
103
|
-
const merged = { ...directionalLightDefaults, ...properties };
|
|
104
|
-
const color = merged.color;
|
|
105
|
-
const intensity = merged.intensity;
|
|
106
|
-
const castShadow = merged.castShadow;
|
|
107
|
-
const shadowMapSize = merged.shadowMapSize;
|
|
108
|
-
const shadowCameraNear = merged.shadowCameraNear;
|
|
109
|
-
const shadowCameraFar = merged.shadowCameraFar;
|
|
110
|
-
const shadowCameraTop = merged.shadowCameraTop;
|
|
111
|
-
const shadowCameraBottom = merged.shadowCameraBottom;
|
|
112
|
-
const shadowCameraLeft = merged.shadowCameraLeft;
|
|
113
|
-
const shadowCameraRight = merged.shadowCameraRight;
|
|
114
|
-
const targetOffset = merged.targetOffset;
|
|
115
|
-
|
|
116
|
-
const directionalLightRef = useRef<DirectionalLight>(null);
|
|
117
|
-
const targetRef = useRef<Object3D>(null);
|
|
118
|
-
const [shadowCamera, setShadowCamera] = useState<OrthographicCamera | null>(null);
|
|
119
|
-
const shadowCameraHelper = useMemo(
|
|
120
|
-
() => shadowCamera ? new CameraHelper(shadowCamera) : null,
|
|
121
|
-
[shadowCamera]
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
useEffect(() => {
|
|
125
|
-
return () => {
|
|
126
|
-
shadowCameraHelper?.dispose();
|
|
127
|
-
};
|
|
128
|
-
}, [shadowCameraHelper]);
|
|
129
|
-
|
|
130
|
-
// Use a local target object so node transforms rotate the light direction naturally.
|
|
131
|
-
useEffect(() => {
|
|
132
|
-
if (directionalLightRef.current && targetRef.current) {
|
|
133
|
-
directionalLightRef.current.target = targetRef.current;
|
|
134
|
-
setShadowCamera(directionalLightRef.current.shadow.camera);
|
|
135
|
-
}
|
|
136
|
-
}, []);
|
|
137
|
-
|
|
138
|
-
useFrame(() => {
|
|
139
|
-
if (!directionalLightRef.current || !targetRef.current) return;
|
|
140
|
-
|
|
141
|
-
directionalLightRef.current.target.updateMatrixWorld();
|
|
142
|
-
|
|
143
|
-
if (shadowCamera && shadowCameraHelper && castShadow) {
|
|
144
|
-
shadowCamera.updateProjectionMatrix();
|
|
145
|
-
shadowCamera.updateMatrixWorld();
|
|
146
|
-
shadowCameraHelper.update();
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
return (
|
|
151
|
-
<>
|
|
152
|
-
<directionalLight
|
|
153
|
-
ref={directionalLightRef}
|
|
154
|
-
color={color}
|
|
155
|
-
intensity={intensity}
|
|
156
|
-
castShadow={castShadow}
|
|
157
|
-
shadow-mapSize-width={shadowMapSize}
|
|
158
|
-
shadow-mapSize-height={shadowMapSize}
|
|
159
|
-
shadow-camera-near={shadowCameraNear}
|
|
160
|
-
shadow-camera-far={shadowCameraFar}
|
|
161
|
-
shadow-camera-top={shadowCameraTop}
|
|
162
|
-
shadow-camera-bottom={shadowCameraBottom}
|
|
163
|
-
shadow-camera-left={shadowCameraLeft}
|
|
164
|
-
shadow-camera-right={shadowCameraRight}
|
|
165
|
-
shadow-bias={-0.001}
|
|
166
|
-
shadow-normalBias={0.02}
|
|
167
|
-
/>
|
|
168
|
-
<object3D ref={targetRef} position={targetOffset as [number, number, number]} />
|
|
169
|
-
{editMode && isSelected && castShadow && shadowCameraHelper && (
|
|
170
|
-
<primitive object={shadowCameraHelper} />
|
|
171
|
-
)}
|
|
172
|
-
{editMode && isSelected && (
|
|
173
|
-
<>
|
|
174
|
-
{/* Light source indicator */}
|
|
175
|
-
<mesh>
|
|
176
|
-
<sphereGeometry args={[0.3, 8, 6]} />
|
|
177
|
-
<meshBasicMaterial color={color} wireframe />
|
|
178
|
-
</mesh>
|
|
179
|
-
{/* Target indicator */}
|
|
180
|
-
<mesh position={targetOffset as [number, number, number]}>
|
|
181
|
-
<sphereGeometry args={[0.2, 8, 6]} />
|
|
182
|
-
<meshBasicMaterial color={color} wireframe opacity={0.5} transparent />
|
|
183
|
-
</mesh>
|
|
184
|
-
{/* Direction line */}
|
|
185
|
-
<line>
|
|
186
|
-
<bufferGeometry
|
|
187
|
-
onUpdate={(geo) => {
|
|
188
|
-
const points = [
|
|
189
|
-
new Vector3(0, 0, 0),
|
|
190
|
-
new Vector3(targetOffset[0], targetOffset[1], targetOffset[2])
|
|
191
|
-
];
|
|
192
|
-
geo.setFromPoints(points);
|
|
193
|
-
}}
|
|
194
|
-
/>
|
|
195
|
-
<lineBasicMaterial color={color} opacity={0.6} transparent />
|
|
196
|
-
</line>
|
|
197
|
-
</>
|
|
198
|
-
)}
|
|
199
|
-
</>
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const DirectionalLightComponent: Component = {
|
|
204
|
-
name: 'DirectionalLight',
|
|
205
|
-
Editor: DirectionalLightComponentEditor,
|
|
206
|
-
View: DirectionalLightView,
|
|
207
|
-
defaultProperties: directionalLightDefaults
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
export default DirectionalLightComponent;
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { Environment } from '@react-three/drei';
|
|
2
|
-
import { Component } from './ComponentRegistry';
|
|
3
|
-
import { FieldGroup, NumberField } from './Input';
|
|
4
|
-
import { Object3D, Texture } from 'three';
|
|
5
|
-
|
|
6
|
-
function EnvironmentView({
|
|
7
|
-
properties,
|
|
8
|
-
children,
|
|
9
|
-
editMode,
|
|
10
|
-
loadedTextures,
|
|
11
|
-
loadedModels,
|
|
12
|
-
}: {
|
|
13
|
-
properties: any;
|
|
14
|
-
children?: React.ReactNode;
|
|
15
|
-
editMode?: boolean;
|
|
16
|
-
loadedTextures?: Record<string, Texture>;
|
|
17
|
-
loadedModels?: Record<string, Object3D>;
|
|
18
|
-
}) {
|
|
19
|
-
const { intensity = 1, resolution = 256 } = properties;
|
|
20
|
-
const assetRevision = `${Object.keys(loadedTextures ?? {}).sort().join('|')}::${Object.keys(loadedModels ?? {}).sort().join('|')}`;
|
|
21
|
-
|
|
22
|
-
return (
|
|
23
|
-
<Environment
|
|
24
|
-
key={assetRevision}
|
|
25
|
-
background={true}
|
|
26
|
-
environmentIntensity={intensity}
|
|
27
|
-
resolution={resolution}
|
|
28
|
-
frames={editMode ? undefined : 1}
|
|
29
|
-
>
|
|
30
|
-
{children}
|
|
31
|
-
</Environment>
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const EnvironmentComponent: Component = {
|
|
36
|
-
name: 'Environment',
|
|
37
|
-
Editor: ({ component, onUpdate }) => (
|
|
38
|
-
<FieldGroup>
|
|
39
|
-
<NumberField name="intensity" label="Intensity" values={component.properties} onChange={onUpdate} min={0} step={0.1} fallback={1} />
|
|
40
|
-
<NumberField name="resolution" label="Resolution" values={component.properties} onChange={onUpdate} min={64} step={64} fallback={256} />
|
|
41
|
-
</FieldGroup>
|
|
42
|
-
),
|
|
43
|
-
View: EnvironmentView,
|
|
44
|
-
defaultProperties: {},
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
export default EnvironmentComponent;
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import { Component } from "./ComponentRegistry";
|
|
2
|
-
import { FieldGroup, NumberField, SelectField } from "./Input";
|
|
3
|
-
|
|
4
|
-
const GEOMETRY_ARGS: Record<string, {
|
|
5
|
-
fields: Array<{
|
|
6
|
-
name: string;
|
|
7
|
-
label: string;
|
|
8
|
-
defaultValue: number;
|
|
9
|
-
min?: number;
|
|
10
|
-
step?: number;
|
|
11
|
-
}>;
|
|
12
|
-
}> = {
|
|
13
|
-
box: {
|
|
14
|
-
fields: [
|
|
15
|
-
{ name: 'width', label: 'Width', defaultValue: 1, min: 0.01, step: 0.1 },
|
|
16
|
-
{ name: 'height', label: 'Height', defaultValue: 1, min: 0.01, step: 0.1 },
|
|
17
|
-
{ name: 'depth', label: 'Depth', defaultValue: 1, min: 0.01, step: 0.1 },
|
|
18
|
-
],
|
|
19
|
-
},
|
|
20
|
-
sphere: {
|
|
21
|
-
fields: [
|
|
22
|
-
{ name: 'radius', label: 'Radius', defaultValue: 1, min: 0.01, step: 0.1 },
|
|
23
|
-
{ name: 'widthSegments', label: 'Width Segments', defaultValue: 32, min: 3, step: 1 },
|
|
24
|
-
{ name: 'heightSegments', label: 'Height Segments', defaultValue: 16, min: 2, step: 1 },
|
|
25
|
-
],
|
|
26
|
-
},
|
|
27
|
-
plane: {
|
|
28
|
-
fields: [
|
|
29
|
-
{ name: 'width', label: 'Width', defaultValue: 1, min: 0.01, step: 0.1 },
|
|
30
|
-
{ name: 'height', label: 'Height', defaultValue: 1, min: 0.01, step: 0.1 },
|
|
31
|
-
],
|
|
32
|
-
},
|
|
33
|
-
cylinder: {
|
|
34
|
-
fields: [
|
|
35
|
-
{ name: 'radiusTop', label: 'Radius Top', defaultValue: 1, min: 0.01, step: 0.1 },
|
|
36
|
-
{ name: 'radiusBottom', label: 'Radius Bottom', defaultValue: 1, min: 0.01, step: 0.1 },
|
|
37
|
-
{ name: 'height', label: 'Height', defaultValue: 1, min: 0.01, step: 0.1 },
|
|
38
|
-
{ name: 'radialSegments', label: 'Radial Segments', defaultValue: 32, min: 3, step: 1 },
|
|
39
|
-
],
|
|
40
|
-
},
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
function getDefaultArgs(geometryType: string) {
|
|
44
|
-
return (GEOMETRY_ARGS[geometryType]?.fields ?? []).map(field => field.defaultValue);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function GeometryComponentEditor({
|
|
48
|
-
component,
|
|
49
|
-
onUpdate,
|
|
50
|
-
}: {
|
|
51
|
-
component: any;
|
|
52
|
-
onUpdate: (newProps: any) => void;
|
|
53
|
-
}) {
|
|
54
|
-
const geometryType = component.properties.geometryType ?? 'box';
|
|
55
|
-
const schema = GEOMETRY_ARGS[geometryType] ?? GEOMETRY_ARGS.box;
|
|
56
|
-
const args = component.properties.args ?? getDefaultArgs(geometryType);
|
|
57
|
-
|
|
58
|
-
// Handle geometry type change to reset args
|
|
59
|
-
const handleChange = (newValues: Record<string, any>) => {
|
|
60
|
-
if ('geometryType' in newValues && newValues.geometryType !== geometryType) {
|
|
61
|
-
onUpdate({ geometryType: newValues.geometryType, args: getDefaultArgs(newValues.geometryType) });
|
|
62
|
-
} else {
|
|
63
|
-
onUpdate(newValues);
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
const updateArg = (index: number, value: number) => {
|
|
68
|
-
const next = [...args];
|
|
69
|
-
next[index] = value;
|
|
70
|
-
onUpdate({ args: next });
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
return (
|
|
74
|
-
<FieldGroup>
|
|
75
|
-
<SelectField
|
|
76
|
-
name="geometryType"
|
|
77
|
-
label="Type"
|
|
78
|
-
values={component.properties}
|
|
79
|
-
onChange={handleChange}
|
|
80
|
-
options={[
|
|
81
|
-
{ value: 'box', label: 'Box' },
|
|
82
|
-
{ value: 'sphere', label: 'Sphere' },
|
|
83
|
-
{ value: 'plane', label: 'Plane' },
|
|
84
|
-
{ value: 'cylinder', label: 'Cylinder' },
|
|
85
|
-
]}
|
|
86
|
-
/>
|
|
87
|
-
{schema.fields.map((field, index) => (
|
|
88
|
-
<NumberField
|
|
89
|
-
key={field.name}
|
|
90
|
-
name={field.name}
|
|
91
|
-
label={field.label}
|
|
92
|
-
values={{ [field.name]: args[index] ?? field.defaultValue }}
|
|
93
|
-
onChange={(next) => updateArg(index, next[field.name])}
|
|
94
|
-
fallback={field.defaultValue}
|
|
95
|
-
min={field.min}
|
|
96
|
-
step={field.step}
|
|
97
|
-
/>
|
|
98
|
-
))}
|
|
99
|
-
</FieldGroup>
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
// View for Geometry component
|
|
105
|
-
function GeometryComponentView({ properties, children }: { properties: any, children?: React.ReactNode }) {
|
|
106
|
-
const { geometryType, args = [] } = properties;
|
|
107
|
-
// Only return the geometry node, do not wrap in mesh or group
|
|
108
|
-
switch (geometryType) {
|
|
109
|
-
case "box":
|
|
110
|
-
return <boxGeometry args={args as [number, number, number]} />;
|
|
111
|
-
case "sphere":
|
|
112
|
-
return <sphereGeometry args={args as [number, number?, number?]} />;
|
|
113
|
-
case "plane":
|
|
114
|
-
return <planeGeometry args={args as [number, number]} />;
|
|
115
|
-
case "cylinder":
|
|
116
|
-
return <cylinderGeometry args={args as [number, number, number, number?]} />;
|
|
117
|
-
default:
|
|
118
|
-
return <boxGeometry args={[1, 1, 1]} />;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const GeometryComponent: Component = {
|
|
123
|
-
name: 'Geometry',
|
|
124
|
-
Editor: GeometryComponentEditor,
|
|
125
|
-
View: GeometryComponentView,
|
|
126
|
-
nonComposable: true,
|
|
127
|
-
defaultProperties: {
|
|
128
|
-
geometryType: 'box',
|
|
129
|
-
args: getDefaultArgs('box'),
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
export default GeometryComponent;
|