Files
pixel.horse/src/ts/client/webgl.ts
T

140 lines
5.3 KiB
TypeScript

import { mergeShader, spriteShader, paletteLayersShader, lightShader } from '../generated/shaders';
import { FrameBuffer, disposeFrameBuffer, createFrameBuffer } from '../graphics/webgl/frameBuffer';
import { CommonPalettes, PaletteManager, Camera } from '../common/interfaces';
import { Shader, ShaderProgramData, disposeShaderProgramData } from '../graphics/webgl/shader';
import { PaletteSpriteBatch } from '../graphics/paletteSpriteBatch';
import { getWebGLContext, unbindAllTexturesAndBuffers } from '../graphics/webgl/webglUtils';
import { createCommonPalettes } from '../graphics/graphicsUtils';
import { createTexturesForSpriteSheets, disposeTexturesForSpriteSheets } from '../graphics/spriteSheetUtils';
import * as sprites from '../generated/sprites';
import { SpriteBatch } from '../graphics/spriteBatch';
import { BATCH_VERTEX_CAPACITY_MAX } from '../common/constants';
export interface WebGL {
gl: WebGLRenderingContext;
frameBuffer?: FrameBuffer;
frameBuffer2?: FrameBuffer;
mergeShader: ShaderProgramData;
paletteShader: ShaderProgramData;
paletteShaderWithDepth: ShaderProgramData;
spriteShader: ShaderProgramData;
spriteShaderWithColor: ShaderProgramData;
lightShader: ShaderProgramData;
spriteBatch: SpriteBatch;
paletteBatch: PaletteSpriteBatch;
palettes: CommonPalettes;
failedFBO: boolean;
failedDepthBuffer: boolean;
renderer: string;
indexBuffer: WebGLBuffer;
}
const mergeShaderSource = mergeShader;
const spriteShaderSource = spriteShader;
const paletteShaderSource = paletteLayersShader;
const lightShaderSource = lightShader;
function createIndices(vertexCount: number) {
const numIndices = (vertexCount * 6 / 4) | 0;
const indices = new Uint16Array(numIndices);
for (let i = 0, j = 0; i < numIndices; j = (j + 4) | 0) {
indices[i++] = (j + 0) | 0;
indices[i++] = (j + 1) | 0;
indices[i++] = (j + 2) | 0;
indices[i++] = (j + 0) | 0;
indices[i++] = (j + 2) | 0;
indices[i++] = (j + 3) | 0;
}
return indices;
}
export function initWebGL(canvas: HTMLCanvasElement, paletteManager: PaletteManager, camera: Camera): WebGL {
const gl = getWebGLContext(canvas);
return initWebGLResources(gl, paletteManager, camera);
}
export function initWebGLResources(gl: WebGLRenderingContext, paletteManager: PaletteManager, camera: Camera): WebGL {
let renderer = '';
let failedFBO = false;
let failedDepthBuffer = false;
let frameBuffer = {} as FrameBuffer;
let frameBuffer2 = {} as FrameBuffer;
gl.enable(gl.DEPTH_TEST); // no reason to not have it enabled at all times, depth reads/writes are controlled by other parameters
gl.disable(gl.DITHER);
try {
createFrameBuffer(gl, frameBuffer, camera.w, camera.h, true, null);
createFrameBuffer(gl, frameBuffer2, camera.w, camera.h, false, frameBuffer.depthStencilRenderbuffer);
} catch (e) {
DEVELOPMENT && console.warn(e);
failedFBO = true;
failedDepthBuffer = true;
}
if (!failedFBO) {
failedDepthBuffer = frameBuffer!.depthStencilRenderbuffer === null;
}
createTexturesForSpriteSheets(gl, sprites.spriteSheets);
const palettes = createCommonPalettes(paletteManager);
const mergeShaderRaw = new Shader(mergeShaderSource);
const paletteShaderRaw = new Shader(paletteShaderSource);
const spriteShaderRaw = new Shader(spriteShaderSource);
const lightShaderRaw = new Shader(lightShaderSource);
const indexBuffer = gl.createBuffer();
if (!indexBuffer) {
throw new Error(`Failed to allocate index buffer`);
}
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, createIndices(BATCH_VERTEX_CAPACITY_MAX), gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
const spriteBatch = new SpriteBatch(gl, BATCH_VERTEX_CAPACITY_MAX, indexBuffer);
const paletteBatch = new PaletteSpriteBatch(gl, BATCH_VERTEX_CAPACITY_MAX, indexBuffer);
spriteBatch.rectSprite = sprites.pixel;
paletteBatch.rectSprite = sprites.pixel2;
paletteBatch.defaultPalette = palettes.defaultPalette;
paletteManager.init(gl);
const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
if (debugInfo) {
renderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
}
const mergeShader = mergeShaderRaw.compile(gl, []);
const paletteShader = paletteShaderRaw.compile(gl, []);
const paletteShaderWithDepth = paletteShaderRaw.compile(gl, ['DEPTH_BUFFERED']);
const spriteShader = spriteShaderRaw.compile(gl, []);
const spriteShaderWithColor = spriteShaderRaw.compile(gl, ['USE_COLOR']);
const lightShader = lightShaderRaw.compile(gl, []);
return {
gl, mergeShader: mergeShader, paletteShader: paletteShader, paletteShaderWithDepth: paletteShaderWithDepth,
spriteShader: spriteShader, spriteShaderWithColor: spriteShaderWithColor, lightShader: lightShader,
spriteBatch, paletteBatch, frameBuffer, frameBuffer2, palettes, failedFBO, failedDepthBuffer, renderer, indexBuffer
};
}
export function disposeWebGL(webgl: WebGL) {
const { gl } = webgl;
unbindAllTexturesAndBuffers(gl);
disposeTexturesForSpriteSheets(gl, sprites.spriteSheets);
disposeFrameBuffer(gl, webgl.frameBuffer);
disposeShaderProgramData(gl, webgl.paletteShader);
disposeShaderProgramData(gl, webgl.paletteShaderWithDepth);
disposeShaderProgramData(gl, webgl.spriteShader);
disposeShaderProgramData(gl, webgl.spriteShaderWithColor);
disposeShaderProgramData(gl, webgl.lightShader);
webgl.spriteBatch.dispose();
webgl.paletteBatch.dispose();
gl.deleteBuffer(webgl.indexBuffer);
}