@bigmistqke/view.gl 0.1.8 → 0.1.9

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/src/tag.ts CHANGED
@@ -6,6 +6,7 @@ import type {
6
6
  AttributeTokenFn as AttributeTagMethod,
7
7
  AttributeToken,
8
8
  CompileResult,
9
+ GL,
9
10
  GLSL,
10
11
  GLSLSlot,
11
12
  GLSLToSchema,
@@ -16,7 +17,7 @@ import type {
16
17
  ViewSchema,
17
18
  ViewSchemaPartial,
18
19
  } from './types'
19
- import { createProgram } from './utils'
20
+ import { createProgram, createUpsertMap } from './utils'
20
21
 
21
22
  export function glsl<TSlot extends GLSLSlot, TSlots extends TSlot[]>(
22
23
  template: TemplateStringsArray,
@@ -94,13 +95,23 @@ export function interleave<
94
95
  /* */
95
96
  /**********************************************************************************/
96
97
 
98
+ export interface CompileOptions<TSchema extends ViewSchemaPartial> {
99
+ schema?: TSchema
100
+ webgl2?: boolean
101
+ }
102
+
97
103
  export function compile<
98
104
  TVertex extends GLSL,
99
105
  TFragment extends GLSL,
100
- TOverride extends ViewSchemaPartial,
101
- >(gl: WebGLRenderingContext, vertex: TVertex, fragment: TFragment, overrideSchema?: TOverride) {
102
- const _vertex = resolveGLSLTag(vertex) as { template: string; schema: ViewSchema }
103
- const _fragment = resolveGLSLTag(fragment) as { template: string; schema: ViewSchema }
106
+ TSchema extends ViewSchemaPartial,
107
+ >(
108
+ gl: WebGLRenderingContext,
109
+ vertex: TVertex,
110
+ fragment: TFragment,
111
+ options?: CompileOptions<TSchema> | undefined,
112
+ ) {
113
+ const _vertex = resolveGLSLTag(vertex, options) as { template: string; schema: ViewSchema }
114
+ const _fragment = resolveGLSLTag(fragment, options) as { template: string; schema: ViewSchema }
104
115
 
105
116
  const schema = {
106
117
  uniforms: {
@@ -117,32 +128,40 @@ export function compile<
117
128
  },
118
129
  }
119
130
 
120
- for (const kind in overrideSchema) {
131
+ // Deep merge options.schema with generated schema
132
+ for (const kind in options?.schema) {
121
133
  const schemaKind = schema[kind as keyof typeof schema]
122
- const overrideSchemaKind = overrideSchema[kind]
134
+ const configSchemaKind = options?.schema[kind]
123
135
 
124
- for (const key in overrideSchemaKind) {
136
+ for (const key in configSchemaKind) {
125
137
  schemaKind[key] = {
126
138
  ...schemaKind[key],
127
- ...overrideSchemaKind[key],
139
+ ...configSchemaKind[key],
128
140
  }
129
141
  }
130
142
  }
131
143
 
132
- const program = createProgram(gl, _vertex.template, _fragment.template)
133
-
134
- return {
135
- program,
136
- schema,
137
- view: view(gl, program, schema),
138
- vertex: _vertex.template,
139
- fragment: _fragment.template,
140
- } as unknown as Prettify<CompileResult<TVertex, TFragment, TOverride>>
144
+ try {
145
+ const program = createProgram(gl, _vertex.template, _fragment.template)
146
+
147
+ return {
148
+ program,
149
+ schema,
150
+ view: view(gl, program, schema),
151
+ vertex: _vertex.template,
152
+ fragment: _fragment.template,
153
+ } as unknown as Prettify<CompileResult<TVertex, TFragment, TSchema>>
154
+ } catch (error) {
155
+ console.error('Error while creating WebGLProgram - vertex\n\n', _vertex.template)
156
+ console.error('Error while creating WebGLProgram - fragment\n\n', _fragment.template)
157
+
158
+ throw error
159
+ }
141
160
  }
142
161
 
143
- function resolveGLSLTag<TTag extends GLSL>(tag: TTag) {
162
+ function resolveGLSLTag<TTag extends GLSL>(tag: TTag, options?: { webgl2?: boolean }) {
144
163
  return {
145
- template: compile.toString(tag),
164
+ template: compile.toString(tag, options),
146
165
  schema: compile.toSchema(tag),
147
166
  }
148
167
  }
@@ -154,13 +173,13 @@ compile.toSchema = function <TTag extends GLSL>(tag: TTag) {
154
173
  interleavedAttributes: {},
155
174
  }
156
175
 
157
- function handleSlot(slot: GLSLSlot) {
158
- if (Array.isArray(slot)) {
159
- slot.forEach(handleSlot)
176
+ tag.slots.forEach(function handleSlot(slot: GLSLSlot) {
177
+ if (typeof slot !== 'object') {
160
178
  return
161
179
  }
162
180
 
163
- if (typeof slot !== 'object') {
181
+ if (Array.isArray(slot)) {
182
+ slot.forEach(handleSlot)
164
183
  return
165
184
  }
166
185
 
@@ -187,49 +206,97 @@ compile.toSchema = function <TTag extends GLSL>(tag: TTag) {
187
206
 
188
207
  // @ts-expect-error
189
208
  result[`${type}s`][name] = rest
190
- }
191
-
192
- tag.slots.forEach(handleSlot)
209
+ })
193
210
 
194
211
  return result as unknown as Prettify<GLSLToSchema<TTag>>
195
212
  }
196
213
 
197
- compile.toString = function <TTag extends GLSL>({ template: [initial, ...rest], slots }: TTag) {
198
- const v300 = !!initial?.startsWith('#version 300 es')
214
+ compile.toString = function <TTag extends GLSL>(
215
+ { template: [initial, ...rest], slots }: TTag,
216
+ config?: { webgl2?: boolean },
217
+ ) {
218
+ const v300 = config?.webgl2 ?? !!initial?.startsWith('#version 300 es')
199
219
 
200
220
  let template = initial ?? ''
201
221
 
202
222
  for (let i = 0; i < rest.length; i++) {
203
- template += `${glslSlotToString(slots[i]!, v300)}${rest[i]}`
223
+ template += `${resolveGlslSlotToString(slots[i]!, v300)}${rest[i]}`
204
224
  }
205
225
 
206
226
  return template
207
227
  }
208
228
 
209
- function glslSlotToString(slot: GLSLSlot, v300: boolean): string {
229
+ function resolveGlslSlotToString(slot: GLSLSlot, v300: boolean): string {
210
230
  if (typeof slot !== 'object') {
211
231
  return toID(slot)
212
232
  }
213
233
 
214
234
  if (Array.isArray(slot)) {
215
- return slot.map(slot => glslSlotToString(slot, v300)).join('')
235
+ return slot.map(slot => resolveGlslSlotToString(slot, v300)).join('')
216
236
  }
217
237
 
218
- if (slot.type === 'glsl') {
219
- return compile.toString(slot)
238
+ switch (slot.type) {
239
+ case 'glsl':
240
+ return compile.toString(slot)
241
+ case 'interleavedAttribute':
242
+ return slot.layout.reduce(
243
+ (a, v) =>
244
+ v300 ? `${a}in ${v.kind} ${toID(v.key)};\n` : `${a}attribute ${v.kind} ${toID(v.key)};\n`,
245
+ '',
246
+ )
247
+ case 'uniform':
248
+ if ('size' in slot) {
249
+ return `${slot.type} ${slot.kind} ${toID(slot.key)}[${slot.size}];`
250
+ }
251
+ return `${slot.type} ${slot.kind} ${toID(slot.key)};`
220
252
  }
221
253
 
222
- if (slot.type === 'interleavedAttribute') {
223
- return slot.layout.reduce(
224
- (a, v) =>
225
- v300 ? `${a}in ${v.kind} ${toID(v.key)};\n` : `${a}attribute ${v.kind} ${toID(v.key)};\n`,
226
- '',
227
- )
254
+ if (slot.type === 'attribute') {
255
+ return `${v300 ? 'in' : slot.type} ${slot.kind} ${toID(slot.key)};`
228
256
  }
229
257
 
230
- if (slot.type === 'uniform' && 'size' in slot) {
231
- return `${slot.type} ${slot.kind} ${toID(slot.key)}[${slot.size}];`
232
- }
258
+ throw new Error(`Unexpected slot: ${JSON.stringify(slot)}`)
259
+ }
260
+
261
+ const QUAD_FLOAT_ARRAY = new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1])
262
+ const QUAD_BUFFER_MAP = createUpsertMap(WeakMap<GL, WebGLBuffer>)
263
+
264
+ compile.toQuad = function <TFragment extends GLSL, TSchema extends ViewSchemaPartial>(
265
+ gl: GL,
266
+ fragment: TFragment,
267
+ options?: CompileOptions<TSchema>,
268
+ ) {
269
+ const buffer = QUAD_BUFFER_MAP.getOrInsert(gl, gl.createBuffer.bind(gl))
270
+
271
+ const webgl2 = options?.webgl2 ?? fragment.template[0]?.startsWith('#version 300 es')
272
+
273
+ const vertex = webgl2
274
+ ? glsl`#version 300 es
275
+ precision mediump float;
276
+
277
+ ${attribute.vec2('a_quad', { buffer })}
278
+
279
+ out vec2 v_uv;
280
+
281
+ void main() {
282
+ v_uv = a_quad;
283
+ gl_Position = vec4(v_uv, 0.0, 1.0);
284
+ }`
285
+ : glsl`precision mediump float;
286
+
287
+ ${attribute.vec2('a_quad', { buffer })}
288
+
289
+ varying vec2 v_uv;
290
+
291
+ void main() {
292
+ v_uv = a_quad;
293
+ gl_Position = vec4(v_uv, 0.0, 1.0);
294
+ }`
295
+
296
+ const result = compile(gl, vertex, fragment, options)
297
+
298
+ // @ts-expect-error
299
+ result.view.attributes.a_quad.set(QUAD_FLOAT_ARRAY)
233
300
 
234
- return `${slot.type === 'attribute' && v300 ? 'in' : slot.type} ${slot.kind} ${toID(slot.key)};`
301
+ return result
235
302
  }
package/src/types.ts CHANGED
@@ -416,7 +416,7 @@ export interface AttributeMethods<T = AttributeKind> {
416
416
  }
417
417
 
418
418
  export type AttributeView<T extends AttributeSchema> = {
419
- [K in keyof T]: AttributeMethods<T[K]['kind']>
419
+ [K in keyof T]: Prettify<AttributeMethods<T[K]['kind']>>
420
420
  }
421
421
 
422
422
  /**********************************************************************************/
@@ -635,12 +635,12 @@ export interface ViewSchemaPartial {
635
635
  export type View<T extends ViewSchema = ViewSchema> = {
636
636
  attributes: T['attributes'] extends AttributeSchema
637
637
  ? Prettify<AttributeView<T['attributes']>>
638
- : never
638
+ : {}
639
639
  interleavedAttributes: T['interleavedAttributes'] extends InterleavedAttributeSchema
640
640
  ? Prettify<InterleavedAttributeView<T['interleavedAttributes']>>
641
- : never
642
- buffers: T['buffers'] extends BufferSchema ? Prettify<BufferView<T['buffers']>> : never
643
- uniforms: T['uniforms'] extends UniformSchema ? Prettify<UniformView<T['uniforms']>> : never
641
+ : {}
642
+ buffers: T['buffers'] extends BufferSchema ? Prettify<BufferView<T['buffers']>> : {}
643
+ uniforms: T['uniforms'] extends UniformSchema ? Prettify<UniformView<T['uniforms']>> : {}
644
644
  }
645
645
 
646
646
  /**********************************************************************************/
@@ -703,7 +703,7 @@ export interface AttributeToken<
703
703
  > {
704
704
  type: 'attribute'
705
705
  kind: TKind
706
- instanced: TInstanced
706
+ instanced: TInstanced extends boolean ? boolean : false
707
707
  key: TKey
708
708
  }
709
709
 
@@ -768,26 +768,40 @@ type _FlattenSlots<T extends Array<GLSLSlot>> = T extends [infer First, ...infer
768
768
  export type MergeGLSLSchema<
769
769
  TVertex extends ViewSchema,
770
770
  TFragment extends ViewSchema,
771
- TOverrides extends ViewSchemaPartial = {},
772
- > = {
773
- uniforms: DeepMerge<
774
- [ShallowMerge<[TVertex['uniforms'], TFragment['uniforms']]>, TOverrides['uniforms']]
775
- >
776
- attributes: DeepMerge<
777
- [ShallowMerge<[TVertex['attributes'], TFragment['attributes']]>, TOverrides['attributes']]
778
- >
779
- interleavedAttributes: DeepMerge<
780
- [
781
- ShallowMerge<[TVertex['interleavedAttributes'], TFragment['interleavedAttributes']]>,
782
- TOverrides['interleavedAttributes'],
783
- ]
784
- >
785
- }
771
+ TOverrides extends ViewSchemaPartial | undefined = {},
772
+ > = TOverrides extends ViewSchemaPartial
773
+ ? {
774
+ uniforms: Prettify<
775
+ DeepMerge<
776
+ [ShallowMerge<[TVertex['uniforms'], TFragment['uniforms']]>, TOverrides['uniforms']]
777
+ >
778
+ >
779
+ attributes: Prettify<
780
+ DeepMerge<
781
+ [ShallowMerge<[TVertex['attributes'], TFragment['attributes']]>, TOverrides['attributes']]
782
+ >
783
+ >
784
+ interleavedAttributes: Prettify<
785
+ DeepMerge<
786
+ [
787
+ ShallowMerge<[TVertex['interleavedAttributes'], TFragment['interleavedAttributes']]>,
788
+ TOverrides['interleavedAttributes'],
789
+ ]
790
+ >
791
+ >
792
+ }
793
+ : {
794
+ uniforms: Prettify<ShallowMerge<[TVertex['uniforms'], TFragment['uniforms']]>>
795
+ attributes: Prettify<ShallowMerge<[TVertex['attributes'], TFragment['attributes']]>>
796
+ interleavedAttributes: Prettify<
797
+ ShallowMerge<[TVertex['interleavedAttributes'], TFragment['interleavedAttributes']]>
798
+ >
799
+ }
786
800
 
787
801
  export type CompileResult<
788
802
  TVertex extends GLSL,
789
803
  TFragment extends GLSL,
790
- TOverrideSchema extends ViewSchemaPartial,
804
+ TOverrideSchema extends ViewSchemaPartial | undefined,
791
805
  > =
792
806
  MergeGLSLSchema<
793
807
  GLSLToSchema<TVertex>,