@bigmistqke/view.gl 0.1.2 → 0.1.4

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.
@@ -1,10 +1,24 @@
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
1
  type Prettify<T> = {
5
2
  [K in keyof T]: T[K];
6
3
  } & {};
7
4
  type GL = WebGLRenderingContext | WebGL2RenderingContext;
5
+ type DeepMerge<A extends readonly [...any]> = A extends [infer L, ...infer R] ? DeepMergeTwo<L, DeepMerge<R>> : unknown;
6
+ type DeepMergeTwo<TBase, TOverride> = {
7
+ [K in keyof TBase | keyof TOverride]: K extends keyof TOverride ? K extends keyof TBase ? ShallowMerge<[TBase[K], TOverride[K]]> : TOverride[K] : K extends keyof TBase ? TBase[K] : never;
8
+ };
9
+ type ShallowMerge<A extends readonly [...any]> = A extends [infer L, ...infer R] ? ShallowMergeTwo<L, ShallowMerge<R>> : unknown;
10
+ type OptionalPropertyNames<T> = {
11
+ [K in keyof T]-?: {} extends {
12
+ [P in K]: T[K];
13
+ } ? K : never;
14
+ }[keyof T];
15
+ type SpreadProperties<L, R, K extends keyof L & keyof R> = {
16
+ [P in K]: L[P] | Exclude<R[P], undefined>;
17
+ };
18
+ type Id<T> = T extends infer U ? {
19
+ [K in keyof U]: U[K];
20
+ } : never;
21
+ type ShallowMergeTwo<L, R> = Id<Pick<L, Exclude<keyof L, keyof R>> & Pick<R, Exclude<keyof R, OptionalPropertyNames<R>>> & Pick<R, Exclude<OptionalPropertyNames<R>, keyof L>> & SpreadProperties<L, R, OptionalPropertyNames<R> & keyof L>>;
8
22
  /**********************************************************************************/
9
23
  /**********************************************************************************/
10
24
  type GLTarget = 'ARRAY_BUFFER' | 'ELEMENT_ARRAY_BUFFER';
@@ -219,19 +233,23 @@ interface ViewSchema {
219
233
  interleavedAttributes?: InterleavedAttributeSchema;
220
234
  buffers?: BufferSchema;
221
235
  }
236
+ interface ViewSchemaPartial {
237
+ uniforms?: Record<string | symbol, Partial<UniformOptions>>;
238
+ attributes?: Record<string | symbol, Partial<AttributeOptions>>;
239
+ interleavedAttributes?: Record<string | symbol, Partial<InterleavedAttributeOptions>>;
240
+ buffers?: Record<string | symbol, Partial<BufferOptions>>;
241
+ }
222
242
  type View<T extends ViewSchema = ViewSchema> = {
223
243
  attributes: T['attributes'] extends AttributeSchema ? AttributeView<T['attributes']> : undefined;
224
244
  interleavedAttributes: T['interleavedAttributes'] extends InterleavedAttributeSchema ? InterleavedAttributeView<T['interleavedAttributes']> : undefined;
225
245
  buffers: T['buffers'] extends BufferSchema ? BufferView<T['buffers']> : undefined;
226
246
  uniforms: T['uniforms'] extends UniformSchema ? UniformView<T['uniforms']> : never;
227
247
  };
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>;
248
+ type GLSLSlot = GLSLTag | UniformTag | AttributeTag | InterleaveTag | string | number | symbol;
249
+ interface GLSLTag<TSlots extends GLSLSlot[] = GLSLSlot[]> {
250
+ type: 'glsl';
251
+ template: TemplateStringsArray;
252
+ slots: TSlots;
235
253
  }
236
254
  type UniformTag<TKey extends string | symbol = string | symbol, TKind extends UniformKind = UniformKind, TOptions extends Partial<UniformOptions> = UniformOptions> = TOptions extends {
237
255
  size: infer TSize;
@@ -245,6 +263,10 @@ type UniformTag<TKey extends string | symbol = string | symbol, TKind extends Un
245
263
  kind: TKind;
246
264
  key: TKey;
247
265
  };
266
+ interface UniformTagFn<TKind extends UniformKind> {
267
+ <const TKey extends string | symbol, const TOptions extends Omit<UniformOptions, 'kind'>>(key: TKey, TOptions: TOptions): Prettify<UniformTag<TKey, TKind, TOptions>>;
268
+ <const TName extends string | symbol>(key: TName): Prettify<UniformTag<TName, TKind>>;
269
+ }
248
270
  interface AttributeTag<TKey extends string | symbol = string | symbol, TKind extends AttributeKind = AttributeKind, TInstanced extends boolean | undefined = boolean | undefined> {
249
271
  type: 'attribute';
250
272
  kind: TKind;
@@ -263,4 +285,4 @@ type InterleaveTag<TKey extends string | symbol = string | symbol, TLayout exten
263
285
  };
264
286
  };
265
287
 
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 };
288
+ export type { AttributeSchema as A, BufferSchema as B, DeepMerge as D, GL as G, InterleavedAttributeSchema as I, Prettify as P, ShallowMerge as S, UniformSchema as U, ViewSchema as V, ViewOptions as a, View as b, UniformView as c, AttributeView as d, InterleavedAttributeView as e, BufferView as f, GLSLSlot as g, GLSLTag as h, UniformTagFn as i, AttributeTagFn as j, AttributeTag as k, AttributeKind as l, AttributeOptions as m, InterleaveTag as n, ViewSchemaPartial as o, UniformTag as p };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bigmistqke/view.gl",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "🪟 Utilities for managing WebGL resources.",
5
5
  "exports": {
6
6
  ".": {
@@ -38,6 +38,7 @@
38
38
  "prepublish": "pnpm build",
39
39
  "test": "vitest --run",
40
40
  "test:coverage": "vitest run --coverage",
41
- "test:ui": "vitest --ui"
41
+ "test:ui": "vitest --ui",
42
+ "lint:types": "tsc --noEmit"
42
43
  }
43
44
  }
