omgkit 2.1.0 → 2.2.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 (56) hide show
  1. package/package.json +1 -1
  2. package/plugin/skills/SKILL_STANDARDS.md +743 -0
  3. package/plugin/skills/databases/mongodb/SKILL.md +797 -28
  4. package/plugin/skills/databases/postgresql/SKILL.md +494 -18
  5. package/plugin/skills/databases/prisma/SKILL.md +776 -30
  6. package/plugin/skills/databases/redis/SKILL.md +885 -25
  7. package/plugin/skills/devops/aws/SKILL.md +686 -28
  8. package/plugin/skills/devops/docker/SKILL.md +466 -18
  9. package/plugin/skills/devops/github-actions/SKILL.md +684 -29
  10. package/plugin/skills/devops/kubernetes/SKILL.md +621 -24
  11. package/plugin/skills/frameworks/django/SKILL.md +920 -20
  12. package/plugin/skills/frameworks/express/SKILL.md +1361 -35
  13. package/plugin/skills/frameworks/fastapi/SKILL.md +1260 -33
  14. package/plugin/skills/frameworks/laravel/SKILL.md +1244 -31
  15. package/plugin/skills/frameworks/nestjs/SKILL.md +1005 -26
  16. package/plugin/skills/frameworks/nextjs/SKILL.md +407 -44
  17. package/plugin/skills/frameworks/rails/SKILL.md +594 -28
  18. package/plugin/skills/frameworks/react/SKILL.md +1006 -32
  19. package/plugin/skills/frameworks/spring/SKILL.md +528 -35
  20. package/plugin/skills/frameworks/vue/SKILL.md +1296 -27
  21. package/plugin/skills/frontend/accessibility/SKILL.md +1108 -34
  22. package/plugin/skills/frontend/frontend-design/SKILL.md +1304 -26
  23. package/plugin/skills/frontend/responsive/SKILL.md +847 -21
  24. package/plugin/skills/frontend/shadcn-ui/SKILL.md +976 -38
  25. package/plugin/skills/frontend/tailwindcss/SKILL.md +831 -35
  26. package/plugin/skills/frontend/threejs/SKILL.md +1298 -29
  27. package/plugin/skills/languages/javascript/SKILL.md +935 -31
  28. package/plugin/skills/languages/python/SKILL.md +489 -25
  29. package/plugin/skills/languages/typescript/SKILL.md +379 -30
  30. package/plugin/skills/methodology/brainstorming/SKILL.md +597 -23
  31. package/plugin/skills/methodology/defense-in-depth/SKILL.md +832 -34
  32. package/plugin/skills/methodology/dispatching-parallel-agents/SKILL.md +665 -31
  33. package/plugin/skills/methodology/executing-plans/SKILL.md +556 -24
  34. package/plugin/skills/methodology/finishing-development-branch/SKILL.md +595 -25
  35. package/plugin/skills/methodology/problem-solving/SKILL.md +429 -61
  36. package/plugin/skills/methodology/receiving-code-review/SKILL.md +536 -24
  37. package/plugin/skills/methodology/requesting-code-review/SKILL.md +632 -21
  38. package/plugin/skills/methodology/root-cause-tracing/SKILL.md +641 -30
  39. package/plugin/skills/methodology/sequential-thinking/SKILL.md +262 -3
  40. package/plugin/skills/methodology/systematic-debugging/SKILL.md +571 -32
  41. package/plugin/skills/methodology/test-driven-development/SKILL.md +779 -24
  42. package/plugin/skills/methodology/testing-anti-patterns/SKILL.md +691 -29
  43. package/plugin/skills/methodology/token-optimization/SKILL.md +598 -29
  44. package/plugin/skills/methodology/verification-before-completion/SKILL.md +543 -22
  45. package/plugin/skills/methodology/writing-plans/SKILL.md +590 -18
  46. package/plugin/skills/omega/omega-architecture/SKILL.md +838 -39
  47. package/plugin/skills/omega/omega-coding/SKILL.md +636 -39
  48. package/plugin/skills/omega/omega-sprint/SKILL.md +855 -48
  49. package/plugin/skills/omega/omega-testing/SKILL.md +940 -41
  50. package/plugin/skills/omega/omega-thinking/SKILL.md +703 -50
  51. package/plugin/skills/security/better-auth/SKILL.md +1065 -28
  52. package/plugin/skills/security/oauth/SKILL.md +968 -31
  53. package/plugin/skills/security/owasp/SKILL.md +894 -33
  54. package/plugin/skills/testing/playwright/SKILL.md +764 -38
  55. package/plugin/skills/testing/pytest/SKILL.md +873 -36
  56. package/plugin/skills/testing/vitest/SKILL.md +980 -35
@@ -1,59 +1,1328 @@
1
1
  ---
2
2
  name: threejs
3
- description: Three.js 3D graphics. Use for 3D scenes, WebGL, animations.
3
+ description: Three.js 3D graphics with WebGL, React Three Fiber, shaders, physics, and immersive experiences
4
+ category: frontend
5
+ triggers:
6
+ - three.js
7
+ - threejs
8
+ - 3d graphics
9
+ - webgl
10
+ - react three fiber
11
+ - r3f
12
+ - 3d animation
13
+ - shaders
14
+ - 3d scenes
4
15
  ---
5
16
 
6
- # Three.js Skill
17
+ # Three.js 3D Graphics
7
18
 
8
- ## Basic Setup
9
- ```javascript
19
+ Enterprise-grade **Three.js 3D graphics** following industry best practices. This skill covers scene setup, React Three Fiber integration, materials and lighting, animations, shaders, physics simulation, and performance optimization used by top engineering teams.
20
+
21
+ ## Purpose
22
+
23
+ Build immersive 3D web experiences:
24
+
25
+ - Create interactive 3D scenes with proper camera controls
26
+ - Build declarative 3D with React Three Fiber
27
+ - Implement physically-based rendering (PBR) materials
28
+ - Add realistic lighting and shadows
29
+ - Create smooth animations and transitions
30
+ - Write custom shaders for visual effects
31
+ - Integrate physics simulations
32
+ - Optimize performance for production
33
+
34
+ ## Features
35
+
36
+ ### 1. Core Three.js Setup
37
+
38
+ ```typescript
39
+ // lib/three/SceneManager.ts
10
40
  import * as THREE from 'three';
41
+ import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
42
+ import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer';
43
+ import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass';
44
+
45
+ interface SceneManagerOptions {
46
+ container: HTMLElement;
47
+ width?: number;
48
+ height?: number;
49
+ antialias?: boolean;
50
+ alpha?: boolean;
51
+ pixelRatio?: number;
52
+ }
53
+
54
+ export class SceneManager {
55
+ private scene: THREE.Scene;
56
+ private camera: THREE.PerspectiveCamera;
57
+ private renderer: THREE.WebGLRenderer;
58
+ private controls: OrbitControls;
59
+ private composer: EffectComposer;
60
+ private clock: THREE.Clock;
61
+ private animationFrameId: number | null = null;
62
+ private updateCallbacks: Set<(delta: number, elapsed: number) => void> = new Set();
63
+
64
+ constructor(options: SceneManagerOptions) {
65
+ const {
66
+ container,
67
+ width = container.clientWidth,
68
+ height = container.clientHeight,
69
+ antialias = true,
70
+ alpha = false,
71
+ pixelRatio = Math.min(window.devicePixelRatio, 2),
72
+ } = options;
73
+
74
+ // Scene
75
+ this.scene = new THREE.Scene();
76
+ this.scene.background = alpha ? null : new THREE.Color(0x111111);
77
+
78
+ // Camera
79
+ this.camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
80
+ this.camera.position.set(0, 2, 5);
81
+
82
+ // Renderer
83
+ this.renderer = new THREE.WebGLRenderer({
84
+ antialias,
85
+ alpha,
86
+ powerPreference: 'high-performance',
87
+ });
88
+ this.renderer.setSize(width, height);
89
+ this.renderer.setPixelRatio(pixelRatio);
90
+ this.renderer.shadowMap.enabled = true;
91
+ this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
92
+ this.renderer.outputColorSpace = THREE.SRGBColorSpace;
93
+ this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
94
+ this.renderer.toneMappingExposure = 1.0;
95
+ container.appendChild(this.renderer.domElement);
96
+
97
+ // Controls
98
+ this.controls = new OrbitControls(this.camera, this.renderer.domElement);
99
+ this.controls.enableDamping = true;
100
+ this.controls.dampingFactor = 0.05;
101
+ this.controls.minDistance = 1;
102
+ this.controls.maxDistance = 100;
103
+
104
+ // Post-processing
105
+ this.composer = new EffectComposer(this.renderer);
106
+ this.composer.addPass(new RenderPass(this.scene, this.camera));
107
+
108
+ // Clock
109
+ this.clock = new THREE.Clock();
110
+
111
+ // Handle resize
112
+ this.handleResize = this.handleResize.bind(this);
113
+ window.addEventListener('resize', this.handleResize);
114
+ }
115
+
116
+ private handleResize(): void {
117
+ const container = this.renderer.domElement.parentElement;
118
+ if (!container) return;
119
+
120
+ const width = container.clientWidth;
121
+ const height = container.clientHeight;
11
122
 
12
- const scene = new THREE.Scene();
13
- const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
14
- const renderer = new THREE.WebGLRenderer();
123
+ this.camera.aspect = width / height;
124
+ this.camera.updateProjectionMatrix();
125
+
126
+ this.renderer.setSize(width, height);
127
+ this.composer.setSize(width, height);
128
+ }
129
+
130
+ public addUpdateCallback(callback: (delta: number, elapsed: number) => void): void {
131
+ this.updateCallbacks.add(callback);
132
+ }
133
+
134
+ public removeUpdateCallback(callback: (delta: number, elapsed: number) => void): void {
135
+ this.updateCallbacks.delete(callback);
136
+ }
137
+
138
+ public start(): void {
139
+ if (this.animationFrameId !== null) return;
140
+
141
+ const animate = () => {
142
+ this.animationFrameId = requestAnimationFrame(animate);
143
+
144
+ const delta = this.clock.getDelta();
145
+ const elapsed = this.clock.getElapsedTime();
146
+
147
+ // Update controls
148
+ this.controls.update();
149
+
150
+ // Run update callbacks
151
+ this.updateCallbacks.forEach((callback) => callback(delta, elapsed));
152
+
153
+ // Render
154
+ this.composer.render();
155
+ };
156
+
157
+ animate();
158
+ }
159
+
160
+ public stop(): void {
161
+ if (this.animationFrameId !== null) {
162
+ cancelAnimationFrame(this.animationFrameId);
163
+ this.animationFrameId = null;
164
+ }
165
+ }
166
+
167
+ public getScene(): THREE.Scene {
168
+ return this.scene;
169
+ }
170
+
171
+ public getCamera(): THREE.PerspectiveCamera {
172
+ return this.camera;
173
+ }
174
+
175
+ public getRenderer(): THREE.WebGLRenderer {
176
+ return this.renderer;
177
+ }
178
+
179
+ public dispose(): void {
180
+ this.stop();
181
+ window.removeEventListener('resize', this.handleResize);
182
+
183
+ // Dispose of all objects in scene
184
+ this.scene.traverse((object) => {
185
+ if (object instanceof THREE.Mesh) {
186
+ object.geometry.dispose();
187
+ if (Array.isArray(object.material)) {
188
+ object.material.forEach((m) => m.dispose());
189
+ } else {
190
+ object.material.dispose();
191
+ }
192
+ }
193
+ });
194
+
195
+ this.renderer.dispose();
196
+ this.controls.dispose();
197
+ }
198
+ }
15
199
 
16
- renderer.setSize(width, height);
17
- document.body.appendChild(renderer.domElement);
200
+ // Usage
201
+ const container = document.getElementById('canvas-container')!;
202
+ const sceneManager = new SceneManager({ container, antialias: true });
203
+
204
+ // Add objects
205
+ const geometry = new THREE.BoxGeometry(1, 1, 1);
206
+ const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
207
+ const cube = new THREE.Mesh(geometry, material);
208
+ cube.castShadow = true;
209
+ sceneManager.getScene().add(cube);
210
+
211
+ // Add lights
212
+ const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
213
+ const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
214
+ directionalLight.position.set(5, 10, 5);
215
+ directionalLight.castShadow = true;
216
+ sceneManager.getScene().add(ambientLight, directionalLight);
217
+
218
+ // Animation
219
+ sceneManager.addUpdateCallback((delta) => {
220
+ cube.rotation.x += delta;
221
+ cube.rotation.y += delta * 0.5;
222
+ });
223
+
224
+ sceneManager.start();
18
225
  ```
19
226
 
20
- ## React Three Fiber
227
+ ### 2. React Three Fiber Integration
228
+
21
229
  ```tsx
