react-three-game 0.0.17 → 0.0.19

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.
Files changed (39) hide show
  1. package/.github/copilot-instructions.md +54 -183
  2. package/README.md +69 -214
  3. package/dist/index.d.ts +3 -1
  4. package/dist/index.js +3 -0
  5. package/dist/tools/prefabeditor/EditorTree.d.ts +2 -4
  6. package/dist/tools/prefabeditor/EditorTree.js +20 -194
  7. package/dist/tools/prefabeditor/EditorUI.js +43 -224
  8. package/dist/tools/prefabeditor/InstanceProvider.d.ts +4 -4
  9. package/dist/tools/prefabeditor/InstanceProvider.js +21 -13
  10. package/dist/tools/prefabeditor/PrefabEditor.js +33 -99
  11. package/dist/tools/prefabeditor/PrefabRoot.d.ts +0 -1
  12. package/dist/tools/prefabeditor/PrefabRoot.js +33 -50
  13. package/dist/tools/prefabeditor/components/DirectionalLightComponent.d.ts +3 -0
  14. package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +102 -0
  15. package/dist/tools/prefabeditor/components/ModelComponent.js +12 -4
  16. package/dist/tools/prefabeditor/components/SpotLightComponent.js +10 -5
  17. package/dist/tools/prefabeditor/components/index.js +2 -0
  18. package/dist/tools/prefabeditor/hooks/useModelLoader.d.ts +10 -0
  19. package/dist/tools/prefabeditor/hooks/useModelLoader.js +40 -0
  20. package/dist/tools/prefabeditor/styles.d.ts +1809 -0
  21. package/dist/tools/prefabeditor/styles.js +168 -0
  22. package/dist/tools/prefabeditor/types.d.ts +3 -14
  23. package/dist/tools/prefabeditor/types.js +0 -1
  24. package/dist/tools/prefabeditor/utils.d.ts +19 -0
  25. package/dist/tools/prefabeditor/utils.js +72 -0
  26. package/package.json +3 -3
  27. package/src/index.ts +5 -1
  28. package/src/tools/prefabeditor/EditorTree.tsx +38 -270
  29. package/src/tools/prefabeditor/EditorUI.tsx +105 -322
  30. package/src/tools/prefabeditor/InstanceProvider.tsx +43 -32
  31. package/src/tools/prefabeditor/PrefabEditor.tsx +40 -151
  32. package/src/tools/prefabeditor/PrefabRoot.tsx +41 -73
  33. package/src/tools/prefabeditor/components/DirectionalLightComponent.tsx +317 -0
  34. package/src/tools/prefabeditor/components/ModelComponent.tsx +14 -4
  35. package/src/tools/prefabeditor/components/SpotLightComponent.tsx +27 -7
  36. package/src/tools/prefabeditor/components/index.ts +2 -0
  37. package/src/tools/prefabeditor/styles.ts +195 -0
  38. package/src/tools/prefabeditor/types.ts +4 -12
  39. package/src/tools/prefabeditor/utils.ts +80 -0
@@ -1,8 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import React, { createContext, useContext, useMemo, useRef, useState, useEffect, useCallback } from "react";
3
3
  import { Merged } from '@react-three/drei';
4
- import * as THREE from 'three';
5
4
  import { InstancedRigidBodies } from "@react-three/rapier";
5
+ import { Mesh, Matrix4 } from "three";
6
+ // Helper functions for comparison
6
7
  function arrayEquals(a, b) {
7
8
  if (a === b)
8
9
  return true;
@@ -30,6 +31,7 @@ export function GameInstanceProvider({ children, models, onSelect, registerRef }
30
31
  setInstances(prev => {
31
32
  const idx = prev.findIndex(i => i.id === instance.id);
32
33
  if (idx !== -1) {
34
+ // Update existing if changed
33
35
  if (instanceEquals(prev[idx], instance)) {
34
36
  return prev;
35
37
  }
@@ -37,6 +39,7 @@ export function GameInstanceProvider({ children, models, onSelect, registerRef }
37
39
  copy[idx] = instance;
38
40
  return copy;
39
41
  }
42
+ // Add new
40
43
  return [...prev, instance];
41
44
  });
42
45
  }, []);
@@ -47,14 +50,14 @@ export function GameInstanceProvider({ children, models, onSelect, registerRef }
47
50
  return prev.filter(i => i.id !== id);
48
51
  });
49
52
  }, []);
50
- // Flatten all model meshes once
53
+ // Flatten all model meshes once (models → flat mesh parts)
51
54
  const { flatMeshes, modelParts } = useMemo(() => {
52
55
  const flatMeshes = {};
53
56
  const modelParts = {};
54
57
  Object.entries(models).forEach(([modelKey, model]) => {
55
58
  const root = model;
56
59
  root.updateWorldMatrix(false, true);
57
- const rootInverse = new THREE.Matrix4().copy(root.matrixWorld).invert();
60
+ const rootInverse = new Matrix4().copy(root.matrixWorld).invert();
58
61
  let partIndex = 0;
59
62
  root.traverse((obj) => {
60
63
  if (obj.isMesh) {
@@ -62,7 +65,7 @@ export function GameInstanceProvider({ children, models, onSelect, registerRef }
62
65
  const relativeTransform = obj.matrixWorld.clone().premultiply(rootInverse);
63
66
  geom.applyMatrix4(relativeTransform);
64
67
  const partKey = `${modelKey}__${partIndex}`;
65
- flatMeshes[partKey] = new THREE.Mesh(geom, obj.material);
68
+ flatMeshes[partKey] = new Mesh(geom, obj.material);
66
69
  partIndex++;
67
70
  }
68
71
  });
@@ -70,7 +73,7 @@ export function GameInstanceProvider({ children, models, onSelect, registerRef }
70
73
  });
71
74
  return { flatMeshes, modelParts };
72
75
  }, [models]);
73
- // Group instances by meshPath + physics type
76
+ // Group instances by meshPath + physics type for batch rendering
74
77
  const grouped = useMemo(() => {
75
78
  var _a;
76
79
  const groups = {};
@@ -104,7 +107,7 @@ export function GameInstanceProvider({ children, models, onSelect, registerRef }
104
107
  const partCount = modelParts[modelKey] || 0;
105
108
  if (partCount === 0)
106
109
  return null;
107
- // Restrict meshes to just this model's parts for this Merged
110
+ // Create mesh subset for this specific model
108
111
  const meshesForModel = {};
109
112
  for (let i = 0; i < partCount; i++) {
110
113
  const partKey = `${modelKey}__${i}`;
@@ -113,7 +116,7 @@ export function GameInstanceProvider({ children, models, onSelect, registerRef }
113
116
  return (_jsx(Merged, { meshes: meshesForModel, castShadow: true, receiveShadow: true, children: (instancesMap) => (_jsx(NonPhysicsInstancedGroup, { modelKey: modelKey, group: group, partCount: partCount, instancesMap: instancesMap, onSelect: onSelect, registerRef: registerRef })) }, key));
114
117
  })] }));
115
118
  }
