Retro western vertical shooter inspired by Gun.Smoke, built with TypeScript and WebGL2. Features 3-direction shooting, vertical scrolling, economy/shop loop, boss fights, and CRT shader effects. Phases implemented: - Phase 1: Engine skeleton (WebGL2 renderer, fixed timestep loop, input) - Phase 2: Shooting identity (3-dir shooting, bullet pools, collision) - Phase 3: Enemies & patterns (JSON waves, 4 enemy types, parallax bg) - Phase 4: Economy loop (pickups, shop, upgrades, HUD) - Phase 5: Boss system (3 bosses, wanted posters, multi-phase attacks) - Phase 6: Shader layer (CRT effects, bloom, scanlines, screen shake) - Phase 7: Performance (VAO batching, O(1) allocation, stress testing) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
73 lines
2.0 KiB
TypeScript
73 lines
2.0 KiB
TypeScript
export class Framebuffer {
|
|
public readonly framebuffer: WebGLFramebuffer;
|
|
public readonly texture: WebGLTexture;
|
|
public readonly width: number;
|
|
public readonly height: number;
|
|
|
|
constructor(gl: WebGL2RenderingContext, width: number, height: number) {
|
|
this.width = width;
|
|
this.height = height;
|
|
|
|
// Create texture for the framebuffer
|
|
const texture = gl.createTexture();
|
|
if (!texture) {
|
|
throw new Error('Failed to create framebuffer texture');
|
|
}
|
|
this.texture = texture;
|
|
|
|
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
gl.texImage2D(
|
|
gl.TEXTURE_2D,
|
|
0,
|
|
gl.RGBA,
|
|
width,
|
|
height,
|
|
0,
|
|
gl.RGBA,
|
|
gl.UNSIGNED_BYTE,
|
|
null
|
|
);
|
|
|
|
// Nearest-neighbor filtering for pixel-perfect upscaling
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
|
|
// Create framebuffer
|
|
const framebuffer = gl.createFramebuffer();
|
|
if (!framebuffer) {
|
|
throw new Error('Failed to create framebuffer');
|
|
}
|
|
this.framebuffer = framebuffer;
|
|
|
|
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
|
|
gl.framebufferTexture2D(
|
|
gl.FRAMEBUFFER,
|
|
gl.COLOR_ATTACHMENT0,
|
|
gl.TEXTURE_2D,
|
|
texture,
|
|
0
|
|
);
|
|
|
|
// Check framebuffer status
|
|
const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
|
|
if (status !== gl.FRAMEBUFFER_COMPLETE) {
|
|
throw new Error(`Framebuffer not complete: ${status}`);
|
|
}
|
|
|
|
// Unbind
|
|
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
}
|
|
|
|
bind(gl: WebGL2RenderingContext): void {
|
|
gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
|
|
gl.viewport(0, 0, this.width, this.height);
|
|
}
|
|
|
|
unbind(gl: WebGL2RenderingContext): void {
|
|
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
}
|
|
}
|