230
+ // components/Scene.tsx
231
+ import { Canvas, useFrame, useThree } from '@react-three/fiber';
232
+ import {
233
+ OrbitControls,
234
+ Environment,
235
+ ContactShadows,
236
+ PerspectiveCamera,
237
+ useGLTF,
238
+ Float,
239
+ Text3D,
240
+ Center,
241
+ MeshTransmissionMaterial,
242
+ } from '@react-three/drei';
243
+ import { Suspense, useRef, useState } from 'react';
244
+ import * as THREE from 'three';
245
+
246
+ // Animated box component
247
+ function AnimatedBox({ position }: { position: [number, number, number] }) {
248
+ const meshRef = useRef<THREE.Mesh>(null);
249
+ const [hovered, setHovered] = useState(false);
250
+ const [clicked, setClicked] = useState(false);
251
+
252
+ useFrame((state, delta) => {
253
+ if (meshRef.current) {
254
+ meshRef.current.rotation.x += delta * 0.5;
255
+ meshRef.current.rotation.y += delta * 0.3;
256
+
257
+ // Smooth scale animation
258
+ const targetScale = clicked ? 1.5 : hovered ? 1.2 : 1;
259
+ meshRef.current.scale.lerp(
260
+ new THREE.Vector3(targetScale, targetScale, targetScale),
261
+ 0.1
262
+ );
263
+ }
264
+ });
265
+
266
+ return (
267
+ <mesh
268
+ ref={meshRef}
269
+ position={position}
270
+ onPointerOver={() => setHovered(true)}
271
+ onPointerOut={() => setHovered(false)}
272
+ onClick={() => setClicked(!clicked)}
273
+ >
274
+ <boxGeometry args={[1, 1, 1]} />
275
+ <meshStandardMaterial
276
+ color={hovered ? '#ff6b6b' : '#4ecdc4'}
277
+ metalness={0.5}
278
+ roughness={0.2}
279
+ />
280
+ </mesh>
281
+ );
282
+ }
283
+
284
+ // GLTF Model loader
285
+ function Model({ url, scale = 1 }: { url: string; scale?: number }) {
286
+ const { scene } = useGLTF(url);
287
+ const modelRef = useRef<THREE.Group>(null);
288
+
289
+ // Clone materials for unique instances
290
+ useEffect(() => {
291
+ scene.traverse((child) => {
292
+ if (child instanceof THREE.Mesh) {
293
+ child.castShadow = true;
294
+ child.receiveShadow = true;
295
+ }
296
+ });
297
+ }, [scene]);
298
+
299
+ return (
300
+ <primitive
301
+ ref={modelRef}
302
+ object={scene}
303
+ scale={scale}
304
+ dispose={null}
305
+ />
306
+ );
307
+ }
308
+
309
+ // Glass material sphere
310
+ function GlassSphere() {
311
+ return (
312
+ <mesh position={[2, 1, 0]}>
313
+ <sphereGeometry args={[0.8, 64, 64]} />
314
+ <MeshTransmissionMaterial
315
+ backside
316
+ samples={16}
317
+ thickness={0.5}
318
+ chromaticAberration={0.5}
319
+ anisotropy={0.3}
320
+ distortion={0.2}
321
+ distortionScale={0.5}
322
+ temporalDistortion={0.1}
323
+ iridescence={1}
324
+ iridescenceIOR={1}
325
+ iridescenceThicknessRange={[0, 1400]}
326
+ />
327
+ </mesh>
328
+ );
329
+ }
330
+
331
+ // 3D Text
332
+ function Text3DComponent({ text }: { text: string }) {
333
+ return (
334
+ <Center position={[0, 2, 0]}>
335
+ <Float speed={2} rotationIntensity={0.5} floatIntensity={1}>
336
+ <Text3D
337
+ font="/fonts/Inter_Bold.json"
338
+ size={0.5}
339
+ height={0.1}
340
+ curveSegments={12}
341
+ >
342
+ {text}
343
+ <meshStandardMaterial color="#ff6b6b" metalness={0.8} roughness={0.2} />
344
+ </Text3D>
345
+ </Float>
346
+ </Center>
347
+ );
348
+ }
349
+
350
+ // Camera rig with smooth following
351
+ function CameraRig({ target }: { target: THREE.Vector3 }) {
352
+ const { camera } = useThree();
353
+
354
+ useFrame(() => {
355
+ camera.position.lerp(
356
+ new THREE.Vector3(target.x + 5, target.y + 3, target.z + 5),
357
+ 0.02
358
+ );
359
+ camera.lookAt(target);
360
+ });
361
+
362
+ return null;
363
+ }
364
+
365
+ // Main scene
366
+ export function Scene() {
367
+ return (
368
+ <Canvas
369
+ shadows
370
+ dpr={[1, 2]}
371
+ gl={{
372
+ antialias: true,
373
+ toneMapping: THREE.ACESFilmicToneMapping,
374
+ toneMappingExposure: 1,
375
+ }}
376
+ >
377
+ <PerspectiveCamera makeDefault position={[5, 3, 5]} fov={50} />
378
+ <OrbitControls
379
+ enableDamping
380
+ dampingFactor={0.05}
381
+ minDistance={2}
382
+ maxDistance={20}
383
+ />
384
+
385
+ {/* Lighting */}
386
+ <ambientLight intensity={0.5} />
387
+ <directionalLight
388
+ position={[10, 10, 5]}
389
+ intensity={1.5}
390
+ castShadow
391
+ shadow-mapSize={[2048, 2048]}
392
+ shadow-camera-far={50}
393
+ shadow-camera-left={-10}
394
+ shadow-camera-right={10}
395
+ shadow-camera-top={10}
396
+ shadow-camera-bottom={-10}
397
+ />
398
+
399
+ {/* Environment */}
400
+ <Environment preset="city" />
401
+
402
+ {/* Content */}
403
+ <Suspense fallback={null}>
404
+ <AnimatedBox position={[-2, 0.5, 0]} />
405
+ <AnimatedBox position={[0, 0.5, 0]} />
406
+ <GlassSphere />
407
+ <Text3DComponent text="Hello 3D" />
408
+
409
+ {/* Ground */}
410
+ <mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, -0.5, 0]} receiveShadow>
411
+ <planeGeometry args={[50, 50]} />
412
+ <meshStandardMaterial color="#1a1a2e" />
413
+ </mesh>
414
+ </Suspense>
415
+
416
+ {/* Contact shadows */}
417
+ <ContactShadows
418
+ position={[0, -0.49, 0]}
419
+ opacity={0.5}
420
+ scale={20}
421
+ blur={2}
422
+ far={10}
423
+ />
424
+ </Canvas>
425
+ );
426
+ }
427
+ ```
428
+
429
+ ### 3. Custom Shaders
430
+
431
+ ```tsx
432
+ // shaders/GradientMaterial.tsx
433
+ import { shaderMaterial } from '@react-three/drei';
434
+ import { extend, useFrame } from '@react-three/fiber';
435
+ import { useRef } from 'react';
436
+ import * as THREE from 'three';
437
+
438
+ // Vertex shader
439
+ const vertexShader = `
440
+ varying vec2 vUv;
441
+ varying vec3 vPosition;
442
+ varying vec3 vNormal;
443
+
444
+ uniform float uTime;
445
+ uniform float uAmplitude;
446
+ uniform float uFrequency;
447
+
448
+ void main() {
449
+ vUv = uv;
450
+ vPosition = position;
451
+ vNormal = normal;
452
+
453
+ // Wave displacement
454
+ vec3 pos = position;
455
+ float displacement = sin(pos.x * uFrequency + uTime) *
456
+ sin(pos.z * uFrequency + uTime) *
457
+ uAmplitude;
458
+ pos.y += displacement;
459
+
460
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
461
+ }
462
+ `;
463
+
464
+ // Fragment shader
465
+ const fragmentShader = `
466
+ varying vec2 vUv;
467
+ varying vec3 vPosition;
468
+ varying vec3 vNormal;
469
+
470
+ uniform float uTime;
471
+ uniform vec3 uColorA;
472
+ uniform vec3 uColorB;
473
+ uniform float uNoiseScale;
474
+
475
+ // Simplex noise function
476
+ vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
477
+ vec4 mod289(vec4 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
478
+ vec4 permute(vec4 x) { return mod289(((x*34.0)+1.0)*x); }
479
+ vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; }
480
+
481
+ float snoise(vec3 v) {
482
+ const vec2 C = vec2(1.0/6.0, 1.0/3.0);
483
+ const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
484
+
485
+ vec3 i = floor(v + dot(v, C.yyy));
486
+ vec3 x0 = v - i + dot(i, C.xxx);
487
+
488
+ vec3 g = step(x0.yzx, x0.xyz);
489
+ vec3 l = 1.0 - g;
490
+ vec3 i1 = min(g.xyz, l.zxy);
491
+ vec3 i2 = max(g.xyz, l.zxy);
492
+
493
+ vec3 x1 = x0 - i1 + C.xxx;
494
+ vec3 x2 = x0 - i2 + C.yyy;
495
+ vec3 x3 = x0 - D.yyy;
496
+
497
+ i = mod289(i);
498
+ vec4 p = permute(permute(permute(
499
+ i.z + vec4(0.0, i1.z, i2.z, 1.0))
500
+ + i.y + vec4(0.0, i1.y, i2.y, 1.0))
501
+ + i.x + vec4(0.0, i1.x, i2.x, 1.0));
502
+
503
+ float n_ = 0.142857142857;
504
+ vec3 ns = n_ * D.wyz - D.xzx;
505
+
506
+ vec4 j = p - 49.0 * floor(p * ns.z * ns.z);
507
+
508
+ vec4 x_ = floor(j * ns.z);
509
+ vec4 y_ = floor(j - 7.0 * x_);
510
+
511
+ vec4 x = x_ *ns.x + ns.yyyy;
512
+ vec4 y = y_ *ns.x + ns.yyyy;
513
+ vec4 h = 1.0 - abs(x) - abs(y);
514
+
515
+ vec4 b0 = vec4(x.xy, y.xy);
516
+ vec4 b1 = vec4(x.zw, y.zw);
517
+
518
+ vec4 s0 = floor(b0)*2.0 + 1.0;
519
+ vec4 s1 = floor(b1)*2.0 + 1.0;
520
+ vec4 sh = -step(h, vec4(0.0));
521
+
522
+ vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy;
523
+ vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww;
524
+
525
+ vec3 p0 = vec3(a0.xy,h.x);
526
+ vec3 p1 = vec3(a0.zw,h.y);
527
+ vec3 p2 = vec3(a1.xy,h.z);
528
+ vec3 p3 = vec3(a1.zw,h.w);
529
+
530
+ vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2,p2), dot(p3,p3)));
531
+ p0 *= norm.x;
532
+ p1 *= norm.y;
533
+ p2 *= norm.z;
534
+ p3 *= norm.w;
535
+
536
+ vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
537
+ m = m * m;
538
+ return 42.0 * dot(m*m, vec4(dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3)));
539
+ }
540
+
541
+ void main() {
542
+ // Animated noise
543
+ float noise = snoise(vec3(vPosition.xz * uNoiseScale, uTime * 0.3));
544
+ noise = noise * 0.5 + 0.5; // Normalize to 0-1
545
+
546
+ // Gradient based on UV and noise
547
+ float gradient = vUv.y + noise * 0.3;
548
+
549
+ // Mix colors
550
+ vec3 color = mix(uColorA, uColorB, gradient);
551
+
552
+ // Add rim lighting
553
+ vec3 viewDirection = normalize(cameraPosition - vPosition);
554
+ float rimLight = 1.0 - max(0.0, dot(viewDirection, vNormal));
555
+ rimLight = pow(rimLight, 3.0);
556
+ color += rimLight * 0.3;
557
+
558
+ gl_FragColor = vec4(color, 1.0);
559
+ }
560
+ `;
561
+
562
+ // Create shader material
563
+ const GradientMaterial = shaderMaterial(
564
+ {
565
+ uTime: 0,
566
+ uColorA: new THREE.Color('#ff6b6b'),
567
+ uColorB: new THREE.Color('#4ecdc4'),
568
+ uAmplitude: 0.2,
569
+ uFrequency: 2.0,
570
+ uNoiseScale: 1.0,
571
+ },
572
+ vertexShader,
573
+ fragmentShader
574
+ );
575
+
576
+ extend({ GradientMaterial });
577
+
578
+ // TypeScript declaration
579
+ declare global {
580
+ namespace JSX {
581
+ interface IntrinsicElements {
582
+ gradientMaterial: any;
583
+ }
584
+ }
585
+ }
586
+
587
+ // Component using custom shader
588
+ export function ShaderPlane() {
589
+ const materialRef = useRef<THREE.ShaderMaterial>(null);
590
+
591
+ useFrame(({ clock }) => {
592
+ if (materialRef.current) {
593
+ materialRef.current.uniforms.uTime.value = clock.getElapsedTime();
594
+ }
595
+ });
596
+
597
+ return (
598
+ <mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, 0, 0]}>
599
+ <planeGeometry args={[10, 10, 64, 64]} />
600
+ <gradientMaterial
601
+ ref={materialRef}
602
+ uColorA="#ff6b6b"
603
+ uColorB="#4ecdc4"
604
+ uAmplitude={0.3}
605
+ uFrequency={3.0}
606
+ />
607
+ </mesh>
608
+ );
609
+ }
610
+
611
+ // Post-processing shader
612
+ const GlitchShader = {
613
+ uniforms: {
614
+ tDiffuse: { value: null },
615
+ uTime: { value: 0 },
616
+ uIntensity: { value: 0.5 },
617
+ },
618
+ vertexShader: `
619
+ varying vec2 vUv;
620
+ void main() {
621
+ vUv = uv;
622
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
623
+ }
624
+ `,
625
+ fragmentShader: `
626
+ uniform sampler2D tDiffuse;
627
+ uniform float uTime;
628
+ uniform float uIntensity;
629
+ varying vec2 vUv;
630
+
631
+ float random(vec2 st) {
632
+ return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123);
633
+ }
634
+
635
+ void main() {
636
+ vec2 uv = vUv;
637
+
638
+ // Random glitch offset
639
+ float glitch = step(0.99, random(vec2(uTime * 0.1, floor(uv.y * 20.0))));
640
+ uv.x += glitch * uIntensity * (random(vec2(uTime)) - 0.5);
641
+
642
+ // RGB split
643
+ float r = texture2D(tDiffuse, uv + vec2(uIntensity * 0.01, 0.0)).r;
644
+ float g = texture2D(tDiffuse, uv).g;
645
+ float b = texture2D(tDiffuse, uv - vec2(uIntensity * 0.01, 0.0)).b;
646
+
647
+ gl_FragColor = vec4(r, g, b, 1.0);
648
+ }
649
+ `,
650
+ };
651
+ ```
652
+
653
+ ### 4. Physics Integration
654
+
655
+ ```tsx
656
+ // components/PhysicsScene.tsx
22
657
  import { Canvas } from '@react-three/fiber';
