react-three-game 0.0.42 → 0.0.45
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/README.md +38 -4
- package/dist/index.d.ts +7 -6
- package/dist/index.js +9 -6
- package/dist/shared/GameCanvas.js +1 -1
- package/dist/tools/assetviewer/page.js +2 -2
- package/dist/tools/prefabeditor/EditorUI.js +3 -5
- package/dist/tools/prefabeditor/components/ComponentRegistry.d.ts +0 -2
- package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +27 -27
- package/dist/tools/prefabeditor/components/GeometryComponent.js +40 -21
- package/dist/tools/prefabeditor/components/Input.d.ts +78 -1
- package/dist/tools/prefabeditor/components/Input.js +65 -0
- package/dist/tools/prefabeditor/components/MaterialComponent.js +57 -26
- package/dist/tools/prefabeditor/components/ModelComponent.js +17 -8
- package/dist/tools/prefabeditor/components/PhysicsComponent.js +25 -14
- package/dist/tools/prefabeditor/components/SpotLightComponent.js +10 -21
- package/dist/tools/prefabeditor/components/TransformComponent.js +27 -19
- package/dist/tools/prefabeditor/page.js +1 -1
- package/package.json +1 -1
- package/src/index.ts +28 -10
- package/src/shared/GameCanvas.tsx +1 -1
- package/src/tools/assetviewer/page.tsx +3 -3
- package/src/tools/prefabeditor/EditorUI.tsx +0 -10
- package/src/tools/prefabeditor/components/ComponentRegistry.ts +3 -5
- package/src/tools/prefabeditor/components/DirectionalLightComponent.tsx +72 -76
- package/src/tools/prefabeditor/components/GeometryComponent.tsx +55 -38
- package/src/tools/prefabeditor/components/Input.tsx +299 -0
- package/src/tools/prefabeditor/components/MaterialComponent.tsx +97 -140
- package/src/tools/prefabeditor/components/ModelComponent.tsx +62 -41
- package/src/tools/prefabeditor/components/PhysicsComponent.tsx +29 -33
- package/src/tools/prefabeditor/components/SpotLightComponent.tsx +17 -65
- package/src/tools/prefabeditor/components/TransformComponent.tsx +83 -56
- package/src/tools/prefabeditor/page.tsx +1 -1
- package/dist/index.umd.js +0 -4622
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
import { ModelListViewer, SingleModelViewer } from '../../assetviewer/page';
|
|
2
2
|
import { useEffect, useState, useMemo } from 'react';
|
|
3
3
|
import { Component } from './ComponentRegistry';
|
|
4
|
-
import {
|
|
4
|
+
import { FieldRenderer, FieldDefinition } from './Input';
|
|
5
5
|
import { GameObject } from '../types';
|
|
6
6
|
|
|
7
|
-
function
|
|
7
|
+
function ModelPicker({
|
|
8
|
+
value,
|
|
9
|
+
onChange,
|
|
10
|
+
basePath,
|
|
11
|
+
nodeId
|
|
12
|
+
}: {
|
|
13
|
+
value: string | undefined;
|
|
14
|
+
onChange: (v: string) => void;
|
|
15
|
+
basePath: string;
|
|
16
|
+
nodeId?: string;
|
|
17
|
+
}) {
|
|
8
18
|
const [modelFiles, setModelFiles] = useState<string[]>([]);
|
|
9
19
|
const [showPicker, setShowPicker] = useState(false);
|
|
10
20
|
|
|
@@ -17,49 +27,60 @@ function ModelComponentEditor({ component, node, onUpdate, basePath = "" }: { co
|
|
|
17
27
|
}, [basePath]);
|
|
18
28
|
|
|
19
29
|
const handleModelSelect = (file: string) => {
|
|
20
|
-
// Remove leading slash for prefab compatibility
|
|
21
30
|
const filename = file.startsWith('/') ? file.slice(1) : file;
|
|
22
|
-
|
|
31
|
+
onChange(filename);
|
|
32
|
+
setShowPicker(false);
|
|
23
33
|
};
|
|
24
34
|
|
|
25
|
-
return
|
|
26
|
-
<div>
|
|
27
|
-
<
|
|
28
|
-
<
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}}
|
|
46
|
-
basePath={basePath}
|
|
47
|
-
/>
|
|
48
|
-
</div>
|
|
49
|
-
)}
|
|
50
|
-
</div>
|
|
51
|
-
</div>
|
|
52
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
|
|
53
|
-
<input
|
|
54
|
-
type="checkbox"
|
|
55
|
-
id="instanced-checkbox"
|
|
56
|
-
checked={component.properties.instanced || false}
|
|
57
|
-
onChange={e => onUpdate({ instanced: e.target.checked })}
|
|
58
|
-
style={{ width: 12, height: 12 }}
|
|
59
|
-
/>
|
|
60
|
-
<label htmlFor="instanced-checkbox" style={{ fontSize: '9px', color: 'rgba(34, 211, 238, 0.6)' }}>Instanced</label>
|
|
35
|
+
return (
|
|
36
|
+
<div style={{ maxHeight: 128, overflowY: 'auto', position: 'relative', display: 'flex', alignItems: 'center' }}>
|
|
37
|
+
<SingleModelViewer file={value ? `/${value}` : undefined} basePath={basePath} />
|
|
38
|
+
<button
|
|
39
|
+
onClick={() => setShowPicker(!showPicker)}
|
|
40
|
+
style={{ padding: '4px 8px', backgroundColor: '#1f2937', color: 'inherit', fontSize: 10, cursor: 'pointer', border: '1px solid rgba(34, 211, 238, 0.3)', marginTop: 4 }}
|
|
41
|
+
>
|
|
42
|
+
{showPicker ? 'Hide' : 'Change'}
|
|
43
|
+
</button>
|
|
44
|
+
{showPicker && (
|
|
45
|
+
<div style={{ position: 'fixed', left: '-10px', top: '50%', transform: 'translate(-100%, -50%)', background: 'rgba(0,0,0,0.9)', padding: 16, border: '1px solid rgba(34, 211, 238, 0.3)', maxHeight: '80vh', overflowY: 'auto', zIndex: 1000 }}>
|
|
46
|
+
<ModelListViewer
|
|
47
|
+
key={nodeId}
|
|
48
|
+
files={modelFiles}
|
|
49
|
+
selected={value ? `/${value}` : undefined}
|
|
50
|
+
onSelect={handleModelSelect}
|
|
51
|
+
basePath={basePath}
|
|
52
|
+
/>
|
|
53
|
+
</div>
|
|
54
|
+
)}
|
|
61
55
|
</div>
|
|
62
|
-
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function ModelComponentEditor({ component, node, onUpdate, basePath = "" }: { component: any; node?: GameObject; onUpdate: (newComp: any) => void; basePath?: string }) {
|
|
60
|
+
const fields: FieldDefinition[] = [
|
|
61
|
+
{
|
|
62
|
+
name: 'filename',
|
|
63
|
+
type: 'custom',
|
|
64
|
+
label: 'Model File',
|
|
65
|
+
render: ({ value, onChange }) => (
|
|
66
|
+
<ModelPicker
|
|
67
|
+
value={value}
|
|
68
|
+
onChange={onChange}
|
|
69
|
+
basePath={basePath}
|
|
70
|
+
nodeId={node?.id}
|
|
71
|
+
/>
|
|
72
|
+
),
|
|
73
|
+
},
|
|
74
|
+
{ name: 'instanced', type: 'boolean', label: 'Instanced' },
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<FieldRenderer
|
|
79
|
+
fields={fields}
|
|
80
|
+
values={component.properties}
|
|
81
|
+
onChange={onUpdate}
|
|
82
|
+
/>
|
|
83
|
+
);
|
|
63
84
|
}
|
|
64
85
|
|
|
65
86
|
// View for Model component
|
|
@@ -2,7 +2,7 @@ import { RigidBody, RapierRigidBody } from "@react-three/rapier";
|
|
|
2
2
|
import type { ReactNode } from 'react';
|
|
3
3
|
import { useEffect, useRef } from 'react';
|
|
4
4
|
import { Component } from "./ComponentRegistry";
|
|
5
|
-
import {
|
|
5
|
+
import { FieldRenderer, FieldDefinition } from "./Input";
|
|
6
6
|
import { Quaternion, Euler } from 'three';
|
|
7
7
|
|
|
8
8
|
export interface PhysicsProps {
|
|
@@ -13,40 +13,36 @@ export interface PhysicsProps {
|
|
|
13
13
|
friction?: number;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
16
|
+
const physicsFields: FieldDefinition[] = [
|
|
17
|
+
{
|
|
18
|
+
name: 'type',
|
|
19
|
+
type: 'select',
|
|
20
|
+
label: 'Type',
|
|
21
|
+
options: [
|
|
22
|
+
{ value: 'dynamic', label: 'Dynamic' },
|
|
23
|
+
{ value: 'fixed', label: 'Fixed' },
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: 'collider',
|
|
28
|
+
type: 'select',
|
|
29
|
+
label: 'Collider',
|
|
30
|
+
options: [
|
|
31
|
+
{ value: 'hull', label: 'Hull (convex)' },
|
|
32
|
+
{ value: 'trimesh', label: 'Trimesh (exact)' },
|
|
33
|
+
{ value: 'cuboid', label: 'Cuboid (box)' },
|
|
34
|
+
{ value: 'ball', label: 'Ball (sphere)' },
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
];
|
|
29
38
|
|
|
39
|
+
function PhysicsComponentEditor({ component, onUpdate }: { component: { properties: { type?: 'dynamic' | 'fixed'; collider?: string;[k: string]: any } }; onUpdate: (props: Partial<Record<string, any>>) => void }) {
|
|
30
40
|
return (
|
|
31
|
-
<
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
<option value="fixed">Fixed</option>
|
|
37
|
-
</select>
|
|
38
|
-
</div>
|
|
39
|
-
|
|
40
|
-
<div>
|
|
41
|
-
<Label>Collider</Label>
|
|
42
|
-
<select style={selectStyle} value={collider} onChange={e => onUpdate({ collider: e.target.value })}>
|
|
43
|
-
<option value="hull">Hull (convex)</option>
|
|
44
|
-
<option value="trimesh">Trimesh (exact)</option>
|
|
45
|
-
<option value="cuboid">Cuboid (box)</option>
|
|
46
|
-
<option value="ball">Ball (sphere)</option>
|
|
47
|
-
</select>
|
|
48
|
-
</div>
|
|
49
|
-
</div>
|
|
41
|
+
<FieldRenderer
|
|
42
|
+
fields={physicsFields}
|
|
43
|
+
values={component.properties}
|
|
44
|
+
onChange={onUpdate}
|
|
45
|
+
/>
|
|
50
46
|
);
|
|
51
47
|
}
|
|
52
48
|
|
|
@@ -1,72 +1,24 @@
|
|
|
1
1
|
import { Component } from "./ComponentRegistry";
|
|
2
2
|
import { useRef, useEffect } from "react";
|
|
3
|
-
import {
|
|
3
|
+
import { FieldRenderer, FieldDefinition } from "./Input";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const textInputStyle = {
|
|
16
|
-
flex: 1,
|
|
17
|
-
backgroundColor: 'rgba(0, 0, 0, 0.4)',
|
|
18
|
-
border: '1px solid rgba(34, 211, 238, 0.3)',
|
|
19
|
-
padding: '2px 4px',
|
|
20
|
-
fontSize: '10px',
|
|
21
|
-
color: 'rgba(165, 243, 252, 1)',
|
|
22
|
-
fontFamily: 'monospace',
|
|
23
|
-
outline: 'none',
|
|
24
|
-
};
|
|
5
|
+
const spotLightFields: FieldDefinition[] = [
|
|
6
|
+
{ name: 'color', type: 'color', label: 'Color' },
|
|
7
|
+
{ name: 'intensity', type: 'number', label: 'Intensity', step: 0.1, min: 0 },
|
|
8
|
+
{ name: 'angle', type: 'number', label: 'Angle', step: 0.1, min: 0, max: Math.PI },
|
|
9
|
+
{ name: 'penumbra', type: 'number', label: 'Penumbra', step: 0.1, min: 0, max: 1 },
|
|
10
|
+
{ name: 'distance', type: 'number', label: 'Distance', step: 1, min: 0 },
|
|
11
|
+
{ name: 'castShadow', type: 'boolean', label: 'Cast Shadow' },
|
|
12
|
+
];
|
|
25
13
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
onChange={e => onUpdate({ ...component.properties, color: e.target.value })}
|
|
35
|
-
/>
|
|
36
|
-
<input
|
|
37
|
-
type="text"
|
|
38
|
-
style={textInputStyle}
|
|
39
|
-
value={props.color}
|
|
40
|
-
onChange={e => onUpdate({ ...component.properties, color: e.target.value })}
|
|
41
|
-
/>
|
|
42
|
-
</div>
|
|
43
|
-
</div>
|
|
44
|
-
<div>
|
|
45
|
-
<Label>Intensity</Label>
|
|
46
|
-
<Input step="0.1" value={props.intensity} onChange={value => onUpdate({ ...component.properties, intensity: value })} />
|
|
47
|
-
</div>
|
|
48
|
-
<div>
|
|
49
|
-
<Label>Angle</Label>
|
|
50
|
-
<Input step="0.1" min={0} max={Math.PI} value={props.angle} onChange={value => onUpdate({ ...component.properties, angle: value })} />
|
|
51
|
-
</div>
|
|
52
|
-
<div>
|
|
53
|
-
<Label>Penumbra</Label>
|
|
54
|
-
<Input step="0.1" min={0} max={1} value={props.penumbra} onChange={value => onUpdate({ ...component.properties, penumbra: value })} />
|
|
55
|
-
</div>
|
|
56
|
-
<div>
|
|
57
|
-
<Label>Distance</Label>
|
|
58
|
-
<Input step="1" min={0} value={props.distance} onChange={value => onUpdate({ ...component.properties, distance: value })} />
|
|
59
|
-
</div>
|
|
60
|
-
<div>
|
|
61
|
-
<Label>Cast Shadow</Label>
|
|
62
|
-
<input
|
|
63
|
-
type="checkbox"
|
|
64
|
-
style={{ height: 16, width: 16, backgroundColor: 'rgba(0, 0, 0, 0.4)', border: '1px solid rgba(34, 211, 238, 0.3)', cursor: 'pointer' }}
|
|
65
|
-
checked={props.castShadow}
|
|
66
|
-
onChange={e => onUpdate({ ...component.properties, castShadow: e.target.checked })}
|
|
67
|
-
/>
|
|
68
|
-
</div>
|
|
69
|
-
</div>;
|
|
14
|
+
function SpotLightComponentEditor({ component, onUpdate }: { component: any; onUpdate: (newComp: any) => void }) {
|
|
15
|
+
return (
|
|
16
|
+
<FieldRenderer
|
|
17
|
+
fields={spotLightFields}
|
|
18
|
+
values={component.properties}
|
|
19
|
+
onChange={onUpdate}
|
|
20
|
+
/>
|
|
21
|
+
);
|
|
70
22
|
}
|
|
71
23
|
|
|
72
24
|
function SpotLightView({ properties, editMode }: { properties: any; editMode?: boolean }) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Component } from "./ComponentRegistry";
|
|
2
|
-
import {
|
|
2
|
+
import { FieldRenderer, FieldDefinition, Label } from "./Input";
|
|
3
3
|
import { useEditorContext } from "../EditorContext";
|
|
4
4
|
|
|
5
5
|
const buttonStyle = {
|
|
@@ -13,65 +13,92 @@ const buttonStyle = {
|
|
|
13
13
|
flex: 1,
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
function
|
|
16
|
+
function TransformModeSelector({
|
|
17
|
+
transformMode,
|
|
18
|
+
setTransformMode,
|
|
19
|
+
snapResolution,
|
|
20
|
+
setSnapResolution
|
|
21
|
+
}: {
|
|
22
|
+
transformMode: "translate" | "rotate" | "scale";
|
|
23
|
+
setTransformMode: (m: "translate" | "rotate" | "scale") => void;
|
|
24
|
+
snapResolution: number;
|
|
25
|
+
setSnapResolution: (v: number) => void;
|
|
26
|
+
}) {
|
|
27
|
+
return (
|
|
28
|
+
<div style={{ marginBottom: 8 }}>
|
|
29
|
+
<Label>Transform Mode {snapResolution > 0 && `(Snap: ${snapResolution})`}</Label>
|
|
30
|
+
<div style={{ display: 'flex', gap: 6 }}>
|
|
31
|
+
{["translate", "rotate", "scale"].map(mode => {
|
|
32
|
+
const isActive = transformMode === mode;
|
|
33
|
+
return (
|
|
34
|
+
<button
|
|
35
|
+
key={mode}
|
|
36
|
+
onClick={() => setTransformMode(mode as any)}
|
|
37
|
+
style={{
|
|
38
|
+
...buttonStyle,
|
|
39
|
+
background: isActive ? 'rgba(255,255,255,0.10)' : 'transparent',
|
|
40
|
+
}}
|
|
41
|
+
onPointerEnter={(e) => {
|
|
42
|
+
if (!isActive) e.currentTarget.style.background = 'rgba(255,255,255,0.08)';
|
|
43
|
+
}}
|
|
44
|
+
onPointerLeave={(e) => {
|
|
45
|
+
if (!isActive) e.currentTarget.style.background = 'transparent';
|
|
46
|
+
}}
|
|
47
|
+
>
|
|
48
|
+
{mode}
|
|
49
|
+
</button>
|
|
50
|
+
);
|
|
51
|
+
})}
|
|
52
|
+
</div>
|
|
53
|
+
<div style={{ marginTop: 6 }}>
|
|
54
|
+
<button
|
|
55
|
+
onClick={() => setSnapResolution(snapResolution > 0 ? 0 : 0.1)}
|
|
56
|
+
style={{
|
|
57
|
+
...buttonStyle,
|
|
58
|
+
background: snapResolution > 0 ? 'rgba(255,255,255,0.10)' : 'transparent',
|
|
59
|
+
width: '100%',
|
|
60
|
+
}}
|
|
61
|
+
onPointerEnter={(e) => {
|
|
62
|
+
if (snapResolution === 0) e.currentTarget.style.background = 'rgba(255,255,255,0.08)';
|
|
63
|
+
}}
|
|
64
|
+
onPointerLeave={(e) => {
|
|
65
|
+
if (snapResolution === 0) e.currentTarget.style.background = 'transparent';
|
|
66
|
+
}}
|
|
67
|
+
>
|
|
68
|
+
Snap: {snapResolution > 0 ? `ON (${snapResolution})` : 'OFF'}
|
|
69
|
+
</button>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function TransformComponentEditor({ component, onUpdate }: {
|
|
17
76
|
component: any;
|
|
18
77
|
onUpdate: (newComp: any) => void;
|
|
19
|
-
transformMode?: "translate" | "rotate" | "scale";
|
|
20
|
-
setTransformMode?: (m: "translate" | "rotate" | "scale") => void;
|
|
21
78
|
}) {
|
|
22
|
-
const { snapResolution, setSnapResolution } = useEditorContext();
|
|
79
|
+
const { transformMode, setTransformMode, snapResolution, setSnapResolution } = useEditorContext();
|
|
23
80
|
|
|
24
|
-
|
|
25
|
-
{
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
>
|
|
46
|
-
{mode}
|
|
47
|
-
</button>
|
|
48
|
-
);
|
|
49
|
-
})}
|
|
50
|
-
</div>
|
|
51
|
-
<div style={{ marginTop: 6 }}>
|
|
52
|
-
<button
|
|
53
|
-
onClick={() => setSnapResolution(snapResolution > 0 ? 0 : 0.1)}
|
|
54
|
-
style={{
|
|
55
|
-
...buttonStyle,
|
|
56
|
-
background: snapResolution > 0 ? 'rgba(255,255,255,0.10)' : 'transparent',
|
|
57
|
-
width: '100%',
|
|
58
|
-
}}
|
|
59
|
-
onPointerEnter={(e) => {
|
|
60
|
-
if (snapResolution === 0) e.currentTarget.style.background = 'rgba(255,255,255,0.08)';
|
|
61
|
-
}}
|
|
62
|
-
onPointerLeave={(e) => {
|
|
63
|
-
if (snapResolution === 0) e.currentTarget.style.background = 'transparent';
|
|
64
|
-
}}
|
|
65
|
-
>
|
|
66
|
-
Snap: {snapResolution > 0 ? `ON (${snapResolution})` : 'OFF'}
|
|
67
|
-
</button>
|
|
68
|
-
</div>
|
|
69
|
-
</div>
|
|
70
|
-
)}
|
|
71
|
-
<Vector3Input label="Position" value={component.properties.position} onChange={v => onUpdate({ position: v })} snap={snapResolution} />
|
|
72
|
-
<Vector3Input label="Rotation" value={component.properties.rotation} onChange={v => onUpdate({ rotation: v })} snap={snapResolution} />
|
|
73
|
-
<Vector3Input label="Scale" value={component.properties.scale} onChange={v => onUpdate({ scale: v })} snap={snapResolution} />
|
|
74
|
-
</div>;
|
|
81
|
+
const fields: FieldDefinition[] = [
|
|
82
|
+
{ name: 'position', type: 'vector3', label: 'Position', snap: snapResolution },
|
|
83
|
+
{ name: 'rotation', type: 'vector3', label: 'Rotation', snap: snapResolution },
|
|
84
|
+
{ name: 'scale', type: 'vector3', label: 'Scale', snap: snapResolution },
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
|
89
|
+
<TransformModeSelector
|
|
90
|
+
transformMode={transformMode}
|
|
91
|
+
setTransformMode={setTransformMode}
|
|
92
|
+
snapResolution={snapResolution}
|
|
93
|
+
setSnapResolution={setSnapResolution}
|
|
94
|
+
/>
|
|
95
|
+
<FieldRenderer
|
|
96
|
+
fields={fields}
|
|
97
|
+
values={component.properties}
|
|
98
|
+
onChange={onUpdate}
|
|
99
|
+
/>
|
|
100
|
+
</div>
|
|
101
|
+
);
|
|
75
102
|
}
|
|
76
103
|
|
|
77
104
|
const TransformComponent: Component = {
|
|
@@ -2,7 +2,7 @@ import PrefabEditor from "./PrefabEditor";
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
export default function PrefabEditorPage() {
|
|
5
|
-
return <div
|
|
5
|
+
return <div style={{ width: '100%', height: '100%' }}>
|
|
6
6
|
<PrefabEditor>
|
|
7
7
|
<directionalLight position={[5, 10, 7.5]} intensity={1} castShadow />
|
|
8
8
|
</PrefabEditor>
|