116
- // Physics instancing stays the same
119
+ // Render physics-enabled instances using InstancedRigidBodies
117
120
  function InstancedRigidGroup({ group, modelKey, partCount, flatMeshes }) {
118
121
  const instances = useMemo(() => group.instances.map(inst => ({
119
122
  key: inst.id,
@@ -126,12 +129,17 @@ function InstancedRigidGroup({ group, modelKey, partCount, flatMeshes }) {
126
129
  return (_jsx("instancedMesh", { args: [mesh.geometry, mesh.material, group.instances.length], castShadow: true, receiveShadow: true, frustumCulled: false }, i));
127
130
  }) }));
128
131
  }
129
- // Non-physics instanced visuals: per-instance group using Merged's Instance components
132
+ // Render non-physics instances using Merged's per-instance groups
130
133
  function NonPhysicsInstancedGroup({ modelKey, group, partCount, instancesMap, onSelect, registerRef }) {
131
134
  const clickValid = useRef(false);
132
- const handlePointerDown = (e) => { e.stopPropagation(); clickValid.current = true; };
133
- const handlePointerMove = () => { if (clickValid.current)
134
- clickValid.current = false; };
135
+ const handlePointerDown = (e) => {
136
+ e.stopPropagation();
137
+ clickValid.current = true;
138
+ };
139
+ const handlePointerMove = () => {
140
+ if (clickValid.current)
141
+ clickValid.current = false;
142
+ };
135
143
  const handlePointerUp = (e, id) => {
136
144
  if (clickValid.current) {
137
145
  e.stopPropagation();
@@ -146,7 +154,7 @@ function NonPhysicsInstancedGroup({ modelKey, group, partCount, instancesMap, on
146
154
  return _jsx(Instance, {}, i);
147
155
  }) }, inst.id))) }));
148
156
  }
149
- // --- GameInstance: just registers an instance, renders nothing ---
157
+ // GameInstance component: registers an instance for batch rendering (renders nothing itself)
150
158
  export const GameInstance = React.forwardRef(({ id, modelUrl, position, rotation, scale, physics = undefined, }, ref) => {
151
159
  const ctx = useContext(GameInstanceContext);
152
160
  const addInstance = ctx === null || ctx === void 0 ? void 0 : ctx.addInstance;
@@ -167,6 +175,6 @@ export const GameInstance = React.forwardRef(({ id, modelUrl, position, rotation
167
175
  removeInstance(instance.id);
168
176
  };
169
177
  }, [addInstance, removeInstance, instance]);
170
- // No visual here provider will render visuals for all instances
178
+ // No visual rendering - provider handles all instanced visuals
171
179
  return null;
172
180
  });
@@ -14,28 +14,24 @@ import { useState, useRef, useEffect } from "react";
14
14
  import PrefabRoot from "./PrefabRoot";
15
15
  import { Physics } from "@react-three/rapier";
16
16
  import EditorUI from "./EditorUI";