23
- import { OrbitControls } from '@react-three/drei';
658
+ import { Physics, RigidBody, CuboidCollider, BallCollider } from '@react-three/rapier';
659
+ import { useRef, useState } from 'react';
660
+ import * as THREE from 'three';
661
+
662
+ // Physics-enabled box
663
+ function PhysicsBox({ position }: { position: [number, number, number] }) {
664
+ const [color, setColor] = useState('#ff6b6b');
665
+
666
+ return (
667
+ <RigidBody
668
+ position={position}
669
+ restitution={0.7}
670
+ friction={0.5}
671
+ onCollisionEnter={() => setColor('#4ecdc4')}
672
+ onCollisionExit={() => setColor('#ff6b6b')}
673
+ >
674
+ <mesh castShadow>
675
+ <boxGeometry args={[1, 1, 1]} />
676
+ <meshStandardMaterial color={color} />
677
+ </mesh>
678
+ </RigidBody>
679
+ );
680
+ }
681
+
682
+ // Bouncing ball
683
+ function BouncingBall({ position }: { position: [number, number, number] }) {
684
+ const rigidBodyRef = useRef(null);
685
+
686
+ const handleClick = () => {
687
+ if (rigidBodyRef.current) {
688
+ // Apply upward impulse on click
689
+ rigidBodyRef.current.applyImpulse({ x: 0, y: 10, z: 0 }, true);
690
+ }
691
+ };
692
+
693
+ return (
694
+ <RigidBody
695
+ ref={rigidBodyRef}
696
+ position={position}
697
+ restitution={0.9}
698
+ friction={0.3}
699
+ colliders="ball"
700
+ >
701
+ <mesh castShadow onClick={handleClick}>
702
+ <sphereGeometry args={[0.5, 32, 32]} />
703
+ <meshStandardMaterial color="#feca57" metalness={0.3} roughness={0.2} />
704
+ </mesh>
705
+ </RigidBody>
706
+ );
707
+ }
24
708
 
