Add palette system
This commit is contained in:
parent
f60ae06f59
commit
98d1fc9ca2
19 changed files with 196 additions and 113 deletions
|
|
@ -10,14 +10,15 @@ export abstract class FrameBuffer {
|
|||
// null means the default framebuffer
|
||||
protected frameBuffer: WebGLFramebuffer | null = null;
|
||||
|
||||
constructor(protected gl: UniversalRenderingContext) {}
|
||||
constructor(protected readonly gl: UniversalRenderingContext) {}
|
||||
|
||||
public bindAndClear(colorInput?: WebGLTexture) {
|
||||
public bindAndClear(inputTextures: Array<WebGLTexture>) {
|
||||
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.frameBuffer);
|
||||
|
||||
if (colorInput) {
|
||||
this.gl.bindTexture(this.gl.TEXTURE_2D, colorInput);
|
||||
}
|
||||
inputTextures.forEach((t, i) => {
|
||||
this.gl.activeTexture(this.gl.TEXTURE0 + i);
|
||||
this.gl.bindTexture(this.gl.TEXTURE_2D, t);
|
||||
});
|
||||
|
||||
this.gl.viewport(0, 0, this.size.x, this.size.y);
|
||||
this.gl.clearColor(0, 0, 0, 0);
|
||||
|
|
|
|||
|
|
@ -4,16 +4,16 @@ import { FrameBuffer } from './frame-buffer';
|
|||
|
||||
export class IntermediateFrameBuffer extends FrameBuffer {
|
||||
private frameTexture: WebGLTexture;
|
||||
private floatEnabled = false;
|
||||
private floatLinearEnabled = false;
|
||||
|
||||
constructor(gl: UniversalRenderingContext) {
|
||||
super(gl);
|
||||
|
||||
if (gl.isWebGL2) {
|
||||
enableExtension(gl, 'EXT_color_buffer_float');
|
||||
}
|
||||
|
||||
try {
|
||||
enableExtension(gl, 'EXT_color_buffer_float');
|
||||
this.floatEnabled = true;
|
||||
|
||||
enableExtension(gl, 'OES_texture_float_linear');
|
||||
this.floatLinearEnabled = true;
|
||||
} catch {
|
||||
|
|
@ -21,6 +21,8 @@ export class IntermediateFrameBuffer extends FrameBuffer {
|
|||
}
|
||||
|
||||
// can only return null on lost context
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
|
||||
this.frameTexture = this.gl.createTexture()!;
|
||||
this.configureTexture();
|
||||
|
||||
|
|
@ -43,17 +45,17 @@ export class IntermediateFrameBuffer extends FrameBuffer {
|
|||
const hasChanged = super.setSize();
|
||||
|
||||
if (hasChanged) {
|
||||
this.gl.bindTexture(this.gl.TEXTURE_2D, this.frameTexture);
|
||||
this.bind();
|
||||
|
||||
this.gl.texImage2D(
|
||||
this.gl.TEXTURE_2D,
|
||||
0,
|
||||
this.gl.isWebGL2 ? this.gl.RG16F : this.gl.RGBA,
|
||||
this.floatEnabled ? this.gl.RG16F : this.gl.RGB,
|
||||
this.size.x,
|
||||
this.size.y,
|
||||
0,
|
||||
this.gl.isWebGL2 ? this.gl.RG : this.gl.RGBA,
|
||||
this.gl.isWebGL2 ? this.gl.FLOAT : this.gl.UNSIGNED_BYTE,
|
||||
this.floatEnabled ? this.gl.RG : this.gl.RGB,
|
||||
this.floatEnabled ? this.gl.FLOAT : this.gl.UNSIGNED_BYTE,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
|
@ -61,8 +63,14 @@ export class IntermediateFrameBuffer extends FrameBuffer {
|
|||
return hasChanged;
|
||||
}
|
||||
|
||||
private configureTexture() {
|
||||
private bind() {
|
||||
this.gl.activeTexture(this.gl.TEXTURE0);
|
||||
this.gl.bindTexture(this.gl.TEXTURE_2D, this.frameTexture);
|
||||
}
|
||||
|
||||
private configureTexture() {
|
||||
this.bind();
|
||||
|
||||
this.gl.texParameteri(
|
||||
this.gl.TEXTURE_2D,
|
||||
this.gl.TEXTURE_MAG_FILTER,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,10 @@ export const loadUniform = (
|
|||
(gl: UniversalRenderingContext, value: any, location: WebGLUniformLocation) => void
|
||||
> = new Map();
|
||||
{
|
||||
converters.set(WebGLRenderingContext.SAMPLER_2D, (gl, v, l) => {
|
||||
gl.uniform1i(l, v);
|
||||
});
|
||||
|
||||
converters.set(WebGLRenderingContext.FLOAT, (gl, v, l) => {
|
||||
if (v instanceof Array || v[0] instanceof Float32Array) {
|
||||
if (v.length == 0) {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export class WebGlStopwatch {
|
|||
private timerExtension: any;
|
||||
private timerQuery?: WebGLQuery;
|
||||
|
||||
constructor(private gl: UniversalRenderingContext) {
|
||||
constructor(private readonly gl: UniversalRenderingContext) {
|
||||
this.timerExtension = enableExtension(gl, 'EXT_disjoint_timer_query_webgl2');
|
||||
}
|
||||
|
||||
|
|
|
|||
66
src/graphics/graphics-library/palette-texture.ts
Normal file
66
src/graphics/graphics-library/palette-texture.ts
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
import { vec3, vec4 } from 'gl-matrix';
|
||||
import { UniversalRenderingContext } from './universal-rendering-context';
|
||||
|
||||
export class PaletteTexture {
|
||||
private texture: WebGLTexture;
|
||||
|
||||
constructor(
|
||||
private readonly gl: UniversalRenderingContext,
|
||||
private readonly paletteSize: number
|
||||
) {
|
||||
this.texture = gl.createTexture()!;
|
||||
this.bind();
|
||||
|
||||
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);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
||||
}
|
||||
|
||||
public get colorTexture(): WebGLTexture {
|
||||
return this.texture;
|
||||
}
|
||||
|
||||
private bind() {
|
||||
this.gl.activeTexture(this.gl.TEXTURE1);
|
||||
this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);
|
||||
}
|
||||
|
||||
public setPalette(colors: Array<vec3 | vec4>) {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = this.paletteSize;
|
||||
canvas.height = 1;
|
||||
|
||||
const ctx = canvas.getContext('2d')!;
|
||||
const imageData = ctx.createImageData(this.paletteSize, 1);
|
||||
|
||||
colors.forEach((c, i) => {
|
||||
imageData.data[4 * i + 0] = c[0] * 255;
|
||||
imageData.data[4 * i + 1] = c[1] * 255;
|
||||
imageData.data[4 * i + 2] = c[2] * 255;
|
||||
imageData.data[4 * i + 3] = c.length == 4 ? c[3] : 255;
|
||||
});
|
||||
ctx.putImageData(imageData, 0, 0);
|
||||
|
||||
document.body.appendChild(canvas);
|
||||
|
||||
this.setImage(canvas);
|
||||
}
|
||||
|
||||
public setImage(image: TexImageSource) {
|
||||
this.bind();
|
||||
|
||||
this.gl.texImage2D(
|
||||
this.gl.TEXTURE_2D,
|
||||
0,
|
||||
this.gl.RGBA,
|
||||
this.gl.RGBA,
|
||||
this.gl.UNSIGNED_BYTE,
|
||||
image
|
||||
);
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
this.gl.deleteTexture(this.texture);
|
||||
}
|
||||
}
|
||||
|
|
@ -75,10 +75,16 @@ export abstract class ParallelCompiler {
|
|||
program: WebGLProgram,
|
||||
substitutions: { [name: string]: string }
|
||||
): ShaderWithSource {
|
||||
const processedSource = source.replace(/{(.+)}/gm, (_, name: string): string => {
|
||||
const value = substitutions[name];
|
||||
return Number.isInteger(value) ? `${value}.0` : value;
|
||||
});
|
||||
let processedSource = source;
|
||||
let replaceHappened: boolean;
|
||||
do {
|
||||
replaceHappened = false;
|
||||
processedSource = processedSource.replace(/{(.+)}/gm, (_, name: string): string => {
|
||||
replaceHappened = true;
|
||||
const value = substitutions[name];
|
||||
return Number.isInteger(value) ? `${value}.0` : value;
|
||||
});
|
||||
} while (replaceHappened);
|
||||
|
||||
// can only return null on lost context
|
||||
const shader = ParallelCompiler.gl.createShader(type)!;
|
||||
|
|
@ -136,11 +142,9 @@ export abstract class ParallelCompiler {
|
|||
try {
|
||||
ParallelCompiler.checkShader(shader);
|
||||
} catch (e) {
|
||||
for (const match of e
|
||||
.toString()
|
||||
.matchAll(/ERROR: 0:(?<line>\d+): (?<error>.*)$/gm)) {
|
||||
const line = Number.parseInt(match.groups.line);
|
||||
const error = match.groups.error;
|
||||
for (const match of e.toString().matchAll(/ERROR: 0:(\d+): (.*)$/gm)) {
|
||||
const line = Number.parseInt(match[1]);
|
||||
const error = match[2];
|
||||
console.error(
|
||||
`Error: ${error}\nSource (line ${line}):\n${
|
||||
shader.source.split('\n')[line - 1]
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ export default abstract class Program implements IProgram {
|
|||
type: GLenum;
|
||||
}> = [];
|
||||
|
||||
constructor(protected gl: UniversalRenderingContext) {}
|
||||
constructor(protected readonly gl: UniversalRenderingContext) {}
|
||||
|
||||
public async initialize(
|
||||
[vertexShaderSource, fragmentShaderSource]: [string, string],
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ export class UniformArrayAutoScalingProgram implements IProgram {
|
|||
private drawingRectangleBottomLeft = vec2.fromValues(0, 0);
|
||||
private drawingRectangleSize = vec2.fromValues(1, 1);
|
||||
|
||||
constructor(private gl: UniversalRenderingContext) {}
|
||||
constructor(private readonly gl: UniversalRenderingContext) {}
|
||||
|
||||
public async initialize(
|
||||
shaderSources: [string, string],
|
||||
|
|
@ -86,10 +86,10 @@ export class UniformArrayAutoScalingProgram implements IProgram {
|
|||
shaderSources: [string, string]
|
||||
): Promise<void> {
|
||||
const processedSubstitutions = {
|
||||
...substitutions,
|
||||
macroDefinitions: this.getMacroDefinitions(combination, descriptors),
|
||||
declarations: this.getDeclarations(combination, descriptors),
|
||||
functionCalls: this.getFunctionCalls(combination, descriptors),
|
||||
...substitutions,
|
||||
};
|
||||
|
||||
const program = new FragmentShaderOnlyProgram(this.gl);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue