arkaos 5.5.0 → 5.7.0

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 (65) hide show
  1. package/README.md +2 -2
  2. package/THE-ARKAOS-GUIDE.md +1 -1
  3. package/VERSION +1 -1
  4. package/arka/SKILL.md +1 -1
  5. package/config/skills-provenance.yaml +17 -0
  6. package/departments/brand/skills/design-system/SKILL.md +4 -2
  7. package/departments/brand/skills/motion-design/SKILL.md +362 -0
  8. package/departments/brand/skills/motion-design/references/director/choreography.md +93 -0
  9. package/departments/brand/skills/motion-design/references/director/context-adaptation.md +83 -0
  10. package/departments/brand/skills/motion-design/references/director/core-philosophy.md +53 -0
  11. package/departments/brand/skills/motion-design/references/director/decision-framework.md +91 -0
  12. package/departments/brand/skills/motion-design/references/director/disney-principles.md +102 -0
  13. package/departments/brand/skills/motion-design/references/director/emotion-mapping.md +71 -0
  14. package/departments/brand/skills/motion-design/references/director/motion-personality.md +89 -0
  15. package/departments/brand/skills/motion-design/references/director/narrative-structure.md +62 -0
  16. package/departments/brand/skills/motion-design/references/motion-design-skill.LICENSE +21 -0
  17. package/departments/brand/skills/motion-design/references/patterns/ambient-continuous.md +81 -0
  18. package/departments/brand/skills/motion-design/references/patterns/entrance-exit.md +82 -0
  19. package/departments/brand/skills/motion-design/references/patterns/multi-element.md +69 -0
  20. package/departments/brand/skills/motion-design/references/patterns/state-feedback.md +96 -0
  21. package/departments/brand/skills/motion-design/references/reference/property-selection.md +95 -0
  22. package/departments/brand/skills/motion-design/references/reference/quality-checklist.md +67 -0
  23. package/departments/brand/skills/motion-design/references/reference/timing-easing-tables.md +106 -0
  24. package/departments/brand/skills/motion-design/references/reference/troubleshooting.md +73 -0
  25. package/departments/dev/skills/canvas-generative/SKILL.md +3 -3
  26. package/departments/dev/skills/threejs/SKILL.md +173 -0
  27. package/departments/dev/skills/threejs/references/genjutsu.LICENSE +21 -0
  28. package/departments/dev/skills/threejs/references/r3f.md +249 -0
  29. package/departments/dev/skills/threejs/references/scene-setup.md +404 -0
  30. package/departments/dev/skills/threejs/references/shaders.md +452 -0
  31. package/harness/codex/AGENTS.md +1 -1
  32. package/harness/copilot/copilot-instructions.md +1 -1
  33. package/harness/cursor/rules/arkaos.mdc +2 -2
  34. package/harness/gemini/GEMINI.md +1 -1
  35. package/harness/opencode/AGENTS.md +1 -1
  36. package/harness/opencode/agents/arka-architect-gabriel.md +1 -1
  37. package/harness/opencode/agents/arka-brand-director-valentina.md +1 -1
  38. package/harness/opencode/agents/arka-cfo-helena.md +1 -1
  39. package/harness/opencode/agents/arka-chief-of-staff-afonso.md +1 -1
  40. package/harness/opencode/agents/arka-community-strategist-beatriz.md +1 -1
  41. package/harness/opencode/agents/arka-content-strategist-rafael.md +1 -1
  42. package/harness/opencode/agents/arka-conversion-strategist-ines.md +1 -1
  43. package/harness/opencode/agents/arka-coo-sofia.md +1 -1
  44. package/harness/opencode/agents/arka-copy-director-eduardo.md +1 -1
  45. package/harness/opencode/agents/arka-cqo-marta.md +1 -1
  46. package/harness/opencode/agents/arka-cto-marco.md +1 -1
  47. package/harness/opencode/agents/arka-design-ops-lead-iris.md +1 -1
  48. package/harness/opencode/agents/arka-ecom-director-ricardo.md +1 -1
  49. package/harness/opencode/agents/arka-knowledge-director-clara.md +1 -1
  50. package/harness/opencode/agents/arka-leadership-director-rodrigo.md +1 -1
  51. package/harness/opencode/agents/arka-marketing-director-luna.md +1 -1
  52. package/harness/opencode/agents/arka-ops-lead-daniel.md +1 -1
  53. package/harness/opencode/agents/arka-pm-director-carolina.md +1 -1
  54. package/harness/opencode/agents/arka-revops-lead-vicente.md +1 -1
  55. package/harness/opencode/agents/arka-saas-strategist-tiago.md +1 -1
  56. package/harness/opencode/agents/arka-sales-director-miguel.md +1 -1
  57. package/harness/opencode/agents/arka-strategy-director-tomas.md +1 -1
  58. package/harness/opencode/agents/arka-tech-director-francisca.md +1 -1
  59. package/harness/opencode/agents/arka-tech-lead-paulo.md +1 -1
  60. package/harness/opencode/agents/arka-video-producer-simao.md +1 -1
  61. package/harness/zed/.rules +1 -1
  62. package/knowledge/commands-registry.json +1 -1
  63. package/knowledge/skills-manifest.json +31 -1
  64. package/package.json +1 -1
  65. package/pyproject.toml +1 -1