17
+ import { base, toolbar } from "./styles";
17
18
  const PrefabEditor = ({ basePath, initialPrefab, onPrefabChange, children }) => {
18
19
  const [editMode, setEditMode] = useState(true);
19
20
  const [loadedPrefab, setLoadedPrefab] = useState(initialPrefab !== null && initialPrefab !== void 0 ? initialPrefab : {
20
- "id": "prefab-default",
21
- "name": "New Prefab",
22
- "root": {
23
- "id": "root",
24
- "components": {
25
- "transform": {
26
- "type": "Transform",
27
- "properties": {
28
- "position": [0, 0, 0],
29
- "rotation": [0, 0, 0],
30
- "scale": [1, 1, 1]
31
- }
21
+ id: "prefab-default",
22
+ name: "New Prefab",
23
+ root: {
24
+ id: "root",
25
+ components: {
26
+ transform: {
27
+ type: "Transform",
28
+ properties: { position: [0, 0, 0], rotation: [0, 0, 0], scale: [1, 1, 1] }
32
29
  }
33
30
  }
34
31
  }
35
32
  });
36
33
  const [selectedId, setSelectedId] = useState(null);
37
34
  const [transformMode, setTransformMode] = useState("translate");
38
- const prefabRef = useRef(null);
39
35
  // Sync internal state with external initialPrefab prop
40
36
  useEffect(() => {
41
37
  if (initialPrefab) {
@@ -48,136 +44,74 @@ const PrefabEditor = ({ basePath, initialPrefab, onPrefabChange, children }) =>
48
44
  const resolved = typeof newPrefab === 'function' ? newPrefab(loadedPrefab) : newPrefab;
49
45
  onPrefabChange === null || onPrefabChange === void 0 ? void 0 : onPrefabChange(resolved);
50
46
  };
51
- return _jsxs(_Fragment, { children: [_jsx(GameCanvas, { children: _jsxs(Physics, { paused: editMode, children: [_jsx("ambientLight", { intensity: 1.5 }), _jsx("gridHelper", { args: [10, 10], position: [0, -1, 0] }), _jsx(PrefabRoot, { data: loadedPrefab, ref: prefabRef,
52
- // props for edit mode
53
- editMode: editMode, onPrefabChange: updatePrefab, selectedId: selectedId, onSelect: setSelectedId, transformMode: transformMode, setTransformMode: setTransformMode, basePath: basePath }), children] }) }), _jsx(SaveDataPanel, { currentData: loadedPrefab, onDataChange: updatePrefab, editMode: editMode, onEditModeChange: setEditMode }), editMode && _jsx(EditorUI, { prefabData: loadedPrefab, setPrefabData: updatePrefab, selectedId: selectedId, setSelectedId: setSelectedId, transformMode: transformMode, setTransformMode: setTransformMode, basePath: basePath })] });
47
+ return _jsxs(_Fragment, { children: [_jsx(GameCanvas, { children: _jsxs(Physics, { paused: editMode, children: [_jsx("ambientLight", { intensity: 1.5 }), _jsx("gridHelper", { args: [10, 10], position: [0, -1, 0] }), _jsx(PrefabRoot, { data: loadedPrefab, editMode: editMode, onPrefabChange: updatePrefab, selectedId: selectedId, onSelect: setSelectedId, transformMode: transformMode, basePath: basePath }), children] }) }), _jsx(SaveDataPanel, { currentData: loadedPrefab, onDataChange: updatePrefab, editMode: editMode, onEditModeChange: setEditMode }), editMode && _jsx(EditorUI, { prefabData: loadedPrefab, setPrefabData: updatePrefab, selectedId: selectedId, setSelectedId: setSelectedId, transformMode: transformMode, setTransformMode: setTransformMode, basePath: basePath })] });
54
48
  };
55
49
  const SaveDataPanel = ({ currentData, onDataChange, editMode, onEditModeChange }) => {
56
50
  const [history, setHistory] = useState([currentData]);
57
51
  const [historyIndex, setHistoryIndex] = useState(0);
58
- const throttleTimeoutRef = useRef(null);
59
- const lastSavedDataRef = useRef(JSON.stringify(currentData));
60
- // Define undo/redo handlers
61
- const handleUndo = () => {
52
+ const throttleRef = useRef(null);
53
+ const lastDataRef = useRef(JSON.stringify(currentData));
54
+ const undo = () => {
62
55
  if (historyIndex > 0) {
63
56
  const newIndex = historyIndex - 1;
64
57
  setHistoryIndex(newIndex);
65
- lastSavedDataRef.current = JSON.stringify(history[newIndex]);
58
+ lastDataRef.current = JSON.stringify(history[newIndex]);
66
59
  onDataChange(history[newIndex]);
67
60
  }
68
61
  };
69
- const handleRedo = () => {
62
+ const redo = () => {
70
63
  if (historyIndex < history.length - 1) {
71
64
  const newIndex = historyIndex + 1;
72
65
  setHistoryIndex(newIndex);
73
- lastSavedDataRef.current = JSON.stringify(history[newIndex]);
66
+ lastDataRef.current = JSON.stringify(history[newIndex]);
74
67
  onDataChange(history[newIndex]);
75
68
  }
76
69
  };
77
- // Keyboard shortcuts for undo/redo
78
70
  useEffect(() => {
79
71
  const handleKeyDown = (e) => {
80
- // Undo: Ctrl+Z (Cmd+Z on Mac)
81
72
  if ((e.ctrlKey || e.metaKey) && e.key === 'z' && !e.shiftKey) {
82
73
  e.preventDefault();
83
- handleUndo();
74
+ undo();
84
75
  }
85
- // Redo: Ctrl+Shift+Z or Ctrl+Y (Cmd+Shift+Z or Cmd+Y on Mac)
86
76
  else if ((e.ctrlKey || e.metaKey) && (e.shiftKey && e.key === 'z' || e.key === 'y')) {
87
77
  e.preventDefault();
88
- handleRedo();
78
+ redo();
89
79
  }
90
80
  };
91
81
  window.addEventListener('keydown', handleKeyDown);
92
82
  return () => window.removeEventListener('keydown', handleKeyDown);
93
83
  }, [historyIndex, history]);
94
- // Throttled history update when currentData changes
95
84
  useEffect(() => {
96
- const currentDataStr = JSON.stringify(currentData);
97
- // Skip if data hasn't actually changed
98
- if (currentDataStr === lastSavedDataRef.current) {
85
+ const currentStr = JSON.stringify(currentData);
86
+ if (currentStr === lastDataRef.current)
99
87
  return;
100
- }
101
- // Clear existing throttle timeout
102
- if (throttleTimeoutRef.current) {
103
- clearTimeout(throttleTimeoutRef.current);
104
- }
105
- // Set new throttled update
106
- throttleTimeoutRef.current = setTimeout(() => {
107
- lastSavedDataRef.current = currentDataStr;
88
+ if (throttleRef.current)
89
+ clearTimeout(throttleRef.current);
90
+ throttleRef.current = setTimeout(() => {
91
+ lastDataRef.current = currentStr;
108
92
  setHistory(prev => {
109
- // Slice history at current index (discard future states)
110
- const newHistory = prev.slice(0, historyIndex + 1);
111
- // Add new state
112
- newHistory.push(currentData);
113
- // Limit history size to 50 states
114
- if (newHistory.length > 50) {
115
- newHistory.shift();
116
- return newHistory;
117
- }
118
- return newHistory;
93
+ const newHistory = [...prev.slice(0, historyIndex + 1), currentData];
94
+ return newHistory.length > 50 ? newHistory.slice(1) : newHistory;
119
95
  });
120
- setHistoryIndex(prev => {
121
- const newHistory = history.slice(0, prev + 1);
122
- newHistory.push(currentData);
123
- return Math.min(newHistory.length - 1, 49);
124
- });
125
- }, 500); // 500ms throttle
96
+ setHistoryIndex(prev => Math.min(prev + 1, 49));
97
+ }, 500);
126
98
  return () => {
127
- if (throttleTimeoutRef.current) {
128
- clearTimeout(throttleTimeoutRef.current);
129
- }
99
+ if (throttleRef.current)
100
+ clearTimeout(throttleRef.current);
130
101
  };
131
- }, [currentData, historyIndex, history]);
102
+ }, [currentData]);
132
103
  const handleLoad = () => __awaiter(void 0, void 0, void 0, function* () {
133
104
  const prefab = yield loadJson();
134
105
  if (prefab) {
135
106
  onDataChange(prefab);
136
- // Reset history when loading new file
137
107
  setHistory([prefab]);
138
108
  setHistoryIndex(0);
139
- lastSavedDataRef.current = JSON.stringify(prefab);
109
+ lastDataRef.current = JSON.stringify(prefab);
140
110
  }
141
111
  });
142
112
  const canUndo = historyIndex > 0;
143
113
  const canRedo = historyIndex < history.length - 1;
144
- return _jsxs("div", { style: {
145
- position: "absolute",
146
- top: 8,
147
- left: "50%",
148
- transform: "translateX(-50%)",
149
- display: "flex",
150
- alignItems: "center",
151
- gap: 6,
152
- padding: "2px 4px",
153
- background: "rgba(0,0,0,0.55)",
154
- border: "1px solid rgba(255,255,255,0.12)",
155
- borderRadius: 4,
156
- color: "rgba(255,255,255,0.9)",
157
- fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace",
158
- fontSize: 11,
159
- lineHeight: 1,
160
- WebkitUserSelect: "none",
161
- userSelect: "none",
162
- }, children: [_jsx(PanelButton, { onClick: () => onEditModeChange(!editMode), children: editMode ? "▶" : "⏸" }), _jsx("span", { style: { opacity: 0.35 }, children: "|" }), _jsx(PanelButton, { onClick: handleUndo, disabled: !canUndo, title: "Undo (Ctrl+Z)", children: "\u21B6" }), _jsx(PanelButton, { onClick: handleRedo, disabled: !canRedo, title: "Redo (Ctrl+Shift+Z)", children: "\u21B7" }), _jsx("span", { style: { opacity: 0.35 }, children: "|" }), _jsx(PanelButton, { onClick: handleLoad, title: "Load JSON", children: "\uD83D\uDCE5" }), _jsx(PanelButton, { onClick: () => saveJson(currentData, "prefab"), title: "Save JSON", children: "\uD83D\uDCBE" })] });
163
- };
164
- const PanelButton = ({ onClick, disabled, title, children }) => {
165
- return _jsx("button", { style: {
166
- padding: "2px 6px",
167
- font: "inherit",
168
- background: "transparent",
169
- color: disabled ? "rgba(255,255,255,0.3)" : "inherit",
170
- border: "1px solid rgba(255,255,255,0.18)",
171
- borderRadius: 3,
172
- cursor: disabled ? "not-allowed" : "pointer",
173
- opacity: disabled ? 0.5 : 1,
174
- }, onClick: onClick, disabled: disabled, title: title, onPointerEnter: (e) => {
175
- if (!disabled) {
176
- e.currentTarget.style.background = "rgba(255,255,255,0.08)";
177
- }
178
- }, onPointerLeave: (e) => {
179
- e.currentTarget.style.background = "transparent";
180
- }, children: children });
114
+ return _jsxs("div", { style: toolbar.panel, children: [_jsx("button", { style: base.btn, onClick: () => onEditModeChange(!editMode), children: editMode ? "▶" : "⏸" }), _jsx("div", { style: toolbar.divider }), _jsx("button", { style: Object.assign(Object.assign({}, base.btn), (canUndo ? {} : toolbar.disabled)), onClick: undo, disabled: !canUndo, children: "\u21B6" }), _jsx("button", { style: Object.assign(Object.assign({}, base.btn), (canRedo ? {} : toolbar.disabled)), onClick: redo, disabled: !canRedo, children: "\u21B7" }), _jsx("div", { style: toolbar.divider }), _jsx("button", { style: base.btn, onClick: handleLoad, children: "\uD83D\uDCE5" }), _jsx("button", { style: base.btn, onClick: () => saveJson(currentData, "prefab"), children: "\uD83D\uDCBE" })] });
181
115
  };
182
116
  const saveJson = (data, filename) => {
183
117
  const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(data, null, 2));
@@ -7,7 +7,6 @@ export declare const PrefabRoot: import("react").ForwardRefExoticComponent<{
7
7
  selectedId?: string | null;
8
8
  onSelect?: (id: string | null) => void;
9
9
  transformMode?: "translate" | "rotate" | "scale";
10
- setTransformMode?: (mode: "translate" | "rotate" | "scale") => void;
11
10
  basePath?: string;
12
11
  } & import("react").RefAttributes<Group<import("three").Object3DEventMap>>>;
13
12
  export default PrefabRoot;
@@ -12,42 +12,26 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
12
12
  import { MapControls, TransformControls } from "@react-three/drei";
13
13
  import { useState, useRef, useEffect, forwardRef, useCallback } from "react";
14
14
  import { Vector3, Euler, Quaternion, SRGBColorSpace, TextureLoader, Matrix4 } from "three";
15
- import { getComponent } from "./components/ComponentRegistry";
15
+ import { getComponent, registerComponent } from "./components/ComponentRegistry";
16
16
  import { loadModel } from "../dragdrop/modelLoader";
17
17
  import { GameInstance, GameInstanceProvider } from "./InstanceProvider";
18
- // register all components
19
- import { registerComponent } from './components/ComponentRegistry';
18
+ import { updateNode } from "./utils";
20
19
  import components from './components/';
20
+ // Register all components
21
21
  components.forEach(registerComponent);
22
- function updatePrefabNode(root, id, update) {
23
- if (root.id === id) {
24
- return update(root);
25
- }
26
- if (root.children) {
27
- return Object.assign(Object.assign({}, root), { children: root.children.map(child => updatePrefabNode(child, id, update)) });
28
- }
29
- return root;
30
- }
31
- export const PrefabRoot = forwardRef(({ editMode, data, onPrefabChange, selectedId, onSelect, transformMode, setTransformMode, basePath = "" }, ref) => {
22
+ export const PrefabRoot = forwardRef(({ editMode, data, onPrefabChange, selectedId, onSelect, transformMode, basePath = "" }, ref) => {
32
23
  const [loadedModels, setLoadedModels] = useState({});
33
24
  const [loadedTextures, setLoadedTextures] = useState({});
34
- // const [prefabRoot, setPrefabRoot] = useState<Prefab>(data); // Removed local state
35
25
  const loadingRefs = useRef(new Set());
36
26
  const objectRefs = useRef({});
37
27
  const [selectedObject, setSelectedObject] = useState(null);
38
28
  const registerRef = useCallback((id, obj) => {
39
29
  objectRefs.current[id] = obj;
40
- if (id === selectedId) {
30
+ if (id === selectedId)
41
31
  setSelectedObject(obj);
42
- }
43
32
  }, [selectedId]);
44
33
  useEffect(() => {
45
- if (selectedId) {
46
- setSelectedObject(objectRefs.current[selectedId] || null);
47
- }
48
- else {
49
- setSelectedObject(null);
50
- }
34
+ setSelectedObject(selectedId ? objectRefs.current[selectedId] || null : null);
51
35
  }, [selectedId]);
52
36
  const onTransformChange = () => {
53
37
  if (!selectedId || !onPrefabChange)
@@ -68,7 +52,7 @@ export const PrefabRoot = forwardRef(({ editMode, data, onPrefabChange, selected
68
52
  localMatrix.decompose(lp, lq, ls);
69
53
  const le = new Euler().setFromQuaternion(lq);
70
54
  // 4. Write back LOCAL transform into the prefab node
71
- const newRoot = updatePrefabNode(data.root, selectedId, (node) => (Object.assign(Object.assign({}, node), { components: Object.assign(Object.assign({}, node === null || node === void 0 ? void 0 : node.components), { transform: {
55
+ const newRoot = updateNode(data.root, selectedId, (node) => (Object.assign(Object.assign({}, node), { components: Object.assign(Object.assign({}, node.components), { transform: {
72
56
  type: "Transform",
73
57
  properties: {
74
58
  position: [lp.x, lp.y, lp.z],
@@ -128,13 +112,15 @@ function GameObjectRenderer({ gameObject, selectedId, onSelect, registerRef, loa
128
112
  // Early return if gameObject is null or undefined
129
113
  if (!gameObject)
130
114
  return null;
131
- // Build a small context object to avoid long param lists
115
+ if (gameObject.disabled === true || gameObject.hidden === true)
116
+ return null;
117
+ // Build context object for passing to helper functions
132
118
  const ctx = { gameObject, selectedId, onSelect, registerRef, loadedModels, loadedTextures, editMode };
133
- // --- 1. Transform (local + world) ---
119
+ // --- 1. Compute transforms (local + world) ---
134
120
  const transformProps = getNodeTransformProps(gameObject);
135
121
  const localMatrix = new Matrix4().compose(new Vector3(...transformProps.position), new Quaternion().setFromEuler(new Euler(...transformProps.rotation)), new Vector3(...transformProps.scale));
136
122
  const worldMatrix = parentMatrix.clone().multiply(localMatrix);
137
- // preserve click/drag detection from previous implementation
123
+ // --- 2. Handle selection interaction (edit mode only) ---
138
124
  const clickValid = useRef(false);
139
125
  const handlePointerDown = (e) => {
140
126
  e.stopPropagation();
@@ -151,20 +137,18 @@ function GameObjectRenderer({ gameObject, selectedId, onSelect, registerRef, loa
151
137
  }
152
138
  clickValid.current = false;
153
139
  };
154
- if (gameObject.disabled === true || gameObject.hidden === true)
155
- return null;
156
- // --- 2. If instanced, short-circuit to a tiny clean branch ---
140
+ // --- 3. If instanced model, short-circuit to GameInstance (terminal node) ---
157
141
  const isInstanced = !!((_c = (_b = (_a = gameObject.components) === null || _a === void 0 ? void 0 : _a.model) === null || _b === void 0 ? void 0 : _b.properties) === null || _c === void 0 ? void 0 : _c.instanced);
158
142
  if (isInstanced) {
159
143
  return renderInstancedNode(gameObject, worldMatrix, ctx);
160
144
  }
161
- // --- 3. Core content decided by component registry ---
145
+ // --- 4. Render core content using component system ---
162
146
  const core = renderCoreNode(gameObject, ctx, parentMatrix);
163
- // --- 5. Render children (always relative transforms) ---
164
- const children = ((_d = gameObject.children) !== null && _d !== void 0 ? _d : []).map((child) => (_jsx(GameObjectRenderer, { gameObject: child, selectedId: selectedId, onSelect: onSelect, registerRef: registerRef, loadedModels: loadedModels, loadedTextures: loadedTextures, editMode: editMode, parentMatrix: worldMatrix }, child.id)));
165
- // --- 4. Wrap with physics if needed ---
147
+ // --- 5. Wrap with physics if needed (except in edit mode) ---
166
148
  const physicsWrapped = wrapPhysicsIfNeeded(gameObject, core, ctx);
167
- // --- 6. Final group wrapper ---
149
+ // --- 6. Render children recursively (always relative transforms) ---
150
+ const children = ((_d = gameObject.children) !== null && _d !== void 0 ? _d : []).map((child) => (_jsx(GameObjectRenderer, { gameObject: child, selectedId: selectedId, onSelect: onSelect, registerRef: registerRef, loadedModels: loadedModels, loadedTextures: loadedTextures, editMode: editMode, parentMatrix: worldMatrix }, child.id)));
151
+ // --- 7. Final group wrapper with local transform ---
168
152
  return (_jsxs("group", { ref: (el) => registerRef(gameObject.id, el), position: transformProps.position, rotation: transformProps.rotation, scale: transformProps.scale, onPointerDown: handlePointerDown, onPointerMove: handlePointerMove, onPointerUp: handlePointerUp, children: [physicsWrapped, children] }));
169
153
  }
170
154
  // Helper: render an instanced GameInstance (terminal node)
@@ -179,16 +163,16 @@ function renderInstancedNode(gameObject, worldMatrix, ctx) {
179
163
  const modelUrl = (_d = (_c = (_b = gameObject.components) === null || _b === void 0 ? void 0 : _b.model) === null || _c === void 0 ? void 0 : _c.properties) === null || _d === void 0 ? void 0 : _d.filename;
180
164
  return (_jsx(GameInstance, { id: gameObject.id, modelUrl: modelUrl, position: [wp.x, wp.y, wp.z], rotation: [we.x, we.y, we.z], scale: [ws.x, ws.y, ws.z], physics: ctx.editMode ? undefined : physics === null || physics === void 0 ? void 0 : physics.properties }));
181
165
  }
182
- // Helper: render main model/geometry content for a non-instanced node
166
+ // Helper: render main content for a non-instanced node using the component system
183
167
  function renderCoreNode(gameObject, ctx, parentMatrix) {
184
168
  var _a, _b, _c;
185
169
  const geometry = (_a = gameObject.components) === null || _a === void 0 ? void 0 : _a.geometry;
186
170
  const material = (_b = gameObject.components) === null || _b === void 0 ? void 0 : _b.material;
187
- const modelComp = (_c = gameObject.components) === null || _c === void 0 ? void 0 : _c.model;
171
+ const model = (_c = gameObject.components) === null || _c === void 0 ? void 0 : _c.model;
188
172
  const geometryDef = geometry ? getComponent('Geometry') : undefined;
189
173
  const materialDef = material ? getComponent('Material') : undefined;
190
- const isModelAvailable = !!(modelComp && modelComp.properties && modelComp.properties.filename && ctx.loadedModels[modelComp.properties.filename]);
191
- // Generic component views (exclude geometry/material/model/transform/physics)
174
+ const modelDef = model ? getComponent('Model') : undefined;
175
+ // Context props for all component Views
192
176
  const contextProps = {
193
177
  loadedModels: ctx.loadedModels,
194
178
  loadedTextures: ctx.loadedTextures,
@@ -197,20 +181,19 @@ function renderCoreNode(gameObject, ctx, parentMatrix) {
197
181
  parentMatrix,
198
182
  registerRef: ctx.registerRef,
199
183
  };
200
- // Separate wrapper components (that accept children) from leaf components
184
+ // Collect wrapper and leaf components (excluding transform/physics which are handled separately)
201
185
  const wrapperComponents = [];
202
186
  const leafComponents = [];
203
187
  if (gameObject.components) {
204
188
  Object.entries(gameObject.components)
205
- .filter(([key]) => key !== 'geometry' && key !== 'material' && key !== 'model' && key !== 'transform' && key !== 'physics')
189
+ .filter(([key]) => !['geometry', 'material', 'model', 'transform', 'physics'].includes(key))
206
190
  .forEach(([key, comp]) => {
207
191
  if (!comp || !comp.type)
208
192
  return;
209
193
  const def = getComponent(comp.type);
210
194
  if (!def || !def.View)
211
195
  return;
212
- // Check if the component View accepts children by checking function signature
213
- // Components that wrap content should accept children prop
196
+ // Components that accept children are wrappers, others are leaves
214
197
  const viewString = def.View.toString();
215
198
  if (viewString.includes('children')) {
216
199
  wrapperComponents.push({ key, View: def.View, properties: comp.properties });
@@ -220,19 +203,19 @@ function renderCoreNode(gameObject, ctx, parentMatrix) {
220
203
  }
221
204
  });
222
205
  }
223
- // Build the core content (model or mesh)
206
+ // Build core content based on what components exist
224
207
  let coreContent;
225
- // If we have a model (non-instanced) render it as a primitive with material override
226
- if (isModelAvailable) {
227
- const modelObj = ctx.loadedModels[modelComp.properties.filename].clone();
228
- coreContent = (_jsxs("primitive", { object: modelObj, children: [material && materialDef && materialDef.View && (_jsx(materialDef.View, { properties: material.properties, loadedTextures: ctx.loadedTextures, isSelected: ctx.selectedId === gameObject.id, editMode: ctx.editMode, parentMatrix: parentMatrix, registerRef: ctx.registerRef }, "material")), leafComponents] }));
208
+ // Priority: Model > Geometry + Material > Empty
209
+ if (model && modelDef && modelDef.View) {
210
+ // Model component wraps its children (including material override)
211
+ coreContent = (_jsxs(modelDef.View, Object.assign({ properties: model.properties }, contextProps, { children: [material && materialDef && materialDef.View && (_jsx(materialDef.View, Object.assign({ properties: material.properties }, contextProps), "material")), leafComponents] })));
229
212
  }
230
213
  else if (geometry && geometryDef && geometryDef.View) {
231
- // Otherwise, if geometry present, render a mesh
232
- coreContent = (_jsxs("mesh", { children: [_jsx(geometryDef.View, Object.assign({ properties: geometry.properties }, contextProps), "geometry"), material && materialDef && materialDef.View && (_jsx(materialDef.View, { properties: material.properties, loadedTextures: ctx.loadedTextures, isSelected: ctx.selectedId === gameObject.id, editMode: ctx.editMode, parentMatrix: parentMatrix, registerRef: ctx.registerRef }, "material")), leafComponents] }));
214
+ // Geometry + Material = mesh
215
+ coreContent = (_jsxs("mesh", { castShadow: true, receiveShadow: true, children: [_jsx(geometryDef.View, Object.assign({ properties: geometry.properties }, contextProps)), material && materialDef && materialDef.View && (_jsx(materialDef.View, Object.assign({ properties: material.properties }, contextProps), "material")), leafComponents] }));
233
216
  }
234
217
  else {
235
- // No geometry or model, just render leaf components
218
+ // No visual component - just render leaves
236
219
  coreContent = _jsx(_Fragment, { children: leafComponents });
237
220
  }
238
221
  // Wrap core content with wrapper components (in order)
@@ -0,0 +1,3 @@
1
+ import { Component } from "./ComponentRegistry";
2
+ declare const DirectionalLightComponent: Component;
3
+ export default DirectionalLightComponent;
@@ -0,0 +1,102 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useRef, useEffect } from "react";
3
+ import { useFrame, useThree } from "@react-three/fiber";
4
+ import { CameraHelper, Object3D, Vector3 } from "three";
5
+ function DirectionalLightComponentEditor({ component, onUpdate }) {
6
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
7
+ const props = {
8
+ color: (_a = component.properties.color) !== null && _a !== void 0 ? _a : '#ffffff',
9
+ intensity: (_b = component.properties.intensity) !== null && _b !== void 0 ? _b : 1.0,
10
+ castShadow: (_c = component.properties.castShadow) !== null && _c !== void 0 ? _c : true,
11
+ shadowMapSize: (_d = component.properties.shadowMapSize) !== null && _d !== void 0 ? _d : 1024,
12
+ shadowCameraNear: (_e = component.properties.shadowCameraNear) !== null && _e !== void 0 ? _e : 0.1,
13
+ shadowCameraFar: (_f = component.properties.shadowCameraFar) !== null && _f !== void 0 ? _f : 100,
14
+ shadowCameraTop: (_g = component.properties.shadowCameraTop) !== null && _g !== void 0 ? _g : 30,
15
+ shadowCameraBottom: (_h = component.properties.shadowCameraBottom) !== null && _h !== void 0 ? _h : -30,
16
+ shadowCameraLeft: (_j = component.properties.shadowCameraLeft) !== null && _j !== void 0 ? _j : -30,
17
+ shadowCameraRight: (_k = component.properties.shadowCameraRight) !== null && _k !== void 0 ? _k : 30,
18
+ targetOffset: (_l = component.properties.targetOffset) !== null && _l !== void 0 ? _l : [0, -5, 0]
19
+ };
20
+ return _jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("div", { children: [_jsx("label", { className: "block text-[9px] text-cyan-400/60 uppercase tracking-wider mb-0.5", children: "Color" }), _jsxs("div", { className: "flex gap-0.5", children: [_jsx("input", { type: "color", className: "h-5 w-5 bg-transparent border-none cursor-pointer", value: props.color, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { 'color': e.target.value })) }), _jsx("input", { type: "text", className: "flex-1 bg-black/40 border border-cyan-500/30 px-1 py-0.5 text-[10px] text-cyan-300 font-mono focus:outline-none focus:border-cyan-400/50", value: props.color, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { 'color': e.target.value })) })] })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-[9px] text-cyan-400/60 uppercase tracking-wider mb-0.5", children: "Intensity" }), _jsx("input", { type: "number", step: "0.1", className: "w-full bg-black/40 border border-cyan-500/30 px-1 py-0.5 text-[10px] text-cyan-300 font-mono focus:outline-none focus:border-cyan-400/50", value: props.intensity, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { 'intensity': parseFloat(e.target.value) })) })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-[9px] text-cyan-400/60 uppercase tracking-wider mb-0.5", children: "Cast Shadow" }), _jsx("input", { type: "checkbox", className: "h-4 w-4 bg-black/40 border border-cyan-500/30 cursor-pointer", checked: props.castShadow, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { 'castShadow': e.target.checked })) })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-[9px] text-cyan-400/60 uppercase tracking-wider mb-0.5", children: "Shadow Map Size" }), _jsx("input", { type: "number", step: "256", className: "w-full bg-black/40 border border-cyan-500/30 px-1 py-0.5 text-[10px] text-cyan-300 font-mono focus:outline-none focus:border-cyan-400/50", value: props.shadowMapSize, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { 'shadowMapSize': parseFloat(e.target.value) })) })] }), _jsxs("div", { className: "border-t border-cyan-500/20 pt-2 mt-2", children: [_jsx("label", { className: "block text-[9px] text-cyan-400/60 uppercase tracking-wider mb-1", children: "Shadow Camera" }), _jsxs("div", { className: "grid grid-cols-2 gap-1", children: [_jsxs("div", { children: [_jsx("label", { className: "block text-[8px] text-cyan-400/50 mb-0.5", children: "Near" }), _jsx("input", { type: "number", step: "0.1", className: "w-full bg-black/40 border border-cyan-500/30 px-1 py-0.5 text-[10px] text-cyan-300 font-mono focus:outline-none focus:border-cyan-400/50", value: props.shadowCameraNear, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { 'shadowCameraNear': parseFloat(e.target.value) })) })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-[8px] text-cyan-400/50 mb-0.5", children: "Far" }), _jsx("input", { type: "number", step: "1", className: "w-full bg-black/40 border border-cyan-500/30 px-1 py-0.5 text-[10px] text-cyan-300 font-mono focus:outline-none focus:border-cyan-400/50", value: props.shadowCameraFar, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { 'shadowCameraFar': parseFloat(e.target.value) })) })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-[8px] text-cyan-400/50 mb-0.5", children: "Top" }), _jsx("input", { type: "number", step: "1", className: "w-full bg-black/40 border border-cyan-500/30 px-1 py-0.5 text-[10px] text-cyan-300 font-mono focus:outline-none focus:border-cyan-400/50", value: props.shadowCameraTop, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { 'shadowCameraTop': parseFloat(e.target.value) })) })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-[8px] text-cyan-400/50 mb-0.5", children: "Bottom" }), _jsx("input", { type: "number", step: "1", className: "w-full bg-black/40 border border-cyan-500/30 px-1 py-0.5 text-[10px] text-cyan-300 font-mono focus:outline-none focus:border-cyan-400/50", value: props.shadowCameraBottom, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { 'shadowCameraBottom': parseFloat(e.target.value) })) })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-[8px] text-cyan-400/50 mb-0.5", children: "Left" }), _jsx("input", { type: "number", step: "1", className: "w-full bg-black/40 border border-cyan-500/30 px-1 py-0.5 text-[10px] text-cyan-300 font-mono focus:outline-none focus:border-cyan-400/50", value: props.shadowCameraLeft, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { 'shadowCameraLeft': parseFloat(e.target.value) })) })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-[8px] text-cyan-400/50 mb-0.5", children: "Right" }), _jsx("input", { type: "number", step: "1", className: "w-full bg-black/40 border border-cyan-500/30 px-1 py-0.5 text-[10px] text-cyan-300 font-mono focus:outline-none focus:border-cyan-400/50", value: props.shadowCameraRight, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { 'shadowCameraRight': parseFloat(e.target.value) })) })] })] })] }), _jsxs("div", { className: "border-t border-cyan-500/20 pt-2 mt-2", children: [_jsx("label", { className: "block text-[9px] text-cyan-400/60 uppercase tracking-wider mb-1", children: "Target Offset" }), _jsxs("div", { className: "grid grid-cols-3 gap-1", children: [_jsxs("div", { children: [_jsx("label", { className: "block text-[8px] text-cyan-400/50 mb-0.5", children: "X" }), _jsx("input", { type: "number", step: "0.5", className: "w-full bg-black/40 border border-cyan-500/30 px-1 py-0.5 text-[10px] text-cyan-300 font-mono focus:outline-none focus:border-cyan-400/50", value: props.targetOffset[0], onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { 'targetOffset': [parseFloat(e.target.value), props.targetOffset[1], props.targetOffset[2]] })) })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-[8px] text-cyan-400/50 mb-0.5", children: "Y" }), _jsx("input", { type: "number", step: "0.5", className: "w-full bg-black/40 border border-cyan-500/30 px-1 py-0.5 text-[10px] text-cyan-300 font-mono focus:outline-none focus:border-cyan-400/50", value: props.targetOffset[1], onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { 'targetOffset': [props.targetOffset[0], parseFloat(e.target.value), props.targetOffset[2]] })) })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-[8px] text-cyan-400/50 mb-0.5", children: "Z" }), _jsx("input", { type: "number", step: "0.5", className: "w-full bg-black/40 border border-cyan-500/30 px-1 py-0.5 text-[10px] text-cyan-300 font-mono focus:outline-none focus:border-cyan-400/50", value: props.targetOffset[2], onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { 'targetOffset': [props.targetOffset[0], props.targetOffset[1], parseFloat(e.target.value)] })) })] })] })] })] });
21
+ }
22
+ function DirectionalLightView({ properties, editMode }) {
23
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
24
+ const color = (_a = properties.color) !== null && _a !== void 0 ? _a : '#ffffff';
25
+ const intensity = (_b = properties.intensity) !== null && _b !== void 0 ? _b : 1.0;
26
+ const castShadow = (_c = properties.castShadow) !== null && _c !== void 0 ? _c : true;
27
+ const shadowMapSize = (_d = properties.shadowMapSize) !== null && _d !== void 0 ? _d : 1024;
28
+ const shadowCameraNear = (_e = properties.shadowCameraNear) !== null && _e !== void 0 ? _e : 0.1;
29
+ const shadowCameraFar = (_f = properties.shadowCameraFar) !== null && _f !== void 0 ? _f : 100;
30
+ const shadowCameraTop = (_g = properties.shadowCameraTop) !== null && _g !== void 0 ? _g : 30;
31
+ const shadowCameraBottom = (_h = properties.shadowCameraBottom) !== null && _h !== void 0 ? _h : -30;
32
+ const shadowCameraLeft = (_j = properties.shadowCameraLeft) !== null && _j !== void 0 ? _j : -30;
33
+ const shadowCameraRight = (_k = properties.shadowCameraRight) !== null && _k !== void 0 ? _k : 30;
34
+ const targetOffset = (_l = properties.targetOffset) !== null && _l !== void 0 ? _l : [0, -5, 0];
35
+ const { scene } = useThree();
36
+ const directionalLightRef = useRef(null);
37
+ const targetRef = useRef(new Object3D());
38
+ const cameraHelperRef = useRef(null);
39
+ // Add target to scene once
40
+ useEffect(() => {
41
+ const target = targetRef.current;
42
+ scene.add(target);
43
+ return () => {
44
+ scene.remove(target);
45
+ };
46
+ }, [scene]);
47
+ // Set up light target reference once
48
+ useEffect(() => {
49
+ if (directionalLightRef.current) {
50
+ directionalLightRef.current.target = targetRef.current;
51
+ }
52
+ }, []);
53
+ // Update target position and mark shadow for update when light moves or offset changes
54
+ useFrame(() => {
55
+ if (!directionalLightRef.current)
56
+ return;
57
+ const lightWorldPos = new Vector3();
58
+ directionalLightRef.current.getWorldPosition(lightWorldPos);
59
+ const newTargetPos = new Vector3(lightWorldPos.x + targetOffset[0], lightWorldPos.y + targetOffset[1], lightWorldPos.z + targetOffset[2]);
60
+ // Only update if position actually changed
61
+ if (!targetRef.current.position.equals(newTargetPos)) {
62
+ targetRef.current.position.copy(newTargetPos);
63
+ if (directionalLightRef.current.shadow) {
64
+ directionalLightRef.current.shadow.needsUpdate = true;
65
+ }
66
+ }
67
+ // Update camera helper in edit mode
68
+ if (editMode && cameraHelperRef.current) {
69
+ cameraHelperRef.current.update();
70
+ }
71
+ });
72
+ // Create/destroy camera helper for edit mode
73
+ useEffect(() => {
74
+ var _a;
75
+ if (editMode && ((_a = directionalLightRef.current) === null || _a === void 0 ? void 0 : _a.shadow.camera)) {
76
+ const helper = new CameraHelper(directionalLightRef.current.shadow.camera);
77
+ cameraHelperRef.current = helper;
78
+ scene.add(helper);
79
+ return () => {
80
+ if (cameraHelperRef.current) {
81
+ scene.remove(cameraHelperRef.current);
82
+ cameraHelperRef.current.dispose();
83
+ cameraHelperRef.current = null;
84
+ }
85
+ };
86
+ }
87
+ }, [editMode, scene]);
88
+ return (_jsxs(_Fragment, { children: [_jsx("directionalLight", { ref: directionalLightRef, color: color, intensity: intensity, castShadow: castShadow, "shadow-mapSize": [shadowMapSize, shadowMapSize], "shadow-bias": -0.001, "shadow-normalBias": 0.02, children: _jsx("orthographicCamera", { attach: "shadow-camera", near: shadowCameraNear, far: shadowCameraFar, top: shadowCameraTop, bottom: shadowCameraBottom, left: shadowCameraLeft, right: shadowCameraRight }) }), editMode && (_jsxs(_Fragment, { children: [_jsxs("mesh", { children: [_jsx("sphereGeometry", { args: [0.3, 8, 6] }), _jsx("meshBasicMaterial", { color: color, wireframe: true })] }), _jsxs("mesh", { position: targetOffset, children: [_jsx("sphereGeometry", { args: [0.2, 8, 6] }), _jsx("meshBasicMaterial", { color: color, wireframe: true, opacity: 0.5, transparent: true })] }), _jsxs("line", { children: [_jsx("bufferGeometry", { onUpdate: (geo) => {
89
+ const points = [
90
+ new Vector3(0, 0, 0),
91
+ new Vector3(targetOffset[0], targetOffset[1], targetOffset[2])
92
+ ];
93
+ geo.setFromPoints(points);
94
+ } }), _jsx("lineBasicMaterial", { color: color, opacity: 0.6, transparent: true })] })] }))] }));
95
+ }
96
+ const DirectionalLightComponent = {
97
+ name: 'DirectionalLight',
98
+ Editor: DirectionalLightComponentEditor,
99
+ View: DirectionalLightView,
100
+ defaultProperties: {}
101
+ };
102
+ export default DirectionalLightComponent;