25
- function Scene() {
709
+ // Ground plane
710
+ function Ground() {
26
711
  return (
27
- <Canvas>
28
- <ambientLight />
29
- <pointLight position={[10, 10, 10]} />
30
- <mesh>
31
- <boxGeometry />
32
- <meshStandardMaterial color="orange" />
712
+ <RigidBody type="fixed" friction={1}>
713
+ <mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, -0.5, 0]} receiveShadow>
714
+ <planeGeometry args={[50, 50]} />
715
+ <meshStandardMaterial color="#1a1a2e" />
33
716
  </mesh>
717
+ </RigidBody>
718
+ );
719
+ }
720
+
721
+ // Walls for containment
722
+ function Walls() {
723
+ return (
724
+ <>
725
+ {/* Back wall */}
726
+ <RigidBody type="fixed">
727
+ <CuboidCollider args={[25, 10, 0.5]} position={[0, 5, -25]} />
728
+ </RigidBody>
729
+ {/* Front wall */}
730
+ <RigidBody type="fixed">
731
+ <CuboidCollider args={[25, 10, 0.5]} position={[0, 5, 25]} />
732
+ </RigidBody>
733
+ {/* Left wall */}
734
+ <RigidBody type="fixed">
735
+ <CuboidCollider args={[0.5, 10, 25]} position={[-25, 5, 0]} />
736
+ </RigidBody>
737
+ {/* Right wall */}
738
+ <RigidBody type="fixed">
739
+ <CuboidCollider args={[0.5, 10, 25]} position={[25, 5, 0]} />
740
+ </RigidBody>
741
+ </>
742
+ );
743
+ }
744
+
745
+ // Spawner for dynamic objects
746
+ function ObjectSpawner() {
747
+ const [objects, setObjects] = useState<Array<{ id: number; type: 'box' | 'ball'; position: [number, number, number] }>>([]);
748
+
749
+ const spawnObject = () => {
750
+ const id = Date.now();
751
+ const type = Math.random() > 0.5 ? 'box' : 'ball';
752
+ const position: [number, number, number] = [
753
+ (Math.random() - 0.5) * 10,
754
+ 10,
755
+ (Math.random() - 0.5) * 10,
756
+ ];
757
+ setObjects((prev) => [...prev, { id, type, position }]);
758
+ };
759
+
760
+ return (
761
+ <>
762
+ {objects.map(({ id, type, position }) =>
763
+ type === 'box' ? (
764
+ <PhysicsBox key={id} position={position} />
765
+ ) : (
766
+ <BouncingBall key={id} position={position} />
767
+ )
768
+ )}
769
+
770
+ {/* Spawn trigger - invisible clickable plane */}
771
+ <mesh position={[0, 15, 0]} onClick={spawnObject}>
772
+ <planeGeometry args={[20, 20]} />
773
+ <meshBasicMaterial visible={false} />
774
+ </mesh>
775
+ </>
776
+ );
777
+ }
778
+
779
+ // Main physics scene
780
+ export function PhysicsScene() {
781
+ return (
782
+ <Canvas shadows camera={{ position: [15, 15, 15], fov: 50 }}>
783
+ <ambientLight intensity={0.5} />
784
+ <directionalLight
785
+ position={[10, 20, 10]}
786
+ intensity={1.5}
787
+ castShadow
788
+ shadow-mapSize={[2048, 2048]}
789
+ />
790
+
791
+ <Physics gravity={[0, -9.81, 0]} debug={false}>
792
+ <Ground />
793
+ <Walls />
794
+ <ObjectSpawner />
795
+
796
+ {/* Initial objects */}
797
+ <PhysicsBox position={[0, 5, 0]} />
798
+ <PhysicsBox position={[0.5, 7, 0.5]} />
799
+ <BouncingBall position={[-2, 5, 0]} />
800
+ <BouncingBall position={[2, 8, 2]} />
801
+ </Physics>
802
+
34
803
  <OrbitControls />
35
804
  </Canvas>
36
805
  );
37
806
  }
38
807
  ```
39
808
 
40
- ## Animation
809
+ ### 5. Animation System
810
+
41
811
  ```tsx
