diff --git a/src/gfx/apis/webgl2/WebGL2Device.ts b/src/gfx/apis/webgl2/WebGL2Device.ts index 0fe38e4..43c3302 100644 --- a/src/gfx/apis/webgl2/WebGL2Device.ts +++ b/src/gfx/apis/webgl2/WebGL2Device.ts @@ -2,20 +2,19 @@ import Device from '../../Device'; import ShaderRegistry from '../../ShaderRegistry'; import { ShaderType } from '../../Shader'; -// import constantsFor from './constants'; - +import constantsFor from './constants'; class WebGL2Device extends Device { gl: WebGL2RenderingContext; shaders: ShaderRegistry; + constants; constructor(canvas: HTMLCanvasElement) { super(); // TODO: Handle context loss this.gl = canvas.getContext('webgl2')!; - // TODO: Constants - // this.constants = constantsFor(this.gl); + this.constants = constantsFor(this.gl); this.shaders = new ShaderRegistry((type, name) => { const ext = type === 'pixel' ? 'frag' : 'vert'; diff --git a/src/gfx/apis/webgl2/constants.ts b/src/gfx/apis/webgl2/constants.ts index e99b565..b4faa19 100644 --- a/src/gfx/apis/webgl2/constants.ts +++ b/src/gfx/apis/webgl2/constants.ts @@ -5,98 +5,84 @@ import { PrimitiveType, } from '../../types'; -const cubeMapFaces = { - 0: 'TEXTURE_CUBE_MAP_POSITIVE_X', - 1: 'TEXTURE_CUBE_MAP_NEGATIVE_X', - 2: 'TEXTURE_CUBE_MAP_POSITIVE_Y', - 3: 'TEXTURE_CUBE_MAP_NEGATIVE_Y', - 4: 'TEXTURE_CUBE_MAP_POSITIVE_Z', - 5: 'TEXTURE_CUBE_MAP_NEGATIVE_Z', -} as const; +import { PickByType } from '../../../utils'; -const blendDestinations = { - [BlendMode.Opaque]: 'ZERO', - [BlendMode.AlphaKey]: 'ZERO', - [BlendMode.Alpha]: 'ONE_MINUS_SRC_ALPHA', - [BlendMode.Add]: 'ONE', - [BlendMode.Mod]: 'ZERO', - [BlendMode.Mod2x]: 'SRC_COLOR', - [BlendMode.ModAdd]: 'ONE', - [BlendMode.InvSrcAlphaAdd]: 'ONE', - [BlendMode.InvSrcAlphaOpaque]: 'ZERO', - [BlendMode.SrcAlphaOpaque]: 'ZERO', - [BlendMode.NoAlphaAdd]: 'ONE', - [BlendMode.ConstantAlpha]: 'ONE_MINUS_CONSTANT_ALPHA', -} as const; +export default (gl: WebGL2RenderingContext) => { + const resolve = (prop: keyof PickByType): number => { + const constant = gl[prop]; + if (constant === undefined) { + throw new Error(`Could not find WebGL2 constant: ${prop}`); + } + return constant; + }; -const blendSources = { - [BlendMode.Opaque]: 'ONE', - [BlendMode.AlphaKey]: 'ONE', - [BlendMode.Alpha]: 'SRC_ALPHA', - [BlendMode.Add]: 'SRC_ALPHA', - [BlendMode.Mod]: 'DST_COLOR', - [BlendMode.Mod2x]: 'DST_COLOR', - [BlendMode.ModAdd]: 'DST_COLOR', - [BlendMode.InvSrcAlphaAdd]: 'ONE_MINUS_SRC_ALPHA', - [BlendMode.InvSrcAlphaOpaque]: 'ONE_MINUS_SRC_ALPHA', - [BlendMode.SrcAlphaOpaque]: 'SRC_ALPHA', - [BlendMode.NoAlphaAdd]: 'ONE', - [BlendMode.ConstantAlpha]: 'CONSTANT_ALPHA', -} as const; + const constants = { + cubeMapFaces: { + 0: resolve('TEXTURE_CUBE_MAP_POSITIVE_X'), + 1: resolve('TEXTURE_CUBE_MAP_NEGATIVE_X'), + 2: resolve('TEXTURE_CUBE_MAP_POSITIVE_Y'), + 3: resolve('TEXTURE_CUBE_MAP_NEGATIVE_Y'), + 4: resolve('TEXTURE_CUBE_MAP_POSITIVE_Z'), + 5: resolve('TEXTURE_CUBE_MAP_NEGATIVE_Z'), + }, -// TODO: Texture format + blendDestinations: { + [BlendMode.Opaque]: resolve('ZERO'), + [BlendMode.AlphaKey]: resolve('ZERO'), + [BlendMode.Alpha]: resolve('ONE_MINUS_SRC_ALPHA'), + [BlendMode.Add]: resolve('ONE'), + [BlendMode.Mod]: resolve('ZERO'), + [BlendMode.Mod2x]: resolve('SRC_COLOR'), + [BlendMode.ModAdd]: resolve('ONE'), + [BlendMode.InvSrcAlphaAdd]: resolve('ONE'), + [BlendMode.InvSrcAlphaOpaque]: resolve('ZERO'), + [BlendMode.SrcAlphaOpaque]: resolve('ZERO'), + [BlendMode.NoAlphaAdd]: resolve('ONE'), + [BlendMode.ConstantAlpha]: resolve('ONE_MINUS_CONSTANT_ALPHA'), + }, -const bufferFormatByPoolTarget = { - [PoolTarget.Vertex]: 'ZERO', - [PoolTarget.Index]: 'UNSIGNED_SHORT', -} as const; + blendSources: { + [BlendMode.Opaque]: resolve('ONE'), + [BlendMode.AlphaKey]: resolve('ONE'), + [BlendMode.Alpha]: resolve('SRC_ALPHA'), + [BlendMode.Add]: resolve('SRC_ALPHA'), + [BlendMode.Mod]: resolve('DST_COLOR'), + [BlendMode.Mod2x]: resolve('DST_COLOR'), + [BlendMode.ModAdd]: resolve('DST_COLOR'), + [BlendMode.InvSrcAlphaAdd]: resolve('ONE_MINUS_SRC_ALPHA'), + [BlendMode.InvSrcAlphaOpaque]: resolve('ONE_MINUS_SRC_ALPHA'), + [BlendMode.SrcAlphaOpaque]: resolve('SRC_ALPHA'), + [BlendMode.NoAlphaAdd]: resolve('ONE'), + [BlendMode.ConstantAlpha]: resolve('CONSTANT_ALPHA'), + }, -const bufferTypeByPooltarget = { - [PoolTarget.Vertex]: 'ARRAY_BUFFER', - [PoolTarget.Index]: 'ELEMENT_ARRAY_BUFFER', -} as const; + // TODO: Texture format -const bufferUsageByPoolTarget = { - [PoolUsage.Static]: 'STATIC_DRAW', - [PoolUsage.Dynamic]: 'DYNAMIC_DRAW', - [PoolUsage.Stream]: 'DYNAMIC_DRAW', -} as const; + bufferFormatByPoolTarget: { + [PoolTarget.Vertex]: resolve('ZERO'), + [PoolTarget.Index]: resolve('UNSIGNED_SHORT'), + }, -const primitiveTypes = { - [PrimitiveType.Points]: 'POINTS', - [PrimitiveType.Lines]: 'LINES', - [PrimitiveType.LineStrip]: 'LINE_STRIP', - [PrimitiveType.Triangles]: 'TRIANGLES', - [PrimitiveType.TriangleStrip]: 'TRIANGLE_STRIP', - [PrimitiveType.TriangleFan]: 'TRIANGLE_FAN', -} as const; + bufferTypeByPooltarget: { + [PoolTarget.Vertex]: resolve('ARRAY_BUFFER'), + [PoolTarget.Index]: resolve('ELEMENT_ARRAY_BUFFER'), + }, -export default (gl: WebGL2RenderingContext) => { - const constants = {}; + bufferUsageByPoolTarget: { + [PoolUsage.Static]: resolve('STATIC_DRAW'), + [PoolUsage.Dynamic]: resolve('DYNAMIC_DRAW'), + [PoolUsage.Stream]: resolve('DYNAMIC_DRAW'), + }, - const categories = { - cubeMapFaces, - blendDestinations, - blendSources, - bufferFormatByPoolTarget, - bufferTypeByPooltarget, - bufferUsageByPoolTarget, - primitiveTypes, - } as const; - - for (const [name, category] of Object.entries(categories)) { - const entry = {}; - for (const [index, prop] of Object.entries(category)) { - const constant = gl[prop]; - if (constant === undefined) { - throw new Error(`Could not find WebGL2 constant: ${prop}`); - } - // @ts-expect-error: currently unused (and untyped) - entry[index] = constant; - } - // @ts-expect-error: currently unused (and untyped) - constants[name] = entry; - } + primitiveTypes: { + [PrimitiveType.Points]: resolve('POINTS'), + [PrimitiveType.Lines]: resolve('LINES'), + [PrimitiveType.LineStrip]: resolve('LINE_STRIP'), + [PrimitiveType.Triangles]: resolve('TRIANGLES'), + [PrimitiveType.TriangleStrip]: resolve('TRIANGLE_STRIP'), + [PrimitiveType.TriangleFan]: resolve('TRIANGLE_FAN'), + }, + }; return constants; }; diff --git a/src/gfx/types.ts b/src/gfx/types.ts index 354d2b3..62475b1 100644 --- a/src/gfx/types.ts +++ b/src/gfx/types.ts @@ -13,7 +13,6 @@ export enum BlendMode { SrcAlphaOpaque = 9, NoAlphaAdd = 10, ConstantAlpha = 11, - Last = 12, } export enum PoolHintBit { @@ -26,14 +25,12 @@ export enum PoolHintBit { export enum PoolTarget { Vertex = 0, Index = 1, - Last = 2, } export enum PoolUsage { Static = 0, Dynamic = 1, Stream = 2, - Last = 3, } export enum PrimitiveType { @@ -43,7 +40,6 @@ export enum PrimitiveType { Triangles = 3, TriangleStrip = 4, TriangleFan = 5, - Last = 6, } export enum RenderStateType { @@ -133,7 +129,6 @@ export enum RenderStateType { PointSprite = 83, Unk84 = 84, ColorMaterial = 85, - Last = 86, } export enum TextureFilter { @@ -192,5 +187,4 @@ export enum VertexBufferFormat { PT2 = 11, PBNT2 = 12, PNC2T2 = 13, - Last = 14, } diff --git a/src/ui/components/abstract/LayoutFrame.ts b/src/ui/components/abstract/LayoutFrame.ts index b497004..b36194b 100644 --- a/src/ui/components/abstract/LayoutFrame.ts +++ b/src/ui/components/abstract/LayoutFrame.ts @@ -14,6 +14,7 @@ import { NDCtoDDCHeight, NDCtoDDCWidth, Status, + enumRecordFor, extractDimensionsFrom, stringToBoolean, } from '../../../utils'; @@ -82,11 +83,7 @@ class LayoutFrame { this.resizeList = LinkedList.using('link'); this.resizeCounter = 0; - this.points = [ - null, null, null, - null, null, null, - null, null, null, - ]; + this.points = enumRecordFor(FramePointType, (_type) => null); } // Note: LayoutFrame is used as an auxiliary baseclass using the `multipleClasses` utility, so creating this diff --git a/src/ui/rendering/Renderer.ts b/src/ui/rendering/Renderer.ts index ecea507..2f7f5c6 100644 --- a/src/ui/rendering/Renderer.ts +++ b/src/ui/rendering/Renderer.ts @@ -22,7 +22,7 @@ class Renderer { draw(batch: RenderBatch) { const root = UIRoot.instance; - const { gl } = (Device.instance as WebGL2Device); + const { constants, gl } = (Device.instance as WebGL2Device); const { pixelShader, vertexShader } = this; if (!this.program) { @@ -49,6 +49,14 @@ class Renderer { for (const mesh of batch.meshes) { const { image } = mesh.texture; + // TODO: Is this correct? + if (mesh.blendMode) { + gl.enable(gl.BLEND); + gl.blendFunc(constants.blendSources[mesh.blendMode], constants.blendDestinations[mesh.blendMode]); + } else { + gl.disable(gl.BLEND); + } + const texture = gl.createTexture(); gl.activeTexture(gl.TEXTURE0 + 0); gl.bindTexture(gl.TEXTURE_2D, texture); diff --git a/src/utils/types.ts b/src/utils/types.ts index 0c27dc1..4a1dd7d 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -25,6 +25,11 @@ export function enumSizeFor(enumeration: EnumType) return enumValuesFor(enumeration).length; } +// See: https://stackoverflow.com/a/69756175 +export type PickByType = { + [P in keyof T as T[P] extends Value | undefined ? P : never]: T[P] +} + // See: https://github.com/microsoft/TypeScript/issues/5863#issuecomment-1336204919 export type ThisConstructor< T extends { prototype: unknown } = { prototype: unknown },