create-threejs-game 1.0.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 (39) hide show
  1. package/README.md +97 -0
  2. package/bin/cli.js +370 -0
  3. package/package.json +29 -0
  4. package/template/.claude/skills/threejs-animation/SKILL.md +552 -0
  5. package/template/.claude/skills/threejs-fundamentals/SKILL.md +488 -0
  6. package/template/.claude/skills/threejs-geometry/SKILL.md +548 -0
  7. package/template/.claude/skills/threejs-interaction/SKILL.md +660 -0
  8. package/template/.claude/skills/threejs-lighting/SKILL.md +481 -0
  9. package/template/.claude/skills/threejs-loaders/SKILL.md +623 -0
  10. package/template/.claude/skills/threejs-materials/SKILL.md +520 -0
  11. package/template/.claude/skills/threejs-postprocessing/SKILL.md +602 -0
  12. package/template/.claude/skills/threejs-shaders/SKILL.md +642 -0
  13. package/template/.claude/skills/threejs-textures/SKILL.md +628 -0
  14. package/template/.codex/skills/threejs-animation/SKILL.md +552 -0
  15. package/template/.codex/skills/threejs-fundamentals/SKILL.md +488 -0
  16. package/template/.codex/skills/threejs-geometry/SKILL.md +548 -0
  17. package/template/.codex/skills/threejs-interaction/SKILL.md +660 -0
  18. package/template/.codex/skills/threejs-lighting/SKILL.md +481 -0
  19. package/template/.codex/skills/threejs-loaders/SKILL.md +623 -0
  20. package/template/.codex/skills/threejs-materials/SKILL.md +520 -0
  21. package/template/.codex/skills/threejs-postprocessing/SKILL.md +602 -0
  22. package/template/.codex/skills/threejs-shaders/SKILL.md +642 -0
  23. package/template/.codex/skills/threejs-textures/SKILL.md +628 -0
  24. package/template/README.md +352 -0
  25. package/template/docs/.gitkeep +0 -0
  26. package/template/plans/.gitkeep +0 -0
  27. package/template/prompts/01-mockup-generation.md +44 -0
  28. package/template/prompts/02-prd-generation.md +119 -0
  29. package/template/prompts/03-tdd-generation.md +127 -0
  30. package/template/prompts/04-execution-plan.md +89 -0
  31. package/template/prompts/05-implementation.md +61 -0
  32. package/template/public/assets/.gitkeep +0 -0
  33. package/template/scripts/config.example.json +12 -0
  34. package/template/scripts/generate-assets-json.js +149 -0
  35. package/template/scripts/generate-mockup.js +197 -0
  36. package/template/scripts/generate-plan.js +295 -0
  37. package/template/scripts/generate-prd.js +297 -0
  38. package/template/scripts/generate-tdd.js +283 -0
  39. package/template/scripts/pipeline.js +229 -0
@@ -0,0 +1,628 @@
1
+ ---
2
+ name: threejs-textures
3
+ description: Three.js textures - texture types, UV mapping, environment maps, texture settings. Use when working with images, UV coordinates, cubemaps, HDR environments, or texture optimization.
4
+ ---
5
+
6
+ # Three.js Textures
7
+
8
+ ## Quick Start
9
+
10
+ ```javascript
11
+ import * as THREE from "three";
12
+
13
+ // Load texture
14
+ const loader = new THREE.TextureLoader();
15
+ const texture = loader.load("texture.jpg");
16
+
17
+ // Apply to material
18
+ const material = new THREE.MeshStandardMaterial({
19
+ map: texture,
20
+ });
21
+ ```
22
+
23
+ ## Texture Loading
24
+
25
+ ### Basic Loading
26
+
27
+ ```javascript
28
+ const loader = new THREE.TextureLoader();
29
+
30
+ // Async with callbacks
31
+ loader.load(
32
+ "texture.jpg",
33
+ (texture) => console.log("Loaded"),
34
+ (progress) => console.log("Progress"),
35
+ (error) => console.error("Error"),
36
+ );
37
+
38
+ // Synchronous style (loads async internally)
39
+ const texture = loader.load("texture.jpg");
40
+ material.map = texture;
41
+ ```
42
+
43
+ ### Promise Wrapper
44
+
45
+ ```javascript
46
+ function loadTexture(url) {
47
+ return new Promise((resolve, reject) => {
48
+ new THREE.TextureLoader().load(url, resolve, undefined, reject);
49
+ });
50
+ }
51
+
52
+ // Usage
53
+ const [colorMap, normalMap, roughnessMap] = await Promise.all([
54
+ loadTexture("color.jpg"),
55
+ loadTexture("normal.jpg"),
56
+ loadTexture("roughness.jpg"),
57
+ ]);
58
+ ```
59
+
60
+ ## Texture Configuration
61
+
62
+ ### Color Space
63
+
64
+ Critical for accurate color reproduction.
65
+
66
+ ```javascript
67
+ // Color/albedo textures - use sRGB
68
+ colorTexture.colorSpace = THREE.SRGBColorSpace;
69
+
70
+ // Data textures (normal, roughness, metalness, AO) - leave as default
71
+ // Do NOT set colorSpace for data textures (NoColorSpace is default)
72
+ ```
73
+
74
+ ### Wrapping Modes
75
+
76
+ ```javascript
77
+ texture.wrapS = THREE.RepeatWrapping; // Horizontal
78
+ texture.wrapT = THREE.RepeatWrapping; // Vertical
79
+
80
+ // Options:
81
+ // THREE.ClampToEdgeWrapping - Stretches edge pixels (default)
82
+ // THREE.RepeatWrapping - Tiles the texture
83
+ // THREE.MirroredRepeatWrapping - Tiles with mirror flip
84
+ ```
85
+
86
+ ### Repeat, Offset, Rotation
87
+
88
+ ```javascript
89
+ // Tile texture 4x4
90
+ texture.repeat.set(4, 4);
91
+ texture.wrapS = THREE.RepeatWrapping;
92
+ texture.wrapT = THREE.RepeatWrapping;
93
+
94
+ // Offset (0-1 range)
95
+ texture.offset.set(0.5, 0.5);
96
+
97
+ // Rotation (radians, around center)
98
+ texture.rotation = Math.PI / 4;
99
+ texture.center.set(0.5, 0.5); // Rotation pivot
100
+ ```
101
+
102
+ ### Filtering
103
+
104
+ ```javascript
105
+ // Minification (texture larger than screen pixels)
106
+ texture.minFilter = THREE.LinearMipmapLinearFilter; // Default, smooth
107
+ texture.minFilter = THREE.NearestFilter; // Pixelated
108
+ texture.minFilter = THREE.LinearFilter; // Smooth, no mipmaps
109
+
110
+ // Magnification (texture smaller than screen pixels)
111
+ texture.magFilter = THREE.LinearFilter; // Smooth (default)
112
+ texture.magFilter = THREE.NearestFilter; // Pixelated (retro games)
113
+
114
+ // Anisotropic filtering (sharper at angles)
115
+ texture.anisotropy = renderer.capabilities.getMaxAnisotropy();
116
+ ```
117
+
118
+ ### Generate Mipmaps
119
+
120
+ ```javascript
121
+ // Usually true by default
122
+ texture.generateMipmaps = true;
123
+
124
+ // Disable for non-power-of-2 textures or data textures
125
+ texture.generateMipmaps = false;
126
+ texture.minFilter = THREE.LinearFilter;
127
+ ```
128
+
129
+ ## Texture Types
130
+
131
+ ### Regular Texture
132
+
133
+ ```javascript
134
+ const texture = new THREE.Texture(image);
135
+ texture.needsUpdate = true;
136
+ ```
137
+
138
+ ### Data Texture
139
+
140
+ Create texture from raw data.
141
+
142
+ ```javascript
143
+ // Create gradient texture
144
+ const size = 256;
145
+ const data = new Uint8Array(size * size * 4);
146
+
147
+ for (let i = 0; i < size; i++) {
148
+ for (let j = 0; j < size; j++) {
149
+ const index = (i * size + j) * 4;
150
+ data[index] = i; // R
151
+ data[index + 1] = j; // G
152
+ data[index + 2] = 128; // B
153
+ data[index + 3] = 255; // A
154
+ }
155
+ }
156
+
157
+ const texture = new THREE.DataTexture(data, size, size);
158
+ texture.needsUpdate = true;
159
+ ```
160
+
161
+ ### Canvas Texture
162
+
163
+ ```javascript
164
+ const canvas = document.createElement("canvas");
165
+ canvas.width = 256;
166
+ canvas.height = 256;
167
+ const ctx = canvas.getContext("2d");
168
+
169
+ // Draw on canvas
170
+ ctx.fillStyle = "red";
171
+ ctx.fillRect(0, 0, 256, 256);
172
+ ctx.fillStyle = "white";
173
+ ctx.font = "48px Arial";
174
+ ctx.fillText("Hello", 50, 150);
175
+
176
+ const texture = new THREE.CanvasTexture(canvas);
177
+
178
+ // Update when canvas changes
179
+ texture.needsUpdate = true;
180
+ ```
181
+
182
+ ### Video Texture
183
+
184
+ ```javascript
185
+ const video = document.createElement("video");
186
+ video.src = "video.mp4";
187
+ video.loop = true;
188
+ video.muted = true;
189
+ video.play();
190
+
191
+ const texture = new THREE.VideoTexture(video);
192
+ texture.colorSpace = THREE.SRGBColorSpace;
193
+
194
+ // No need to set needsUpdate - auto-updates
195
+ ```
196
+
197
+ ### Compressed Textures
198
+
199
+ ```javascript
200
+ import { KTX2Loader } from "three/examples/jsm/loaders/KTX2Loader.js";
201
+
202
+ const ktx2Loader = new KTX2Loader();
203
+ ktx2Loader.setTranscoderPath("path/to/basis/");
204
+ ktx2Loader.detectSupport(renderer);
205
+
206
+ ktx2Loader.load("texture.ktx2", (texture) => {
207
+ material.map = texture;
208
+ });
209
+ ```
210
+
211
+ ## Cube Textures
212
+
213
+ For environment maps and skyboxes.
214
+
215
+ ### CubeTextureLoader
216
+
217
+ ```javascript
218
+ const loader = new THREE.CubeTextureLoader();
219
+ const cubeTexture = loader.load([
220
+ "px.jpg",
221
+ "nx.jpg", // +X, -X
222
+ "py.jpg",
223
+ "ny.jpg", // +Y, -Y
224
+ "pz.jpg",
225
+ "nz.jpg", // +Z, -Z
226
+ ]);
227
+
228
+ // As background
229
+ scene.background = cubeTexture;
230
+
231
+ // As environment map
232
+ scene.environment = cubeTexture;
233
+ material.envMap = cubeTexture;
234
+ ```
235
+
236
+ ### Equirectangular to Cubemap
237
+
238
+ ```javascript
239
+ import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
240
+
241
+ const pmremGenerator = new THREE.PMREMGenerator(renderer);
242
+ pmremGenerator.compileEquirectangularShader();
243
+
244
+ new RGBELoader().load("environment.hdr", (texture) => {
245
+ const envMap = pmremGenerator.fromEquirectangular(texture).texture;
246
+ scene.environment = envMap;
247
+ scene.background = envMap;
248
+
249
+ texture.dispose();
250
+ pmremGenerator.dispose();
251
+ });
252
+ ```
253
+
254
+ ## HDR Textures
255
+
256
+ ### RGBELoader
257
+
258
+ ```javascript
259
+ import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
260
+
261
+ const loader = new RGBELoader();
262
+ loader.load("environment.hdr", (texture) => {
263
+ texture.mapping = THREE.EquirectangularReflectionMapping;
264
+ scene.environment = texture;
265
+ scene.background = texture;
266
+ });
267
+ ```
268
+
269
+ ### EXRLoader
270
+
271
+ ```javascript
272
+ import { EXRLoader } from "three/examples/jsm/loaders/EXRLoader.js";
273
+
274
+ const loader = new EXRLoader();
275
+ loader.load("environment.exr", (texture) => {
276
+ texture.mapping = THREE.EquirectangularReflectionMapping;
277
+ scene.environment = texture;
278
+ });
279
+ ```
280
+
281
+ ### Background Options
282
+
283
+ ```javascript
284
+ scene.background = texture;
285
+ scene.backgroundBlurriness = 0.5; // 0-1, blur background
286
+ scene.backgroundIntensity = 1.0; // Brightness
287
+ scene.backgroundRotation.y = Math.PI; // Rotate background
288
+ ```
289
+
290
+ ## Render Targets
291
+
292
+ Render to texture for effects.
293
+
294
+ ```javascript
295
+ // Create render target
296
+ const renderTarget = new THREE.WebGLRenderTarget(512, 512, {
297
+ minFilter: THREE.LinearFilter,
298
+ magFilter: THREE.LinearFilter,
299
+ format: THREE.RGBAFormat,
300
+ });
301
+
302
+ // Render scene to target
303
+ renderer.setRenderTarget(renderTarget);
304
+ renderer.render(scene, camera);
305
+ renderer.setRenderTarget(null); // Back to screen
306
+
307
+ // Use as texture
308
+ material.map = renderTarget.texture;
309
+ ```
310
+
311
+ ### Depth Texture
312
+
313
+ ```javascript
314
+ const renderTarget = new THREE.WebGLRenderTarget(512, 512);
315
+ renderTarget.depthTexture = new THREE.DepthTexture(
316
+ 512,
317
+ 512,
318
+ THREE.UnsignedShortType,
319
+ );
320
+
321
+ // Access depth
322
+ const depthTexture = renderTarget.depthTexture;
323
+ ```
324
+
325
+ ### Multi-Sample Render Target
326
+
327
+ ```javascript
328
+ const renderTarget = new THREE.WebGLRenderTarget(512, 512, {
329
+ samples: 4, // MSAA
330
+ });
331
+ ```
332
+
333
+ ## CubeCamera
334
+
335
+ Dynamic environment maps for reflections.
336
+
337
+ ```javascript
338
+ const cubeRenderTarget = new THREE.WebGLCubeRenderTarget(256, {
339
+ generateMipmaps: true,
340
+ minFilter: THREE.LinearMipmapLinearFilter,
341
+ });
342
+
343
+ const cubeCamera = new THREE.CubeCamera(0.1, 1000, cubeRenderTarget);
344
+ scene.add(cubeCamera);
345
+
346
+ // Apply to reflective material
347
+ reflectiveMaterial.envMap = cubeRenderTarget.texture;
348
+
349
+ // Update in animation loop (expensive!)
350
+ function animate() {
351
+ // Hide reflective object, update env map, show again
352
+ reflectiveObject.visible = false;
353
+ cubeCamera.position.copy(reflectiveObject.position);
354
+ cubeCamera.update(renderer, scene);
355
+ reflectiveObject.visible = true;
356
+ }
357
+ ```
358
+
359
+ ## UV Mapping
360
+
361
+ ### Accessing UVs
362
+
363
+ ```javascript
364
+ const uvs = geometry.attributes.uv;
365
+
366
+ // Read UV
367
+ const u = uvs.getX(vertexIndex);
368
+ const v = uvs.getY(vertexIndex);
369
+
370
+ // Modify UV
371
+ uvs.setXY(vertexIndex, newU, newV);
372
+ uvs.needsUpdate = true;
373
+ ```
374
+
375
+ ### Second UV Channel (for AO maps)
376
+
377
+ ```javascript
378
+ // Required for aoMap
379
+ geometry.setAttribute("uv2", geometry.attributes.uv);
380
+
381
+ // Or create custom second UV
382
+ const uv2 = new Float32Array(vertexCount * 2);
383
+ // ... fill uv2 data
384
+ geometry.setAttribute("uv2", new THREE.BufferAttribute(uv2, 2));
385
+ ```
386
+
387
+ ### UV Transform in Shader
388
+
389
+ ```javascript
390
+ const material = new THREE.ShaderMaterial({
391
+ uniforms: {
392
+ map: { value: texture },
393
+ uvOffset: { value: new THREE.Vector2(0, 0) },
394
+ uvScale: { value: new THREE.Vector2(1, 1) },
395
+ },
396
+ vertexShader: `
397
+ varying vec2 vUv;
398
+ uniform vec2 uvOffset;
399
+ uniform vec2 uvScale;
400
+
401
+ void main() {
402
+ vUv = uv * uvScale + uvOffset;
403
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
404
+ }
405
+ `,
406
+ fragmentShader: `
407
+ varying vec2 vUv;
408
+ uniform sampler2D map;
409
+
410
+ void main() {
411
+ gl_FragColor = texture2D(map, vUv);
412
+ }
413
+ `,
414
+ });
415
+ ```
416
+
417
+ ## Texture Atlas
418
+
419
+ Multiple images in one texture.
420
+
421
+ ```javascript
422
+ // Atlas with 4 sprites (2x2 grid)
423
+ const atlas = loader.load("atlas.png");
424
+ atlas.wrapS = THREE.ClampToEdgeWrapping;
425
+ atlas.wrapT = THREE.ClampToEdgeWrapping;
426
+
427
+ // Select sprite by UV offset/scale
428
+ function selectSprite(row, col, gridSize = 2) {
429
+ atlas.offset.set(col / gridSize, 1 - (row + 1) / gridSize);
430
+ atlas.repeat.set(1 / gridSize, 1 / gridSize);
431
+ }
432
+
433
+ // Select top-left sprite
434
+ selectSprite(0, 0);
435
+ ```
436
+
437
+ ## Material Texture Maps
438
+
439
+ ### PBR Texture Set
440
+
441
+ ```javascript
442
+ const material = new THREE.MeshStandardMaterial({
443
+ // Base color (sRGB)
444
+ map: colorTexture,
445
+
446
+ // Surface detail (Linear)
447
+ normalMap: normalTexture,
448
+ normalScale: new THREE.Vector2(1, 1),
449
+
450
+ // Roughness (Linear, grayscale)
451
+ roughnessMap: roughnessTexture,
452
+ roughness: 1, // Multiplier
453
+
454
+ // Metalness (Linear, grayscale)
455
+ metalnessMap: metalnessTexture,
456
+ metalness: 1, // Multiplier
457
+
458
+ // Ambient occlusion (Linear, uses uv2)
459
+ aoMap: aoTexture,
460
+ aoMapIntensity: 1,
461
+
462
+ // Self-illumination (sRGB)
463
+ emissiveMap: emissiveTexture,
464
+ emissive: 0xffffff,
465
+ emissiveIntensity: 1,
466
+
467
+ // Vertex displacement (Linear)
468
+ displacementMap: displacementTexture,
469
+ displacementScale: 0.1,
470
+ displacementBias: 0,
471
+
472
+ // Alpha (Linear)
473
+ alphaMap: alphaTexture,
474
+ transparent: true,
475
+ });
476
+
477
+ // Don't forget UV2 for AO
478
+ geometry.setAttribute("uv2", geometry.attributes.uv);
479
+ ```
480
+
481
+ ### Normal Map Types
482
+
483
+ ```javascript
484
+ // OpenGL style normals (default)
485
+ material.normalMapType = THREE.TangentSpaceNormalMap;
486
+
487
+ // Object space normals
488
+ material.normalMapType = THREE.ObjectSpaceNormalMap;
489
+ ```
490
+
491
+ ## Procedural Textures
492
+
493
+ ### Noise Texture
494
+
495
+ ```javascript
496
+ function generateNoiseTexture(size = 256) {
497
+ const data = new Uint8Array(size * size * 4);
498
+
499
+ for (let i = 0; i < size * size; i++) {
500
+ const value = Math.random() * 255;
501
+ data[i * 4] = value;
502
+ data[i * 4 + 1] = value;
503
+ data[i * 4 + 2] = value;
504
+ data[i * 4 + 3] = 255;
505
+ }
506
+
507
+ const texture = new THREE.DataTexture(data, size, size);
508
+ texture.needsUpdate = true;
509
+ return texture;
510
+ }
511
+ ```
512
+
513
+ ### Gradient Texture
514
+
515
+ ```javascript
516
+ function generateGradientTexture(color1, color2, size = 256) {
517
+ const canvas = document.createElement("canvas");
518
+ canvas.width = size;
519
+ canvas.height = 1;
520
+ const ctx = canvas.getContext("2d");
521
+
522
+ const gradient = ctx.createLinearGradient(0, 0, size, 0);
523
+ gradient.addColorStop(0, color1);
524
+ gradient.addColorStop(1, color2);
525
+
526
+ ctx.fillStyle = gradient;
527
+ ctx.fillRect(0, 0, size, 1);
528
+
529
+ return new THREE.CanvasTexture(canvas);
530
+ }
531
+ ```
532
+
533
+ ## Texture Memory Management
534
+
535
+ ### Dispose Textures
536
+
537
+ ```javascript
538
+ // Single texture
539
+ texture.dispose();
540
+
541
+ // Material textures
542
+ function disposeMaterial(material) {
543
+ const maps = [
544
+ "map",
545
+ "normalMap",
546
+ "roughnessMap",
547
+ "metalnessMap",
548
+ "aoMap",
549
+ "emissiveMap",
550
+ "displacementMap",
551
+ "alphaMap",
552
+ "envMap",
553
+ "lightMap",
554
+ "bumpMap",
555
+ "specularMap",
556
+ ];
557
+
558
+ maps.forEach((mapName) => {
559
+ if (material[mapName]) {
560
+ material[mapName].dispose();
561
+ }
562
+ });
563
+
564
+ material.dispose();
565
+ }
566
+ ```
567
+
568
+ ### Texture Pooling
569
+
570
+ ```javascript
571
+ class TexturePool {
572
+ constructor() {
573
+ this.textures = new Map();
574
+ this.loader = new THREE.TextureLoader();
575
+ }
576
+
577
+ async get(url) {
578
+ if (this.textures.has(url)) {
579
+ return this.textures.get(url);
580
+ }
581
+
582
+ const texture = await new Promise((resolve, reject) => {
583
+ this.loader.load(url, resolve, undefined, reject);
584
+ });
585
+
586
+ this.textures.set(url, texture);
587
+ return texture;
588
+ }
589
+
590
+ dispose(url) {
591
+ const texture = this.textures.get(url);
592
+ if (texture) {
593
+ texture.dispose();
594
+ this.textures.delete(url);
595
+ }
596
+ }
597
+
598
+ disposeAll() {
599
+ this.textures.forEach((t) => t.dispose());
600
+ this.textures.clear();
601
+ }
602
+ }
603
+ ```
604
+
605
+ ## Performance Tips
606
+
607
+ 1. **Use power-of-2 dimensions**: 256, 512, 1024, 2048
608
+ 2. **Compress textures**: KTX2/Basis for web delivery
609
+ 3. **Use texture atlases**: Reduce texture switches
610
+ 4. **Enable mipmaps**: For distant objects
611
+ 5. **Limit texture size**: 2048 usually sufficient for web
612
+ 6. **Reuse textures**: Same texture = better batching
613
+
614
+ ```javascript
615
+ // Check texture memory
616
+ console.log(renderer.info.memory.textures);
617
+
618
+ // Optimize for mobile
619
+ const maxSize = renderer.capabilities.maxTextureSize;
620
+ const isMobile = /iPhone|iPad|Android/i.test(navigator.userAgent);
621
+ const textureSize = isMobile ? 1024 : 2048;
622
+ ```
623
+
624
+ ## See Also
625
+
626
+ - `threejs-materials` - Applying textures to materials
627
+ - `threejs-loaders` - Loading texture files
628
+ - `threejs-shaders` - Custom texture sampling