42
- import { useFrame } from '@react-three/fiber';
812
+ // lib/three/AnimationManager.ts
813
+ import * as THREE from 'three';
814
+ import gsap from 'gsap';
815
+
816
+ interface AnimationConfig {
817
+ duration?: number;
818
+ ease?: string;
819
+ delay?: number;
820
+ repeat?: number;
821
+ yoyo?: boolean;
822
+ onComplete?: () => void;
823
+ }
43
824
 
44
- function Box() {
45
- const ref = useRef<THREE.Mesh>(null);
825
+ export class AnimationManager {
826
+ private mixer: THREE.AnimationMixer | null = null;
827
+ private actions: Map<string, THREE.AnimationAction> = new Map();
828
+ private timelines: Map<string, gsap.core.Timeline> = new Map();
46
829
 
47
- useFrame((state, delta) => {
48
- if (ref.current) ref.current.rotation.y += delta;
830
+ // GLTF animation setup
831
+ public setupMixer(model: THREE.Object3D, animations: THREE.AnimationClip[]): void {
832
+ this.mixer = new THREE.AnimationMixer(model);
833
+
834
+ animations.forEach((clip) => {
835
+ const action = this.mixer!.clipAction(clip);
836
+ this.actions.set(clip.name, action);
837
+ });
838
+ }
839
+
840
+ public playAnimation(name: string, options: { loop?: boolean; crossFade?: number } = {}): void {
841
+ const action = this.actions.get(name);
842
+ if (!action) return;
843
+
844
+ const { loop = true, crossFade = 0.3 } = options;
845
+
846
+ // Stop other actions with crossfade
847
+ this.actions.forEach((a, n) => {
848
+ if (n !== name && a.isRunning()) {
849
+ a.fadeOut(crossFade);
850
+ }
851
+ });
852
+
853
+ action.reset();
854
+ action.setLoop(loop ? THREE.LoopRepeat : THREE.LoopOnce, Infinity);
855
+ action.clampWhenFinished = !loop;
856
+ action.fadeIn(crossFade);
857
+ action.play();
858
+ }
859
+
860
+ public update(delta: number): void {
861
+ this.mixer?.update(delta);
862
+ }
863
+
864
+ // GSAP-based object animation
865
+ public animateTo(
866
+ object: THREE.Object3D,
867
+ properties: Partial<{
868
+ position: { x?: number; y?: number; z?: number };
869
+ rotation: { x?: number; y?: number; z?: number };
870
+ scale: { x?: number; y?: number; z?: number };
871
+ }>,
872
+ config: AnimationConfig = {}
873
+ ): gsap.core.Tween {
874
+ const { duration = 1, ease = 'power2.out', delay = 0, onComplete } = config;
875
+
876
+ const targets: any[] = [];
877
+ const props: any = {};
878
+
879
+ if (properties.position) {
880
+ targets.push(object.position);
881
+ Object.assign(props, properties.position);
882
+ }
883
+ if (properties.rotation) {
884
+ targets.push(object.rotation);
885
+ Object.assign(props, properties.rotation);
886
+ }
887
+ if (properties.scale) {
888
+ targets.push(object.scale);
889
+ Object.assign(props, properties.scale);
890
+ }
891
+
892
+ return gsap.to(targets, {
893
+ ...props,
894
+ duration,
895
+ ease,
896
+ delay,
897
+ onComplete,
898
+ });
899
+ }
900
+
901
+ // Timeline-based complex animations
902
+ public createTimeline(id: string): gsap.core.Timeline {
903
+ const timeline = gsap.timeline({ paused: true });
904
+ this.timelines.set(id, timeline);
905
+ return timeline;
906
+ }
907
+
908
+ public playTimeline(id: string): void {
909
+ this.timelines.get(id)?.play();
910
+ }
911
+
912
+ public dispose(): void {
913
+ this.actions.clear();
914
+ this.timelines.forEach((tl) => tl.kill());
915
+ this.timelines.clear();
916
+ }
917
+ }
918
+
919
+ // React hook for animations
920
+ function useAnimation(meshRef: React.RefObject<THREE.Mesh>) {
921
+ const [isAnimating, setIsAnimating] = useState(false);
922
+
923
+ const animateIn = useCallback(() => {
924
+ if (!meshRef.current || isAnimating) return;
925
+
926
+ setIsAnimating(true);
927
+
928
+ gsap.timeline()
929
+ .fromTo(
930
+ meshRef.current.scale,
931
+ { x: 0, y: 0, z: 0 },
932
+ { x: 1, y: 1, z: 1, duration: 0.5, ease: 'back.out(1.7)' }
933
+ )
934
+ .fromTo(
935
+ meshRef.current.rotation,
936
+ { y: -Math.PI },
937
+ { y: 0, duration: 0.5, ease: 'power2.out' },
938
+ '<'
939
+ )
940
+ .call(() => setIsAnimating(false));
941
+ }, [isAnimating]);
942
+
943
+ const animateOut = useCallback(() => {
944
+ if (!meshRef.current || isAnimating) return;
945
+
946
+ setIsAnimating(true);
947
+
948
+ gsap.to(meshRef.current.scale, {
949
+ x: 0,
950
+ y: 0,
951
+ z: 0,
952
+ duration: 0.3,
953
+ ease: 'back.in(1.7)',
954
+ onComplete: () => setIsAnimating(false),
955
+ });
956
+ }, [isAnimating]);
957
+
958
+ return { animateIn, animateOut, isAnimating };
959
+ }
960
+
961
+ // Animated component example
962
+ function AnimatedObject() {
963
+ const meshRef = useRef<THREE.Mesh>(null);
964
+ const { animateIn, animateOut, isAnimating } = useAnimation(meshRef);
965
+
966
+ useEffect(() => {
967
+ animateIn();
968
+ }, []);
969
+
970
+ return (
971
+ <mesh
972
+ ref={meshRef}
973
+ onClick={animateOut}
974
+ scale={[0, 0, 0]}
975
+ >
976
+ <boxGeometry />
977
+ <meshStandardMaterial color="#ff6b6b" />
978
+ </mesh>
979
+ );
980
+ }
981
+ ```
982
+
983
+ ### 6. Performance Optimization
984
+
985
+ ```tsx
986
+ // lib/three/PerformanceOptimizer.ts
987
+ import * as THREE from 'three';
988
+ import { useThree, useFrame } from '@react-three/fiber';
989
+ import { useEffect, useMemo, useRef } from 'react';
990
+
991
+ // Level of Detail (LOD) component
992
+ function LODMesh({ position }: { position: [number, number, number] }) {
993
+ const lodRef = useRef<THREE.LOD>(null);
994
+ const { camera } = useThree();
995
+
996
+ const geometries = useMemo(() => ({
997
+ high: new THREE.IcosahedronGeometry(1, 4), // 320 triangles
998
+ medium: new THREE.IcosahedronGeometry(1, 2), // 80 triangles
999
+ low: new THREE.IcosahedronGeometry(1, 1), // 20 triangles
1000
+ }), []);
1001
+
1002
+ const material = useMemo(
1003
+ () => new THREE.MeshStandardMaterial({ color: '#4ecdc4' }),
1004
+ []
1005
+ );
1006
+
1007
+ useEffect(() => {
1008
+ if (!lodRef.current) return;
1009
+
1010
+ const meshHigh = new THREE.Mesh(geometries.high, material);
1011
+ const meshMedium = new THREE.Mesh(geometries.medium, material);
1012
+ const meshLow = new THREE.Mesh(geometries.low, material);
1013
+
1014
+ lodRef.current.addLevel(meshHigh, 0);
1015
+ lodRef.current.addLevel(meshMedium, 10);
1016
+ lodRef.current.addLevel(meshLow, 20);
1017
+
1018
+ return () => {
1019
+ geometries.high.dispose();
1020
+ geometries.medium.dispose();
1021
+ geometries.low.dispose();
1022
+ material.dispose();
1023
+ };
1024
+ }, [geometries, material]);
1025
+
1026
+ useFrame(() => {
1027
+ lodRef.current?.update(camera);
49
1028
  });
50
1029
 
51
- return <mesh ref={ref}>...</mesh>;
1030
+ return <lod ref={lodRef} position={position} />;
1031
+ }
1032
+
1033
+ // Instanced mesh for many identical objects
1034
+ function InstancedBoxes({ count = 1000 }: { count?: number }) {
1035
+ const meshRef = useRef<THREE.InstancedMesh>(null);
1036
+ const tempObject = useMemo(() => new THREE.Object3D(), []);
1037
+ const tempColor = useMemo(() => new THREE.Color(), []);
1038
+
1039
+ useEffect(() => {
1040
+ if (!meshRef.current) return;
1041
+
1042
+ // Set up instance matrices and colors
1043
+ for (let i = 0; i < count; i++) {
1044
+ tempObject.position.set(
1045
+ (Math.random() - 0.5) * 50,
1046
+ (Math.random() - 0.5) * 50,
1047
+ (Math.random() - 0.5) * 50
1048
+ );
1049
+ tempObject.rotation.set(
1050
+ Math.random() * Math.PI,
1051
+ Math.random() * Math.PI,
1052
+ Math.random() * Math.PI
1053
+ );
1054
+ tempObject.scale.setScalar(0.5 + Math.random() * 0.5);
1055
+ tempObject.updateMatrix();
1056
+
1057
+ meshRef.current.setMatrixAt(i, tempObject.matrix);
1058
+ meshRef.current.setColorAt(
1059
+ i,
1060
+ tempColor.setHSL(Math.random(), 0.7, 0.5)
1061
+ );
1062
+ }
1063
+
1064
+ meshRef.current.instanceMatrix.needsUpdate = true;
1065
+ if (meshRef.current.instanceColor) {
1066
+ meshRef.current.instanceColor.needsUpdate = true;
1067
+ }
1068
+ }, [count, tempObject, tempColor]);
1069
+
1070
+ // Animate instances
1071
+ useFrame(({ clock }) => {
1072
+ if (!meshRef.current) return;
1073
+
1074
+ const time = clock.getElapsedTime();
1075
+
1076
+ for (let i = 0; i < count; i++) {
1077
+ meshRef.current.getMatrixAt(i, tempObject.matrix);
1078
+ tempObject.matrix.decompose(
1079
+ tempObject.position,
1080
+ tempObject.quaternion,
1081
+ tempObject.scale
1082
+ );
1083
+
1084
+ tempObject.rotation.x = time * 0.5 + i * 0.01;
1085
+ tempObject.rotation.y = time * 0.3 + i * 0.01;
1086
+ tempObject.updateMatrix();
1087
+
1088
+ meshRef.current.setMatrixAt(i, tempObject.matrix);
1089
+ }
1090
+
1091
+ meshRef.current.instanceMatrix.needsUpdate = true;
1092
+ });
1093
+
1094
+ return (
1095
+ <instancedMesh ref={meshRef} args={[undefined, undefined, count]} castShadow>
1096
+ <boxGeometry args={[1, 1, 1]} />
1097
+ <meshStandardMaterial />
1098
+ </instancedMesh>
1099
+ );
1100
+ }
1101
+
1102
+ // Frustum culling helper
1103
+ function useFrustumCulling(meshRef: React.RefObject<THREE.Mesh>) {
1104
+ const { camera } = useThree();
1105
+ const frustum = useMemo(() => new THREE.Frustum(), []);
1106
+ const projScreenMatrix = useMemo(() => new THREE.Matrix4(), []);
1107
+
1108
+ useFrame(() => {
1109
+ if (!meshRef.current) return;
1110
+
1111
+ projScreenMatrix.multiplyMatrices(
1112
+ camera.projectionMatrix,
1113
+ camera.matrixWorldInverse
1114
+ );
1115
+ frustum.setFromProjectionMatrix(projScreenMatrix);
1116
+
1117
+ // Check if mesh is in frustum
1118
+ const isVisible = frustum.intersectsObject(meshRef.current);
1119
+ meshRef.current.visible = isVisible;
1120
+ });
1121
+ }
1122
+
1123
+ // Texture optimization
1124
+ function useOptimizedTexture(url: string) {
1125
+ const texture = useMemo(() => {
1126
+ const loader = new THREE.TextureLoader();
1127
+ const tex = loader.load(url);
1128
+
1129
+ // Optimize texture settings
1130
+ tex.minFilter = THREE.LinearMipmapLinearFilter;
1131
+ tex.magFilter = THREE.LinearFilter;
1132
+ tex.anisotropy = 4; // Balance quality and performance
1133
+ tex.generateMipmaps = true;
1134
+
1135
+ return tex;
1136
+ }, [url]);
1137
+
1138
+ useEffect(() => {
1139
+ return () => texture.dispose();
1140
+ }, [texture]);
1141
+
1142
+ return texture;
1143
+ }
1144
+
1145
+ // GPU particle system
1146
+ function ParticleSystem({ count = 10000 }: { count?: number }) {
1147
+ const particlesRef = useRef<THREE.Points>(null);
1148
+
1149
+ const [positions, velocities] = useMemo(() => {
1150
+ const positions = new Float32Array(count * 3);
1151
+ const velocities = new Float32Array(count * 3);
1152
+
1153
+ for (let i = 0; i < count; i++) {
1154
+ const i3 = i * 3;
1155
+ positions[i3] = (Math.random() - 0.5) * 20;
1156
+ positions[i3 + 1] = Math.random() * 20;
1157
+ positions[i3 + 2] = (Math.random() - 0.5) * 20;
1158
+
1159
+ velocities[i3] = (Math.random() - 0.5) * 0.02;
1160
+ velocities[i3 + 1] = -Math.random() * 0.05;
1161
+ velocities[i3 + 2] = (Math.random() - 0.5) * 0.02;
1162
+ }
1163
+
1164
+ return [positions, velocities];
1165
+ }, [count]);
1166
+
1167
+ useFrame(() => {
1168
+ if (!particlesRef.current) return;
1169
+
1170
+ const positions = particlesRef.current.geometry.attributes.position.array as Float32Array;
1171
+
1172
+ for (let i = 0; i < count; i++) {
1173
+ const i3 = i * 3;
1174
+
1175
+ positions[i3] += velocities[i3];
1176
+ positions[i3 + 1] += velocities[i3 + 1];
1177
+ positions[i3 + 2] += velocities[i3 + 2];
1178
+
1179
+ // Reset particle when it falls below ground
1180
+ if (positions[i3 + 1] < 0) {
1181
+ positions[i3 + 1] = 20;
1182
+ }
1183
+ }
1184
+
1185
+ particlesRef.current.geometry.attributes.position.needsUpdate = true;
1186
+ });
1187
+
1188
+ return (
1189
+ <points ref={particlesRef}>
1190
+ <bufferGeometry>
1191
+ <bufferAttribute
1192
+ attach="attributes-position"
1193
+ count={count}
1194
+ array={positions}
1195
+ itemSize={3}
1196
+ />
1197
+ </bufferGeometry>
1198
+ <pointsMaterial
1199
+ size={0.05}
1200
+ color="#ffffff"
1201
+ transparent
1202
+ opacity={0.8}
1203
+ sizeAttenuation
1204
+ />
1205
+ </points>
1206
+ );
1207
+ }
1208
+ ```
1209
+
1210
+ ## Use Cases
1211
+
1212
+ ### Interactive Product Viewer
1213
+
1214
+ ```tsx
1215
+ // components/ProductViewer.tsx
1216
+ import { Canvas } from '@react-three/fiber';
1217
+ import { useGLTF, OrbitControls, Environment, Html, useProgress } from '@react-three/drei';
1218
+ import { Suspense, useState } from 'react';
1219
+
1220
+ function Loader() {
1221
+ const { progress } = useProgress();
1222
+ return (
1223
+ <Html center>
1224
+ <div className="text-white text-xl">
1225
+ Loading... {progress.toFixed(0)}%
1226
+ </div>
1227
+ </Html>
1228
+ );
1229
+ }
1230
+
1231
+ interface ProductProps {
1232
+ modelUrl: string;
1233
+ hotspots: Array<{
1234
+ id: string;
1235
+ position: [number, number, number];
1236
+ label: string;
1237
+ description: string;
1238
+ }>;
1239
+ }
1240
+
1241
+ function Product({ modelUrl, hotspots }: ProductProps) {
1242
+ const { scene } = useGLTF(modelUrl);
1243
+ const [activeHotspot, setActiveHotspot] = useState<string | null>(null);
1244
+
1245
+ return (
1246
+ <group>
1247
+ <primitive object={scene} scale={1} />
1248
+
1249
+ {hotspots.map((hotspot) => (
1250
+ <group key={hotspot.id} position={hotspot.position}>
1251
+ <mesh onClick={() => setActiveHotspot(hotspot.id)}>
1252
+ <sphereGeometry args={[0.1, 16, 16]} />
1253
+ <meshBasicMaterial
1254
+ color={activeHotspot === hotspot.id ? '#ff6b6b' : '#4ecdc4'}
1255
+ />
1256
+ </mesh>
1257
+
1258
+ {activeHotspot === hotspot.id && (
1259
+ <Html distanceFactor={5}>
1260
+ <div className="bg-white p-4 rounded-lg shadow-lg min-w-[200px]">
1261
+ <h3 className="font-bold">{hotspot.label}</h3>
1262
+ <p className="text-sm text-gray-600">{hotspot.description}</p>
1263
+ </div>
1264
+ </Html>
1265
+ )}
1266
+ </group>
1267
+ ))}
1268
+ </group>
1269
+ );
1270
+ }
1271
+
1272
+ export function ProductViewer({ modelUrl, hotspots }: ProductProps) {
1273
+ return (
1274
+ <div className="w-full h-screen">
1275
+ <Canvas camera={{ position: [0, 0, 5], fov: 50 }}>
1276
+ <Suspense fallback={<Loader />}>
1277
+ <Product modelUrl={modelUrl} hotspots={hotspots} />
1278
+ <Environment preset="studio" />
1279
+ </Suspense>
1280
+
1281
+ <OrbitControls
1282
+ enablePan={false}
1283
+ minDistance={2}
1284
+ maxDistance={10}
1285
+ autoRotate
1286
+ autoRotateSpeed={0.5}
1287
+ />
1288
+ </Canvas>
1289
+ </div>
1290
+ );
52
1291
  }
53
1292
  ```
54
1293
 
55
1294
  ## Best Practices
56
- - Use React Three Fiber for React
57
- - Dispose resources
58
- - Optimize draw calls
59
- - Use instances for repeated objects
1295
+
1296
+ ### Do's
1297
+
1298
+ - Use React Three Fiber for React applications
1299
+ - Dispose of geometries, materials, and textures when unmounting
1300
+ - Use instancing for many identical objects
1301
+ - Implement LOD for complex scenes
1302
+ - Use texture atlases to reduce draw calls
1303
+ - Enable shadow maps only when needed
1304
+ - Use object pooling for frequently created/destroyed objects
1305
+ - Optimize geometry with BufferGeometry
1306
+ - Use compressed textures (KTX2, Basis)
1307
+ - Profile performance with Chrome DevTools and Spector.js
1308
+
1309
+ ### Don'ts
1310
+
1311
+ - Don't create new geometries/materials in render loops
1312
+ - Don't use too many lights (limit to 3-4 dynamic lights)
1313
+ - Don't skip frustum culling for large scenes
1314
+ - Don't forget to update instanceMatrix when animating instanced meshes
1315
+ - Don't use transparent materials unless necessary
1316
+ - Don't load uncompressed high-resolution textures
1317
+ - Don't forget to set power-of-two texture dimensions
1318
+ - Don't ignore memory leaks from undisposed resources
1319
+ - Don't use complex shaders without fallbacks
1320
+ - Don't skip mobile optimization and testing
1321
+
1322
+ ## References
1323
+
1324
+ - [Three.js Documentation](https://threejs.org/docs/)
1325
+ - [React Three Fiber](https://docs.pmnd.rs/react-three-fiber/)
1326
+ - [Drei Helpers](https://github.com/pmndrs/drei)
1327
+ - [Three.js Fundamentals](https://threejs.org/manual/)
1328
+ - [WebGL Best Practices](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_best_practices)