Archive update v0.53.2

This commit is contained in:
Eliot Partridge
2019-09-17 20:19:23 -05:00
parent ac640dbe1a
commit 2c4e29eb70
133 changed files with 8047 additions and 9286 deletions
+28 -24
View File
@@ -1,4 +1,4 @@
import { Sprite, Batch, SpriteBatchBase, SpriteSheet } from '../common/interfaces';
import { Sprite, Batch, SpriteBatchBase } from '../common/interfaces';
import { WHITE } from '../common/colors';
import { colorToFloat, colorToFloatAlpha } from '../common/color';
import { BaseStateBatch } from './baseStateBatch';
@@ -38,36 +38,45 @@ export function getColorFloat(color: number, alpha: number) {
}
export abstract class BaseSpriteBatch extends BaseStateBatch implements SpriteBatchBase {
tris = 0;
depth = 1;
drawnTrisStats = 0;
flushes = 0;
index = 0;
spritesCount = 0;
vertices!: Float32Array;
verticesUint32!: Uint32Array;
vao: VAO | undefined = undefined;
rectSprite: Sprite | undefined = undefined;
vertexBuffer: WebGLBuffer | undefined = undefined;
indexBuffer: WebGLBuffer | undefined = undefined;
spriteSheet: SpriteSheet | undefined = undefined;
floatsPerSprite: number;
batching = false;
vao: VAO | undefined;
vertexBuffer: WebGLBuffer | undefined;
protected indexBuffer: WebGLBuffer;
protected vertices!: Float32Array;
protected spritesCapacity: number;
private floatsPerSprite: number;
private batching = false;
private startBatchIndex = 0;
private startBatchSprites = 0;
constructor(
public gl: WebGLRenderingContext,
public capacity: number,
buffer: ArrayBuffer,
vertexBuffer: WebGLBuffer,
public vertexCapacityMax: number,
indexBuffer: WebGLBuffer,
public attributes: VAOAttributeDefinition[],
) {
super();
this.floatsPerSprite = getVAOAttributesSize(gl, attributes);
this.vertices = new Float32Array(buffer, 0, capacity * this.floatsPerSprite);
this.verticesUint32 = new Uint32Array(buffer, 0, capacity * this.floatsPerSprite);
const bytesPerVertex = getVAOAttributesSize(gl, attributes);
this.vertices = new Float32Array(vertexCapacityMax * bytesPerVertex);
this.spritesCapacity = (vertexCapacityMax / 4) | 0; // 4 vertices per sprite
this.floatsPerSprite = bytesPerVertex | 0; // bytesPerVertex * 4 / sizeof(float)
const vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
throw new Error(`Failed to allocate vertex buffer`);
}
this.vertexBuffer = vertexBuffer;
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW);
this.indexBuffer = indexBuffer;
this.vao = createVAO(gl, createVAOAttributes(gl, attributes, vertexBuffer), indexBuffer);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
}
dispose() {
disposeBuffers(this.gl, this);
@@ -95,14 +104,13 @@ export abstract class BaseSpriteBatch extends BaseStateBatch implements SpriteBa
const batchSpriteCount = (batch.length / this.floatsPerSprite) | 0;
if (this.capacity < (this.spritesCount + batchSpriteCount)) {
if (this.spritesCapacity < (this.spritesCount + batchSpriteCount)) {
this.flush();
}
this.vertices.set(batch, this.index);
this.index += batch.length;
this.spritesCount += batchSpriteCount;
this.tris += batchSpriteCount * 2;
}
startBatch() {
if (this.batching) {
@@ -156,6 +164,7 @@ export abstract class BaseSpriteBatch extends BaseStateBatch implements SpriteBa
this.vao.draw(this.gl.TRIANGLES, this.startBatchSprites * 6, 0);
TIMING && timeEnd();
this.drawnTrisStats += this.startBatchSprites * 2;
this.spritesCount -= this.startBatchSprites;
this.index -= this.startBatchIndex;
this.vertices.copyWithin(0, this.startBatchIndex, this.startBatchIndex + this.index);
@@ -171,6 +180,7 @@ export abstract class BaseSpriteBatch extends BaseStateBatch implements SpriteBa
this.vao.draw(this.gl.TRIANGLES, this.spritesCount * 6, 0);
TIMING && timeEnd();
this.drawnTrisStats += this.spritesCount * 2;
this.spritesCount = 0;
this.index = 0;
}
@@ -184,19 +194,13 @@ function disposeBuffers(gl: WebGLRenderingContext, batch: BaseSpriteBatch) {
if (batch.vao) {
batch.vao.dispose();
}
if (batch.vertexBuffer) {
gl.deleteBuffer(batch.vertexBuffer);
}
if (batch.indexBuffer) {
gl.deleteBuffer(batch.indexBuffer);
}
} catch (e) {
DEVELOPMENT && console.error(e);
}
batch.vao = undefined;
batch.vertexBuffer = undefined;
batch.indexBuffer = undefined;
}
+3
View File
@@ -23,6 +23,7 @@ export function drawCanvas(
}
export class ContextSpriteBatch extends BaseStateBatch implements PaletteSpriteBatch, SpriteBatch {
depth = 1;
pixelSize = 1;
disableShading = false;
ignoreColor = 0;
@@ -137,6 +138,8 @@ export class ContextSpriteBatch extends BaseStateBatch implements PaletteSpriteB
}
releaseBatch() {
}
flush() {
}
}
const min = Math.min;
+1 -1
View File
@@ -158,7 +158,7 @@ export class PaletteManager implements IPaletteManager {
private initializeTexture(gl: WebGLRenderingContext, size: number) {
try {
if (!this.paletteTexture) {
this.paletteTexture = createEmptyTexture(gl, size, size, gl.RGBA, gl.UNSIGNED_BYTE);
this.paletteTexture = createEmptyTexture(gl, true, size, size, gl.RGBA, gl.UNSIGNED_BYTE);
} else if (this.paletteTexture.width !== size) {
resizeTexture(gl, this.paletteTexture, size, size);
}
+56 -59
View File
@@ -59,10 +59,12 @@ function pushQuad(
*/
function pushQuad(
vertices: Float32Array, //_verticesUint32: Uint32Array,
vertices: Float32Array,
transform: Matrix2D, index: number, type: number, color: number, palette: Palette,
sx: number, sy: number, sw: number, sh: number, dx: number, dy: number, dw: number, dh: number,
sx: number, sy: number, sw: number, sh: number, dx: number, dy: number, dz: number, dw: number, dh: number,
) {
index |= 0;
const c1 = types[type];
const y2 = dy + dh;
@@ -83,63 +85,62 @@ function pushQuad(
const t4 = transform[4];
const t5 = transform[5];
// pushVertex(vertices, index, dx, dy, u1, v1, pu, pv, color, c1, transform);
vertices[(index + 0) | 0] = t0 * dx + t2 * dy + t4;
vertices[(index + 1) | 0] = t1 * dx + t3 * dy + t5;
vertices[(index + 2) | 0] = u1;
vertices[(index + 3) | 0] = v1;
vertices[(index + 4) | 0] = pu;
vertices[(index + 5) | 0] = pv;
vertices[(index + 6) | 0] = color;
vertices[(index + 7) | 0] = c1;
vertices[index++] = t0 * dx + t2 * dy + t4;
vertices[index++] = t1 * dx + t3 * dy + t5;
vertices[index++] = dz;
vertices[index++] = u1;
vertices[index++] = v1;
vertices[index++] = pu;
vertices[index++] = pv;
vertices[index++] = color;
vertices[index++] = c1;
// pushVertex(vertices, index + 8, x2, dy, u2, v1, pu, pv, color, c1, transform);
vertices[(index + 8) | 0] = t0 * x2 + t2 * dy + t4;
vertices[(index + 9) | 0] = t1 * x2 + t3 * dy + t5;
vertices[(index + 10) | 0] = u2;
vertices[(index + 11) | 0] = v1;
vertices[(index + 12) | 0] = pu;
vertices[(index + 13) | 0] = pv;
vertices[(index + 14) | 0] = color;
vertices[(index + 15) | 0] = c1;
vertices[index++] = t0 * x2 + t2 * dy + t4;
vertices[index++] = t1 * x2 + t3 * dy + t5;
vertices[index++] = dz;
vertices[index++] = u2;
vertices[index++] = v1;
vertices[index++] = pu;
vertices[index++] = pv;
vertices[index++] = color;
vertices[index++] = c1;
// pushVertex(vertices, index + 16, x2, y2, u2, v2, pu, pv, color, c1, transform);
vertices[(index + 16) | 0] = t0 * x2 + t2 * y2 + t4;
vertices[(index + 17) | 0] = t1 * x2 + t3 * y2 + t5;
vertices[(index + 18) | 0] = u2;
vertices[(index + 19) | 0] = v2;
vertices[(index + 20) | 0] = pu;
vertices[(index + 21) | 0] = pv;
vertices[(index + 22) | 0] = color;
vertices[(index + 23) | 0] = c1;
vertices[index++] = t0 * x2 + t2 * y2 + t4;
vertices[index++] = t1 * x2 + t3 * y2 + t5;
vertices[index++] = dz;
vertices[index++] = u2;
vertices[index++] = v2;
vertices[index++] = pu;
vertices[index++] = pv;
vertices[index++] = color;
vertices[index++] = c1;
// pushVertex(vertices, index + 24, dx, y2, u1, v2, pu, pv, color, c1, transform);
vertices[(index + 24) | 0] = t0 * dx + t2 * y2 + t4;
vertices[(index + 25) | 0] = t1 * dx + t3 * y2 + t5;
vertices[(index + 26) | 0] = u1;
vertices[(index + 27) | 0] = v2;
vertices[(index + 28) | 0] = pu;
vertices[(index + 29) | 0] = pv;
vertices[(index + 30) | 0] = color;
vertices[(index + 31) | 0] = c1;
vertices[index++] = t0 * dx + t2 * y2 + t4;
vertices[index++] = t1 * dx + t3 * y2 + t5;
vertices[index++] = dz;
vertices[index++] = u1;
vertices[index++] = v2;
vertices[index++] = pu;
vertices[index++] = pv;
vertices[index++] = color;
vertices[index++] = c1;
return index + 32;
return index;
}
// function colorWithAlpha(color: number, alpha: number) {
// return ((color & 0xffffff00) | (((color & 0xff) * alpha) & 0xff)) >>> 0;
// }
export const PALETTE_BATCH_BYTES_PER_VERTEX = 2 * 4 + 4 * 4 + 4 + 4;
export class PaletteSpriteBatch extends BaseSpriteBatch implements IPaletteSpriteBatch {
depth = 1;
palette = true;
defaultPalette: Palette = createPalette(new Uint32Array(0));
constructor(
gl: WebGLRenderingContext, capacity: number, buffer: ArrayBuffer, vertexBuffer: WebGLBuffer, indexBuffer: WebGLBuffer
gl: WebGLRenderingContext, vertexCapacityMax: number, indexBuffer: WebGLBuffer
) {
super(gl, capacity, buffer, vertexBuffer, indexBuffer, [
{ name: 'position', size: 2 },
super(gl, vertexCapacityMax, indexBuffer, [
{ name: 'position', size: 3 },
{ name: 'texcoord0', size: 4 }, //, type: gl.UNSIGNED_SHORT },
{ name: 'color', size: 4, type: gl.UNSIGNED_BYTE, normalized: true },
{ name: 'color1', size: 4, type: gl.UNSIGNED_BYTE, normalized: true },
@@ -149,37 +150,35 @@ export class PaletteSpriteBatch extends BaseSpriteBatch implements IPaletteSprit
type: number, color: number, palette: Palette | undefined,
sx: number, sy: number, sw: number, sh: number, dx: number, dy: number, dw: number, dh: number
) {
if (this.capacity <= this.spritesCount) {
if (this.spritesCapacity <= this.spritesCount) {
this.flush();
}
this.index = pushQuad(
this.vertices, //this.verticesUint32,
this.vertices,
this.transform, this.index, type, getColorFloat(color, this.globalAlpha),
palette || this.defaultPalette, sx, sy, sw, sh, dx, dy, dw, dh,
palette || this.defaultPalette, sx, sy, sw, sh, dx, dy, this.depth, dw, dh,
);
this.spritesCount++;
this.tris += 2;
}
drawRect(color: number, x: number, y: number, w: number, h: number) {
if (w !== 0 && h !== 0) {
if (this.capacity <= this.spritesCount) {
if (this.spritesCapacity <= this.spritesCount) {
this.flush();
}
const s = this.rectSprite || defaultRectSprite;
this.index = pushQuad(
this.vertices, //this.verticesUint32,
this.vertices,
this.transform, this.index, s.type, getColorFloat(color, this.globalAlpha),
this.defaultPalette, s.x, s.y, s.w, s.h, x, y, w, h,
this.defaultPalette, s.x, s.y, s.w, s.h, x, y, this.depth, w, h,
);
this.spritesCount++;
this.tris += 2;
}
}
drawSprite(s: Sprite, color: number, palette: Palette | undefined, x: number, y: number) {
if (s.w !== 0 && s.h !== 0) {
if (this.capacity <= this.spritesCount) {
if (this.spritesCapacity <= this.spritesCount) {
this.flush();
}
@@ -222,21 +221,19 @@ export class PaletteSpriteBatch extends BaseSpriteBatch implements IPaletteSprit
if (w > 0 && h > 0) {
this.index = pushQuad(
this.vertices, //this.verticesUint32,
this.vertices,
this.transform, this.index, s.type, getColorFloat(color, this.globalAlpha),
palette || this.defaultPalette, sx, sy, w, h, dx, dy, w, h,
palette || this.defaultPalette, sx, sy, w, h, dx, dy, this.depth, w, h,
);
this.spritesCount++;
this.tris += 2;
}
} else {
this.index = pushQuad(
this.vertices, //this.verticesUint32,
this.vertices,
this.transform, this.index, s.type, getColorFloat(color, this.globalAlpha),
palette || this.defaultPalette, s.x, s.y, s.w, s.h, x + s.ox, y + s.oy, s.w, s.h,
palette || this.defaultPalette, s.x, s.y, s.w, s.h, x + s.ox, y + s.oy, this.depth, s.w, s.h,
);
this.spritesCount++;
this.tris += 2;
}
}
}
+3 -2
View File
@@ -24,7 +24,8 @@ varying vec2 textureCoord;
varying vec4 vColor;
void main() {
float d = clamp((1.0 - length(textureCoord)) + 0.1, 0.0, 1.0);
float m = d * d * d;
float d = clamp(1.1 - length(textureCoord), 0.0, 1.0);
float m = d * d; // * d;
gl_FragColor = vec4(m, m, m, 1) * vColor;
}
+29
View File
@@ -0,0 +1,29 @@
// VERTEX
attribute vec2 position;
attribute vec2 texcoords;
uniform mat4 transform;
uniform vec2 textureSize;
varying vec2 textureCoord;
void main() {
textureCoord = texcoords / textureSize;
gl_Position = transform * vec4(position, 0, 1);
}
// FRAGMENT
precision mediump float;
uniform sampler2D sampler1;
uniform sampler2D sampler2;
varying vec2 textureCoord;
void main() {
vec3 color1 = texture2D(sampler1, textureCoord).rgb;
vec3 color2 = texture2D(sampler2, textureCoord).rgb;
gl_FragColor = vec4(color1 * color2, 1.0);
}
+17 -5
View File
@@ -1,26 +1,33 @@
// VERTEX
attribute vec2 position;
attribute vec3 position;
attribute vec4 texcoords;
attribute vec4 vertexColor;
attribute vec4 vertexColor1;
uniform mat4 transform;
uniform vec4 lighting;
uniform float textureSize;
varying vec4 textureCoord;
varying vec4 vColor;
varying vec4 vColor1;
void main() {
textureCoord = texcoords;
textureCoord = vec4(texcoords.xy * textureSize, texcoords.zw);
// float f = texcoords.z;
// float fr = fract(texcoords.z);
// textureCoord.z = fr;
// textureCoord.w = (f - fr) / 1024.0;
vColor = vertexColor * lighting;
vColor1 = vertexColor1;
gl_Position = transform * vec4(position, 0, 1);
float depth = position.z;
#ifdef DEPTH_BUFFERED
if (vColor.a < 0.975) {
depth = 0.0;
}
#endif
gl_Position = transform * vec4(position.xy, depth, 1);
}
// FRAGMENT
@@ -30,14 +37,13 @@ precision mediump float;
uniform sampler2D sampler1; // sprite
uniform sampler2D sampler2; // palette
uniform float pixelSize;
uniform float textureSize;
varying vec4 textureCoord;
varying vec4 vColor;
varying vec4 vColor1;
void main() {
vec4 sprite = texture2D(sampler1, textureCoord.xy / textureSize);
vec4 sprite = texture2D(sampler1, textureCoord.xy);
float shade = clamp(sprite.g + vColor1.a, 0.0, 1.0);
@@ -48,4 +54,10 @@ void main() {
vec4 palette = texture2D(sampler2, vec2(paletteCoord.x + paletteIndex * pixelSize, paletteCoord.y));
gl_FragColor = vec4(palette.xyz * shade, palette.w) * vColor;
#ifdef DEPTH_BUFFERED
if (gl_FragColor.a < 0.01) {
discard;
}
#endif
}
+9 -5
View File
@@ -1,19 +1,20 @@
// VERTEX
attribute vec2 position;
attribute vec3 position;
attribute vec2 texcoords;
attribute vec4 vertexColor;
uniform mat4 transform;
uniform vec4 lighting;
uniform vec2 textureSize;
varying vec2 textureCoord;
varying vec4 vColor;
void main() {
textureCoord = texcoords;
textureCoord = texcoords / textureSize;
vColor = vertexColor * lighting;
gl_Position = transform * vec4(position, 0, 1);
gl_Position = transform * vec4(position, 1);
}
// FRAGMENT
@@ -21,11 +22,14 @@ void main() {
precision mediump float;
uniform sampler2D sampler1;
uniform float textureSize;
varying vec2 textureCoord;
varying vec4 vColor;
void main() {
gl_FragColor = texture2D(sampler1, textureCoord / textureSize) * vColor;
gl_FragColor = texture2D(sampler1, textureCoord);
#ifdef USE_COLOR
gl_FragColor *= vColor;
#endif
}
+12 -20
View File
@@ -2,11 +2,12 @@ import { Sprite, SpriteBatch as ISpriteBatch, Matrix2D } from '../common/interfa
import { BaseSpriteBatch, getColorFloat } from './baseSpriteBatch';
function vertex(
vertices: Float32Array, _verticesUint32: Uint32Array, index: number,
x: number, y: number, u: number, v: number, c: number, transform: Matrix2D
vertices: Float32Array, index: number,
x: number, y: number, depth: number, u: number, v: number, c: number, transform: Matrix2D
) {
vertices[index++] = transform[0] * x + transform[2] * y + transform[4];
vertices[index++] = transform[1] * x + transform[3] * y + transform[5];
vertices[index++] = depth;
vertices[index++] = u;
vertices[index++] = v;
vertices[index++] = c;
@@ -18,16 +19,9 @@ function vertex(
export class SpriteBatch extends BaseSpriteBatch implements ISpriteBatch {
palette = false;
depth = 0;
constructor(
gl: WebGLRenderingContext,
capacity: number,
buffer: ArrayBuffer,
vertexBuffer: WebGLBuffer,
indexBuffer: WebGLBuffer
) {
super(gl, capacity, buffer, vertexBuffer, indexBuffer, [
{ name: 'position', size: 2 },
constructor(gl: WebGLRenderingContext, vertexCapacityMax: number, indexBuffer: WebGLBuffer) {
super(gl, vertexCapacityMax, indexBuffer, [
{ name: 'position', size: 3 },
{ name: 'texcoord0', size: 2 }, // , type: gl.UNSIGNED_SHORT },
{ name: 'color', size: 4, type: gl.UNSIGNED_BYTE, normalized: true },
]);
@@ -36,7 +30,7 @@ export class SpriteBatch extends BaseSpriteBatch implements ISpriteBatch {
color: number, sx: number, sy: number, sw: number, sh: number,
dx: number, dy: number, dw: number, dh: number
) {
if (this.capacity <= this.spritesCount) {
if (this.spritesCapacity <= this.spritesCount) {
this.flush();
}
@@ -51,19 +45,17 @@ export class SpriteBatch extends BaseSpriteBatch implements ISpriteBatch {
const v2 = sy + sh;
const vertices = this.vertices;
const verticesUint32 = this.verticesUint32;
const transform = this.transform;
const index = this.index;
vertex(vertices, verticesUint32, index, dx, dy, u1, v1, c, transform);
vertex(vertices, verticesUint32, index + 5, x2, dy, u2, v1, c, transform);
vertex(vertices, verticesUint32, index + 10, x2, y2, u2, v2, c, transform);
vertex(vertices, verticesUint32, index + 15, dx, y2, u1, v2, c, transform);
vertex(vertices, index, dx, dy, this.depth, u1, v1, c, transform);
vertex(vertices, index + 6, x2, dy, this.depth, u2, v1, c, transform);
vertex(vertices, index + 12, x2, y2, this.depth, u2, v2, c, transform);
vertex(vertices, index + 18, dx, y2, this.depth, u1, v2, c, transform);
this.index += 20;
this.index += 24;
this.spritesCount++;
this.tris += 2;
}
drawRect(color: number, x: number, y: number, w: number, h: number) {
if (w && h) {
+2 -1
View File
@@ -4,7 +4,8 @@ import { createTexture, disposeTexture } from './webgl/texture2d';
export function createTexturesForSpriteSheets(gl: WebGLRenderingContext, sheets: SpriteSheet[], texture = createTexture) {
sheets.forEach(sheet => {
if (sheet.data) {
sheet.texture = texture(gl, sheet.data);
let format = sheet.isSingleChannel ? gl.LUMINANCE : gl.RGBA;
sheet.texture = texture(gl, sheet.data, format);
}
});
}
+73 -17
View File
@@ -1,28 +1,50 @@
import { Texture2D, createEmptyTexture, resizeTexture } from './texture2d';
import { Texture2D, createEmptyTexture } from './texture2d';
export interface FrameBuffer {
handle: WebGLFramebuffer;
texture: Texture2D;
colorTexture: Texture2D;
depthStencilRenderbuffer: WebGLRenderbuffer | null;
width: number;
height: number;
owningDepthStencil: boolean;
}
export function createFrameBuffer(gl: WebGLRenderingContext, width: number, height: number): FrameBuffer {
export function createFrameBuffer(
gl: WebGLRenderingContext, target: FrameBuffer, width: number, height: number, useLinearMagnify: boolean,
createDepthStencil: boolean, depthStencilRenderbuffer: WebGLRenderbuffer | null
) {
const handle = gl.createFramebuffer();
if (!handle) {
throw new Error('Failed to create frame buffer');
}
const texture = createEmptyTexture(gl, width, height, gl.RGB);
return { handle, texture, width, height };
const resources = createFrameBufferResources(gl, width, height, createDepthStencil, useLinearMagnify);
target.handle = handle;
target.colorTexture = resources.colorTexture;
target.depthStencilRenderbuffer = resources.depthStencilRenderbuffer;
target.width = width;
target.height = height;
target.owningDepthStencil = createDepthStencil;
if (!createDepthStencil) {
target.depthStencilRenderbuffer = depthStencilRenderbuffer;
}
gl.bindFramebuffer(gl.FRAMEBUFFER, handle);
bindFrameBufferAttachments(gl, target);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
}
export function disposeFrameBuffer(gl: WebGLRenderingContext | undefined, buffer: FrameBuffer | undefined) {
export function disposeFrameBuffer(gl?: WebGLRenderingContext, buffer?: FrameBuffer) {
try {
if (gl && buffer) {
gl.deleteFramebuffer(buffer.handle);
gl.deleteTexture(buffer.texture.handle);
if (buffer.colorTexture) {
gl.deleteTexture(buffer.colorTexture.handle);
}
if (buffer.depthStencilRenderbuffer && buffer.owningDepthStencil) {
gl.deleteRenderbuffer(buffer.depthStencilRenderbuffer);
}
}
} catch (e) {
DEVELOPMENT && console.error(e);
@@ -31,18 +53,52 @@ export function disposeFrameBuffer(gl: WebGLRenderingContext | undefined, buffer
return undefined;
}
export function resizeFrameBuffer(gl: WebGLRenderingContext, frameBuffer: FrameBuffer, width: number, height: number) {
resizeTexture(gl, frameBuffer.texture, width, height);
frameBuffer.width = width;
frameBuffer.height = height;
}
export function bindFrameBuffer(gl: WebGLRenderingContext, { handle, texture }: FrameBuffer) {
export function bindFrameBuffer(gl: WebGLRenderingContext, { handle }: FrameBuffer) {
gl.bindFramebuffer(gl.FRAMEBUFFER, handle);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture.handle, 0);
}
export function unbindFrameBuffer(gl: WebGLRenderingContext) {
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, null, 0);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
}
function bindFrameBufferAttachments(gl: WebGLRenderingContext, { colorTexture, depthStencilRenderbuffer }: FrameBuffer) {
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, colorTexture.handle, 0);
if (depthStencilRenderbuffer) {
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthStencilRenderbuffer);
}
gl.depthMask(true);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.depthMask(false);
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) {
throw new Error('Failed to set framebuffer attachments');
}
}
function createFrameBufferResources(
gl: WebGLRenderingContext, width: number, height: number, createDepthBuffer: boolean, useLinearMagnify: boolean
) {
const colorTexture = createEmptyTexture(gl, false, width, height, gl.RGB);
if (!colorTexture) {
throw new Error('Failed to create frame buffer\'s color texture');
}
if (useLinearMagnify) {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
}
let depthStencilRenderbuffer: WebGLRenderbuffer | null = null;
if (createDepthBuffer) {
depthStencilRenderbuffer = gl.createRenderbuffer();
if (depthStencilRenderbuffer) {
gl.bindRenderbuffer(gl.RENDERBUFFER, depthStencilRenderbuffer);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height);
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
}
else {
console.warn('depth/stencil is not available');
}
}
return { colorTexture, depthStencilRenderbuffer };
}
+5 -3
View File
@@ -293,9 +293,11 @@ function throwFBOError(gl: WebGL, status: number, message = '') {
}
function initTexture(gl: WebGL, width: number, height: number, type: number, format: number, attachment: number) {
const texture = createEmptyTexture(gl, width, height, format, type);
gl.bindTexture(gl.TEXTURE_2D, texture.handle);
gl.framebufferTexture2D(gl.FRAMEBUFFER, attachment, gl.TEXTURE_2D, texture.handle, 0);
const texture = createEmptyTexture(gl, true, width, height, format, type);
if (texture) {
gl.bindTexture(gl.TEXTURE_2D, texture.handle);
gl.framebufferTexture2D(gl.FRAMEBUFFER, attachment, gl.TEXTURE_2D, texture.handle, 0);
}
return texture;
}
+1
View File
@@ -62,6 +62,7 @@ class VAONative implements VAO {
}
dispose() {
this.ext.deleteVertexArrayOES(this.handle);
this.handle = {};
}
update(attributes: VAOAttributes[], elements: WebGLBuffer | null, elementsType?: number) {
this.bind();
+86 -63
View File
@@ -1,85 +1,98 @@
export interface Shader {
export interface ShaderProgramData {
program: WebGLProgram;
vertexShader: WebGLShader;
fragmentShader: WebGLShader;
uniforms: { [key: string]: WebGLUniformLocation; };
}
export function createShader(gl: WebGLRenderingContext, source: string | { vertex: string; fragment: string; }): Shader {
if (typeof source === 'string') {
const index = source.indexOf('// FRAGMENT');
export class Shader {
private programs: { [key: string]: ShaderProgramData; } = {};
private vertexCode: string;
private fragmentCode: string;
if (index === -1) {
throw new Error(`Missing fragment shader separator`);
constructor(source: string | { vertex: string; fragment: string; }) {
if (typeof source === 'string') {
const index = source.indexOf('// FRAGMENT');
if (index === -1) {
throw new Error(`Missing fragment shader separator`);
}
source = {
vertex: source.substring(0, index),
fragment: source.substring(index),
};
}
source = {
vertex: source.substring(0, index),
fragment: source.substring(index),
};
this.vertexCode = source.vertex;
this.fragmentCode = source.fragment;
}
const vertexShader = createWebGLShader(gl, gl.VERTEX_SHADER, source.vertex);
const fragmentShader = createWebGLShader(gl, gl.FRAGMENT_SHADER, source.fragment);
const program = gl.createProgram();
compile(gl: WebGLRenderingContext, defines: string[]) {
defines.sort();
let definesString = defines.reduce((prev, cur) => prev + cur, '');
if (!program) {
throw new Error('Failed to create shader program');
}
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
const attribs = source.vertex.match(/^attribute [a-zA-Z0-9_]+ ([a-zA-Z0-9_]+)/mg)!;
for (var i = 0; i < attribs.length; ++i) {
const [, name] = /attribute [a-zA-Z0-9_]+ ([a-zA-Z0-9_]+)/.exec(attribs[i])!;
gl.bindAttribLocation(program, i, name);
}
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
throw new Error('Failed to link shader program');
}
gl.useProgram(program);
const uniforms: any = {};
const samplers: string[] = [];
for (let i = 0; i < gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); i++) {
const info = gl.getActiveUniform(program, i)!;
uniforms[info.name] = gl.getUniformLocation(program, info.name);
if (!uniforms[info.name]) {
throw new Error(`Failed to get uniform location (${info.name})`);
let data = this.programs[definesString];
if (!data) {
data = Shader.compileShader(gl, this.vertexCode, this.fragmentCode, defines);
this.programs[definesString] = data;
}
if (info.type === gl.SAMPLER_2D) {
samplers.push(info.name);
}
return data;
}
samplers.sort().forEach((name, i) => gl.uniform1i(uniforms[name], i));
gl.useProgram(null);
private static compileShader(gl: WebGLRenderingContext, vertexCode: string, fragmentCode: string, defines: string[]) {
let shaderDefines = defines.reduce((prev, cur) => prev + '#define ' + cur + '\n', '');
return { program, vertexShader, fragmentShader, uniforms };
}
const vertexShader = createWebGLShader(gl, gl.VERTEX_SHADER, shaderDefines + vertexCode);
const fragmentShader = createWebGLShader(gl, gl.FRAGMENT_SHADER, shaderDefines + fragmentCode);
const program = gl.createProgram();
export function disposeShader(gl: WebGLRenderingContext | undefined, shader: Shader | undefined) {
try {
if (gl && shader) {
gl.deleteProgram(shader.program);
gl.deleteShader(shader.vertexShader);
gl.deleteShader(shader.fragmentShader);
if (!program) {
throw new Error('Failed to create shader program');
}
} catch (e) {
DEVELOPMENT && console.error(e);
}
return undefined;
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
const attribs = vertexCode.match(/^attribute [a-zA-Z0-9_]+ ([a-zA-Z0-9_]+)/mg)!;
for (var i = 0; i < attribs.length; ++i) {
const [, name] = /attribute [a-zA-Z0-9_]+ ([a-zA-Z0-9_]+)/.exec(attribs[i])!;
gl.bindAttribLocation(program, i, name);
}
gl.linkProgram(program);
gl.deleteShader(vertexShader);
gl.deleteShader(fragmentShader);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
throw new Error('Failed to link shader program');
}
gl.useProgram(program);
const uniforms: any = {};
const samplers: string[] = [];
for (let i = 0; i < gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); i++) {
const info = gl.getActiveUniform(program, i)!;
uniforms[info.name] = gl.getUniformLocation(program, info.name);
if (!uniforms[info.name]) {
throw new Error(`Failed to get uniform location (${info.name})`);
}
if (info.type === gl.SAMPLER_2D) {
samplers.push(info.name);
}
}
samplers.sort().forEach((name, i) => gl.uniform1i(uniforms[name], i));
gl.useProgram(null);
return {program, uniforms};
}
}
function createWebGLShader(gl: WebGLRenderingContext, type: number, source: string) {
@@ -98,3 +111,13 @@ function createWebGLShader(gl: WebGLRenderingContext, type: number, source: stri
return shader;
}
export function disposeShaderProgramData(gl: WebGLRenderingContext, data: ShaderProgramData) {
try {
if (gl) {
gl.deleteProgram(data.program);
}
} catch (e) {
DEVELOPMENT && console.error(e);
}
}
+51 -8
View File
@@ -1,3 +1,5 @@
import { clearWebGLErrors, hasWebGLErrors, isWebGL2 } from './webglUtils';
type WebGL = WebGLRenderingContext;
type Pixels = ImageBitmap | ImageData | HTMLVideoElement | HTMLImageElement | HTMLCanvasElement;
@@ -7,9 +9,12 @@ export interface Texture2D {
height: number;
format: number;
type: number;
internalFormat: number;
}
export function createEmptyTexture(gl: WebGL, width: number, height: number, format?: number, type?: number): Texture2D {
export function createEmptyTexture(
gl: WebGL, isResizable: boolean, width: number, height: number, format?: number, type?: number, internalFormat?: number
) {
if (format === undefined) {
format = gl.RGBA;
}
@@ -18,6 +23,10 @@ export function createEmptyTexture(gl: WebGL, width: number, height: number, for
type = gl.UNSIGNED_BYTE;
}
if (internalFormat === undefined) {
internalFormat = format;
}
const maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE) as number | null;
if (maxTextureSize != null && (width < 0 || width > maxTextureSize || height < 0 || height > maxTextureSize)) {
@@ -28,12 +37,36 @@ export function createEmptyTexture(gl: WebGL, width: number, height: number, for
throw new Error('Floating point textures not supported on this platform');
}
clearWebGLErrors(gl);
const handle = createTextureHandle(gl);
gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, format, type, null);
return { handle, width, height, format, type };
if (!isResizable && isWebGL2(gl)) {
const gl2 = gl as WebGL2RenderingContext;
let format2: number;
if (internalFormat === gl.RGB) {
format2 = gl2.RGB8;
}
else if (internalFormat === gl.RGBA) {
format2 = gl2.RGBA8;
}
else {
throw new Error('Cannot convert internal format into WebGL2 format');
}
gl2.texStorage2D(gl.TEXTURE_2D, 1, format2, width, height);
}
else {
gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, null);
}
if (hasWebGLErrors(gl)) {
console.warn('createEmptyTexture failed due to a WebGL error');
return undefined;
}
return { handle, width, height, format, type, internalFormat };
}
export function createTexture(gl: WebGL, data: Pixels, format?: number, type?: number): Texture2D {
export function createTexture(
gl: WebGL, data: Pixels, format?: number, type?: number, internalFormat?: number
) {
if (format === undefined) {
format = gl.RGBA;
}
@@ -42,9 +75,19 @@ export function createTexture(gl: WebGL, data: Pixels, format?: number, type?: n
type = gl.UNSIGNED_BYTE;
}
if (internalFormat === undefined) {
internalFormat = format;
}
clearWebGLErrors(gl);
const handle = createTextureHandle(gl);
gl.texImage2D(gl.TEXTURE_2D, 0, format, format, type, data);
return { handle, width: data.width, height: data.height, format, type };
gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, format, type, data);
if (hasWebGLErrors(gl)) {
console.warn('createTexture failed due to a WebGL error');
return undefined;
}
return { handle, width: data.width, height: data.height, format, type, internalFormat };
}
export function disposeTexture(gl: WebGL | undefined, texture: Texture2D | undefined): undefined {
@@ -68,7 +111,7 @@ export function resizeTexture(gl: WebGL, texture: Texture2D, width: number, heig
width = width | 0;
height = height | 0;
const { format, type } = texture;
const { format, type, internalFormat } = texture;
const maxSize = gl.getParameter(gl.MAX_TEXTURE_SIZE) as number | null;
if (maxSize != null && (width < 0 || width > maxSize || height < 0 || height > maxSize)) {
@@ -78,7 +121,7 @@ export function resizeTexture(gl: WebGL, texture: Texture2D, width: number, heig
texture.width = width;
texture.height = height;
gl.bindTexture(gl.TEXTURE_2D, texture.handle);
gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, format, type, null);
gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, null);
}
function createTextureHandle(gl: WebGL) {
+26 -12
View File
@@ -1,24 +1,16 @@
import { makeDebugContext } from 'webgl-debug';
import { WEBGL_CREATION_ERROR } from '../../common/errors';
export function getRenderTargetSize(width: number, height: number) {
const max = Math.max(width, height);
let pow = 256;
while (pow < max) {
pow *= 2;
}
return pow;
}
export function getWebGLContext(canvas: HTMLCanvasElement): WebGLRenderingContext {
const options: WebGLContextAttributes = {
alpha: false,
preserveDrawingBuffer: false,
premultipliedAlpha: false,
antialias: false,
depth: false
};
const gl = canvas.getContext('webgl2', options)
let gl = canvas.getContext('webgl2', options)
|| canvas.getContext('webgl', options)
|| canvas.getContext('experimental-webgl', options);
@@ -26,6 +18,20 @@ export function getWebGLContext(canvas: HTMLCanvasElement): WebGLRenderingContex
throw new Error(WEBGL_CREATION_ERROR);
}
// debug context will check every GL call for errors and will
// emit a console message with a stack trace if there was an error,
// but it can slow execution down by more than 2x depending
// on the platform so it isn't on by default even in DEVELOPMENT
const useDebugContext = false;
if (useDebugContext) {
gl = makeDebugContext(gl);
if (!gl) {
throw new Error(WEBGL_CREATION_ERROR);
}
}
return gl;
}
@@ -65,3 +71,11 @@ export function unbindAllTexturesAndBuffers(gl: WebGLRenderingContext) {
DEVELOPMENT && console.error(e);
}
}
export function clearWebGLErrors(gl: WebGLRenderingContext) {
while (hasWebGLErrors(gl));
}
export function hasWebGLErrors(gl: WebGLRenderingContext) {
return gl.getError() !== gl.NO_ERROR;
}