Claude Code Guidance for Goblin Grinder
Instructions for working with the Goblin Grinder codebase - a WebGL2-based 3D game built with TypeScript featuring custom rendering, shadow mapping, and spring physics.
Project Overview
Goblin Grinder is an experimental survivors game focused on trying out rendering techniques. It features a custom WebGL2 renderer with shadow mapping, GLB model loading, and spring physics for camera movement. The goal is to experiment with various render passes and shaders to achieve a pixel art look with quality lighting.
Development Commands
When working with this project, use these commands:
`npm run dev` - Start development server (DO NOT start this yourself - it's already running)`npm run build` - Build for production (TypeScript + Vite)`npm run preview` - Preview production build`npm run typecheck` - Run TypeScript type checking`npm run lint` - Run Biome linter with auto-fix`npm run format` - Format code with BiomeArchitecture Understanding
Entry Point (src/main.ts)
The `Game` class manages the entire application lifecycle:
Initializes WebGL2 contextManages main render loop via `requestAnimationFrame`Coordinates player input, camera updates, and scene renderingRendering Pipeline
The renderer uses a multi-pass approach:
1. **Shadow Pass** (Light.ts) - For each light:
- Renders scene to 2048x2048 shadow map framebuffer
- Uses depth-only shaders (depth.vert/depth.frag)
- Orthographic projection from light's perspective
2. **Main Pass** (Camera.ts) - From camera:
- Binds shadow maps from all lights
- Renders scene with basic.vert/basic.frag shaders
- Applies lighting, shadows, and textures
Core Classes
**Mesh** (Mesh.ts)
Manages VAO, vertex buffers, and index buffersVertex format: position(3) + normal(3) + uv(2) + color(3) = 11 floatsFactory methods: `createCube()`, `createPlane()`, `fromMeshData()`Texture loading via `loadTexture(url)`**Entity** (Entity.ts)
Game object with position, rotation (quaternion), scaleGenerates model matrices for renderingReferences a Mesh for geometry**Player** (Player.ts)
Controls player character (supports multiple entities/meshes)Movement via `move(x, z, deltaTime)` at 3.0 units/secondAuto-rotates to face movement direction**Camera** (Camera.ts)
Perspective projection with spring physics follow system`setFollowTarget()` sets goal position from player`updateFollow()` provides smooth movement with overshootConfigurable stiffness (20.0) and damping (0.7)**Light** (Light.ts)
Directional light with shadow map support`renderShadowMap()` renders depth passSupports up to 4 simultaneous lights**Shader** (Shader.ts)
Compiles and links vertex/fragment shadersManages uniform locations with cachingHelper methods for setting matrices, vectors, floatsArray uniform support for lights and shadow maps**GLBLoader** (GLBLoader.ts)
Custom GLB/glTF 2.0 parser (no external dependencies)Returns format-agnostic `MeshData` interfaceExtracts positions, normals, UVs, colors, indicesHandles multiple primitives/meshes per fileShader System
**basic.vert/basic.frag**
Vertex shader outputs: world position, normal, UV, color, light space positionsFragment shader samples texture or uses vertex colorCalculates shadows for each light using shadow mapsApplies diffuse lighting with configurable ambientLight loop is unrolled (GLSL limitation with sampler arrays)**depth.vert/depth.frag**
Minimal shaders for shadow map generationOnly outputs depth informationInput System
Keyboard controls (defined in main.ts):
WASD: Move player (relative to camera angle)Q/E: Rotate camera 90° with spring physicsUses Set for simultaneous key pressesMovement normalized for consistent diagonal speedCamera System
**Spring Physics**
Separate springs for position and targetCamera offset: 5 units distance, 8 units height**Rotation**
90° increments around playerSpring physics for smooth rotation with overshootStiffness: 40.0, Damping: 0.6Important Implementation Rules
1. **Never start dev server** - It's always running in another terminal
2. **Renderer is custom** - No game engine used
3. **Vertex format is interleaved** - 11 floats per vertex for efficiency
4. **Texture slots** - Shadow maps use 0-3, material textures use 4+
5. **Camera follows player** - Not hardcoded to specific coordinates
6. **Player rotation** - Automatically faces movement direction
7. **Spring physics** - Used for camera follow and rotation for dynamic feel
File Organization
**Models**: `src/assets/models/` (GLB format preferred)**Shaders**: `src/shaders/` (import with `?raw` suffix)**Source**: `src/` (TypeScript files)TypeScript Configuration
Target: ES2022Module: ESNext with bundler resolutionStrict mode enabledTypeScript only for type checking; Vite handles transpilationDependencies
**gl-matrix**: Vector and matrix math**lit**: UI framework (included but unused)**@biomejs/biome**: Linting and formattingCode Modification Workflow
When modifying code:
1. Read the relevant files first to understand current implementation
2. Check TypeScript types and interfaces
3. Maintain the interleaved vertex format if touching geometry
4. Preserve spring physics parameters unless specifically requested
5. Keep shader array indices as constants (GLSL limitation)
6. Run `npm run lint` before committing changes
7. Use `npm run typecheck` to verify TypeScript correctness
Adding New Features
**Adding new geometry:**
Create factory method in Mesh.ts or use `fromMeshData()`Follow 11-float vertex format: pos(3) + normal(3) + uv(2) + color(3)Update Entity references**Adding new shaders:**
Place in `src/shaders/` with .vert/.frag extensionsImport with `?raw` suffixCreate Shader instance in Game classUpdate uniform bindings in Camera.ts if needed**Adding new models:**
Place GLB files in `src/assets/models/`Load via GLBLoaderCreate Mesh from returned MeshDataAttach to Entity**Modifying lighting:**
Update Light class for light-specific changesModify fragment shader for lighting calculationsKeep unrolled light loop structure (GLSL limitation)