package/src/index.ts CHANGED
@@ -52,12 +52,12 @@ export function toID(key: string | number | symbol) {
52
52
  /* */
53
53
  /**********************************************************************************/
54
54
 
55
- export function view<T extends ViewSchema>(
55
+ export function view<TSchema extends ViewSchema>(
56
56
  gl: GL,
57
57
  program: WebGLProgram,
58
- schema: T,
58
+ schema: TSchema,
59
59
  options?: ViewOptions,
60
- ): View<T> {
60
+ ): View<TSchema> {
61
61
  return {
62
62
  uniforms: !schema.uniforms ? undefined : uniformView(gl, program, schema.uniforms),
63
63
  attributes: !schema.attributes
@@ -67,7 +67,7 @@ export function view<T extends ViewSchema>(
67
67
  ? undefined
68
68
  : interleavedAttributeView(gl, program, schema.interleavedAttributes, options),
69
69
  buffers: !schema.buffers ? undefined : bufferView(gl, schema.buffers),
70
- } as View<T>
70
+ } as View<TSchema>
71
71
  }
72
72
 
73
73
  /**********************************************************************************/
package/src/tag.ts CHANGED
@@ -1,36 +1,35 @@
1
- import { toID } from '.'
1
+ import { toID, view } from '.'
2
2
  import type {
3
3
  AttributeKind,
4
4
  AttributeOptions,
5
5
  AttributeTag,
6
- AttributeTagFn,
7
- GLSL,
6
+ AttributeTagFn as AttributeTagMethod,
7
+ DeepMerge,
8
+ GLSLSlot,
9
+ GLSLTag,
8
10
  InterleaveTag,
9
- Merge,
10
11
  Prettify,
12
+ ShallowMerge,
11
13
  UniformKind,
12
14
  UniformOptions,
13
15
  UniformTag,
16
+ UniformTagFn as UniformTagMethod,
17
+ View,
18
+ ViewSchema,
19
+ ViewSchemaPartial,
14
20
  } from './types'
15
21
  import { createProgram } from './utils'
16
22
 
17
- /**********************************************************************************/
18
- /* */
19
- /* Tag Utilities */
20
- /* */
21
- /**********************************************************************************/
22
-
23
- interface UniformTagFn<TKind extends UniformKind> {
24
- <const TKey extends string | symbol, const TOptions extends Omit<UniformOptions, 'kind'>>(
25
- key: TKey,
26
- TOptions: TOptions,
27
- ): Prettify<UniformTag<TKey, TKind, TOptions>>
28
- <const TName extends string | symbol>(key: TName): Prettify<UniformTag<TName, TKind>>
23
+ export function glsl<TSlot extends GLSLSlot, TSlots extends TSlot[]>(
24
+ template: TemplateStringsArray,
25
+ ...slots: [...TSlots]
26
+ ): GLSLTag<TSlots> {
27
+ return { template, slots, type: 'glsl' }
29
28
  }
30
29
 
31
30
  export const uniform = new Proxy(
32
31
  {} as {
33
- [TKey in UniformKind]: UniformTagFn<TKey>
32
+ [TKey in UniformKind]: UniformTagMethod<TKey>
34
33
  },
35
34
  {
36
35
  get(target, property) {
@@ -48,7 +47,7 @@ export const uniform = new Proxy(
48
47
 
49
48
  export const attribute = new Proxy(
50
49
  {} as {
51
- [TKey in AttributeKind]: AttributeTagFn<TKey>
50
+ [TKey in AttributeKind]: AttributeTagMethod<TKey>
52
51
  },
53
52
  {
54
53
  get(target, property) {
@@ -93,109 +92,205 @@ export function interleave<
93
92
 
94
93
  /**********************************************************************************/
95
94
  /* */
96
- /* GLSL */
95
+ /* Compile */
97
96
  /* */
98
97
  /**********************************************************************************/
99
98
 
100
- export function glsl<
101
- Elem extends string,
102
- Template extends ReadonlyArray<Elem>,
103
- Hole extends UniformTag | AttributeTag | InterleaveTag | string | number | symbol,
104
- Holes extends Hole[],
105
- >([initial, ...rest]: Template, ...holes: [...Holes]) {
106
- const v300 = initial?.startsWith('#version 300 es')
107
-
108
- const template = rest.reduce((out, templatePart, index) => {
109
- const hole = holes[index]
110
- const holePart =
111
- typeof hole === 'string' || typeof hole === 'number' || typeof hole === 'symbol'
112
- ? toID(hole)
113
- : hole.type === 'interleavedAttribute'
114
- ? hole.layout.reduce(
115
- (a, v) =>
116
- v300
117
- ? `${a}in ${v.kind} ${toID(v.key)};\n`
118
- : `${a}attribute ${v.kind} ${toID(v.key)};\n`,
119
- '',
120
- )
121
- : typeof hole === 'object' && hole.type === 'uniform' && 'size' in hole
122
- ? `${hole.type} ${hole.kind} ${toID(hole.key)}[${hole.size}];`
123
- : `${hole.type === 'attribute' && v300 ? 'in' : hole.type} ${hole.kind} ${toID(hole.key)};`
124
- return out + holePart + templatePart
125
- }, initial || '')
126
-
127
- return holes.reduce(
128
- (resources, resource) => {
129
- if (
130
- typeof resource === 'string' ||
131
- typeof resource === 'number' ||
132
- typeof resource === 'symbol'
133
- ) {
134
- return resources
99
+ type glslTagToSchema<TTag extends GLSLTag> =
100
+ FlattenSlots<TTag> extends infer TSlots
101
+ ? TSlots extends Array<any>
102
+ ? GLSLSlotsToSchema<TSlots>
103
+ : never
104
+ : never
105
+
106
+ type GLSLSlotsToSchema<TSlots extends Array<GLSLSlot>> = {
107
+ uniforms: {
108
+ [K in Extract<TSlots[number], UniformTag> as K['key']]: Prettify<Omit<K, 'name' | 'type'>>
109
+ }
110
+ attributes: {
111
+ [K in Extract<TSlots[number], AttributeTag> as K['key']]: Prettify<Omit<K, 'name' | 'type'>>
112
+ }
113
+ interleavedAttributes: {
114
+ [K in Extract<TSlots[number], InterleaveTag> as K['key']]: Prettify<Omit<K, 'name' | 'type'>>
115
+ }
116
+ }
117
+
118
+ type FlattenSlots<T> =
119
+ T extends GLSLTag<infer TSlots>
120
+ ? _FlattenSlots<TSlots>
121
+ : T extends Array<GLSLSlot>
122
+ ? _FlattenSlots<T>
123
+ : never
124
+
125
+ type _FlattenSlots<T extends Array<GLSLSlot>> = T extends [infer First, ...infer Rest]
126
+ ? First extends GLSLTag
127
+ ? [...FlattenSlots<First>, ...FlattenSlots<Rest>]
128
+ : First extends unknown[]
129
+ ? [...FlattenSlots<First>, ...FlattenSlots<Rest>]
130
+ : [First, ...FlattenSlots<Rest>]
131
+ : []
132
+
133
+ type MergeGLSLSchema<
134
+ TVertex extends ViewSchema,
135
+ TFragment extends ViewSchema,
136
+ TOverrides extends ViewSchemaPartial,
137
+ > = {
138
+ uniforms: DeepMerge<
139
+ [ShallowMerge<[TVertex['uniforms'], TFragment['uniforms']]>, TOverrides['uniforms']]
140
+ >
141
+ attributes: DeepMerge<
142
+ [ShallowMerge<[TVertex['attributes'], TFragment['attributes']]>, TOverrides['attributes']]
143
+ >
144
+ interleavedAttributes: DeepMerge<
145
+ [
146
+ ShallowMerge<[TVertex['interleavedAttributes'], TFragment['interleavedAttributes']]>,
147
+ TOverrides['interleavedAttributes'],
148
+ ]
149
+ >
150
+ }
151
+
152
+ type CompileResult<
153
+ TVertex extends GLSLTag,
154
+ TFragment extends GLSLTag,
155
+ TOverrides extends ViewSchemaPartial,
156
+ > =
157
+ MergeGLSLSchema<
158
+ glslTagToSchema<TVertex>,
159
+ glslTagToSchema<TFragment>,
160
+ TOverrides
161
+ > extends infer TSchema extends ViewSchema
162
+ ? {
163
+ program: WebGLProgram
164
+ schema: TSchema
165
+ view: View<TSchema>
135
166
  }
136
- // @ts-expect-error
137
- const { key: name, type, ...rest } = resource
138
- // @ts-expect-error
139
- resources[`${type}s`][name] = rest
140
- return resources
167
+ : never
168
+
169
+ export function compile<
170
+ TVertex extends GLSLTag,
171
+ TFragment extends GLSLTag,
172
+ TOverride extends ViewSchemaPartial,
173
+ >(gl: WebGLRenderingContext, vertex: TVertex, fragment: TFragment, overrideSchema?: TOverride) {
174
+ const _vertex = resolveGLSLTag(vertex)
175
+ const _fragment = resolveGLSLTag(fragment)
176
+
177
+ const schema = {
178
+ uniforms: {
179
+ ..._vertex.schema.uniforms,
180
+ ..._fragment.schema.uniforms,
141
181
  },
142
- {
143
- template,
144
- uniforms: {},
145
- attributes: {},
146
- interleavedAttributes: {},
147
- } as {
148
- template: string
149
- uniforms: {
150
- [K in Extract<Holes[number], UniformTag> as K['key']]: Prettify<Omit<K, 'name' | 'type'>>
151
- }
152
- attributes: {
153
- [K in Extract<Holes[number], AttributeTag> as K['key']]: Prettify<Omit<K, 'name' | 'type'>>
154
- }
155
- interleavedAttributes: {
156
- [K in Extract<Holes[number], InterleaveTag> as K['key']]: Prettify<Omit<K, 'name' | 'type'>>
157
- }
182
+ attributes: {
183
+ ..._vertex.schema.attributes,
184
+ ..._fragment.schema.attributes,
158
185
  },
159
- )
160
- }
186
+ interleavedAttributes: {
187
+ ..._vertex.schema.interleavedAttributes,
188
+ ..._fragment.schema.interleavedAttributes,
189
+ },
190
+ }
161
191
 
162
- /**********************************************************************************/
163
- /* */
164
- /* Compile */
165
- /* */
166
- /**********************************************************************************/
192
+ for (const kind in overrideSchema) {
193
+ const schemaKind = schema[kind as keyof typeof schema]
194
+ const overrideSchemaKind = overrideSchema[kind]
195
+
196
+ for (const key in overrideSchemaKind) {
197
+ // @ts-expect-error
198
+ schemaKind[key] = {
199
+ // @ts-expect-error
200
+ ...schemaKind[key],
201
+ ...overrideSchemaKind[key],
202
+ }
203
+ }
204
+ }
167
205
 
168
- export function compile<TVertex extends GLSL, TFragment extends GLSL>(
169
- gl: WebGLRenderingContext,
170
- vertex: TVertex,
171
- fragment: TFragment,
172
- ) {
173
- const program = createProgram(gl, vertex.template, fragment.template)
206
+ const program = createProgram(gl, _vertex.template, _fragment.template)
174
207
 
175
208
  return {
176
209
  program,
177
- schema: {
178
- uniforms: {
179
- ...vertex.uniforms,
180
- ...fragment.uniforms,
181
- },
182
- attributes: {
183
- ...vertex.attributes,
184
- ...fragment.attributes,
185
- },
186
- interleavedAttributes: {
187
- ...vertex.interleavedAttributes,
188
- ...fragment.interleavedAttributes,
189
- },
190
- },
191
- } as {
192
- program: WebGLProgram
193
- schema: {
194
- uniforms: Prettify<Merge<TVertex['uniforms'], TFragment['uniforms']>>
195
- attributes: Prettify<Merge<TVertex['attributes'], TFragment['attributes']>>
196
- interleavedAttributes: Prettify<
197
- Merge<TVertex['interleavedAttributes'], TFragment['interleavedAttributes']>
198
- >
210
+ schema,
211
+ view: view(gl, program, schema),
212
+ } as CompileResult<TVertex, TFragment, TOverride>
213
+ }
214
+
215
+ function resolveGLSLTag<TTag extends GLSLTag>(tag: TTag) {
216
+ return {
217
+ template: glslTagToString(tag),
218
+ schema: glslTagToSchema(tag),
219
+ }
220
+ }
221
+
222
+ function glslTagToSchema<TTag extends GLSLTag>(tag: TTag) {
223
+ const result = {
224
+ uniforms: {},
225
+ attributes: {},
226
+ interleavedAttributes: {},
227
+ }
228
+
229
+ for (const slot of tag.slots) {
230
+ if (typeof slot !== 'object') {
231
+ continue
199
232
  }
233
+
234
+ if (slot.type === 'glsl') {
235
+ const { uniforms, attributes, interleavedAttributes } = glslTagToSchema(slot)
236
+
237
+ result.uniforms = {
238
+ ...result.uniforms,
239
+ ...uniforms,
240
+ }
241
+ result.attributes = {
242
+ ...result.attributes,
243
+ ...attributes,
244
+ }
245
+ result.interleavedAttributes = {
246
+ ...result.interleavedAttributes,
247
+ ...interleavedAttributes,
248
+ }
249
+
250
+ continue
251
+ }
252
+
253
+ const { key: name, type, ...rest } = slot
254
+
255
+ // @ts-expect-error
256
+ result[`${type}s`][name] = rest
200
257
  }
258
+
259
+ return result
260
+ }
261
+
262
+ function glslTagToString<TTag extends GLSLTag>({ template: [initial, ...rest], slots }: TTag) {
263
+ const v300 = !!initial?.startsWith('#version 300 es')
264
+
265
+ let template = initial ?? ''
266
+
267
+ for (let i = 0; i < rest.length; i++) {
268
+ template += `${glslSlotToString(slots[i]!, v300)}${rest[i]}`
269
+ }
270
+
271
+ return template
272
+ }
273
+
274
+ function glslSlotToString(slot: GLSLSlot, v300: boolean) {
275
+ if (typeof slot !== 'object') {
276
+ return toID(slot)
277
+ }
278
+
279
+ if (slot.type === 'glsl') {
280
+ return glslTagToString(slot)
281
+ }
282
+
283
+ if (slot.type === 'interleavedAttribute') {
284
+ return slot.layout.reduce(
285
+ (a, v) =>
286
+ v300 ? `${a}in ${v.kind} ${toID(v.key)};\n` : `${a}attribute ${v.kind} ${toID(v.key)};\n`,
287
+ '',
288
+ )
289
+ }
290
+
291
+ if (slot.type === 'uniform' && 'size' in slot) {
292
+ return `${slot.type} ${slot.kind} ${toID(slot.key)}[${slot.size}];`
293
+ }
294
+
295
+ return `${slot.type === 'attribute' && v300 ? 'in' : slot.type} ${slot.kind} ${toID(slot.key)};`
201
296
  }
package/src/types.ts CHANGED
@@ -18,6 +18,42 @@ export type RemoveSuffix<T, S extends string> = {
18
18
 
19
19
  export type GL = WebGLRenderingContext | WebGL2RenderingContext
20
20
 
21
+ export type DeepMerge<A extends readonly [...any]> = A extends [infer L, ...infer R]
22
+ ? DeepMergeTwo<L, DeepMerge<R>>
23
+ : unknown
24
+
25
+ type DeepMergeTwo<TBase, TOverride> = {
26
+ [K in keyof TBase | keyof TOverride]: K extends keyof TOverride
27
+ ? K extends keyof TBase
28
+ ? ShallowMerge<[TBase[K], TOverride[K]]>
29
+ : TOverride[K]
30
+ : K extends keyof TBase
31
+ ? TBase[K]
32
+ : never
33
+ }
34
+
35
+ // see https://stackoverflow.com/a/49683575
36
+ export type ShallowMerge<A extends readonly [...any]> = A extends [infer L, ...infer R]
37
+ ? ShallowMergeTwo<L, ShallowMerge<R>>
38
+ : unknown
39
+
40
+ type OptionalPropertyNames<T> = {
41
+ [K in keyof T]-?: {} extends { [P in K]: T[K] } ? K : never
42
+ }[keyof T]
43
+
44
+ type SpreadProperties<L, R, K extends keyof L & keyof R> = {
45
+ [P in K]: L[P] | Exclude<R[P], undefined>
46
+ }
47
+
48
+ type Id<T> = T extends infer U ? { [K in keyof U]: U[K] } : never
49
+
50
+ type ShallowMergeTwo<L, R> = Id<
51
+ Pick<L, Exclude<keyof L, keyof R>> &
52
+ Pick<R, Exclude<keyof R, OptionalPropertyNames<R>>> &
53
+ Pick<R, Exclude<OptionalPropertyNames<R>, keyof L>> &
54
+ SpreadProperties<L, R, OptionalPropertyNames<R> & keyof L>
55
+ >
56
+
21
57
  /**********************************************************************************/
22
58
  /* */
23
59
  /* Constants */
@@ -359,6 +395,7 @@ export interface UniformOptions {
359
395
  }
360
396
 
361
397
  export type UniformSchema = Record<string | symbol, UniformOptions>
398
+ export type UniformSchemaPartial = Record<string | symbol, Partial<UniformOptions>>
362
399
 
363
400
  export type UniformMethods<TOptions extends UniformOptions> = TOptions['size'] extends number
364
401
  ? {
@@ -645,6 +682,13 @@ export interface ViewSchema {
645
682
  buffers?: BufferSchema
646
683
  }
647
684
 
685
+ export interface ViewSchemaPartial {
686
+ uniforms?: Record<string | symbol, Partial<UniformOptions>>
687
+ attributes?: Record<string | symbol, Partial<AttributeOptions>>
688
+ interleavedAttributes?: Record<string | symbol, Partial<InterleavedAttributeOptions>>
689
+ buffers?: Record<string | symbol, Partial<BufferOptions>>
690
+ }
691
+
648
692
  export type View<T extends ViewSchema = ViewSchema> = {
649
693
  attributes: T['attributes'] extends AttributeSchema ? AttributeView<T['attributes']> : undefined
650
694
  interleavedAttributes: T['interleavedAttributes'] extends InterleavedAttributeSchema
@@ -660,11 +704,24 @@ export type View<T extends ViewSchema = ViewSchema> = {
660
704
  /* */
661
705
  /**********************************************************************************/
662
706
 
663
- export interface GLSL {
664
- template: string
665
- uniforms: Record<string | symbol, UniformOptions>
666
- attributes: Record<string | symbol, AttributeOptions>
667
- interleavedAttributes: Record<string | symbol, InterleavedAttributeOptions>
707
+ export interface GLSLResult {
708
+ program: WebGLProgram
709
+ schema: ViewSchema
710
+ }
711
+
712
+ export type GLSLSlot =
713
+ | GLSLTag
714
+ | UniformTag
715
+ | AttributeTag
716
+ | InterleaveTag
717
+ | string
718
+ | number
719
+ | symbol
720
+
721
+ export interface GLSLTag<TSlots extends GLSLSlot[] = GLSLSlot[]> {
722
+ type: 'glsl'
723
+ template: TemplateStringsArray
724
+ slots: TSlots
668
725
  }
669
726
 
670
727
  export type UniformTag<
@@ -684,6 +741,14 @@ export type UniformTag<
684
741
  key: TKey
685
742
  }
686
743
 
744
+ export interface UniformTagFn<TKind extends UniformKind> {
745
+ <const TKey extends string | symbol, const TOptions extends Omit<UniformOptions, 'kind'>>(
746
+ key: TKey,
747
+ TOptions: TOptions,
748
+ ): Prettify<UniformTag<TKey, TKind, TOptions>>
749
+ <const TName extends string | symbol>(key: TName): Prettify<UniformTag<TName, TKind>>
750
+ }
751
+
687
752
  export interface AttributeTag<
688
753
  TKey extends string | symbol = string | symbol,
689
754
  TKind extends AttributeKind = AttributeKind,
@@ -71,26 +71,26 @@ export const kindToUniformFnName = <T extends UniformKind>(
71
71
  switch (kind[0]) {
72
72
  // mat
73
73
  case 'm':
74
- return 'Matrix' + kind.slice(3) + 'fv'
74
+ return ('Matrix' + kind.slice(3) + 'fv') as KIND_TO_UNIFORM_FN_NAME_MAP[T]
75
75
  // sampler/booleans/integer
76
76
  case 's':
77
77
  case 'b':
78
78
  case 'i':
79
- return (kind.match(/\d/)?.[0] ?? '1') + 'i'
79
+ return ((kind.match(/\d/)?.[0] ?? '1') + 'i') as KIND_TO_UNIFORM_FN_NAME_MAP[T]
80
80
  // unsigned integers
81
81
  case 'u':
82
- return (kind[4] || '1') + 'ui'
82
+ return ((kind[4] || '1') + 'ui') as KIND_TO_UNIFORM_FN_NAME_MAP[T]
83
83
  // vec
84
84
  case 'v':
85
- return kind[3] + 'f'
85
+ return (kind[3] + 'f') as KIND_TO_UNIFORM_FN_NAME_MAP[T]
86
86
  default:
87
87
  switch (kind) {
88
88
  case 'float':
89
- return '1f'
89
+ return '1f' as KIND_TO_UNIFORM_FN_NAME_MAP[T]
90
90
  case 'uint':
91
- return '1ui'
91
+ return '1ui' as KIND_TO_UNIFORM_FN_NAME_MAP[T]
92
92
  default:
93
- return '1i'
93
+ return '1i' as KIND_TO_UNIFORM_FN_NAME_MAP[T]
94
94
  }
95
95
  }
96
96
  }
@@ -167,7 +167,7 @@ export function createTexture(
167
167
  if (!(name in gl)) {
168
168
  throw new Error(`Attempted to create webgl2-only texture (${name}) in webgl1`)
169
169
  }
170
- return gl[name]
170
+ return gl[name as keyof typeof gl] as any
171
171
  }
172
172
 
173
173
  gl.bindTexture(gl[target], texture)
@@ -268,7 +268,7 @@ export function createFramebuffer(gl: GL, { attachment, texture, ...options }: F
268
268
  /* */
269
269
  /**********************************************************************************/
270
270
 
271
- type UpsertMapKind<T, U> = Map<T, U> | WeakMap<T, U>
271
+ type UpsertMapKind<T, U> = Map<T, U> | (T extends WeakKey ? WeakMap<T, U> : never)
272
272
 
273
273
  export function createUpsertMap<T extends UpsertMapKind<any, any> = Map<any, any>>(
274
274
  constructor?: new () => T,