diff --git a/.agent/skills/debug.md b/.agent/skills/debug.md
index be23b67..dd6999a 100644
--- a/.agent/skills/debug.md
+++ b/.agent/skills/debug.md
@@ -89,6 +89,6 @@ Usage in Canvas:
- All debug UI goes through `Debug.getInstance()` — never inline `if (isDev)` checks
- r3f-perf is always lazy-imported, never a hard dependency in scene components
-- Debug folders should be organized by domain (Lighting, PostFX, Player, Zone)
+- Debug folders should be organized by domain (Lighting, Player, Zone, Interaction)
- Debug panel must not affect production builds — it simply doesn't mount when `?debug` is absent
- Clean up debug folders in `destroy()` when relevant
diff --git a/README.md b/README.md
index 41d4525..8ae9f38 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,6 @@ Built with React, Three.js, and Vite. Runs in the browser, no installation requi
| [@react-three/fiber](https://docs.pmnd.rs/react-three-fiber/getting-started/introduction) |
| [@react-three/drei](https://pmndrs.github.io/drei) |
| [@react-three/rapier](https://rapier.rs/docs/) |
-| [@react-three/postprocessing](https://github.com/pmndrs/postprocessing) |
| [GSAP](https://gsap.com/docs/v3/Installation/) |
### Performance & Effects
@@ -97,16 +96,11 @@ la-fabrik/
│ └── fragment.glsl
│
├── utils/
- │ ├── EventEmitter.ts # Simple typed pub/sub utility
- │ ├── Sizes.ts # Viewport size tracking
- │ ├── Time.ts # Animation frame timing utility
- │ └── debug/ # Dev-only tools and scene inspection
- │ ├── Debug.ts # Global lil-gui manager
- │ ├── DebugPerf.tsx # r3f-perf overlay mounted in Canvas
- │ ├── isDebugEnabled.ts # Debug query-string helper
- │ └── scene/
- │ ├── DebugHelpers.tsx # Grid + axes helpers shown in debug mode
- │ └── DebugCameraControls.tsx # Free debug camera for map inspection
+ │ ├── core/ # Logger and generic utilities
+ │ ├── debug/ # Dev-only tools and scene inspection
+ │ ├── editor/ # Editor-only parsing utilities
+ │ ├── map/ # Map loading and validation
+ │ └── three/ # Three.js helpers
├── hooks/
│ └── debug/
│ ├── useCameraMode.ts
diff --git a/docs/technical/animation.md b/docs/technical/animation.md
index ac06e59..ce22beb 100644
--- a/docs/technical/animation.md
+++ b/docs/technical/animation.md
@@ -1,343 +1,51 @@
-# Animation & 3D Model System
+# Animation & 3D Components
-This document describes how to use the 3D model components and animation system in La-Fabrik.
+This document describes the 3D components that are currently used in the runtime.
-## Table of Contents
+## Runtime Components
-1. [Model Types Overview](#model-types-overview)
-2. [SimpleModel - Static Models](#simplemodel---static-models)
-3. [AnimatedModel - Animated Models](#animatedmodel---animated-models)
-4. [Animation Control](#animation-control)
-5. [Other 3D Components](#other-3d-components)
-6. [Technical Notes](#technical-notes)
+| Domain | Component | Role |
+| ----------- | -------------------- | --------------------------------------------------------------------- |
+| Interaction | `InteractableObject` | Focus detection through distance and raycasting |
+| Interaction | `TriggerObject` | Press-to-trigger interactions, optional sound, optional spawned model |
+| Interaction | `GrabbableObject` | Physics grab and hand-tracking grab behavior |
+| Model | `ExplodableModel` | Split/reassemble a GLTF model into separated parts |
+| Gameplay | `RepairCaseModel` | Repair case lid animation, proximity float, and wobble |
----
+## Continuous Animation
-## Model Types Overview
+Use `useFrame` for per-frame 3D behavior. Current examples:
-The project provides three main types of model instantiation:
+- `GrabbableObject` updates held object velocity every frame.
+- `ExplodableModel` updates split part positions every frame.
+- `RepairCaseModel` updates proximity float and rotation wobble every frame.
+- `SkyModel` follows the camera position every frame.
-| Type | Component | Use Case |
-| ----------- | -------------------------------------------------------- | -------------------------------------------- |
-| Static | `SimpleModel` | Props, decoration, objects without animation |
-| Animated | `AnimatedModel` | Characters, animated objects with skeleton |
-| Interactive | `GrabbableObject`, `TriggerObject`, `InteractableObject` | Objects player can interact with |
+## Timeline Animation
----
+Use GSAP only for discrete timeline-style transitions. Current example:
-## SimpleModel - Static Models
+- `RepairCaseModel` animates the case lid between open and closed rotations.
-Use for GLTF models **without** skeleton/armature and no animations.
+## GLTF Reuse
-```tsx
-import { SimpleModel } from "@/components/three/models/SimpleModel";
-
-;
-```
-
-### Props
-
-| Prop | Type | Default | Description |
-| --------------- | ------------------------ | ----------- | --------------------------------- |
-| `modelPath` | `string` | required | Path to GLTF file in `/public` |
-| `position` | `Vector3Tuple` | `[0, 0, 0]` | World position [x, y, z] |
-| `rotation` | `Vector3Tuple` | `[0, 0, 0]` | Rotation in radians [x, y, z] |
-| `scale` | `number \| Vector3Tuple` | `1` | Scale factor or [x, y, z] |
-| `castShadow` | `boolean` | `true` | Enable shadow casting |
-| `receiveShadow` | `boolean` | `true` | Enable shadow receiving |
-| `children` | `ReactNode` | - | Child components to render inside |
-
----
-
-## AnimatedModel - Animated Models
-
-Use for GLTF models **with** skeleton/armature and animations (like Mixamo characters).
-
-```tsx
-import { AnimatedModel } from "@/components/three/models/AnimatedModel";
-import { useAnimatedModel } from "@/components/three/models/useAnimatedModel";
-
-// Basic usage
-;
-```
-
-### Props
-
-| Prop | Type | Default | Description |
-| ------------------ | ------------------------ | ----------- | --------------------------------------------- |
-| `modelPath` | `string` | required | Path to GLTF file in `/public` |
-| `defaultAnimation` | `string` | `"Idle"` | Animation name to play by default |
-| `animations` | `string[]` | `[]` | List of animation names (optional) |
-| `position` | `Vector3Tuple` | `[0, 0, 0]` | World position [x, y, z] |
-| `rotation` | `Vector3Tuple` | `[0, 0, 0]` | Rotation in radians [x, y, z] |
-| `scale` | `number \| Vector3Tuple` | `1` | Scale factor |
-| `autoPlay` | `boolean` | `true` | Auto-play default animation |
-| `speed` | `number` | `1` | Animation playback speed |
-| `fadeDuration` | `number` | `0.3` | Transition duration in seconds |
-| `onLoaded` | `() => void` | - | Callback when model loads |
-| `onAnimationEnd` | `(name: string) => void` | - | Callback when animation ends |
-| `children` | `ReactNode` | - | Child components (can use `useAnimatedModel`) |
-
-### Important: Scale
-
-Animated models (like Mixamo exports) often need a small scale (e.g., `0.01`) because they are exported in meters while Three.js uses different units. Adjust until the model appears at the right size.
-
----
-
-## Animation Control
-
-To control animations from inside or outside the `AnimatedModel`, use the `useAnimatedModel` hook.
-
-### Basic Control
-
-```tsx
-import { AnimatedModel } from "@/components/three/models/AnimatedModel";
-import { useAnimatedModel } from "@/components/three/models/useAnimatedModel";
-
-// Create a controller component to use inside AnimatedModel
-function AnimationController() {
- const { play, stop, fadeTo, currentAnimation, names, setSpeed, isReady } =
- useAnimatedModel();
-
- // names contains all available animation names
- // currentAnimation is the name of the currently playing animation
- // isReady is true when model and animations are loaded
-
- return (
- play("Run", 0.5)}>
-
-
- );
-}
-
-// Usage
-
-
-;
-```
-
-### Available Methods
-
-| Method | Signature | Description |
-| ------------------ | --------------------------------------- | ------------------------------------ |
-| `play` | `(name: string, fade?: number) => void` | Play animation with optional fade |
-| `fadeTo` | `(name: string, fade?: number) => void` | Fade to another animation |
-| `stop` | `(fade?: number) => void` | Stop and return to default animation |
-| `setSpeed` | `(speed: number) => void` | Set animation speed |
-| `currentAnimation` | `string` | Current animation name (getter) |
-| `names` | `string[]` | Available animation names |
-| `isReady` | `boolean` | Whether model is loaded |
-
-### Transition Example
-
-```tsx
-function Character() {
- const { play, fadeTo, currentAnimation } = useAnimatedModel();
-
- const handleWalk = () => fadeTo("Walk", 0.5); // 0.5s fade
- const handleRun = () => play("Run", 0.3); // 0.3s fade
- const handleIdle = () => play("Idle", 0.5); // return to idle
-
- return (
-
-
-
-
-
-
-
-
-
-
-
- );
-}
-```
-
-### Combined: GrabbableObject with Animation
-
-You can combine `AnimatedModel` inside `GrabbableObject` to create animated objects that can be picked up:
-
-```tsx
-import { GrabbableObject } from "@/components/three/interaction/GrabbableObject";
-import { AnimatedModel } from "@/components/three/models/AnimatedModel";
-
-// Animated weapon/tool that player can pick up
-
-
-;
-```
-
-Or create an animated character that can be grabbed:
-
-```tsx
-import { GrabbableObject } from "@/components/three/interaction/GrabbableObject";
-import { AnimatedModel } from "@/components/three/models/AnimatedModel";
-import { useAnimatedModel } from "@/components/three/models/useAnimatedModel";
-
-// Controller that triggers animations when grabbed
-function AnimatedGrabber() {
- const { play, fadeTo } = useAnimatedModel();
-
- return (
-
- );
-}
-
-// When grabbed, play "Grab" animation
- {
- // This would require a context or store to trigger
- console.log("Object grabbed!");
- }}
->
-
-;
-```
-
-**Note:** For complex interactions (like playing specific animations when grabbing), you'll need to connect the grab events to animation controls via a state manager or context.
-
----
-
-## Other 3D Components
-
-### GrabbableObject
-
-Objects that can be picked up by the player.
-
-```tsx
-import { GrabbableObject } from "@/components/three/interaction/GrabbableObject";
-
-
-
-
-
-
-;
-```
-
-### TriggerObject
-
-Objects that trigger events when interacted with.
-
-```tsx
-import { TriggerObject } from "@/components/three/interaction/TriggerObject";
-
- console.log("Triggered!")}
->
-
-
-
-
-;
-```
-
-### InteractableObject
-
-Base object for interactions.
-
-```tsx
-import { InteractableObject } from "@/components/three/interaction/InteractableObject";
-
- console.log("Interacted!")}
->
-
-
-
-
-;
-```
-
----
-
-## Technical Notes
-
-### GLTF Models
-
-- Models should be placed in `/public/models/`
-- Supported formats: `.gltf`, `.glb`
-- Animated models must have an Armature/skeleton for animations to work
-
-### Model Scale Issue
-
-If animated models don't appear, they may be too small or too large. Try:
-
-- Scale `0.01` for Mixamo-exported models
-- Scale `1` for models in correct units
-
-### Cloning
-
-- `SimpleModel` memoizes a cloned scene for proper React lifecycle
-- `AnimatedModel` memoizes a cloned scene and binds animations through a group ref
-
-### Animation System
-
-The animation system uses:
-
-- `@react-three/drei`: `useGLTF` for loading, `useAnimations` for animation control
-- Three.js: `AnimationMixer` for playback
-
-### No State Machine
-
-This system intentionally avoids complex state machines (like Unity's Animator). For simple animation transitions, use the `play`, `fadeTo`, and `stop` methods directly.
-
----
+Use `useClonedObject` when a GLTF scene is reused by a component instance. It memoizes `scene.clone(true)` and keeps clone creation out of render churn.
## File Structure
-```
-src/
-├── components/three/
-│ ├── models/
-│ │ ├── AnimatedModel.tsx # Animated model component + context
-│ │ ├── SimpleModel.tsx # Static model component
-│ │ └── useAnimatedModel.ts # Animated model context hook
-│ └── interaction/
-│ ├── GrabbableObject.tsx # Pickable object
-│ ├── TriggerObject.tsx # Trigger event object
-│ └── InteractableObject.tsx
-└── hooks/
- └── useCharacterAnimation.ts # Animation hook (legacy)
+```txt
+src/components/three/
+├── gameplay/
+│ ├── RepairCaseModel.tsx
+│ ├── RepairCaseObject.tsx
+│ ├── RepairGameZone.tsx
+│ └── RepairModuleSlot.tsx
+├── interaction/
+│ ├── GrabbableObject.tsx
+│ ├── InteractableObject.tsx
+│ └── TriggerObject.tsx
+├── models/
+│ └── ExplodableModel.tsx
+└── world/
+ └── SkyModel.tsx
```
diff --git a/docs/technical/architecture.md b/docs/technical/architecture.md
index 1464c8d..4ed399e 100644
--- a/docs/technical/architecture.md
+++ b/docs/technical/architecture.md
@@ -44,7 +44,7 @@ This document describes the code that exists today in the repository.
## 3D Component Domains
-- `src/components/three/models/` contains reusable model loaders such as `SimpleModel`, `AnimatedModel`, and `ExplodableModel`.
+- `src/components/three/models/` contains reusable model helpers such as `ExplodableModel`.
- `src/components/three/interaction/` contains reusable interaction wrappers such as `InteractableObject`, `TriggerObject`, and `GrabbableObject`.
- `src/components/three/gameplay/repairGame/` contains the current core repair gameplay prototype: the repair case, repair game zone, and module slots.
- `src/components/three/world/` contains reusable world/environment objects such as `SkyModel`.
diff --git a/docs/technical/editor.md b/docs/technical/editor.md
index c232513..2729cfa 100644
--- a/docs/technical/editor.md
+++ b/docs/technical/editor.md
@@ -138,7 +138,7 @@ Editor styles are in `src/index.css` under the `/* Editor page */` section. Clas
## Known Limitations
-- Uploaded model object URLs are not currently revoked after replacement or unmount.
+- Uploaded model object URLs are not revoked after replacement or unmount.
- Large `map.json` files are not virtualized, culled, or LOD-managed.
- There is no snap-to-grid, duplication, material editing, or object creation workflow.
- Save to Server is a Vite dev-server helper, not a production backend API.
diff --git a/docs/technical/hand-tracking.md b/docs/technical/hand-tracking.md
index 9a9db9e..da1ff88 100644
--- a/docs/technical/hand-tracking.md
+++ b/docs/technical/hand-tracking.md
@@ -6,7 +6,7 @@ This document describes the hand tracking system that exists in the current code
Hand tracking is a debug-stage interaction system used to test direct 3D object manipulation with a webcam. It allows a user to close their fist to grab a nearby object and move it in 3D space without relying on the center crosshair.
-The feature is currently scoped to the debug physics scene and is not yet a production gameplay input system.
+The feature is scoped to the debug physics scene rather than production gameplay input.
## Runtime Flow
@@ -27,7 +27,7 @@ The current activation conditions are:
- scene mode is `physics`
- the player is near an interaction, is holding an object, or is hand-holding an object
-This prevents the previous issue where hand tracking depended on crosshair focus. The system now remains active while the player is inside an interaction zone, even if the camera is not aimed directly at the object.
+This keeps hand tracking active while the player is inside an interaction zone, even if the camera is not aimed directly at the object.
## Backend
@@ -113,8 +113,8 @@ The hand tracking overlay is an HTML overlay outside the canvas. The hand wirefr
## Known Limitations
-- The feature is debug-only and currently focused on the physics test scene.
+- The feature is debug-only and focused on the physics test scene.
- MediaPipe depth is relative and can be noisy.
- The virtual hit zone is an approximation based on multiple raycasts, not a real 3D collider.
- There is no smoothing layer for hand position or depth yet.
-- The hand visualization is a temporary SVG wireframe.
+- The hand visualization is an SVG landmark wireframe.
diff --git a/docs/user/main-feature.md b/docs/user/main-feature.md
index 06fb77e..457c88a 100644
--- a/docs/user/main-feature.md
+++ b/docs/user/main-feature.md
@@ -1,6 +1,6 @@
# Main Feature
-This document explains the main interactive feature currently being prototyped in La-Fabrik: grabbing and moving 3D objects with hand tracking.
+This document explains La-Fabrik's debug hand-tracking feature: grabbing and moving 3D objects with a webcam.
## What It Does
@@ -18,7 +18,7 @@ The intended user flow is:
## Why It Matters
-This prototype tests whether La-Fabrik interactions can feel more physical and embodied than a classic mouse or keyboard interaction.
+This feature tests whether La-Fabrik interactions can feel more physical and embodied than a classic mouse or keyboard interaction.
For the final experience, this can support low-tech repair gestures, object manipulation, and more expressive interaction sequences.
@@ -32,7 +32,7 @@ Moving the hand left, right, up, or down moves the object in that direction. Mov
## Debug Requirements
-Hand tracking currently requires:
+Hand tracking requires:
- Chrome or another browser that allows `getUserMedia()` reliably
- the local Python backend running
@@ -73,15 +73,7 @@ The wireframe turns yellow when the detected hand is a fist.
## Current Limitations
-- The feature is still a prototype.
- It is enabled only in the debug physics scene.
-- The SVG hand wireframe is temporary.
+- The SVG hand wireframe is a debug visualization, not final gameplay UI.
- Depth movement depends on relative webcam tracking and may need tuning.
-- The system has not yet been integrated into final mission gameplay.
-
-## Expected Next Improvements
-
-- Smooth the hand position and depth signal.
-- Add a better 3D hand representation.
-- Add calibration controls for grab radius and depth sensitivity.
-- Connect hand gestures to final repair or transformation tasks.
+- The system is not integrated into mission gameplay.
diff --git a/package-lock.json b/package-lock.json
index aa6d43a..db445bc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,7 +10,6 @@
"dependencies": {
"@react-three/drei": "^10.7.7",
"@react-three/fiber": "^9.6.0",
- "@react-three/postprocessing": "^3.0.4",
"@react-three/rapier": "^2.2.0",
"@tanstack/react-router": "^1.168.25",
"gsap": "^3.15.0",
@@ -30,8 +29,6 @@
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.1",
"eslint": "^9.39.4",
- "eslint-config-prettier": "^10.1.8",
- "eslint-plugin-prettier": "^5.5.5",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.5.2",
"globals": "^17.4.0",
@@ -653,19 +650,6 @@
"url": "https://github.com/sponsors/Boshen"
}
},
- "node_modules/@pkgr/core": {
- "version": "0.2.9",
- "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz",
- "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/pkgr"
- }
- },
"node_modules/@radix-ui/react-icons": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.2.tgz",
@@ -763,32 +747,6 @@
}
}
},
- "node_modules/@react-three/postprocessing": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/@react-three/postprocessing/-/postprocessing-3.0.4.tgz",
- "integrity": "sha512-e4+F5xtudDYvhxx3y0NtWXpZbwvQ0x1zdOXWTbXMK6fFLVDd4qucN90YaaStanZGS4Bd5siQm0lGL/5ogf8iDQ==",
- "license": "MIT",
- "dependencies": {
- "maath": "^0.6.0",
- "n8ao": "^1.9.4",
- "postprocessing": "^6.36.6"
- },
- "peerDependencies": {
- "@react-three/fiber": "^9.0.0",
- "react": "^19.0",
- "three": ">= 0.156.0"
- }
- },
- "node_modules/@react-three/postprocessing/node_modules/maath": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/maath/-/maath-0.6.0.tgz",
- "integrity": "sha512-dSb2xQuP7vDnaYqfoKzlApeRcR2xtN8/f7WV/TMAkBC8552TwTLtOO0JTcSygkYMjNDPoo6V01jTw/aPi4JrMw==",
- "license": "MIT",
- "peerDependencies": {
- "@types/three": ">=0.144.0",
- "three": ">=0.144.0"
- }
- },
"node_modules/@react-three/rapier": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@react-three/rapier/-/rapier-2.2.0.tgz",
@@ -2249,53 +2207,6 @@
}
}
},
- "node_modules/eslint-config-prettier": {
- "version": "10.1.8",
- "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz",
- "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "eslint-config-prettier": "bin/cli.js"
- },
- "funding": {
- "url": "https://opencollective.com/eslint-config-prettier"
- },
- "peerDependencies": {
- "eslint": ">=7.0.0"
- }
- },
- "node_modules/eslint-plugin-prettier": {
- "version": "5.5.5",
- "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.5.tgz",
- "integrity": "sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "prettier-linter-helpers": "^1.0.1",
- "synckit": "^0.11.12"
- },
- "engines": {
- "node": "^14.18.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint-plugin-prettier"
- },
- "peerDependencies": {
- "@types/eslint": ">=8.0.0",
- "eslint": ">=8.0.0",
- "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0",
- "prettier": ">=3.0.0"
- },
- "peerDependenciesMeta": {
- "@types/eslint": {
- "optional": true
- },
- "eslint-config-prettier": {
- "optional": true
- }
- }
- },
"node_modules/eslint-plugin-react-hooks": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz",
@@ -2449,13 +2360,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/fast-diff": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
- "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
- "dev": true,
- "license": "Apache-2.0"
- },
"node_modules/fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
@@ -4190,16 +4094,6 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
- "node_modules/n8ao": {
- "version": "1.10.1",
- "resolved": "https://registry.npmjs.org/n8ao/-/n8ao-1.10.1.tgz",
- "integrity": "sha512-hhI1pC+BfOZBV1KMwynBrVlIm8wqLxj/abAWhF2nZ0qQKyzTSQa1QtLVS2veRiuoBQXojxobcnp0oe+PUoxf/w==",
- "license": "ISC",
- "peerDependencies": {
- "postprocessing": ">=6.30.0",
- "three": ">=0.137"
- }
- },
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
@@ -4389,15 +4283,6 @@
"node": "^10 || ^12 || >=14"
}
},
- "node_modules/postprocessing": {
- "version": "6.39.1",
- "resolved": "https://registry.npmjs.org/postprocessing/-/postprocessing-6.39.1.tgz",
- "integrity": "sha512-R2dG2zy+BAx3USl5EHw+PvnrlbT5PKnZVp3se0HCR0pWH8WQdh742yNG4YWOsq6c0bFpffk0Gd2RqPeoP/wKng==",
- "license": "Zlib",
- "peerDependencies": {
- "three": ">= 0.168.0 < 0.185.0"
- }
- },
"node_modules/potpack": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz",
@@ -4430,19 +4315,6 @@
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
- "node_modules/prettier-linter-helpers": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz",
- "integrity": "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "fast-diff": "^1.1.2"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
"node_modules/promise-worker-transferable": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/promise-worker-transferable/-/promise-worker-transferable-1.0.4.tgz",
@@ -4891,22 +4763,6 @@
"react": ">=17.0"
}
},
- "node_modules/synckit": {
- "version": "0.11.12",
- "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz",
- "integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@pkgr/core": "^0.2.9"
- },
- "engines": {
- "node": "^14.18.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/synckit"
- }
- },
"node_modules/three": {
"version": "0.183.2",
"resolved": "https://registry.npmjs.org/three/-/three-0.183.2.tgz",
diff --git a/src/components/editor/scene/EditorMap.tsx b/src/components/editor/scene/EditorMap.tsx
index c63f36c..38129e9 100644
--- a/src/components/editor/scene/EditorMap.tsx
+++ b/src/components/editor/scene/EditorMap.tsx
@@ -1,8 +1,9 @@
-import { useMemo, useRef, useEffect, useState } from "react";
+import { useRef, useEffect, useState } from "react";
import { Grid, TransformControls, useGLTF } from "@react-three/drei";
import type { ThreeEvent } from "@react-three/fiber";
import * as THREE from "three";
+import { useClonedObject } from "@/hooks/three/useClonedObject";
import type { SceneData, MapNode, TransformMode } from "@/types/editor/editor";
interface EditorMapProps {
@@ -138,7 +139,7 @@ export function EditorMap({
const objectsMapRef = useRef