glre 0.34.0 → 0.36.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glre",
3
- "version": "0.34.0",
3
+ "version": "0.36.0",
4
4
  "author": "tseijp",
5
5
  "license": "MIT",
6
6
  "private": false,
package/src/index.ts CHANGED
@@ -8,9 +8,6 @@ import type { GL } from './types'
8
8
  import { float, fract, int, iResolution, position, vec4, vertexIndex } from './node'
9
9
  export * from './node'
10
10
  export * from './types'
11
- export * from './utils/helpers'
12
- export * from './utils/pipeline'
13
- export * from './utils/program'
14
11
  export * from './webgl'
15
12
  export * from './webgpu'
16
13
 
@@ -45,9 +42,10 @@ const defaultVertex = () =>
45
42
  )
46
43
 
47
44
  export const createGL = (props?: Partial<GL>) => {
48
- const gl = event<Partial<GL>>({
45
+ const gl = event({
49
46
  isNative: false,
50
47
  isWebGL: true,
48
+ isError: false,
51
49
  isLoop: true,
52
50
  isGL: true,
53
51
  size: [0, 0],
@@ -55,27 +53,37 @@ export const createGL = (props?: Partial<GL>) => {
55
53
  count: 6,
56
54
  webgl: {},
57
55
  webgpu: {},
56
+ loading: 0,
57
+ error() {
58
+ gl.isError = true
59
+ gl.isLoop = false
60
+ gl.clean()
61
+ },
58
62
  }) as EventState<GL>
59
63
 
60
64
  gl.queue = createQueue()
61
65
  gl.frame = createFrame()
62
66
 
63
67
  gl.attribute = durable((k, v, i) => gl.queue(() => gl._attribute?.(k, v, i)), gl)
64
- gl.uniform = durable((k, v, i) => gl.queue(() => gl._uniform?.(k, v, i)), gl)
68
+ gl.storage = durable((k, v) => gl.queue(() => gl._storage?.(k, v)), gl)
69
+ gl.uniform = durable((k, v) => gl.queue(() => gl._uniform?.(k, v)), gl)
65
70
  gl.texture = durable((k, v) => gl.queue(() => gl._texture?.(k, v)), gl)
66
71
  gl.uniform({ iResolution: gl.size, iMouse: [0, 0], iTime })
67
72
 
68
73
  gl('mount', async () => {
69
74
  gl.vs = gl.vs || gl.vert || gl.vertex || defaultVertex()
70
75
  gl.fs = gl.fs || gl.frag || gl.fragment || defaultFragment()
76
+ gl.cs = gl.cs || gl.comp || gl.compute
71
77
  if (!isWebGPUSupported()) gl.isWebGL = true
72
78
  if (gl.isWebGL) {
73
79
  gl((await webgl(gl)) as GL)
74
80
  } else gl((await webgpu(gl)) as GL)
81
+ if (gl.isError) return // stop if error
75
82
  gl.resize()
76
83
  gl.frame(() => {
77
84
  gl.loop()
78
85
  gl.queue.flush()
86
+ if (gl.loading) return true // wait for textures @TODO FIX
79
87
  gl.render()
80
88
  return gl.isLoop
81
89
  })
@@ -86,7 +94,6 @@ export const createGL = (props?: Partial<GL>) => {
86
94
 
87
95
  gl('clean', () => {
88
96
  gl.frame.stop()
89
- gl.frame.clean(gl.render)
90
97
  if (gl.isNative) return
91
98
  window.removeEventListener('resize', gl.resize)
92
99
  gl.el.removeEventListener('mousemove', gl.mousemove)
@@ -0,0 +1,100 @@
1
+ import { code } from './utils'
2
+ import { is } from '../utils/helpers'
3
+ import type { NodeContext, X } from './types'
4
+
5
+ const GLSL_FRAGMENT_HEAD = `
6
+ #version 300 es
7
+ precision mediump float;
8
+ out vec4 fragColor;
9
+ `.trim()
10
+
11
+ const topological = (headers: Map<string, string>, dependencies: Map<string, Set<string>>) => {
12
+ const sorted: [string, string][] = []
13
+ const visited = new Set<string>()
14
+ const visiting = new Set<string>()
15
+ const visit = (id: string) => {
16
+ if (visiting.has(id)) return
17
+ if (visited.has(id)) return
18
+ visiting.add(id)
19
+ const deps = dependencies.get(id) || new Set()
20
+ for (const dep of deps) if (headers.has(dep)) visit(dep)
21
+ visiting.delete(id)
22
+ visited.add(id)
23
+ if (headers.has(id)) sorted.push([id, headers.get(id)!])
24
+ }
25
+ for (const [id] of headers) visit(id)
26
+ return sorted
27
+ }
28
+
29
+ const generateHead = (x: X, c: NodeContext) => {
30
+ const body = code(x, c)
31
+ let head = ''
32
+ if (c.isWebGL && c.code?.dependencies) {
33
+ const sorted = topological(c.code.headers, c.code.dependencies)
34
+ head = sorted.map(([, value]) => value).join('\n')
35
+ } else head = Array.from(c.code?.headers?.values() || []).join('\n')
36
+ return [head, body]
37
+ }
38
+
39
+ const generateStruct = (id: string, map: Map<string, string>) => {
40
+ return `struct ${id} {\n ${Array.from(map.values()).join(',\n ')}\n}`
41
+ }
42
+
43
+ export const fragment = (x: X, c: NodeContext) => {
44
+ if (is.str(x)) return x.trim()
45
+ c.code?.headers?.clear()
46
+ c.isFrag = true // for varying inputs or outputs
47
+ const [head, body] = generateHead(x, c)
48
+ const ret = []
49
+ if (c.isWebGL) {
50
+ ret.push(GLSL_FRAGMENT_HEAD)
51
+ for (const code of c.code?.fragInputs?.values() || []) ret.push(`in ${code}`)
52
+ ret.push(head)
53
+ ret.push(`void main() {\n fragColor = ${body};`)
54
+ } else {
55
+ if (c.code?.fragInputs?.size) ret.push(generateStruct('Out', c.code.fragInputs))
56
+ ret.push(head)
57
+ ret.push(`@fragment\nfn main(out: Out) -> @location(0) vec4f {`)
58
+ ret.push(` return ${body};`)
59
+ }
60
+ ret.push('}')
61
+ const main = ret.filter(Boolean).join('\n').trim()
62
+ // console.log(`↓↓↓generated↓↓↓\n${main}`)
63
+ return main
64
+ }
65
+
66
+ export const vertex = (x: X, c: NodeContext) => {
67
+ if (is.str(x)) return x.trim()
68
+ c.code?.headers?.clear()
69
+ c.isFrag = false // for varying inputs or outputs
70
+ const [head, body] = generateHead(x, c)
71
+ const ret = []
72
+ if (c.isWebGL) {
73
+ ret.push('#version 300 es')
74
+ for (const code of c.code?.vertInputs?.values() || []) ret.push(`in ${code}`)
75
+ for (const code of c.code?.vertOutputs?.values() || []) ret.push(`out ${code}`)
76
+ ret.push(head)
77
+ ret.push('void main() {')
78
+ ret.push(` gl_Position = ${body};`)
79
+ for (const [id, code] of c.code?.vertVaryings?.entries() || []) ret.push(` ${id} = ${code};`)
80
+ } else {
81
+ if (c.code?.vertInputs?.size) ret.push(generateStruct('In', c.code.vertInputs))
82
+ if (c.code?.vertOutputs?.size) ret.push(generateStruct('Out', c.code.vertOutputs))
83
+ ret.push(head)
84
+ ret.push('@vertex')
85
+ ret.push(`fn main(${c.code?.vertInputs?.size ? 'in: In' : ''}) -> Out {`)
86
+ ret.push(' var out: Out;')
87
+ ret.push(` out.position = ${body};`)
88
+ for (const [id, code] of c.code?.vertVaryings?.entries() || []) ret.push(` out.${id} = ${code};`)
89
+ ret.push(' return out;')
90
+ }
91
+ ret.push('}')
92
+ const main = ret.filter(Boolean).join('\n').trim()
93
+ // console.log(`↓↓↓generated↓↓↓\n${main}`)
94
+ return main
95
+ }
96
+
97
+ export const compute = (x: X, _c: NodeContext) => {
98
+ if (is.str(x)) return x.trim()
99
+ return ''
100
+ }
package/src/node/index.ts CHANGED
@@ -1,107 +1,31 @@
1
+ import { hex2rgb } from './utils'
2
+ import { builtin as b, conversion as c, function_ as f, uniform as u } from './node'
1
3
  import { is } from '../utils/helpers'
2
- import { code } from './code'
3
- import { builtin, conversion as c, function_ as f, uniform as u } from './node'
4
- import { hex2rgb, sortHeadersByDependencies } from './utils'
5
- import type { Constants as C, NodeContext, X, Vec2, Float } from './types'
6
- export * from './code'
4
+ import type { Constants as C, X, Vec2, Float, NodeProxy } from './types'
5
+ export * from './core'
7
6
  export * from './node'
8
7
  export * from './scope'
9
8
  export * from './types'
10
- export * from './utils'
11
-
12
- const GLSL_FRAGMENT_HEAD = `
13
- #version 300 es
14
- precision mediump float;
15
- out vec4 fragColor;
16
- `.trim()
17
-
18
- const generateHead = (x: X, c: NodeContext) => {
19
- const body = code(x, c)
20
- let head = ''
21
- if (c.isWebGL && c.code?.dependencies) {
22
- const sorted = sortHeadersByDependencies(c.code.headers, c.code.dependencies)
23
- head = sorted.map(([, value]) => value).join('\n')
24
- } else head = Array.from(c.code?.headers?.values() || []).join('\n')
25
- return [head, body]
26
- }
27
-
28
- const generateStruct = (id: string, map: Map<string, string>) => {
29
- return `struct ${id} {\n ${Array.from(map.values()).join(',\n ')}\n}`
30
- }
31
-
32
- export const vertex = (x: X, c: NodeContext) => {
33
- if (is.str(x)) return x.trim()
34
- c.code?.headers?.clear()
35
- c.isFrag = false // for varying inputs or outputs
36
- const [head, body] = generateHead(x, c)
37
- const ret = []
38
- if (c.isWebGL) {
39
- ret.push('#version 300 es')
40
- for (const code of c.code?.vertInputs?.values() || []) ret.push(`in ${code}`)
41
- for (const code of c.code?.vertOutputs?.values() || []) ret.push(`out ${code}`)
42
- ret.push(head)
43
- ret.push('void main() {')
44
- ret.push(` gl_Position = ${body};`)
45
- for (const [id, code] of c.code?.vertVaryings?.entries() || []) ret.push(` ${id} = ${code};`)
46
- } else {
47
- if (c.code?.vertInputs?.size) ret.push(generateStruct('In', c.code.vertInputs))
48
- if (c.code?.vertOutputs?.size) ret.push(generateStruct('Out', c.code.vertOutputs))
49
- ret.push(head)
50
- ret.push('@vertex')
51
- ret.push(`fn main(${c.code?.vertInputs?.size ? 'in: In' : ''}) -> Out {`)
52
- ret.push(' var out: Out;')
53
- ret.push(` out.position = ${body};`)
54
- for (const [id, code] of c.code?.vertVaryings?.entries() || []) ret.push(` out.${id} = ${code};`)
55
- ret.push(' return out;')
56
- }
57
- ret.push('}')
58
- const main = ret.filter(Boolean).join('\n').trim()
59
- console.log(`↓↓↓generated↓↓↓\n${main}`)
60
- return main
61
- }
62
-
63
- export const fragment = (x: X, c: NodeContext) => {
64
- if (is.str(x)) return x.trim()
65
- c.code?.headers?.clear()
66
- c.isFrag = true // for varying inputs or outputs
67
- const [head, body] = generateHead(x, c)
68
- const ret = []
69
- if (c.isWebGL) {
70
- ret.push(GLSL_FRAGMENT_HEAD)
71
- for (const code of c.code?.fragInputs?.values() || []) ret.push(`in ${code}`)
72
- ret.push(head)
73
- ret.push(`void main() {\n fragColor = ${body};`)
74
- } else {
75
- if (c.code?.fragInputs?.size) ret.push(generateStruct('Out', c.code.fragInputs))
76
- ret.push(head)
77
- ret.push(`@fragment\nfn main(out: Out) -> @location(0) vec4f {`)
78
- ret.push(` return ${body};`)
79
- }
80
- ret.push('}')
81
- const main = ret.filter(Boolean).join('\n').trim()
82
- console.log(`↓↓↓generated↓↓↓\n${main}`)
83
- return main
84
- }
85
9
 
86
10
  // Builtin Variables
87
- export const position = builtin<'vec4'>('position')
88
- export const vertexIndex = builtin<'uint'>('vertex_index')
89
- export const instanceIndex = builtin<'uint'>('instance_index')
90
- export const frontFacing = builtin<'bool'>('front_facing')
91
- export const fragDepth = builtin<'float'>('frag_depth')
92
- export const sampleIndex = builtin<'uint'>('sample_index')
93
- export const sampleMask = builtin<'uint'>('sample_mask')
94
- export const pointCoord = builtin<'vec2'>('point_coord')
11
+ export const position = b<'vec4'>('position')
12
+ export const vertexIndex = b<'uint'>('vertex_index')
13
+ export const instanceIndex = b<'uint'>('instance_index')
14
+ export const frontFacing = b<'bool'>('front_facing')
15
+ export const fragDepth = b<'float'>('frag_depth')
16
+ export const sampleIndex = b<'uint'>('sample_index')
17
+ export const sampleMask = b<'uint'>('sample_mask')
18
+ export const pointCoord = b<'vec2'>('point_coord')
95
19
 
96
20
  // TSL Compatible Builtin Variables
97
- export const positionLocal = builtin<'vec3'>('position')
98
- export const positionWorld = builtin<'vec3'>('positionWorld')
99
- export const positionView = builtin<'vec3'>('positionView')
100
- export const normalLocal = builtin<'vec3'>('normalLocal')
101
- export const normalWorld = builtin<'vec3'>('normalWorld')
102
- export const normalView = builtin<'vec3'>('normalView')
103
- export const screenCoordinate = builtin<'vec2'>('screenCoordinate')
104
- export const screenUV = builtin<'vec2'>('screenUV')
21
+ export const positionLocal = b<'vec3'>('position')
22
+ export const positionWorld = b<'vec3'>('positionWorld')
23
+ export const positionView = b<'vec3'>('positionView')
24
+ export const normalLocal = b<'vec3'>('normalLocal')
25
+ export const normalWorld = b<'vec3'>('normalWorld')
26
+ export const normalView = b<'vec3'>('normalView')
27
+ export const screenCoordinate = b<'vec2'>('screenCoordinate')
28
+ export const screenUV = b<'vec2'>('screenUV')
105
29
 
106
30
  // Type constructors with proper type inference
107
31
  export const float = (x?: X) => c('float', x)
@@ -204,7 +128,7 @@ export const step = <T extends C>(edge: X, x: X<T>) => f<T>('step', edge, x)
204
128
  export const smoothstep = <T extends C>(e0: X, e1: X, x: X<T>) => f<T>('smoothstep', e0, e1, x)
205
129
 
206
130
  // Two-argument functions with highest priority type
207
- export const atan2 = <T extends C>(y: X<T>, x: X) => f<T>('atan2', y, x)
131
+ export const atan2 = <T extends C>(x: X<T>, y: X) => f<T>('atan2', x, y)
208
132
  export const pow = <T extends C>(x: X<T>, y: X) => f<T>('pow', x, y)
209
133
 
210
134
  // Component-wise power functions
@@ -219,3 +143,4 @@ export const difference = <T extends C>(x: X<T>, y: X) => f<T>('difference', x,
219
143
  export const equals = (x: X, y: X) => f<'bool'>('equals', x, y)
220
144
  export const faceforward = <T extends C>(N: X<T>, I: X, Nref: X) => f<T>('faceforward', N, I, Nref)
221
145
  export const transformDirection = <T extends C>(dir: X<T>, matrix: X) => f<T>('transformDirection', dir, matrix)
146
+ export const mod = <T extends C>(x: NodeProxy<T>, y: X<T>) => x.sub(x.div(y).floor().mul(y))
package/src/node/node.ts CHANGED
@@ -1,7 +1,6 @@
1
- import { is } from '../utils/helpers'
2
- import { code } from './code'
1
+ import { code, getConstant, isConversion, isFunction, isOperator, getId, isArrayAccess } from './utils'
3
2
  import { assign, toVar } from './scope'
4
- import { conversionToConstant, isConversion, isFunction, isOperator, getId } from './utils'
3
+ import { is } from '../utils/helpers'
5
4
  import type { Functions, NodeProps, NodeProxy, NodeTypes, Operators, X, Constants as C } from './types'
6
5
 
7
6
  const toPrimitive = (x: X, hint: string) => {
@@ -12,23 +11,31 @@ export const node = <T extends C>(type: NodeTypes, props?: NodeProps | null, ...
12
11
  if (!props) props = {}
13
12
  if (args.length) props.children = args
14
13
  const listeners = new Set<(value: any) => void>()
15
- const get = (_: unknown, key: string | Symbol) => {
16
- if (key === 'type') return type
17
- if (key === 'props') return props
18
- if (key === 'toVar') return toVar.bind(null, x)
19
- if (key === 'assign') return assign.bind(null, x)
20
- if (key === 'isProxy') return true
21
- if (key === 'toString') return code.bind(null, x)
22
- if (key === Symbol.toPrimitive) return toPrimitive.bind(null, x)
23
- if (key === 'listeners') return listeners
24
- if (isOperator(key)) return (...y: X[]) => operator(key, x, ...y)
25
- if (isFunction(key)) return (...y: X[]) => function_(key, x, ...y)
26
- if (isConversion(key)) return () => conversion(conversionToConstant(key), x)
27
- if (is.str(key)) return member(key, x) // for struct and swizzling
14
+ const get = (_: unknown, y: string | Symbol) => {
15
+ if (y === 'type') return type
16
+ if (y === 'props') return props
17
+ if (y === 'toVar') return toVar.bind(null, x)
18
+ if (y === 'assign') return assign.bind(null, x)
19
+ if (y === 'isProxy') return true
20
+ if (y === 'toString') return code.bind(null, x)
21
+ if (y === Symbol.toPrimitive) return toPrimitive.bind(null, x)
22
+ if (y === 'listeners') return listeners
23
+ if (y === 'attribute') return (id = getId()) => attribute(x, id)
24
+ if (y === 'constant') return (id = getId()) => constant(x, id)
25
+ if (y === 'uniform') return (id = getId()) => uniform(x, id)
26
+ if (y === 'variable') return (id = getId()) => variable(id)
27
+ if (y === 'builtin') return (id = getId()) => builtin(id)
28
+ if (y === 'vertexStage') return (id = getId()) => vertexStage(x, id)
29
+ if (y === 'element') return (z: X) => element(x, z)
30
+ if (y === 'member') return (z: X) => member(x, z)
31
+ if (isOperator(y)) return (...z: X[]) => operator(y, x, ...z)
32
+ if (isFunction(y)) return (...z: X[]) => function_(y, x, ...z)
33
+ if (isConversion(y)) return () => conversion(getConstant(y), x)
34
+ if (is.str(y)) return isArrayAccess(y) ? element(x, y) : member(x, y)
28
35
  }
29
- const set = (_: unknown, key: string, y: X) => {
30
- if (key === 'value') listeners.forEach((fun) => fun(y))
31
- if (is.str(key)) member(key, x).assign(y)
36
+ const set = (_: unknown, y: string, z: X) => {
37
+ if (y === 'value') listeners.forEach((fun) => fun(z))
38
+ if (is.str(y)) member(x, y).assign(z)
32
39
  return true
33
40
  }
34
41
  const x = new Proxy({}, { get, set }) as unknown as NodeProxy<T>
@@ -46,7 +53,8 @@ export const vertexStage = <T extends C>(x: X<T>, id = getId()) => {
46
53
  }
47
54
 
48
55
  // Node shorthands with proper typing
49
- export const member = <T extends C>(key: string, x: X) => node<T>('member', null, key, x)
56
+ export const member = <T extends C>(x: X, index: X) => node<T>('member', null, x, index)
57
+ export const element = <T extends C>(x: X, index: X) => node<T>('element', null, x, index)
50
58
  export const select = <T extends C>(x: X<T>, y: X<T>, z: X) => node<T>('ternary', null, x, y, z) // z ? x : y @TODO REMOVE
51
59
  export const operator = <T extends C>(key: Operators, ...x: X[]) => node<T>('operator', null, key, ...x)
52
60
  export const function_ = <T extends C>(key: Functions, ...x: X[]) => node<T>('function', null, key, ...x)
package/src/node/scope.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { conversion, node } from './node'
2
1
  import { getId } from './utils'
2
+ import { conversion, node } from './node'
3
3
  import type { FnLayout, NodeProps, NodeProxy, X, Constants, Int } from './types'
4
4
 
5
5
  let scope: NodeProxy | null = null
@@ -45,18 +45,11 @@ export const struct = <T extends Record<string, NodeProxy>>(fields: T, id = getI
45
45
  }
46
46
 
47
47
  const scoped = (x: NodeProxy, fun: () => NodeProxy | void, y = define) => {
48
- // cache to revert
49
- const _scope = scope
50
- const _define = define
51
- // update
52
- scope = x
53
- define = y
54
- if (_scope) x.props.parent = _scope
48
+ const [_scope, _define] = [scope, define]
49
+ ;[scope, define] = [x, y]
55
50
  const z = fun()
56
51
  if (z) Return(z)
57
- // revert
58
- scope = _scope
59
- define = _define
52
+ ;[scope, define] = [_scope, _define]
60
53
  }
61
54
 
62
55
  export const If = (x: NodeProxy, fun: () => void) => {
@@ -82,8 +75,9 @@ export const If = (x: NodeProxy, fun: () => void) => {
82
75
 
83
76
  export const Loop = (x: NodeProxy, fun: (params: { i: Int }) => void) => {
84
77
  const y = node('scope')
85
- scoped(y, () => fun({ i: node<'int'>('variable', { id: 'i', inferFrom: [conversion('int', 0)] }) }))
86
- const ret = node('loop', null, x, y)
78
+ const id = getId()
79
+ scoped(y, () => fun({ i: node<'int'>('variable', { id, inferFrom: [conversion('int', 0)] }) }))
80
+ const ret = node('loop', { id }, x, y)
87
81
  addToScope(ret)
88
82
  return ret
89
83
  }
package/src/node/types.ts CHANGED
@@ -1,13 +1,9 @@
1
- import { GL } from '../types'
2
-
3
- import { CONSTANTS, CONVERSIONS, FUNCTIONS, OPERATOR_KEYS } from './const'
1
+ import { CONSTANTS, CONVERSIONS, FUNCTIONS, OPERATOR_KEYS } from './utils/const'
2
+ import type { GL } from '../types'
4
3
 
5
4
  export type Constants = (typeof CONSTANTS)[number] | 'void'
6
-
7
5
  export type Conversions = (typeof CONVERSIONS)[number]
8
-
9
6
  export type Functions = (typeof FUNCTIONS)[number]
10
-
11
7
  export type Operators = (typeof OPERATOR_KEYS)[number]
12
8
 
13
9
  export interface FnLayout {
@@ -38,6 +34,7 @@ export type NodeTypes =
38
34
  // struct
39
35
  | 'struct'
40
36
  | 'member'
37
+ | 'element'
41
38
  // scopes
42
39
  | 'scope'
43
40
  | 'assign'
@@ -55,7 +52,6 @@ export interface NodeProps<T extends Record<string, NodeProxy> = {}> {
55
52
  children?: any[]
56
53
  inferFrom?: any[]
57
54
  layout?: FnLayout
58
- parent?: NodeProxy
59
55
  // for struct
60
56
  fields?: T
61
57
  initialValues?: T
@@ -81,6 +77,20 @@ export interface NodeContext {
81
77
  /**
82
78
  * infer
83
79
  */
80
+ type _StringLength<S extends string> = S extends `${infer _}${infer Rest}`
81
+ ? Rest extends ''
82
+ ? 1
83
+ : Rest extends `${infer _}${infer Rest2}`
84
+ ? Rest2 extends ''
85
+ ? 2
86
+ : Rest2 extends `${infer _}${infer Rest3}`
87
+ ? Rest3 extends ''
88
+ ? 3
89
+ : 4
90
+ : never
91
+ : never
92
+ : 0
93
+
84
94
  // Unified logic with infer.ts inferOperator function
85
95
  // prettier-ignore
86
96
  type InferOperator<L extends Constants, R extends Constants> =
@@ -97,19 +107,13 @@ type InferOperator<L extends Constants, R extends Constants> =
97
107
  L extends 'vec3' ? R extends 'mat3' ? L /* default */ : L :
98
108
  L extends 'vec2' ? R extends 'mat2' ? L /* default */ : L : L
99
109
 
100
- type _StringLength<S extends string> = S extends `${infer _}${infer Rest}`
101
- ? Rest extends ''
102
- ? 1
103
- : Rest extends `${infer _}${infer Rest2}`
104
- ? Rest2 extends ''
105
- ? 2
106
- : Rest2 extends `${infer _}${infer Rest3}`
107
- ? Rest3 extends ''
108
- ? 3
109
- : 4
110
- : never
111
- : never
112
- : 0
110
+ // Unified logic with infer.ts inferArrayElement function
111
+ // prettier-ignore
112
+ type InferArrayElement<T extends Constants> =
113
+ T extends 'mat4' ? 'vec4' :
114
+ T extends 'mat3' ? 'vec3' :
115
+ T extends 'mat2' ? 'vec2' :
116
+ 'float'
113
117
 
114
118
  type InferSwizzleType<S extends string> = _StringLength<S> extends 4
115
119
  ? 'vec4'
@@ -142,6 +146,7 @@ type NodeProxyMethods =
142
146
  | 'assign'
143
147
  | 'toVar'
144
148
  | 'toString'
149
+ | 'element'
145
150
 
146
151
  export interface BaseNodeProxy<T extends Constants> {
147
152
  // System properties
@@ -261,6 +266,9 @@ export interface BaseNodeProxy<T extends Constants> {
261
266
  pow2(): NodeProxy<T>
262
267
  pow3(): NodeProxy<T>
263
268
  pow4(): NodeProxy<T>
269
+
270
+ // Element access for array/matrix types
271
+ element<Index extends X>(index: Index): NodeProxy<InferArrayElement<T>>
264
272
  }
265
273
 
266
274
  type ReadNodeProxy = {
@@ -1,4 +1,3 @@
1
- import { is } from '../utils/helpers'
2
1
  import { infer } from './infer'
3
2
  import {
4
3
  parseArray,
@@ -14,8 +13,11 @@ import {
14
13
  parseVaryingHead,
15
14
  parseUniformHead,
16
15
  } from './parse'
17
- import { getBluiltin, getOperator, formatConversions, safeEventCall, getEventFun, initNodeContext } from './utils'
18
- import type { Constants, NodeContext, X } from './types'
16
+ import { getBluiltin, getOperator, getConversions, safeEventCall, getEventFun, initNodeContext } from './utils'
17
+ import { is } from '../../utils/helpers'
18
+ import type { Constants, NodeContext, X } from '../types'
19
+
20
+ export * from './utils'
19
21
 
20
22
  export const code = <T extends Constants>(target: X<T>, c?: NodeContext | null): string => {
21
23
  if (!c) c = {}
@@ -31,26 +33,29 @@ export const code = <T extends Constants>(target: X<T>, c?: NodeContext | null):
31
33
  }
32
34
  if (is.bol(target)) return target ? 'true' : 'false'
33
35
  if (!target) return ''
34
- const { type, props } = target
35
- const { id = '', children = [], fields, initialValues } = props
36
+ const { type, props = {} } = target
37
+ const { id = 'i', children = [], fields, initialValues } = props
36
38
  const [x, y, z, w] = children
37
39
  /**
38
40
  * variables
39
41
  */
40
42
  if (type === 'variable') return id
41
- if (type === 'member') return `${code(y, c)}.${code(x, c)}`
43
+ if (type === 'member') return `${code(x, c)}.${code(y, c)}`
44
+ if (type === 'element') return `${code(x, c)}[${code(y, c)}]`
42
45
  if (type === 'ternary')
43
46
  return c.isWebGL
44
47
  ? `(${code(z, c)} ? ${code(x, c)} : ${code(y, c)})`
45
48
  : `select(${code(x, c)}, ${code(y, c)}, ${code(z, c)})`
46
- if (type === 'conversion') return `${formatConversions(x, c)}(${parseArray(children.slice(1), c)})`
49
+ if (type === 'conversion') return `${getConversions(x, c)}(${parseArray(children.slice(1), c)})`
47
50
  if (type === 'operator') {
48
51
  if (x === 'not' || x === 'bitNot') return `!${code(y, c)}`
49
52
  return `(${code(y, c)} ${getOperator(x)} ${code(z, c)})`
50
53
  }
51
54
  if (type === 'function') {
52
- if (x === 'negate') return `(-${parseArray(children.slice(1), c)})`
55
+ if (x === 'negate') return `(-${code(y, c)})`
56
+ if (x === 'oneMinus') return `(1.0-${code(y, c)})`
53
57
  if (x === 'texture') return parseTexture(c, y, z, w)
58
+ if (x === 'atan2' && c.isWebGL) return `atan(${code(y, c)}, ${code(z, c)})`
54
59
  return `${x}(${parseArray(children.slice(1), c)})`
55
60
  }
56
61
  /**
@@ -61,8 +66,8 @@ export const code = <T extends Constants>(target: X<T>, c?: NodeContext | null):
61
66
  if (type === 'return') return `return ${code(x, c)};`
62
67
  if (type === 'loop')
63
68
  return c.isWebGL
64
- ? `for (int i = 0; i < ${code(x, c)}; i += 1) {\n${code(y, c)}\n}`
65
- : `for (var i: i32 = 0; i < ${code(x, c)}; i++) {\n${code(y, c)}\n}`
69
+ ? `for (int ${id} = 0; ${id} < ${code(x, c)}; ${id} += 1) {\n${code(y, c)}\n}`
70
+ : `for (var ${id}: i32 = 0; ${id} < ${code(x, c)}; ${id}++) {\n${code(y, c)}\n}`
66
71
  if (type === 'if') return parseIf(c, x, y, children)
67
72
  if (type === 'switch') return parseSwitch(c, x, children)
68
73
  if (type === 'declare') return parseDeclare(c, x, y)
@@ -88,7 +93,7 @@ export const code = <T extends Constants>(target: X<T>, c?: NodeContext | null):
88
93
  if (type === 'builtin') {
89
94
  if (c.isWebGL) return getBluiltin(id)
90
95
  if (id === 'position') return 'out.position'
91
- const field = `@builtin(${id}) ${id}: ${formatConversions(infer(target, c), c)}`
96
+ const field = `@builtin(${id}) ${id}: ${getConversions(infer(target, c), c)}`
92
97
  if (c.isFrag) {
93
98
  c.code?.fragInputs.set(id, field)
94
99
  } else c.code?.vertInputs.set(id, field)
@@ -1,4 +1,4 @@
1
- import { is } from '../utils/helpers'
1
+ import { isConstants, isNodeProxy, isSwizzle } from './utils'
2
2
  import {
3
3
  BUILTIN_TYPES,
4
4
  COMPARISON_OPERATORS,
@@ -6,8 +6,8 @@ import {
6
6
  FUNCTION_RETURN_TYPES,
7
7
  LOGICAL_OPERATORS,
8
8
  } from './const'
9
- import { isConstants, isNodeProxy, isSwizzle } from './utils'
10
- import type { Constants as C, NodeContext, NodeProxy, X } from './types'
9
+ import { is } from '../../utils/helpers'
10
+ import type { Constants as C, NodeContext, NodeProxy, X } from '../types'
11
11
 
12
12
  const inferBuiltin = <T extends C>(id: string | undefined) => {
13
13
  return BUILTIN_TYPES[id as keyof typeof BUILTIN_TYPES] as T
@@ -31,6 +31,14 @@ const inferOperator = <T extends C>(L: T, R: T, op: string): T => {
31
31
  return L
32
32
  }
33
33
 
34
+ // Unified logic with infer.ts InferArrayElement type
35
+ const inferArrayElement = <T extends C>(arrayType: T): T => {
36
+ if (arrayType === 'mat4') return 'vec4' as T
37
+ if (arrayType === 'mat3') return 'vec3' as T
38
+ if (arrayType === 'mat2') return 'vec2' as T
39
+ return 'float' as T
40
+ }
41
+
34
42
  export const inferPrimitiveType = <T extends C>(x: X) => {
35
43
  if (is.bol(x)) return 'bool' as T
36
44
  if (is.str(x)) return 'texture' as T
@@ -68,10 +76,11 @@ export const inferImpl = <T extends C>(target: NodeProxy<T>, c: NodeContext): T
68
76
  if (type === 'function') return inferFunction(x) || infer(y, c)
69
77
  if (type === 'define' && isConstants(layout?.type)) return layout?.type as T
70
78
  if (type === 'attribute' && is.arr(x) && c.gl?.count) return inferFromCount(x.length / c.gl.count)
79
+ if (type === 'element') return inferArrayElement(infer(x, c) as T)
71
80
  if (type === 'member') {
72
- if (isSwizzle(x)) return inferFromCount(x.length)
73
- if (isNodeProxy(y) && is.str(x)) {
74
- const field = (y as any).props.fields?.[x] // for variable node of struct member
81
+ if (isSwizzle(y)) return inferFromCount(y.length)
82
+ if (isNodeProxy(x)) {
83
+ const field = (x as any).props.fields[y] // for variable node of struct member
75
84
  if (field) return infer(field, c)
76
85
  }
77
86
  return 'float' as T // fallback @TODO FIX