@@ -0,0 +1,249 @@
1
+ > Derived from genjutsu's `threejs-r3f` skill (MIT — `genjutsu.LICENSE`
2
+ > in this directory). The React Three Fiber layer of `dev/threejs`.
3
+
4
+ # Three.js & React Three Fiber
5
+
6
+ > 3D on the web. Three.js is the engine, R3F is the React renderer.
7
+ > Concise rules here. Deep-dive in `references/`.
8
+
9
+ ---
10
+
11
+ ## When to Use What
12
+
13
+ | Need | Tool | Why |
14
+ |---|---|---|
15
+ | Full 3D scene (models, lights, physics) | R3F + drei | Declarative, React-friendly, ecosystem |
16
+ | Vanilla 3D (no React) | Three.js direct | Lighter, no React overhead |
17
+ | Simple 3D transforms on UI | CSS `transform3d` | GPU-composited, no WebGL context |
18
+ | 2D particles / generative | Canvas 2D | Simpler API, less GPU overhead |
19
+ | Shader-only visuals (no scene graph) | Raw WebGL / ShaderMaterial | Maximum control, minimal abstraction |
20
+
21
+ ---
22
+
23
+ ## Scene Setup Patterns
24
+
25
+ ```tsx
26
+ import { Canvas } from '@react-three/fiber'
27
+ import { Environment, OrbitControls } from '@react-three/drei'
28
+ import { Suspense } from 'react'
29
+
30
+ <Canvas camera={{ position: [0, 2, 5], fov: 45 }} dpr={[1, 2]} gl={{ antialias: true }}>
31
+ <Suspense fallback={null}>
32
+ <Environment preset="studio" />
33
+ <OrbitControls makeDefault />
34
+ <Scene />
35
+ </Suspense>
36
+ </Canvas>
37
+ ```
38
+
39
+ **Rules:**
40
+ - Always wrap scene content in `<Suspense>` -- loaders (GLTF, textures, HDRI) need it
41
+ - Set `dpr={[1, 2]}` to clamp pixel ratio (Retina without melting GPUs)
42
+ - Keep the Canvas parent component minimal -- re-renders propagate into the scene
43
+
44
+ ---
45
+
46
+ ## R3F Hooks
47
+
48
+ | Hook | Purpose | Gotcha |
49
+ |---|---|---|
50
+ | `useFrame((state, delta) => {})` | Per-frame logic (animation, physics) | Never setState inside |
51
+ | `useThree()` | Access gl, scene, camera, size, viewport, pointer | Destructure only what you need |
52
+ | `useLoader(TextureLoader, url)` | Load any Three.js resource | Wrap parent in Suspense |
53
+ | `useGraph(scene)` | Extract nodes/materials from loaded scene | Useful after useGLTF |
54
+
55
+ ### useFrame Tips
56
+
57
+ ```tsx
58
+ useFrame((state, delta) => {
59
+ // Use delta for framerate-independent animation
60
+ meshRef.current.rotation.y += delta * 0.5
61
+ // Access clock for time-based effects
62
+ material.uniforms.uTime.value = state.clock.elapsedTime
63
+ })
64
+ ```
65
+
66
+ ---
67
+
68
+ ## Drei Essentials
69
+
70
+ | Component | Use Case |
71
+ |---|---|
72
+ | `Environment` | HDRI lighting (presets: studio, sunset, city, forest, dawn) |
73
+ | `Float` | Idle floating animation (speed, rotationIntensity, floatIntensity) |
74
+ | `Text3D` | Extruded 3D text (needs JSON font from Facetype.js) |
75
+ | `useGLTF` | Load .glb/.gltf models (returns { nodes, materials, scene }) |
76
+ | `useGLTF.preload(url)` | Preload model before component mounts |
77
+ | `MeshTransmissionMaterial` | Glass/crystal/liquid refraction effects |
78
+ | `PresentationControls` | Drag-to-rotate for product showcases |
79
+ | `Center` | Auto-center any group of meshes |
80
+ | `Detailed` | LOD -- swap geometry by camera distance |
81
+ | `useTexture` | Load textures with Suspense support |
82
+ | `Instances` | Declarative instancing for repeated meshes |
83
+
84
+ ---
85
+
86
+ ## Postprocessing
87
+
88
+ ```tsx
89
+ import { EffectComposer, Bloom, ChromaticAberration } from '@react-three/postprocessing'
90
+ import { BlendFunction } from 'postprocessing'
91
+
92
+ <EffectComposer>
93
+ <Bloom
94
+ luminanceThreshold={1}
95
+ luminanceSmoothing={0.4}
96
+ intensity={0.6}
97
+ />
98
+ <ChromaticAberration
99
+ blendFunction={BlendFunction.NORMAL}
100
+ offset={[0.002, 0.002]}
101
+ />
102
+ </EffectComposer>
103
+ ```
104
+
105
+ **Rules:**
106
+ - Bloom is selective by default -- lift material color/emissive above 1.0 to make it glow
107
+ - `luminanceThreshold={1}` = nothing glows unless explicitly emissive
108
+ - Order matters inside EffectComposer
109
+ - Effects are merged into a single pass (performant by design)
110
+
111
+ ---
112
+
113
+ ## Performance Patterns
114
+
115
+ | Pattern | When |
116
+ |---|---|
117
+ | `<Instances>` / `InstancedMesh` | 100+ identical meshes (particles, trees, crowds) |
118
+ | `<Detailed distances={[0, 50, 100]}>` | LOD: swap hi/lo models by distance |
119
+ | `dispose={null}` on `<primitive>` | Prevent auto-dispose when reusing shared geometry |
120
+ | `useGLTF` + Draco | Compress .glb models (70-90% size reduction) |
121
+ | `useKTX2` (drei) | Compressed GPU textures (1/4 VRAM) — `useTexture` is TextureLoader-only and cannot decode .ktx2 |
122
+ | `frameloop="demand"` on Canvas | Only render when something changes (static scenes) |
123
+ | `invalidate()` from useThree | Trigger a render in demand mode |
124
+ | `<Canvas eventSource={ref}>` | Delegate pointer events to an ancestor element (overlay UIs); rendering stays on the main thread |
125
+
126
+ **Target metrics:** < 100 draw calls, < 1M triangles, 60fps on mid-range GPU.
127
+ Use `stats-gl` or `r3f-perf` to monitor.
128
+
129
+ ---
130
+
131
+ ## Do Not
132
+
133
+ ### 1. Never setState in useFrame
134
+
135
+ Causes full React re-render 60x/second. Mutate refs directly.
136
+
137
+ ```tsx
138
+ // BAD
139
+ useFrame(() => {
140
+ setRotation(prev => prev + 0.01) // React re-render every frame
141
+ })
142
+
143
+ // GOOD
144
+ useFrame((_, delta) => {
145
+ meshRef.current.rotation.y += delta * 0.5 // Direct mutation, zero re-renders
146
+ })
147
+ ```
148
+
149
+ ### 2. Never allocate in the render loop
150
+
151
+ `new Vector3()` per frame = GC spikes = stutter.
152
+
153
+ ```tsx
154
+ // BAD
155
+ useFrame((state) => {
156
+ const target = new THREE.Vector3(0, Math.sin(state.clock.elapsedTime), 0)
157
+ meshRef.current.position.copy(target)
158
+ })
159
+
160
+ // GOOD
161
+ const _target = useMemo(() => new THREE.Vector3(), [])
162
+ useFrame((state) => {
163
+ _target.set(0, Math.sin(state.clock.elapsedTime), 0)
164
+ meshRef.current.position.copy(_target)
165
+ })
166
+ ```
167
+
168
+ ### 3. Never forget dispose (memory leak)
169
+
170
+ Three.js textures, geometries, and materials live on the GPU. Unmounting a React component does NOT free them.
171
+
172
+ ```tsx
173
+ // BAD -- texture stays in VRAM after unmount
174
+ const texture = useLoader(TextureLoader, '/big-texture.jpg')
175
+
176
+ // GOOD -- R3F auto-disposes objects mounted as JSX primitives.
177
+ // useLoader results are CACHED globally: disposing the texture directly
178
+ // poisons the cache and the next consumer renders a disposed resource.
179
+ // Evict from the cache instead, then dispose:
180
+ useEffect(() => {
181
+ return () => {
182
+ useLoader.clear(TextureLoader, '/big-texture.jpg')
183
+ texture.dispose()
184
+ }
185
+ }, [texture])
186
+
187
+ // Resources YOU constructed (not via useLoader) are yours to dispose:
188
+ useEffect(() => () => { geometry.dispose(); material.dispose() }, [])
189
+ ```
190
+
191
+ ### 4. Never re-render the Canvas parent
192
+
193
+ Parent state changes re-render the Canvas subtree — the Canvas does not
194
+ remount, but every child re-renders, props reconcile against the scene
195
+ graph, and R3F work you meant to keep out of React's render path runs
196
+ again. Keep fast-changing state out of the Canvas parent (zustand or
197
+ refs).
198
+
199
+ ```tsx
200
+ // BAD
201
+ function App() {
202
+ const [uiState, setUiState] = useState(false) // fast-changing state here re-renders the whole Canvas subtree
203
+ return (
204
+ <>
205
+ <button onClick={() => setUiState(!uiState)}>Toggle</button>
206
+ <Canvas><Scene config={uiState} /></Canvas>
207
+ </>
208
+ )
209
+ }
210
+
211
+ // GOOD -- isolate Canvas in its own component
212
+ function App() {
213
+ return (
214
+ <>
215
+ <UI />
216
+ <SceneCanvas />
217
+ </>
218
+ )
219
+ }
220
+ ```
221
+
222
+ ### 5. Never load assets without Suspense
223
+
224
+ Loaders (useGLTF, useTexture, useLoader) throw promises. Without Suspense, you get crashes.
225
+
226
+ ```tsx
227
+ // BAD
228
+ <Canvas>
229
+ <Model /> {/* useGLTF inside -- will throw */}
230
+ </Canvas>
231
+
232
+ // GOOD
233
+ <Canvas>
234
+ <Suspense fallback={<Loader />}>
235
+ <Model />
236
+ </Suspense>
237
+ </Canvas>
238
+ ```
239
+
240
+ ---
241
+
242
+ ## Quick Reference: Loading Sub-resources
243
+
244
+ | Need | Load |
245
+ |---|---|
246
+ | Scene boilerplate, lighting rigs, controls | `scene-setup.md` (this directory) |
247
+ | Custom shaders, GLSL patterns, uniforms | `shaders.md` (this directory) |
248
+ | Animation principles, easing, timing | the brand guidelines' Motion System (§06), reached via `departments/brand/references/uiux-knowledge-and-tools.md` |
249
+ | Scroll-scrubbed pre-rendered worlds (no live engine) | `departments/dev/skills/scroll-world/SKILL.md` |
@@ -0,0 +1,404 @@
1
+ # Scene Setup Reference
2
+
3
+ > Complete R3F boilerplate patterns with TypeScript.
4
+ > Copy-paste ready. Adapt to your project.
5
+
6
+ ---
7
+
8
+ ## Minimal Canvas Setup
9
+
10
+ ```tsx
11
+ import { Canvas } from '@react-three/fiber'
12
+ import { Suspense } from 'react'
13
+
14
+ function App() {
15
+ return (
16
+ <div style={{ width: '100vw', height: '100vh' }}>
17
+ <Canvas
18
+ camera={{ position: [0, 2, 5], fov: 45, near: 0.1, far: 100 }}
19
+ dpr={[1, 2]}
20
+ gl={{ antialias: true, alpha: false, powerPreference: 'high-performance' }}
21
+ >
22
+ <Suspense fallback={null}>
23
+ <Scene />
24
+ </Suspense>
25
+ </Canvas>
26
+ </div>
27
+ )
28
+ }
29
+ ```
30
+
31
+ **Props breakdown:**
32
+ - `dpr={[1, 2]}` -- clamp device pixel ratio (retina without burning GPUs)
33
+ - `alpha: false` -- opaque background = faster compositing
34
+ - `powerPreference: 'high-performance'` -- request discrete GPU on laptops
35
+ - `frameloop="demand"` -- add for static scenes (render only on invalidate)
36
+
37
+ ---
38
+
39
+ ## Lighting Rigs
40
+
41
+ ### Studio (product shots, clean shadows)
42
+
43
+ ```tsx
44
+ function StudioLighting() {
45
+ return (
46
+ <>
47
+ <ambientLight intensity={0.4} />
48
+ <directionalLight
49
+ position={[5, 8, 3]}
50
+ intensity={1.2}
51
+ castShadow
52
+ shadow-mapSize-width={2048}
53
+ shadow-mapSize-height={2048}
54
+ shadow-camera-near={0.5}
55
+ shadow-camera-far={50}
56
+ shadow-camera-left={-10}
57
+ shadow-camera-right={10}
58
+ shadow-camera-top={10}
59
+ shadow-camera-bottom={-10}
60
+ />
61
+ <directionalLight position={[-3, 4, -5]} intensity={0.3} />
62
+ </>
63
+ )
64
+ }
65
+ ```
66
+
67
+ ### Studio with HDRI (easiest, best quality)
68
+
69
+ ```tsx
70
+ import { Environment } from '@react-three/drei'
71
+
72
+ function StudioHDRI() {
73
+ return (
74
+ <>
75
+ <Environment preset="studio" background={false} />
76
+ {/* Optional fill light to reduce contrast */}
77
+ <ambientLight intensity={0.2} />
78
+ </>
79
+ )
80
+ }
81
+ ```
82
+
83
+ **Environment presets:** `apartment`, `city`, `dawn`, `forest`, `lobby`, `night`, `park`, `studio`, `sunset`, `warehouse`
84
+
85
+ ### Outdoor (natural, directional sun)
86
+
87
+ ```tsx
88
+ function OutdoorLighting() {
89
+ return (
90
+ <>
91
+ <Environment preset="sunset" background />
92
+ <directionalLight
93
+ position={[10, 15, 5]}
94
+ intensity={2}
95
+ castShadow
96
+ shadow-mapSize={2048}
97
+ color="#ffeedd"
98
+ />
99
+ <hemisphereLight args={['#87CEEB', '#362907', 0.3]} />
100
+ </>
101
+ )
102
+ }
103
+ ```
104
+
105
+ ### Dramatic (high contrast, noir)
106
+
107
+ ```tsx
108
+ function DramaticLighting() {
109
+ return (
110
+ <>
111
+ <color attach="background" args={['#000000']} />
112
+ <fog attach="fog" args={['#000000', 5, 20]} />
113
+ <spotLight
114
+ position={[3, 8, 2]}
115
+ angle={0.3}
116
+ penumbra={0.8}
117
+ intensity={3}
118
+ castShadow
119
+ color="#ff6644"
120
+ />
121
+ <pointLight position={[-5, 2, -3]} intensity={0.5} color="#4466ff" />
122
+ {/* No ambient -- pure darkness outside lights */}
123
+ </>
124
+ )
125
+ }
126
+ ```
127
+
128
+ ---
129
+
130
+ ## Camera Configurations
131
+
132
+ ### Perspective (default, most scenes)
133
+
134
+ ```tsx
135
+ <Canvas camera={{ position: [0, 2, 5], fov: 45 }}>
136
+ ```
137
+
138
+ - **fov 35-45:** Natural, cinematic feel
139
+ - **fov 60-75:** Wider, more immersive
140
+ - **fov 20-30:** Telephoto, compressed depth
141
+
142
+ ### Orthographic (isometric, UI, 2D-in-3D)
143
+
144
+ ```tsx
145
+ <Canvas orthographic camera={{ position: [0, 5, 10], zoom: 50 }}>
146
+ ```
147
+
148
+ ### Animated Camera (smooth transitions)
149
+
150
+ ```tsx
151
+ import { useFrame, useThree } from '@react-three/fiber'
152
+ import { useMemo } from 'react'
153
+ import * as THREE from 'three'
154
+
155
+ function CameraRig({ target }: { target: [number, number, number] }) {
156
+ const _target = useMemo(() => new THREE.Vector3(), [])
157
+ const _position = useMemo(() => new THREE.Vector3(), [])
158
+
159
+ useFrame((state, delta) => {
160
+ // Smooth lerp to target
161
+ _target.set(...target)
162
+ state.camera.position.lerp(_position.set(target[0], target[1] + 2, target[2] + 5), delta * 2)
163
+ state.camera.lookAt(_target)
164
+ })
165
+
166
+ return null
167
+ }
168
+ ```
169
+
170
+ ---
171
+
172
+ ## Controls
173
+
174
+ ### OrbitControls (inspect / product viewer)
175
+
176
+ ```tsx
177
+ import { OrbitControls } from '@react-three/drei'
178
+
179
+ <OrbitControls
180
+ makeDefault
181
+ enableDamping
182
+ dampingFactor={0.05}
183
+ minDistance={2}
184
+ maxDistance={20}
185
+ minPolarAngle={0}
186
+ maxPolarAngle={Math.PI / 2} // prevent going below ground
187
+ autoRotate={false}
188
+ autoRotateSpeed={0.5}
189
+ />
190
+ ```
191
+
192
+ ### PresentationControls (drag-to-rotate, bounded)
193
+
194
+ ```tsx
195
+ import { PresentationControls } from '@react-three/drei'
196
+
197
+ <PresentationControls
198
+ global // listen on entire canvas
199
+ snap // snap back to rest
200
+ speed={1.5}
201
+ zoom={0.8}
202
+ rotation={[0, -Math.PI / 4, 0]} // initial angle
203
+ polar={[-Math.PI / 4, Math.PI / 4]}
204
+ azimuth={[-Math.PI / 4, Math.PI / 4]}
205
+ >
206
+ <Model />
207
+ </PresentationControls>
208
+ ```
209
+
210
+ ### MapControls (top-down, pan-based)
211
+
212
+ ```tsx
213
+ import { MapControls } from '@react-three/drei'
214
+
215
+ <MapControls
216
+ makeDefault
217
+ enableRotate={false}
218
+ minDistance={5}
219
+ maxDistance={50}
220
+ />
221
+ ```
222
+
223
+ ### Pointer-based camera (follow cursor)
224
+
225
+ ```tsx
226
+ function PointerCamera() {
227
+ useFrame((state) => {
228
+ const { x, y } = state.pointer // normalized -1 to 1
229
+ state.camera.position.x = THREE.MathUtils.lerp(state.camera.position.x, x * 2, 0.05)
230
+ state.camera.position.y = THREE.MathUtils.lerp(state.camera.position.y, y * 1.5 + 1, 0.05)
231
+ state.camera.lookAt(0, 0, 0)
232
+ })
233
+ return null
234
+ }
235
+ ```
236
+
237
+ ---
238
+
239
+ ## Loading Models
240
+
241
+ ### useGLTF (standard)
242
+
243
+ ```tsx
244
+ import * as THREE from 'three'
245
+ import { useGLTF } from '@react-three/drei'
246
+ import type { ComponentProps } from 'react'
247
+ import type { GLTF } from 'three-stdlib'
248
+
249
+ type ModelGLTF = GLTF & {
250
+ nodes: {
251
+ Body: THREE.Mesh
252
+ Eyes: THREE.Mesh
253
+ }
254
+ materials: {
255
+ skin: THREE.MeshStandardMaterial
256
+ eyes: THREE.MeshStandardMaterial
257
+ }
258
+ }
259
+
260
+ // React 19 typings removed the global JSX namespace; use the element's
261
+ // component props. The double cast is deliberate: GLTF's nodes/materials
262
+ // are loosely typed upstream, so the assertion must go through unknown.
263
+ function Model(props: ComponentProps<'group'>) {
264
+ const { nodes, materials } = useGLTF('/model.glb') as unknown as ModelGLTF
265
+
266
+ return (
267
+ <group {...props} dispose={null}>
268
+ <mesh geometry={nodes.Body.geometry} material={materials.skin} />
269
+ <mesh geometry={nodes.Eyes.geometry} material={materials.eyes} />
270
+ </group>
271
+ )
272
+ }
273
+
274
+ useGLTF.preload('/model.glb')
275
+ ```
276
+
277
+ **Tip:** Use [gltf.pmnd.rs](https://gltf.pmnd.rs/) to auto-generate typed R3F components from .glb files.
278
+
279
+ ### useGLTF with Draco compression
280
+
281
+ ```tsx
282
+ import { useGLTF } from '@react-three/drei'
283
+
284
+ // Draco decoder loaded from CDN automatically
285
+ const { nodes } = useGLTF('/model-draco.glb', true) // second arg enables Draco
286
+
287
+ useGLTF.preload('/model-draco.glb', true)
288
+ ```
289
+
290
+ ### useTexture
291
+
292
+ ```tsx
293
+ import { useTexture } from '@react-three/drei'
294
+
295
+ function TexturedMesh() {
296
+ const [colorMap, normalMap, roughnessMap] = useTexture([
297
+ '/textures/color.jpg',
298
+ '/textures/normal.jpg',
299
+ '/textures/roughness.jpg',
300
+ ])
301
+
302
+ return (
303
+ <mesh>
304
+ <sphereGeometry args={[1, 64, 64]} />
305
+ <meshStandardMaterial
306
+ map={colorMap}
307
+ normalMap={normalMap}
308
+ roughnessMap={roughnessMap}
309
+ />
310
+ </mesh>
311
+ )
312
+ }
313
+
314
+ useTexture.preload(['/textures/color.jpg', '/textures/normal.jpg', '/textures/roughness.jpg'])
315
+ ```
316
+
317
+ ---
318
+
319
+ ## Responsive Canvas
320
+
321
+ ### Fill parent container
322
+
323
+ ```tsx
324
+ // Parent must have explicit dimensions
325
+ <div style={{ width: '100%', height: '100vh' }}>
326
+ <Canvas>
327
+ <Scene />
328
+ </Canvas>
329
+ </div>
330
+ ```
331
+
332
+ ### Respond to viewport size inside scene
333
+
334
+ ```tsx
335
+ function ResponsiveObject() {
336
+ const { viewport } = useThree()
337
+ // viewport.width/height = world units visible at z=0
338
+
339
+ return (
340
+ <mesh scale={viewport.width > 6 ? 1 : 0.6}>
341
+ <boxGeometry />
342
+ <meshStandardMaterial />
343
+ </mesh>
344
+ )
345
+ }
346
+ ```
347
+
348
+ ### Adaptive performance
349
+
350
+ ```tsx
351
+ import { useState } from 'react'
352
+ import { Canvas } from '@react-three/fiber'
353
+ import { PerformanceMonitor } from '@react-three/drei'
354
+
355
+ function App() {
356
+ const [dpr, setDpr] = useState(1.5)
357
+ return (
358
+ <Canvas dpr={dpr}>
359
+ <PerformanceMonitor
360
+ onIncline={() => setDpr(2)}
361
+ onDecline={() => setDpr(1)}
362
+ >
363
+ <Scene />
364
+ </PerformanceMonitor>
365
+ </Canvas>
366
+ )
367
+ }
368
+ ```
369
+
370
+ ---
371
+
372
+ ## Shadows Setup
373
+
374
+ ```tsx
375
+ <Canvas shadows>
376
+ <directionalLight castShadow shadow-mapSize={2048} />
377
+ <mesh castShadow>
378
+ <boxGeometry />
379
+ <meshStandardMaterial />
380
+ </mesh>
381
+ <mesh receiveShadow rotation-x={-Math.PI / 2} position-y={-1}>
382
+ <planeGeometry args={[20, 20]} />
383
+ <shadowMaterial opacity={0.3} />
384
+ </mesh>
385
+ </Canvas>
386
+ ```
387
+
388
+ - `shadows` on Canvas enables the shadow system
389
+ - `castShadow` on lights and meshes that cast
390
+ - `receiveShadow` on meshes that receive
391
+ - `<shadowMaterial>` for transparent ground shadows (product shots)
392
+ - `<ContactShadows>` from drei for soft baked-look shadows without shadow maps
393
+
394
+ ```tsx
395
+ import { ContactShadows } from '@react-three/drei'
396
+
397
+ <ContactShadows
398
+ position={[0, -1, 0]}
399
+ opacity={0.4}
400
+ scale={10}
401
+ blur={2}
402
+ far={4}
403
+ />
404
+ ```