@bigmistqke/view.gl 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +2 -1
- package/.github/ISSUE_TEMPLATE/bug-report.yml +31 -0
- package/.github/ISSUE_TEMPLATE/feature-request.yml +27 -0
- package/.github/workflows/codeql.yml +46 -0
- package/.github/workflows/deploy-page.yml +38 -0
- package/.github/workflows/format.yml +40 -0
- package/README.md +92 -35
- package/dev/404.html +19 -0
- package/dev/dist/404.html +19 -0
- package/dev/dist/assets/bezier-DuLTb2d1.js +294 -0
- package/dev/dist/assets/game_of_life-D7qHdFcv.js +202 -0
- package/dev/dist/assets/grid-Ie22U_9K.js +173 -0
- package/dev/dist/assets/index-0qrXMlfv.js +109 -0
- package/dev/dist/assets/index-C2N3u8wj.css +12 -0
- package/dev/dist/assets/main-BZ1N6pp6.js +32 -0
- package/dev/dist/assets/overview-DAxtMIw5.js +78 -0
- package/dev/dist/assets/pix_sim-DIhzM_vu.js +501 -0
- package/dev/dist/assets/symbol-BouRL-Ni.js +65 -0
- package/dev/dist/assets/tag-C_bg62fD.js +516 -0
- package/dev/dist/assets/tetris-IMnkSi9p.js +344 -0
- package/dev/dist/assets/utils-CvCgsM08.js +50 -0
- package/dev/dist/index.html +51 -0
- package/dev/index.html +39 -10
- package/dev/{bezier.ts → src/examples/bezier.ts} +1 -1
- package/dev/{grid.ts → src/examples/grid.ts} +1 -1
- package/dev/{pix_sim.ts → src/examples/pix_sim.ts} +112 -97
- package/dev/src/examples/symbol.ts +79 -0
- package/dev/{tetris.ts → src/examples/tetris.ts} +1 -0
- package/dev/src/index.css +12 -0
- package/dev/src/index.ts +69 -0
- package/dev/{utils.ts → src/utils.ts} +8 -2
- package/dev/vite.config.ts +9 -1
- package/dist/index.d.mts +18 -0
- package/dist/index.mjs +324 -0
- package/dist/index.mjs.map +1 -0
- package/dist/tag.d.mts +97 -0
- package/dist/tag.mjs +162 -0
- package/dist/tag.mjs.map +1 -0
- package/dist/types-DoFAJoh-.d.mts +266 -0
- package/package.json +24 -10
- package/src/index.ts +39 -17
- package/src/tag.ts +47 -39
- package/src/types.ts +24 -24
- package/src/{utils.ts → utils/index.ts} +39 -14
- package/src/utils/object.ts +27 -0
- package/test/index.test.ts +1017 -0
- package/test/mocks/webgl.ts +248 -0
- package/test/setup.ts +13 -0
- package/test/tag.test.ts +790 -0
- package/vitest.config.ts +15 -0
- /package/dev/{game_of_life.ts → src/examples/game_of_life.ts} +0 -0
- /package/dev/{index.ts → src/examples/overview.ts} +0 -0
package/dist/tag.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/index.ts","../src/index.ts","../src/tag.ts"],"sourcesContent":["import type {\n AttributeKind,\n FramebufferOptions,\n GL,\n KIND_TO_SIZE_MAP,\n KIND_TO_UNIFORM_FN_NAME_MAP,\n TextureOptions,\n UniformKind,\n} from '../types'\n\nexport * as ObjectUtils from './object'\n\nexport function assertedNotNullish<T>(value: T, message?: string): NonNullable<T> {\n if (value === undefined || value === null) throw new Error(message)\n return value\n}\n\nfunction createGLShader(gl: GL, type: number, source: string): WebGLShader {\n const shader = gl.createShader(type)\n if (!shader) throw new Error('Failed to create shader')\n\n gl.shaderSource(shader, source)\n gl.compileShader(shader)\n\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n const info = gl.getShaderInfoLog(shader)\n gl.deleteShader(shader)\n throw new Error(\n `Failed to compile ${type === gl.VERTEX_SHADER ? 'vertex' : 'fragment'} shader: ${info}`,\n )\n }\n\n return shader\n}\n\nexport function createProgram(gl: GL, vertexSource: string, fragmentSource: string): WebGLProgram {\n const program = gl.createProgram()\n if (!program) throw new Error('Failed to create WebGL program')\n\n const vertexShader = createGLShader(gl, gl.VERTEX_SHADER, vertexSource)\n const fragmentShader = createGLShader(gl, gl.FRAGMENT_SHADER, fragmentSource)\n\n gl.attachShader(program, vertexShader)\n gl.attachShader(program, fragmentShader)\n gl.linkProgram(program)\n\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n const info = gl.getProgramInfoLog(program)\n gl.deleteProgram(program)\n gl.deleteShader(vertexShader)\n gl.deleteShader(fragmentShader)\n throw new Error(`Failed to link program: ${info}`)\n }\n\n // Clean up shaders after linking\n gl.deleteShader(vertexShader)\n gl.deleteShader(fragmentShader)\n\n return program\n}\n\n/**********************************************************************************/\n/* */\n/* Kind To Uniform Name */\n/* */\n/**********************************************************************************/\n\nexport const kindToUniformFnName = <T extends UniformKind>(\n kind: T,\n): KIND_TO_UNIFORM_FN_NAME_MAP[T] => {\n switch (kind[0]) {\n // mat\n case 'm':\n return 'Matrix' + kind.slice(3) + 'fv'\n // sampler/booleans/integer\n case 's':\n case 'b':\n case 'i':\n return (kind.match(/\\d/)?.[0] ?? '1') + 'i'\n // unsigned integers\n case 'u':\n return (kind[4] || '1') + 'ui'\n // vec\n case 'v':\n return kind[3] + 'f'\n default:\n switch (kind) {\n case 'float':\n return '1f'\n case 'uint':\n return '1ui'\n default:\n return '1i'\n }\n }\n}\n\n/**********************************************************************************/\n/* */\n/* Kind To Size */\n/* */\n/**********************************************************************************/\n\nexport const kindToSize = <T extends UniformKind>(kind: T): KIND_TO_SIZE_MAP[T] => {\n switch (kind[0]) {\n case 'm':\n const [a, b] = kind.match(/\\d+/g)!.map(Number) as [number, number?]\n return (a * (b ?? a)) as KIND_TO_SIZE_MAP[T]\n case 'v':\n case 'i':\n case 'b':\n case 'u':\n const match = kind.match(/\\d/) as [string] | undefined\n return (match ? +match[0] : 1) as KIND_TO_SIZE_MAP[T]\n default:\n return 1 as KIND_TO_SIZE_MAP[T]\n }\n}\n\n/**********************************************************************************/\n/* */\n/* Guards */\n/* */\n/**********************************************************************************/\n\nexport const isMatKind = <\n T extends UniformKind | AttributeKind,\n TPrefix extends string,\n TPostfix extends string,\n>(\n kind: T,\n): kind is T & `${TPrefix}mat${TPostfix}` => kind.includes('mat')\n\nexport const isSamplerKind = <TPrefix extends string, TPostfix extends string>(\n kind: UniformKind,\n): kind is UniformKind & `${TPrefix}sampler${TPostfix}` => kind.includes('sampler')\n\nexport const isVecKind = <TPrefix extends string, TPostfix extends string>(\n kind: UniformKind,\n): kind is UniformKind & `${TPrefix}vec${TPostfix}` => kind.includes('vec')\n\n/**********************************************************************************/\n/* */\n/* Create Texture */\n/* */\n/**********************************************************************************/\n\nexport function createTexture(\n gl: GL,\n {\n target = 'TEXTURE_2D',\n internalFormat = 'RGBA',\n format = 'RGBA',\n type = 'UNSIGNED_BYTE',\n minFilter = 'NEAREST',\n magFilter = 'NEAREST',\n wrapS = 'CLAMP_TO_EDGE',\n wrapT = 'CLAMP_TO_EDGE',\n width,\n height,\n }: TextureOptions,\n data?: ArrayBufferView | null,\n): WebGLTexture {\n const texture = gl.createTexture()\n\n function getTextureConstant(name: string) {\n if (!(name in gl)) {\n throw new Error(`Attempted to create webgl2-only texture (${name}) in webgl1`)\n }\n return gl[name]\n }\n\n gl.bindTexture(gl[target], texture)\n gl.texImage2D(\n gl[target],\n 0,\n getTextureConstant(internalFormat),\n width,\n height,\n 0,\n getTextureConstant(format),\n getTextureConstant(type),\n data ?? null,\n )\n\n console.log(minFilter)\n // Set texture parameters\n gl.texParameteri(gl[target], gl.TEXTURE_MIN_FILTER, gl[minFilter])\n gl.texParameteri(gl[target], gl.TEXTURE_MAG_FILTER, gl[magFilter])\n gl.texParameteri(gl[target], gl.TEXTURE_WRAP_S, gl[wrapS])\n gl.texParameteri(gl[target], gl.TEXTURE_WRAP_T, gl[wrapT])\n\n return texture\n}\n\n/**********************************************************************************/\n/* */\n/* Create Framebuffer */\n/* */\n/**********************************************************************************/\n\nconst FRAMEBUFFER_ATTACHMENT_MAP = {\n color: 'COLOR_ATTACHMENT0',\n depth: 'DEPTH_ATTACHMENT',\n stencil: 'STENCIL_ATTACHMENT',\n depthStencil: 'DEPTH_STENCIL_ATTACHMENT',\n} as const\n\nclass FramebufferError extends Error {\n constructor(gl: GL, status: number) {\n let errorMessage = `Framebuffer '${name}' not complete. Status: `\n switch (status) {\n case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT:\n errorMessage += 'FRAMEBUFFER_INCOMPLETE_ATTACHMENT'\n break\n case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:\n errorMessage += 'FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT'\n break\n case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS:\n errorMessage += 'FRAMEBUFFER_INCOMPLETE_DIMENSIONS'\n break\n case gl.FRAMEBUFFER_UNSUPPORTED:\n errorMessage += 'FRAMEBUFFER_UNSUPPORTED'\n break\n default:\n errorMessage += `Unknown (${status})`\n }\n super(errorMessage)\n }\n}\n\nexport function createFramebuffer(gl: GL, { attachment, texture, ...options }: FramebufferOptions) {\n // Create texture for the framebuffer\n texture ??= createTexture(gl, options)\n\n // Create framebuffer\n const framebuffer = assertedNotNullish(\n gl.createFramebuffer(),\n `Failed to create framebuffer: ${name}`,\n )\n\n gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer)\n\n gl.framebufferTexture2D(\n gl.FRAMEBUFFER,\n // Determine attachment point based on attachment kind\n gl[FRAMEBUFFER_ATTACHMENT_MAP[attachment]],\n gl.TEXTURE_2D,\n texture,\n 0,\n )\n\n // Check framebuffer completeness\n const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER)\n if (status !== gl.FRAMEBUFFER_COMPLETE) {\n throw new FramebufferError(gl, status)\n }\n\n return {\n texture,\n framebuffer,\n }\n}\n\n/**********************************************************************************/\n/* */\n/* Create Upsert */\n/* */\n/**********************************************************************************/\n\ntype UpsertMapKind<T, U> = Map<T, U> | WeakMap<T, U>\n\nexport function createUpsertMap<T extends UpsertMapKind<any, any> = Map<any, any>>(\n constructor?: new () => T,\n) {\n const map = new (constructor ?? Map)() as T\n return Object.assign(map, {\n getOrInsert(\n key: T extends UpsertMapKind<infer U, any> ? U : never,\n value: T extends UpsertMapKind<any, infer U> ? () => U : never,\n ) {\n let result = map.get(key)\n if (result) {\n return result\n }\n result = value()\n map.set(key, result)\n return result\n },\n })\n}\n","import { getInstancedArrays, getVertexArrayObject } from './features'\nimport type {\n AttributeMethods,\n AttributeSchema,\n AttributeView,\n BufferSchema,\n BufferView,\n GL,\n UniformView as InferUniformView,\n InterleavedAttributeMethods,\n InterleavedAttributeSchema,\n InterleavedAttributeView,\n UniformSchema,\n View,\n ViewOptions,\n ViewSchema,\n} from './types'\nimport {\n assertedNotNullish,\n createUpsertMap,\n isMatKind,\n isSamplerKind,\n kindToSize,\n kindToUniformFnName,\n ObjectUtils,\n} from './utils'\n\n/**********************************************************************************/\n/* */\n/* To ID */\n/* */\n/**********************************************************************************/\n\nlet index = 0\nconst PREFIX = 'VIEW_GL_ALIAS'\n\nconst SYMBOL_MAP = createUpsertMap(WeakMap<symbol, string>)\n\nexport function toID(key: string | number | symbol) {\n if (typeof key === 'string') {\n return key\n }\n if (typeof key === 'symbol') {\n return SYMBOL_MAP.getOrInsert(key, () => `${PREFIX}_${index++}`)\n }\n return key.toString()\n}\n\n/**********************************************************************************/\n/* */\n/* View */\n/* */\n/**********************************************************************************/\n\nexport function view<T extends ViewSchema>(\n gl: GL,\n program: WebGLProgram,\n schema: T,\n options?: ViewOptions,\n): View<T> {\n return {\n uniforms: !schema.uniforms ? undefined : uniformView(gl, program, schema.uniforms),\n attributes: !schema.attributes\n ? undefined\n : attributeView(gl, program, schema.attributes, options),\n interleavedAttributes: !schema.interleavedAttributes\n ? undefined\n : interleavedAttributeView(gl, program, schema.interleavedAttributes, options),\n buffers: !schema.buffers ? undefined : bufferView(gl, schema.buffers),\n } as View<T>\n}\n\n/**********************************************************************************/\n/* */\n/* Uniform View */\n/* */\n/**********************************************************************************/\n\nexport function uniformView<T extends UniformSchema>(\n gl: GL,\n program: WebGLProgram,\n schema: T,\n): InferUniformView<T> {\n return ObjectUtils.map(schema, ({ kind, size }, key) => {\n const name = toID(key)\n\n const location = gl.getUniformLocation(program, name)\n\n if (isSamplerKind(kind)) {\n return {\n set(arg: number) {\n gl.uniform1i(location, arg)\n },\n }\n }\n\n const fnName = `uniform${kindToUniformFnName(kind)}${size ? 'v' : ''}`\n\n // @ts-ignore FIX WEBGL/WEBGL2 TYPES\n const fn = gl[fnName].bind(gl)\n\n if (isMatKind(kind)) {\n return {\n set(...args: any[]) {\n fn(location, false, args[0])\n },\n }\n }\n\n return {\n set(...args: any[]) {\n fn(location, ...args)\n },\n }\n })\n}\n\n/**********************************************************************************/\n/* */\n/* Attribute View */\n/* */\n/**********************************************************************************/\n\n// Shared attribute helper functions\n// between attributeView and interleavedAttributeView\nfunction handleAttribute(\n gl: GL,\n location: number,\n size: number,\n stride: number,\n offset: number,\n type: 'FLOAT' | 'INT',\n instanced?: boolean,\n) {\n gl.enableVertexAttribArray(location)\n gl.vertexAttribPointer(location, size, gl[type], false, stride, offset)\n\n if (instanced) {\n // Get instanced-arrays-feature: extension if webgl, gl if webgl2\n assertedNotNullish(getInstancedArrays(gl)).vertexAttribDivisor(location, 1)\n }\n}\n\nexport function attributeView<T extends AttributeSchema>(\n gl: GL,\n program: WebGLProgram,\n schema: T,\n { signal }: ViewOptions = {},\n): AttributeView<T> {\n const attributes = ObjectUtils.map(\n schema,\n (\n { kind, instanced, buffer = assertedNotNullish(gl.createBuffer()) },\n key,\n ): AttributeMethods => {\n const name = toID(key)\n\n const location = gl.getAttribLocation(program, name)\n if (location < 0) {\n throw new Error(`Attribute '${name}' not found`)\n }\n\n const size = kindToSize(kind)\n const type = kind.startsWith('i') ? 'INT' : 'FLOAT'\n\n return {\n buffer,\n bind() {\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n handleAttribute(gl, location, size, 0, 0, type, instanced)\n },\n dispose() {\n gl.deleteBuffer(buffer)\n },\n set(data, usage = 'STATIC_DRAW') {\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n gl.bufferData(gl.ARRAY_BUFFER, data, gl[usage])\n return this\n },\n }\n },\n )\n\n signal?.addEventListener('abort', function dispose() {\n ObjectUtils.forEach(attributes, value => value.dispose())\n })\n\n return attributes\n}\n\n/**********************************************************************************/\n/* */\n/* Interleaved Attribute View */\n/* */\n/**********************************************************************************/\n\nexport function interleavedAttributeView<T extends InterleavedAttributeSchema>(\n gl: GL,\n program: WebGLProgram,\n schema: T,\n { signal }: ViewOptions = {},\n): InterleavedAttributeView<T> {\n // Initialize interleaved attributes\n const interleavedAttributes = ObjectUtils.map(schema, ({ layout, instanced }) => {\n // Increment number to keep track of offset\n let index = 0\n\n // Calculate layout information\n const handles = layout.map(layout => {\n const name = toID(layout.key)\n\n const location = gl.getAttribLocation(program, name)\n\n if (location < 0) {\n throw new Error(`Attribute '${name}' not found`)\n }\n\n const size = kindToSize(layout.kind)\n const offset = index\n const type = layout.kind.startsWith('i') ? 'INT' : 'FLOAT'\n index += size * 4\n\n return () => handleAttribute(gl, location, size, stride, offset, type, instanced)\n })\n\n // Set stride to final index\n const stride = index\n\n // Create a buffer\n const buffer = assertedNotNullish(gl.createBuffer())\n\n // Create VAO to cache attribute state\n let vao: { unbind(): void; bind(): void; dispose(): void } | undefined = undefined\n\n // Get VAO-feature: extension if webgl1, gl if webgl2\n const feature = getVertexArrayObject(gl)\n if (feature) {\n const vertexArray = feature.createVertexArray()\n feature.bindVertexArray(vertexArray)\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n for (const handle of handles) {\n handle()\n }\n feature.bindVertexArray(null)\n vao = {\n unbind() {\n feature.bindVertexArray(null)\n },\n bind() {\n feature.bindVertexArray(vertexArray)\n },\n dispose() {\n feature.deleteVertexArray(vertexArray)\n },\n }\n }\n\n return {\n bind() {\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n if (vao) {\n vao.bind()\n } else {\n // Fallback: manual attribute setup\n for (const handle of handles) {\n handle()\n }\n }\n },\n unbind() {\n if (vao) {\n vao.unbind()\n }\n },\n dispose() {\n gl.deleteBuffer(buffer)\n if (vao) {\n vao.dispose()\n }\n },\n set(value, usage = 'STATIC_DRAW') {\n if (vao) {\n vao.bind()\n } else {\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n }\n gl.bufferData(gl.ARRAY_BUFFER, value, gl[usage])\n if (vao) {\n vao.unbind()\n }\n },\n } satisfies InterleavedAttributeMethods\n })\n\n signal?.addEventListener('abort', function dispose() {\n ObjectUtils.forEach(interleavedAttributes, value => value.dispose())\n })\n\n return interleavedAttributes\n}\n\n/**********************************************************************************/\n/* */\n/* Buffer View */\n/* */\n/**********************************************************************************/\n\nexport function bufferView<T extends BufferSchema>(\n gl: GL,\n schema: T,\n { signal }: ViewOptions = {},\n): BufferView<T> {\n // Initialize buffers\n const buffers = ObjectUtils.map(schema, ({ target = 'ARRAY_BUFFER', usage = 'STATIC_DRAW' }) => {\n const buffer = assertedNotNullish(gl.createBuffer())\n return {\n bind() {\n gl.bindBuffer(gl[target], buffer)\n },\n dispose() {\n gl.deleteBuffer(buffer)\n },\n set(data: Float32Array) {\n gl.bindBuffer(gl[target], buffer)\n gl.bufferData(gl[target], data, gl[usage])\n },\n }\n })\n\n signal?.addEventListener('abort', function dispose() {\n ObjectUtils.forEach(buffers, value => value.dispose())\n })\n\n return buffers\n}\n","import { toID } from '.'\nimport type {\n AttributeKind,\n AttributeOptions,\n AttributeTag,\n AttributeTagFn,\n GLSL,\n InterleaveTag,\n Merge,\n Prettify,\n UniformKind,\n UniformOptions,\n UniformTag,\n} from './types'\nimport { createProgram } from './utils'\n\n/**********************************************************************************/\n/* */\n/* Tag Utilities */\n/* */\n/**********************************************************************************/\n\ninterface UniformTagFn<TKind extends UniformKind> {\n <const TKey extends string | symbol, const TOptions extends Omit<UniformOptions, 'kind'>>(\n key: TKey,\n TOptions: TOptions,\n ): Prettify<UniformTag<TKey, TKind, TOptions>>\n <const TName extends string | symbol>(key: TName): Prettify<UniformTag<TName, TKind>>\n}\n\nexport const uniform = new Proxy(\n {} as {\n [TKey in UniformKind]: UniformTagFn<TKey>\n },\n {\n get(target, property) {\n // @ts-expect-error\n if (typeof property === 'symbol') return target[property]\n return (key: string, options?: Omit<UniformOptions, 'kind'>) => ({\n type: 'uniform',\n key,\n kind: property,\n ...options,\n })\n },\n },\n)\n\nexport const attribute = new Proxy(\n {} as {\n [TKey in AttributeKind]: AttributeTagFn<TKey>\n },\n {\n get(target, property) {\n // @ts-expect-error\n if (typeof property === 'symbol') return target[property]\n return (key: string, options?: Omit<AttributeOptions, 'kind'>) => ({\n type: 'attribute',\n key,\n kind: property,\n ...options,\n })\n },\n },\n)\n\nexport function interleave<\n const TKey extends string | symbol,\n const TLayout extends Array<AttributeTag<string | symbol, AttributeKind, undefined>>,\n const TOptions extends Omit<AttributeOptions, 'kind'>,\n>(key: TKey, layout: TLayout, instanced: TOptions): InterleaveTag<TKey, TLayout, TOptions>\nexport function interleave<\n const TKey extends string | symbol,\n const TLayout extends Array<AttributeTag<string | symbol, AttributeKind, undefined>>,\n>(key: TKey, layout: TLayout): InterleaveTag<TKey, TLayout, { instanced: false }>\nexport function interleave<\n const TKey extends string | symbol,\n const TLayout extends Array<AttributeTag<string | symbol, AttributeKind, undefined>>,\n const TOptions extends Omit<AttributeOptions, 'kind'>,\n>(key: TKey, layout: TLayout, { instanced, buffer }: Partial<TOptions> = {}) {\n return {\n type: 'interleavedAttribute',\n key,\n // remove instanced- and type-property\n layout: layout.map(({ key, kind }) => ({\n key,\n kind,\n })),\n instanced: !!instanced,\n buffer,\n } as unknown as InterleaveTag<TKey, TLayout, TOptions>\n}\n\n/**********************************************************************************/\n/* */\n/* GLSL */\n/* */\n/**********************************************************************************/\n\nexport function glsl<\n Elem extends string,\n Template extends ReadonlyArray<Elem>,\n Hole extends UniformTag | AttributeTag | InterleaveTag | string | number | symbol,\n Holes extends Hole[],\n>([initial, ...rest]: Template, ...holes: [...Holes]) {\n const v300 = initial?.startsWith('#version 300 es')\n\n const template = rest.reduce((out, templatePart, index) => {\n const hole = holes[index]\n const holePart =\n typeof hole === 'string' || typeof hole === 'number' || typeof hole === 'symbol'\n ? toID(hole)\n : hole.type === 'interleavedAttribute'\n ? hole.layout.reduce(\n (a, v) =>\n v300\n ? `${a}in ${v.kind} ${toID(v.key)};\\n`\n : `${a}attribute ${v.kind} ${toID(v.key)};\\n`,\n '',\n )\n : typeof hole === 'object' && hole.type === 'uniform' && 'size' in hole\n ? `${hole.type} ${hole.kind} ${toID(hole.key)}[${hole.size}];`\n : `${hole.type === 'attribute' && v300 ? 'in' : hole.type} ${hole.kind} ${toID(hole.key)};`\n return out + holePart + templatePart\n }, initial || '')\n\n return holes.reduce(\n (resources, resource) => {\n if (\n typeof resource === 'string' ||\n typeof resource === 'number' ||\n typeof resource === 'symbol'\n ) {\n return resources\n }\n // @ts-expect-error\n const { key: name, type, ...rest } = resource\n // @ts-expect-error\n resources[`${type}s`][name] = rest\n return resources\n },\n {\n template,\n uniforms: {},\n attributes: {},\n interleavedAttributes: {},\n } as {\n template: string\n uniforms: {\n [K in Extract<Holes[number], UniformTag> as K['key']]: Prettify<Omit<K, 'name' | 'type'>>\n }\n attributes: {\n [K in Extract<Holes[number], AttributeTag> as K['key']]: Prettify<Omit<K, 'name' | 'type'>>\n }\n interleavedAttributes: {\n [K in Extract<Holes[number], InterleaveTag> as K['key']]: Prettify<Omit<K, 'name' | 'type'>>\n }\n },\n )\n}\n\n/**********************************************************************************/\n/* */\n/* Compile */\n/* */\n/**********************************************************************************/\n\nexport function compile<TVertex extends GLSL, TFragment extends GLSL>(\n gl: WebGLRenderingContext,\n vertex: TVertex,\n fragment: TFragment,\n) {\n const program = createProgram(gl, vertex.template, fragment.template)\n\n return {\n program,\n schema: {\n uniforms: {\n ...vertex.uniforms,\n ...fragment.uniforms,\n },\n attributes: {\n ...vertex.attributes,\n ...fragment.attributes,\n },\n interleavedAttributes: {\n ...vertex.interleavedAttributes,\n ...fragment.interleavedAttributes,\n },\n },\n } as {\n program: WebGLProgram\n schema: {\n uniforms: Prettify<Merge<TVertex['uniforms'], TFragment['uniforms']>>\n attributes: Prettify<Merge<TVertex['attributes'], TFragment['attributes']>>\n interleavedAttributes: Prettify<\n Merge<TVertex['interleavedAttributes'], TFragment['interleavedAttributes']>\n >\n }\n }\n}\n"],"mappings":"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","names":["key","index","name","rest"]}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
type Merge<A, B> = {
|
|
2
|
+
[K in keyof A | keyof B]: K extends keyof A ? K extends keyof B ? A[K] | B[K] : A[K] : K extends keyof B ? B[K] : never;
|
|
3
|
+
};
|
|
4
|
+
type Prettify<T> = {
|
|
5
|
+
[K in keyof T]: T[K];
|
|
6
|
+
} & {};
|
|
7
|
+
type GL = WebGLRenderingContext | WebGL2RenderingContext;
|
|
8
|
+
/**********************************************************************************/
|
|
9
|
+
/**********************************************************************************/
|
|
10
|
+
type GLTarget = 'ARRAY_BUFFER' | 'ELEMENT_ARRAY_BUFFER';
|
|
11
|
+
type GLUsage = 'STATIC_DRAW' | 'DYNAMIC_DRAW' | 'STREAM_DRAW';
|
|
12
|
+
type UniformKind = 'float' | 'vec2' | 'vec3' | 'vec4' | 'int' | 'ivec2' | 'ivec3' | 'ivec4' | 'bool' | 'bvec2' | 'bvec3' | 'bvec4' | 'mat2' | 'mat3' | 'mat4' | 'sampler2D' | 'samplerCube' | 'uint' | 'uvec2' | 'uvec3' | 'uvec4' | 'mat2x3' | 'mat2x4' | 'mat3x2' | 'mat3x4' | 'mat4x2' | 'mat4x3' | 'sampler3D' | 'sampler2DArray' | 'sampler2DShadow' | 'samplerCubeShadow' | 'sampler2DArrayShadow' | 'isampler2D' | 'isampler3D' | 'isamplerCube' | 'isampler2DArray' | 'usampler2D' | 'usampler3D' | 'usamplerCube' | 'usampler2DArray';
|
|
13
|
+
interface UniformKindMap {
|
|
14
|
+
float: [number];
|
|
15
|
+
vec2: [number, number];
|
|
16
|
+
vec3: [number, number, number];
|
|
17
|
+
vec4: [number, number, number, number];
|
|
18
|
+
int: [number];
|
|
19
|
+
ivec2: [number, number];
|
|
20
|
+
ivec3: [number, number, number];
|
|
21
|
+
ivec4: [number, number, number, number];
|
|
22
|
+
bool: [number];
|
|
23
|
+
bvec2: [number, number];
|
|
24
|
+
bvec3: [number, number, number];
|
|
25
|
+
bvec4: [number, number, number, number];
|
|
26
|
+
uint: [number];
|
|
27
|
+
uvec2: [number, number];
|
|
28
|
+
uvec3: [number, number, number];
|
|
29
|
+
uvec4: [number, number, number, number];
|
|
30
|
+
mat2: [Float32Array] | [
|
|
31
|
+
number,
|
|
32
|
+
number,
|
|
33
|
+
number,
|
|
34
|
+
number
|
|
35
|
+
];
|
|
36
|
+
mat3: [Float32Array] | [
|
|
37
|
+
number,
|
|
38
|
+
number,
|
|
39
|
+
number,
|
|
40
|
+
number,
|
|
41
|
+
number,
|
|
42
|
+
number,
|
|
43
|
+
number,
|
|
44
|
+
number,
|
|
45
|
+
number
|
|
46
|
+
];
|
|
47
|
+
mat4: [Float32Array] | [
|
|
48
|
+
number,
|
|
49
|
+
number,
|
|
50
|
+
number,
|
|
51
|
+
number,
|
|
52
|
+
number,
|
|
53
|
+
number,
|
|
54
|
+
number,
|
|
55
|
+
number,
|
|
56
|
+
number,
|
|
57
|
+
number,
|
|
58
|
+
number,
|
|
59
|
+
number,
|
|
60
|
+
number,
|
|
61
|
+
number,
|
|
62
|
+
number,
|
|
63
|
+
number
|
|
64
|
+
];
|
|
65
|
+
mat2x3: [Float32Array] | [
|
|
66
|
+
number,
|
|
67
|
+
number,
|
|
68
|
+
number,
|
|
69
|
+
number,
|
|
70
|
+
number,
|
|
71
|
+
number
|
|
72
|
+
];
|
|
73
|
+
mat2x4: [Float32Array] | [
|
|
74
|
+
number,
|
|
75
|
+
number,
|
|
76
|
+
number,
|
|
77
|
+
number,
|
|
78
|
+
number,
|
|
79
|
+
number,
|
|
80
|
+
number,
|
|
81
|
+
number
|
|
82
|
+
];
|
|
83
|
+
mat3x2: [Float32Array] | [
|
|
84
|
+
number,
|
|
85
|
+
number,
|
|
86
|
+
number,
|
|
87
|
+
number,
|
|
88
|
+
number,
|
|
89
|
+
number
|
|
90
|
+
];
|
|
91
|
+
mat3x4: [Float32Array] | [
|
|
92
|
+
number,
|
|
93
|
+
number,
|
|
94
|
+
number,
|
|
95
|
+
number,
|
|
96
|
+
number,
|
|
97
|
+
number,
|
|
98
|
+
number,
|
|
99
|
+
number,
|
|
100
|
+
number,
|
|
101
|
+
number,
|
|
102
|
+
number,
|
|
103
|
+
number
|
|
104
|
+
];
|
|
105
|
+
mat4x2: [Float32Array] | [
|
|
106
|
+
number,
|
|
107
|
+
number,
|
|
108
|
+
number,
|
|
109
|
+
number,
|
|
110
|
+
number,
|
|
111
|
+
number,
|
|
112
|
+
number,
|
|
113
|
+
number
|
|
114
|
+
];
|
|
115
|
+
mat4x3: [Float32Array] | [
|
|
116
|
+
number,
|
|
117
|
+
number,
|
|
118
|
+
number,
|
|
119
|
+
number,
|
|
120
|
+
number,
|
|
121
|
+
number,
|
|
122
|
+
number,
|
|
123
|
+
number,
|
|
124
|
+
number,
|
|
125
|
+
number,
|
|
126
|
+
number,
|
|
127
|
+
number
|
|
128
|
+
];
|
|
129
|
+
sampler2D: [number];
|
|
130
|
+
samplerCube: [number];
|
|
131
|
+
sampler3D: [number];
|
|
132
|
+
sampler2DArray: [number];
|
|
133
|
+
sampler2DShadow: [number];
|
|
134
|
+
samplerCubeShadow: [number];
|
|
135
|
+
sampler2DArrayShadow: [number];
|
|
136
|
+
isampler2D: [number];
|
|
137
|
+
isampler3D: [number];
|
|
138
|
+
isamplerCube: [number];
|
|
139
|
+
isampler2DArray: [number];
|
|
140
|
+
usampler2D: [number];
|
|
141
|
+
usampler3D: [number];
|
|
142
|
+
usamplerCube: [number];
|
|
143
|
+
usampler2DArray: [number];
|
|
144
|
+
}
|
|
145
|
+
interface UniformOptions {
|
|
146
|
+
kind: UniformKind;
|
|
147
|
+
size?: number;
|
|
148
|
+
}
|
|
149
|
+
type UniformSchema = Record<string | symbol, UniformOptions>;
|
|
150
|
+
type UniformMethods<TOptions extends UniformOptions> = TOptions['size'] extends number ? {
|
|
151
|
+
set(arg: Float32Array): void;
|
|
152
|
+
} : {
|
|
153
|
+
set(...args: UniformKindMap[TOptions['kind']]): void;
|
|
154
|
+
};
|
|
155
|
+
type UniformView<T extends UniformSchema> = {
|
|
156
|
+
[K in keyof T]: UniformMethods<T[K]>;
|
|
157
|
+
};
|
|
158
|
+
type AttributeKind = 'float' | 'vec2' | 'vec3' | 'vec4' | 'mat2' | 'mat3' | 'mat4' | 'mat2x3' | 'mat2x4' | 'mat3x2' | 'mat3x4' | 'mat4x2' | 'mat4x3' | 'int' | 'ivec2' | 'ivec3' | 'ivec4' | 'uint' | 'uvec2' | 'uvec3' | 'uvec4';
|
|
159
|
+
interface AttributeOptions {
|
|
160
|
+
kind: AttributeKind;
|
|
161
|
+
instanced?: boolean;
|
|
162
|
+
buffer?: WebGLBuffer;
|
|
163
|
+
}
|
|
164
|
+
type AttributeSchema = Record<string | symbol, AttributeOptions>;
|
|
165
|
+
interface AttributeMethods<T = AttributeKind> {
|
|
166
|
+
buffer: WebGLBuffer;
|
|
167
|
+
bind(): void;
|
|
168
|
+
set(data: Float32Array, usage?: GLUsage): {
|
|
169
|
+
bind(): void;
|
|
170
|
+
};
|
|
171
|
+
dispose(): void;
|
|
172
|
+
}
|
|
173
|
+
type AttributeView<T extends AttributeSchema> = {
|
|
174
|
+
[K in keyof T]: AttributeMethods<T[K]['kind']>;
|
|
175
|
+
};
|
|
176
|
+
/**********************************************************************************/
|
|
177
|
+
/**********************************************************************************/
|
|
178
|
+
interface InterleavedAttributeLayout {
|
|
179
|
+
key: string | symbol;
|
|
180
|
+
kind: AttributeKind;
|
|
181
|
+
}
|
|
182
|
+
interface InterleavedAttributeOptions {
|
|
183
|
+
layout: InterleavedAttributeLayout[];
|
|
184
|
+
instanced?: boolean;
|
|
185
|
+
}
|
|
186
|
+
type InterleavedAttributeSchema = Record<string | symbol, InterleavedAttributeOptions>;
|
|
187
|
+
interface InterleavedAttributeMethods<T extends InterleavedAttributeLayout[] = InterleavedAttributeLayout[]> {
|
|
188
|
+
bind(): void;
|
|
189
|
+
unbind(): void;
|
|
190
|
+
set(data: Float32Array, usage?: GLUsage): void;
|
|
191
|
+
dispose(): void;
|
|
192
|
+
}
|
|
193
|
+
type InterleavedAttributeView<T extends InterleavedAttributeSchema> = {
|
|
194
|
+
[K in keyof T]: InterleavedAttributeMethods<T[K]['layout']>;
|
|
195
|
+
};
|
|
196
|
+
/**********************************************************************************/
|
|
197
|
+
/**********************************************************************************/
|
|
198
|
+
interface BufferOptions {
|
|
199
|
+
target?: GLTarget;
|
|
200
|
+
usage?: GLUsage;
|
|
201
|
+
}
|
|
202
|
+
type BufferSchema = Record<string | symbol, BufferOptions>;
|
|
203
|
+
interface BufferMethods {
|
|
204
|
+
set(data: Float32Array | Uint16Array | Uint32Array): void;
|
|
205
|
+
bind(): void;
|
|
206
|
+
dispose(): void;
|
|
207
|
+
}
|
|
208
|
+
type BufferView<T extends BufferSchema> = {
|
|
209
|
+
[K in keyof T]: BufferMethods;
|
|
210
|
+
};
|
|
211
|
+
/**********************************************************************************/
|
|
212
|
+
/**********************************************************************************/
|
|
213
|
+
interface ViewOptions {
|
|
214
|
+
signal?: AbortSignal;
|
|
215
|
+
}
|
|
216
|
+
interface ViewSchema {
|
|
217
|
+
uniforms?: UniformSchema;
|
|
218
|
+
attributes?: AttributeSchema;
|
|
219
|
+
interleavedAttributes?: InterleavedAttributeSchema;
|
|
220
|
+
buffers?: BufferSchema;
|
|
221
|
+
}
|
|
222
|
+
type View<T extends ViewSchema = ViewSchema> = {
|
|
223
|
+
attributes: T['attributes'] extends AttributeSchema ? AttributeView<T['attributes']> : undefined;
|
|
224
|
+
interleavedAttributes: T['interleavedAttributes'] extends InterleavedAttributeSchema ? InterleavedAttributeView<T['interleavedAttributes']> : undefined;
|
|
225
|
+
buffers: T['buffers'] extends BufferSchema ? BufferView<T['buffers']> : undefined;
|
|
226
|
+
uniforms: T['uniforms'] extends UniformSchema ? UniformView<T['uniforms']> : never;
|
|
227
|
+
};
|
|
228
|
+
/**********************************************************************************/
|
|
229
|
+
/**********************************************************************************/
|
|
230
|
+
interface GLSL {
|
|
231
|
+
template: string;
|
|
232
|
+
uniforms: Record<string | symbol, UniformOptions>;
|
|
233
|
+
attributes: Record<string | symbol, AttributeOptions>;
|
|
234
|
+
interleavedAttributes: Record<string | symbol, InterleavedAttributeOptions>;
|
|
235
|
+
}
|
|
236
|
+
type UniformTag<TKey extends string | symbol = string | symbol, TKind extends UniformKind = UniformKind, TOptions extends Partial<UniformOptions> = UniformOptions> = TOptions extends {
|
|
237
|
+
size: infer TSize;
|
|
238
|
+
} ? {
|
|
239
|
+
type: 'uniform';
|
|
240
|
+
kind: TKind;
|
|
241
|
+
key: TKey;
|
|
242
|
+
size: TSize;
|
|
243
|
+
} : {
|
|
244
|
+
type: 'uniform';
|
|
245
|
+
kind: TKind;
|
|
246
|
+
key: TKey;
|
|
247
|
+
};
|
|
248
|
+
interface AttributeTag<TKey extends string | symbol = string | symbol, TKind extends AttributeKind = AttributeKind, TInstanced extends boolean | undefined = boolean | undefined> {
|
|
249
|
+
type: 'attribute';
|
|
250
|
+
kind: TKind;
|
|
251
|
+
instanced: TInstanced;
|
|
252
|
+
key: TKey;
|
|
253
|
+
}
|
|
254
|
+
interface AttributeTagFn<TKey extends AttributeKind> {
|
|
255
|
+
<TName extends string | symbol, TOptions extends Omit<AttributeOptions, 'kind'>>(key: TName, options: TOptions): Prettify<AttributeTag<TName, TKey, TOptions['instanced']>>;
|
|
256
|
+
<TName extends string | symbol>(key: TName): Prettify<AttributeTag<TName, TKey, undefined>>;
|
|
257
|
+
}
|
|
258
|
+
type InterleaveTag<TKey extends string | symbol = string | symbol, TLayout extends Array<AttributeTag<string | symbol, AttributeKind, undefined>> = Array<AttributeTag<string | symbol, AttributeKind, undefined>>, TOptions extends Omit<AttributeOptions, 'kind'> = Omit<AttributeOptions, 'kind'>> = TOptions & {
|
|
259
|
+
key: TKey;
|
|
260
|
+
type: 'interleavedAttribute';
|
|
261
|
+
layout: {
|
|
262
|
+
[TKey in keyof TLayout]: Prettify<Pick<TLayout[TKey], 'kind' | 'key'>>;
|
|
263
|
+
};
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
export type { AttributeSchema as A, BufferSchema as B, GL as G, InterleavedAttributeSchema as I, Merge as M, Prettify as P, UniformSchema as U, ViewSchema as V, ViewOptions as a, View as b, UniformView as c, AttributeView as d, InterleavedAttributeView as e, BufferView as f, UniformKind as g, UniformOptions as h, UniformTag as i, AttributeTagFn as j, AttributeTag as k, AttributeKind as l, AttributeOptions as m, InterleaveTag as n, GLSL as o };
|
package/package.json
CHANGED
|
@@ -1,29 +1,43 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bigmistqke/view.gl",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "🪟 Utilities for managing WebGL resources.",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
7
|
-
"import": "./index.mjs",
|
|
8
|
-
"types": "./index.d.mts"
|
|
7
|
+
"import": "./dist/index.mjs",
|
|
8
|
+
"types": "./dist/index.d.mts"
|
|
9
9
|
},
|
|
10
10
|
"./tag": {
|
|
11
|
-
"import": "./tag.mjs",
|
|
12
|
-
"types": "./tag.d.mts"
|
|
11
|
+
"import": "./dist/tag.mjs",
|
|
12
|
+
"types": "./dist/tag.d.mts"
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
|
-
"keywords": [
|
|
15
|
+
"keywords": [
|
|
16
|
+
"WebGL"
|
|
17
|
+
],
|
|
16
18
|
"author": "",
|
|
17
19
|
"license": "ISC",
|
|
18
|
-
"dependencies": {},
|
|
19
20
|
"devDependencies": {
|
|
21
|
+
"@vitest/ui": "^3.2.4",
|
|
22
|
+
"concurrently": "^9.2.1",
|
|
23
|
+
"happy-dom": "^20.0.0",
|
|
24
|
+
"prettier": "^3.6.2",
|
|
20
25
|
"tsup": "^8.5.0",
|
|
21
26
|
"typescript": "^5.8.3",
|
|
22
27
|
"vite": "^7.0.2",
|
|
23
|
-
"vite-tsconfig-paths": "^5.1.4"
|
|
28
|
+
"vite-tsconfig-paths": "^5.1.4",
|
|
29
|
+
"vitest": "^3.2.4"
|
|
24
30
|
},
|
|
25
31
|
"scripts": {
|
|
32
|
+
"build": "tsup",
|
|
33
|
+
"dev:build": "vite build dev",
|
|
34
|
+
"dev:preview": "vite preview dev",
|
|
35
|
+
"dev:watch": "concurrently \"pnpm dev:preview\" \"vite dev:build --watch\"",
|
|
26
36
|
"dev": "vite serve dev",
|
|
27
|
-
"
|
|
37
|
+
"format": "prettier -w \"src/**/*.{js,ts,json,css,tsx,jsx}\" \"dev/**/*.{js,ts,json,css,tsx,jsx}\"",
|
|
38
|
+
"prepublish": "pnpm build",
|
|
39
|
+
"test": "vitest --run",
|
|
40
|
+
"test:coverage": "vitest run --coverage",
|
|
41
|
+
"test:ui": "vitest --ui"
|
|
28
42
|
}
|
|
29
43
|
}
|
package/src/index.ts
CHANGED
|
@@ -17,13 +17,35 @@ import type {
|
|
|
17
17
|
} from './types'
|
|
18
18
|
import {
|
|
19
19
|
assertedNotNullish,
|
|
20
|
+
createUpsertMap,
|
|
20
21
|
isMatKind,
|
|
21
22
|
isSamplerKind,
|
|
22
23
|
kindToSize,
|
|
23
24
|
kindToUniformFnName,
|
|
24
|
-
|
|
25
|
+
ObjectUtils,
|
|
25
26
|
} from './utils'
|
|
26
27
|
|
|
28
|
+
/**********************************************************************************/
|
|
29
|
+
/* */
|
|
30
|
+
/* To ID */
|
|
31
|
+
/* */
|
|
32
|
+
/**********************************************************************************/
|
|
33
|
+
|
|
34
|
+
let index = 0
|
|
35
|
+
const PREFIX = 'VIEW_GL_ALIAS'
|
|
36
|
+
|
|
37
|
+
const SYMBOL_MAP = createUpsertMap(WeakMap<symbol, string>)
|
|
38
|
+
|
|
39
|
+
export function toID(key: string | number | symbol) {
|
|
40
|
+
if (typeof key === 'string') {
|
|
41
|
+
return key
|
|
42
|
+
}
|
|
43
|
+
if (typeof key === 'symbol') {
|
|
44
|
+
return SYMBOL_MAP.getOrInsert(key, () => `${PREFIX}_${index++}`)
|
|
45
|
+
}
|
|
46
|
+
return key.toString()
|
|
47
|
+
}
|
|
48
|
+
|
|
27
49
|
/**********************************************************************************/
|
|
28
50
|
/* */
|
|
29
51
|
/* View */
|
|
@@ -59,7 +81,9 @@ export function uniformView<T extends UniformSchema>(
|
|
|
59
81
|
program: WebGLProgram,
|
|
60
82
|
schema: T,
|
|
61
83
|
): InferUniformView<T> {
|
|
62
|
-
return
|
|
84
|
+
return ObjectUtils.map(schema, ({ kind, size }, key) => {
|
|
85
|
+
const name = toID(key)
|
|
86
|
+
|
|
63
87
|
const location = gl.getUniformLocation(program, name)
|
|
64
88
|
|
|
65
89
|
if (isSamplerKind(kind)) {
|
|
@@ -123,12 +147,14 @@ export function attributeView<T extends AttributeSchema>(
|
|
|
123
147
|
schema: T,
|
|
124
148
|
{ signal }: ViewOptions = {},
|
|
125
149
|
): AttributeView<T> {
|
|
126
|
-
const attributes =
|
|
150
|
+
const attributes = ObjectUtils.map(
|
|
127
151
|
schema,
|
|
128
152
|
(
|
|
129
153
|
{ kind, instanced, buffer = assertedNotNullish(gl.createBuffer()) },
|
|
130
|
-
|
|
154
|
+
key,
|
|
131
155
|
): AttributeMethods => {
|
|
156
|
+
const name = toID(key)
|
|
157
|
+
|
|
132
158
|
const location = gl.getAttribLocation(program, name)
|
|
133
159
|
if (location < 0) {
|
|
134
160
|
throw new Error(`Attribute '${name}' not found`)
|
|
@@ -156,9 +182,7 @@ export function attributeView<T extends AttributeSchema>(
|
|
|
156
182
|
)
|
|
157
183
|
|
|
158
184
|
signal?.addEventListener('abort', function dispose() {
|
|
159
|
-
|
|
160
|
-
attributes[name].dispose()
|
|
161
|
-
}
|
|
185
|
+
ObjectUtils.forEach(attributes, value => value.dispose())
|
|
162
186
|
})
|
|
163
187
|
|
|
164
188
|
return attributes
|
|
@@ -177,16 +201,18 @@ export function interleavedAttributeView<T extends InterleavedAttributeSchema>(
|
|
|
177
201
|
{ signal }: ViewOptions = {},
|
|
178
202
|
): InterleavedAttributeView<T> {
|
|
179
203
|
// Initialize interleaved attributes
|
|
180
|
-
const interleavedAttributes =
|
|
204
|
+
const interleavedAttributes = ObjectUtils.map(schema, ({ layout, instanced }) => {
|
|
181
205
|
// Increment number to keep track of offset
|
|
182
206
|
let index = 0
|
|
183
207
|
|
|
184
208
|
// Calculate layout information
|
|
185
209
|
const handles = layout.map(layout => {
|
|
186
|
-
const
|
|
210
|
+
const name = toID(layout.key)
|
|
211
|
+
|
|
212
|
+
const location = gl.getAttribLocation(program, name)
|
|
187
213
|
|
|
188
214
|
if (location < 0) {
|
|
189
|
-
throw new Error(`Attribute '${
|
|
215
|
+
throw new Error(`Attribute '${name}' not found`)
|
|
190
216
|
}
|
|
191
217
|
|
|
192
218
|
const size = kindToSize(layout.kind)
|
|
@@ -267,9 +293,7 @@ export function interleavedAttributeView<T extends InterleavedAttributeSchema>(
|
|
|
267
293
|
})
|
|
268
294
|
|
|
269
295
|
signal?.addEventListener('abort', function dispose() {
|
|
270
|
-
|
|
271
|
-
interleavedAttributes[name].dispose()
|
|
272
|
-
}
|
|
296
|
+
ObjectUtils.forEach(interleavedAttributes, value => value.dispose())
|
|
273
297
|
})
|
|
274
298
|
|
|
275
299
|
return interleavedAttributes
|
|
@@ -287,7 +311,7 @@ export function bufferView<T extends BufferSchema>(
|
|
|
287
311
|
{ signal }: ViewOptions = {},
|
|
288
312
|
): BufferView<T> {
|
|
289
313
|
// Initialize buffers
|
|
290
|
-
const buffers =
|
|
314
|
+
const buffers = ObjectUtils.map(schema, ({ target = 'ARRAY_BUFFER', usage = 'STATIC_DRAW' }) => {
|
|
291
315
|
const buffer = assertedNotNullish(gl.createBuffer())
|
|
292
316
|
return {
|
|
293
317
|
bind() {
|
|
@@ -304,9 +328,7 @@ export function bufferView<T extends BufferSchema>(
|
|
|
304
328
|
})
|
|
305
329
|
|
|
306
330
|
signal?.addEventListener('abort', function dispose() {
|
|
307
|
-
|
|
308
|
-
buffers[name].dispose()
|
|
309
|
-
}
|
|
331
|
+
ObjectUtils.forEach(buffers, value => value.dispose())
|
|
310
332
|
})
|
|
311
333
|
|
|
312
334
|
return buffers
|