glre 0.21.0 → 0.23.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/README.md +148 -85
- package/dist/index.d.ts +364 -7
- package/dist/index.js +44 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +44 -13
- package/dist/index.mjs.map +1 -1
- package/dist/native.d.ts +27 -41
- package/dist/native.js +44 -13
- package/dist/native.js.map +1 -1
- package/dist/native.mjs +44 -13
- package/dist/native.mjs.map +1 -1
- package/dist/react.d.ts +6 -11
- package/dist/react.js +44 -13
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +44 -13
- package/dist/react.mjs.map +1 -1
- package/dist/solid.d.ts +5 -63
- package/dist/solid.js +44 -13
- package/dist/solid.js.map +1 -1
- package/dist/solid.mjs +44 -13
- package/dist/solid.mjs.map +1 -1
- package/package.json +29 -60
- package/src/code/glsl.ts +148 -0
- package/src/code/wgsl.ts +134 -0
- package/src/index.ts +107 -0
- package/src/native.ts +29 -0
- package/src/node/cache.ts +60 -0
- package/src/node/const.ts +130 -0
- package/src/node/conv.ts +111 -0
- package/src/node/index.ts +93 -0
- package/src/node/node.ts +100 -0
- package/src/node/types.ts +101 -0
- package/src/node/uniform.ts +92 -0
- package/src/react.ts +17 -0
- package/src/solid.ts +14 -0
- package/src/types.ts +89 -0
- package/src/utils/helpers.ts +44 -0
- package/src/utils/pipeline.ts +128 -0
- package/src/utils/program.ts +94 -0
- package/src/webgl.ts +78 -0
- package/src/webgpu.ts +70 -0
- package/dist/types-f429c8b1.d.ts +0 -79
package/src/code/wgsl.ts
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { is } from '../utils/helpers'
|
|
2
|
+
import type { Node, NodeType, ConversionContext, X } from '../node'
|
|
3
|
+
|
|
4
|
+
// WGSLコード生成コンテキスト
|
|
5
|
+
interface WGSLContext extends ConversionContext {
|
|
6
|
+
target: 'webgpu'
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// ノードからWGSLコードを生成
|
|
10
|
+
export const nodeToWGSL = (nodeProxy: X, context?: Partial<WGSLContext>): string => {
|
|
11
|
+
const ctx: WGSLContext = {
|
|
12
|
+
target: 'webgpu',
|
|
13
|
+
nodes: new Map(),
|
|
14
|
+
variables: new Map(),
|
|
15
|
+
functions: new Map(),
|
|
16
|
+
...context,
|
|
17
|
+
}
|
|
18
|
+
return generateWGSLExpression(nodeProxy as any, ctx)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// WGSL式を生成
|
|
22
|
+
const generateWGSLExpression = (node: Node, context: WGSLContext): string => {
|
|
23
|
+
if (!node) return '0.0'
|
|
24
|
+
// 値ノード
|
|
25
|
+
if (node.value !== undefined) return formatWGSLValue(node.value, node.type)
|
|
26
|
+
// プロパティアクセス(スウィズル)
|
|
27
|
+
if (node.property && node.parent) {
|
|
28
|
+
const parentExpr = generateWGSLExpression(node.parent, context)
|
|
29
|
+
return `${parentExpr}.${node.property}`
|
|
30
|
+
}
|
|
31
|
+
// 演算子ノード
|
|
32
|
+
if (node.operator && node.children && node.children.length >= 2) return generateWGSLOperator(node, context)
|
|
33
|
+
// 数学関数ノード
|
|
34
|
+
if (node.mathFunction && node.children && node.children.length >= 1)
|
|
35
|
+
return generateWGSLMathFunction(node, context)
|
|
36
|
+
return '0.0'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// WGSL値をフォーマット
|
|
40
|
+
const formatWGSLValue = (value: any, type: NodeType): string => {
|
|
41
|
+
if (type === 'float') {
|
|
42
|
+
const num = Number(value)
|
|
43
|
+
return num % 1 === 0 ? `${num}.0` : `${num}`
|
|
44
|
+
}
|
|
45
|
+
if (type === 'int') return `${Math.floor(Number(value))}`
|
|
46
|
+
if (type === 'bool') return Boolean(value) ? 'true' : 'false'
|
|
47
|
+
if (is.arr(value)) {
|
|
48
|
+
const values = value
|
|
49
|
+
.map((v) => {
|
|
50
|
+
const num = Number(v)
|
|
51
|
+
return num % 1 === 0 ? `${num}.0` : `${num}`
|
|
52
|
+
})
|
|
53
|
+
.join(', ')
|
|
54
|
+
if (type === 'vec2') return `vec2<f32>(${values})`
|
|
55
|
+
if (type === 'vec3') return `vec3<f32>(${values})`
|
|
56
|
+
if (type === 'vec4') return `vec4<f32>(${values})`
|
|
57
|
+
if (type === 'color') return `vec3<f32>(${values})`
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return '0.0'
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// WGSL演算子を生成
|
|
64
|
+
const generateWGSLOperator = (node: Node, context: WGSLContext): string => {
|
|
65
|
+
if (!node.children || node.children.length < 2) return '0.0'
|
|
66
|
+
const left = generateWGSLExpression(node.children[0], context)
|
|
67
|
+
const right = generateWGSLExpression(node.children[1], context)
|
|
68
|
+
if (node.operator === 'add') return `(${left} + ${right})`
|
|
69
|
+
if (node.operator === 'sub') return `(${left} - ${right})`
|
|
70
|
+
if (node.operator === 'mul') return `(${left} * ${right})`
|
|
71
|
+
if (node.operator === 'div') return `(${left} / ${right})`
|
|
72
|
+
if (node.operator === 'mod') return `(${left} % ${right})`
|
|
73
|
+
if (node.operator === 'equal') return `(${left} == ${right})`
|
|
74
|
+
if (node.operator === 'notEqual') return `(${left} != ${right})`
|
|
75
|
+
if (node.operator === 'lessThan') return `(${left} < ${right})`
|
|
76
|
+
if (node.operator === 'lessThanEqual') return `(${left} <= ${right})`
|
|
77
|
+
if (node.operator === 'greaterThan') return `(${left} > ${right})`
|
|
78
|
+
if (node.operator === 'greaterThanEqual') return `(${left} >= ${right})`
|
|
79
|
+
if (node.operator === 'and') return `(${left} && ${right})`
|
|
80
|
+
if (node.operator === 'or') return `(${left} || ${right})`
|
|
81
|
+
return `(${left} + ${right})`
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// WGSL数学関数を生成
|
|
85
|
+
const generateWGSLMathFunction = (node: Node, context: WGSLContext): string => {
|
|
86
|
+
if (!node.children || node.children.length < 1) return '0.0'
|
|
87
|
+
const fun = node.mathFunction
|
|
88
|
+
const args = node.children.map((child) => generateWGSLExpression(child, context))
|
|
89
|
+
const [x, y, z] = args
|
|
90
|
+
// @TODO FIX
|
|
91
|
+
// if (fun === 'toVar') return x // toVarは変数化のヒントなので、そのまま返す
|
|
92
|
+
if (args.length === 1) return `${fun}(${x})`
|
|
93
|
+
if (args.length === 2) return `${fun}(${x}, ${y})`
|
|
94
|
+
if (args.length === 3) return `${fun}(${x}, ${y}, ${z})`
|
|
95
|
+
return x || '0.0'
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// 完全なWGSLシェーダーを生成
|
|
99
|
+
export const wgsl = (
|
|
100
|
+
fragmentNode: X,
|
|
101
|
+
options?: {
|
|
102
|
+
uniforms?: Record<string, any>
|
|
103
|
+
}
|
|
104
|
+
) => {
|
|
105
|
+
let shader = ''
|
|
106
|
+
// ユニフォーム変数の追加
|
|
107
|
+
if (options?.uniforms) {
|
|
108
|
+
Object.entries(options.uniforms).forEach(([name, value]) => {
|
|
109
|
+
const type = inferWGSLType(value)
|
|
110
|
+
shader += `@group(0) @binding(0) var<uniform> ${name}: ${type};\n`
|
|
111
|
+
})
|
|
112
|
+
shader += '\n'
|
|
113
|
+
}
|
|
114
|
+
shader += '@fragment\n'
|
|
115
|
+
shader += 'fn main() -> @location(0) vec4<f32> {\n'
|
|
116
|
+
const fragmentExpr = nodeToWGSL(fragmentNode)
|
|
117
|
+
shader += ` return ${fragmentExpr};\n`
|
|
118
|
+
shader += '}\n'
|
|
119
|
+
|
|
120
|
+
return shader
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 値からWGSL型を推定
|
|
124
|
+
const inferWGSLType = (value: any): string => {
|
|
125
|
+
if (is.num(value)) return 'f32'
|
|
126
|
+
if (is.bol(value)) return 'bool'
|
|
127
|
+
if (is.arr(value)) {
|
|
128
|
+
const len = value.length
|
|
129
|
+
if (len === 2) return 'vec2<f32>'
|
|
130
|
+
if (len === 3) return 'vec3<f32>'
|
|
131
|
+
if (len === 4) return 'vec4<f32>'
|
|
132
|
+
}
|
|
133
|
+
return 'f32'
|
|
134
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { durable, event } from 'reev'
|
|
2
|
+
import { createFrame, createQueue } from 'refr'
|
|
3
|
+
import { webgl } from './webgl'
|
|
4
|
+
import { webgpu } from './webgpu'
|
|
5
|
+
import { is } from './utils/helpers'
|
|
6
|
+
import type { EventState } from 'reev'
|
|
7
|
+
import type { GL } from './types'
|
|
8
|
+
export * from './code/glsl'
|
|
9
|
+
export * from './code/wgsl'
|
|
10
|
+
export * from './node'
|
|
11
|
+
export * from './types'
|
|
12
|
+
export * from './utils/helpers'
|
|
13
|
+
export * from './utils/pipeline'
|
|
14
|
+
export * from './utils/program'
|
|
15
|
+
export * from './webgl'
|
|
16
|
+
export * from './webgpu'
|
|
17
|
+
|
|
18
|
+
export const isGL = (a: unknown): a is EventState<GL> => {
|
|
19
|
+
if (!is.obj(a)) return false
|
|
20
|
+
if ('isGL' in a) return true
|
|
21
|
+
return false
|
|
22
|
+
}
|
|
23
|
+
export const isServer = () => {
|
|
24
|
+
return typeof window === 'undefined'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const isWebGPUSupported = () => {
|
|
28
|
+
if (isServer()) return false
|
|
29
|
+
return 'gpu' in navigator
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let iTime = performance.now(),
|
|
33
|
+
iPrevTime = 0,
|
|
34
|
+
iDeltaTime = 0
|
|
35
|
+
|
|
36
|
+
export const createGL = (props?: Partial<GL>) => {
|
|
37
|
+
const gl = event<Partial<GL>>({
|
|
38
|
+
isNative: false,
|
|
39
|
+
isWebGL: true,
|
|
40
|
+
isLoop: true,
|
|
41
|
+
isGL: true,
|
|
42
|
+
size: [0, 0],
|
|
43
|
+
mouse: [0, 0],
|
|
44
|
+
count: 3,
|
|
45
|
+
webgl: {},
|
|
46
|
+
webgpu: {},
|
|
47
|
+
}) as EventState<GL>
|
|
48
|
+
|
|
49
|
+
gl.queue = createQueue()
|
|
50
|
+
gl.frame = createFrame()
|
|
51
|
+
|
|
52
|
+
gl.attribute = durable((k, v, i) => gl.queue(() => gl._attribute?.(k, v, i)))
|
|
53
|
+
gl.uniform = durable((k, v, i) => gl.queue(() => gl._uniform?.(k, v, i)))
|
|
54
|
+
gl.texture = durable((k, v) => gl.queue(() => gl._texture?.(k, v)))
|
|
55
|
+
|
|
56
|
+
gl('mount', async () => {
|
|
57
|
+
if (!isWebGPUSupported()) gl.isWebGL = true
|
|
58
|
+
if (gl.isWebGL) {
|
|
59
|
+
await webgl(gl)
|
|
60
|
+
} else await webgpu(gl)
|
|
61
|
+
gl.resize()
|
|
62
|
+
gl.frame(() => {
|
|
63
|
+
gl.loop()
|
|
64
|
+
gl.render()
|
|
65
|
+
return gl.isLoop
|
|
66
|
+
})
|
|
67
|
+
if (gl.isNative) return
|
|
68
|
+
window.addEventListener('resize', gl.resize)
|
|
69
|
+
gl.el.addEventListener('mousemove', gl.mousemove)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
gl('clean', () => {
|
|
73
|
+
gl.frame.stop()
|
|
74
|
+
gl.frame.clean(gl.render)
|
|
75
|
+
if (gl.isNative) return
|
|
76
|
+
window.removeEventListener('resize', gl.resize)
|
|
77
|
+
gl.el.removeEventListener('mousemove', gl.mousemove)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
gl('loop', () => {
|
|
81
|
+
iPrevTime = iTime
|
|
82
|
+
iTime = performance.now() / 1000
|
|
83
|
+
iDeltaTime = iTime - iPrevTime
|
|
84
|
+
gl.uniform({ iPrevTime, iTime, iDeltaTime })
|
|
85
|
+
// if (gl.fragmentNode) updateUniforms({ time: iTime }) // @TODO FIX
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
gl('resize', () => {
|
|
89
|
+
const w = gl.width || window.innerWidth
|
|
90
|
+
const h = gl.height || window.innerHeight
|
|
91
|
+
gl.size[0] = gl.el.width = w
|
|
92
|
+
gl.size[1] = gl.el.height = h
|
|
93
|
+
gl.uniform('iResolution', gl.size)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
gl('mousemove', (_e: any, x = _e.clientX, y = _e.clientY) => {
|
|
97
|
+
const [w, h] = gl.size
|
|
98
|
+
const { top, left } = gl.el.getBoundingClientRect()
|
|
99
|
+
gl.mouse[0] = (x - top - w / 2) / (w / 2)
|
|
100
|
+
gl.mouse[1] = -(y - left - h / 2) / (h / 2)
|
|
101
|
+
gl.uniform('iMouse', gl.mouse)
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
return gl(props)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export default createGL
|
package/src/native.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { useState } from 'react' // @ts-ignore
|
|
2
|
+
import { Dimensions } from 'react-native'
|
|
3
|
+
import { createGL, isGL } from './index'
|
|
4
|
+
import type { GL } from './types'
|
|
5
|
+
export * from './index'
|
|
6
|
+
|
|
7
|
+
export const useGL = (props: Partial<GL> = {}) => {
|
|
8
|
+
return useState(() => {
|
|
9
|
+
const gl = isGL(props) ? props : createGL(props)
|
|
10
|
+
gl.ref = (ctx: any) => {
|
|
11
|
+
gl.el = {} as any
|
|
12
|
+
gl({
|
|
13
|
+
render() {
|
|
14
|
+
ctx.flush()
|
|
15
|
+
ctx.endFrameEXP()
|
|
16
|
+
},
|
|
17
|
+
})
|
|
18
|
+
gl.mount()
|
|
19
|
+
const resize = () => {
|
|
20
|
+
gl.width = ctx.drawingBufferWidth
|
|
21
|
+
gl.height = ctx.drawingBufferHeight
|
|
22
|
+
gl.resize()
|
|
23
|
+
}
|
|
24
|
+
resize()
|
|
25
|
+
Dimensions.addEventListener('change', resize)
|
|
26
|
+
}
|
|
27
|
+
return gl({ isNative: true })
|
|
28
|
+
})[0]
|
|
29
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { CACHE_BOOLS, CACHE_INTS, CACHE_FLOATS } from './const'
|
|
2
|
+
import { node } from '.'
|
|
3
|
+
import type { X } from './types'
|
|
4
|
+
|
|
5
|
+
const boolCache = new Map<boolean, X>()
|
|
6
|
+
const intCache = new Map<number, X>()
|
|
7
|
+
const floatCache = new Map<number, X>()
|
|
8
|
+
|
|
9
|
+
const initializeCache = () => {
|
|
10
|
+
CACHE_BOOLS.forEach((value) => {
|
|
11
|
+
boolCache.set(value, node('bool', value))
|
|
12
|
+
})
|
|
13
|
+
CACHE_INTS.forEach((value) => {
|
|
14
|
+
intCache.set(value, node('int', value))
|
|
15
|
+
})
|
|
16
|
+
CACHE_FLOATS.forEach((value) => {
|
|
17
|
+
floatCache.set(value, node('float', value))
|
|
18
|
+
})
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const getCachedBool = (x: boolean): X => {
|
|
22
|
+
if (!boolCache.has(x)) initializeCache()
|
|
23
|
+
return boolCache.get(x) || node('bool', x)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// キャッシュされたintノードを取得
|
|
27
|
+
export const getCachedInt = (x: number): X => {
|
|
28
|
+
if (intCache.has(x)) return intCache.get(x)!
|
|
29
|
+
return node('int', x)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// キャッシュされたfloatノードを取得
|
|
33
|
+
export const getCachedFloat = (x: number): X => {
|
|
34
|
+
if (floatCache.has(x)) return floatCache.get(x)!
|
|
35
|
+
return node('float', x)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ノードの重複を検出
|
|
39
|
+
export const findDuplicateNodes = (nodes: X[]): Map<string, X[]> => {
|
|
40
|
+
const duplicates = new Map<string, X[]>()
|
|
41
|
+
const signatures = new Map<string, X>()
|
|
42
|
+
|
|
43
|
+
nodes.forEach((nodeProxy) => {
|
|
44
|
+
const signature = generateNodeSignature(nodeProxy)
|
|
45
|
+
if (signatures.has(signature)) {
|
|
46
|
+
if (!duplicates.has(signature)) duplicates.set(signature, [signatures.get(signature)!])
|
|
47
|
+
duplicates.get(signature)!.push(nodeProxy)
|
|
48
|
+
} else signatures.set(signature, nodeProxy)
|
|
49
|
+
})
|
|
50
|
+
return duplicates
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ノードのシグネチャを生成
|
|
54
|
+
const generateNodeSignature = (nodeProxy: X): string => {
|
|
55
|
+
const parts = [nodeProxy.type, `${nodeProxy.value}`, nodeProxy.property || '']
|
|
56
|
+
return parts.join('|')
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 初期化を実行
|
|
60
|
+
// initializeCache()
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// 基本型定数
|
|
2
|
+
export const TYPES = [
|
|
3
|
+
'float',
|
|
4
|
+
'int',
|
|
5
|
+
'uint',
|
|
6
|
+
'bool',
|
|
7
|
+
'color',
|
|
8
|
+
'vec2',
|
|
9
|
+
'vec3',
|
|
10
|
+
'vec4',
|
|
11
|
+
'mat2',
|
|
12
|
+
'mat3',
|
|
13
|
+
'mat4',
|
|
14
|
+
'ivec2',
|
|
15
|
+
'ivec3',
|
|
16
|
+
'ivec4',
|
|
17
|
+
'uvec2',
|
|
18
|
+
'uvec3',
|
|
19
|
+
'uvec4',
|
|
20
|
+
'bvec2',
|
|
21
|
+
'bvec3',
|
|
22
|
+
'bvec4',
|
|
23
|
+
] as const
|
|
24
|
+
|
|
25
|
+
export type NodeType = (typeof TYPES)[number]
|
|
26
|
+
|
|
27
|
+
// スウィズル定数
|
|
28
|
+
export const SWIZZLES = ['x', 'y', 'z', 'w', 'r', 'g', 'b', 'a', 's', 't', 'p', 'q'] as const
|
|
29
|
+
|
|
30
|
+
type AllSwizzles<T extends string> = T | `${T}${T}` | `${T}${T}${T}` | `${T}${T}${T}${T}`
|
|
31
|
+
|
|
32
|
+
export type Swillzes =
|
|
33
|
+
| AllSwizzles<'x' | 'y' | 'z' | 'w'>
|
|
34
|
+
| AllSwizzles<'r' | 'g' | 'b' | 'a'>
|
|
35
|
+
| AllSwizzles<'p' | 'q'>
|
|
36
|
+
| AllSwizzles<'s' | 't'>
|
|
37
|
+
|
|
38
|
+
// 演算子定数
|
|
39
|
+
export const OPERATORS = [
|
|
40
|
+
'add',
|
|
41
|
+
'sub',
|
|
42
|
+
'mul',
|
|
43
|
+
'div',
|
|
44
|
+
'mod',
|
|
45
|
+
'equal',
|
|
46
|
+
'notEqual',
|
|
47
|
+
'lessThan',
|
|
48
|
+
'lessThanEqual',
|
|
49
|
+
'greaterThan',
|
|
50
|
+
'greaterThanEqual',
|
|
51
|
+
'and',
|
|
52
|
+
'or',
|
|
53
|
+
'not',
|
|
54
|
+
'assign',
|
|
55
|
+
'xor',
|
|
56
|
+
'bitAnd',
|
|
57
|
+
'bitNot',
|
|
58
|
+
'bitOr',
|
|
59
|
+
'bitXor',
|
|
60
|
+
'shiftLeft',
|
|
61
|
+
'shiftRight',
|
|
62
|
+
] as const
|
|
63
|
+
|
|
64
|
+
export type Operator = (typeof OPERATORS)[number]
|
|
65
|
+
|
|
66
|
+
// 数学関数定数
|
|
67
|
+
export const FUNCTIONS = [
|
|
68
|
+
'abs',
|
|
69
|
+
'acos',
|
|
70
|
+
'asin',
|
|
71
|
+
'atan',
|
|
72
|
+
'atan2',
|
|
73
|
+
'ceil',
|
|
74
|
+
'clamp',
|
|
75
|
+
'cos',
|
|
76
|
+
'cross',
|
|
77
|
+
'degrees',
|
|
78
|
+
'distance',
|
|
79
|
+
'dot',
|
|
80
|
+
'exp',
|
|
81
|
+
'exp2',
|
|
82
|
+
'faceforward',
|
|
83
|
+
'floor',
|
|
84
|
+
'fract',
|
|
85
|
+
'length',
|
|
86
|
+
'all',
|
|
87
|
+
'any',
|
|
88
|
+
'bitcast',
|
|
89
|
+
'cbrt',
|
|
90
|
+
'dFdx',
|
|
91
|
+
'dFdy',
|
|
92
|
+
'difference',
|
|
93
|
+
'equals',
|
|
94
|
+
'fwidth',
|
|
95
|
+
'inverseSqrt',
|
|
96
|
+
'lengthSq',
|
|
97
|
+
'log',
|
|
98
|
+
'log2',
|
|
99
|
+
'max',
|
|
100
|
+
'min',
|
|
101
|
+
'mix',
|
|
102
|
+
'negate',
|
|
103
|
+
'normalize',
|
|
104
|
+
'oneMinus',
|
|
105
|
+
'pow',
|
|
106
|
+
'pow2',
|
|
107
|
+
'pow3',
|
|
108
|
+
'pow4',
|
|
109
|
+
'radians',
|
|
110
|
+
'reciprocal',
|
|
111
|
+
'reflect',
|
|
112
|
+
'refract',
|
|
113
|
+
'round',
|
|
114
|
+
'saturate',
|
|
115
|
+
'sign',
|
|
116
|
+
'sin',
|
|
117
|
+
'smoothstep',
|
|
118
|
+
'sqrt',
|
|
119
|
+
'step',
|
|
120
|
+
'tan',
|
|
121
|
+
'transformDirection',
|
|
122
|
+
'trunc',
|
|
123
|
+
] as const
|
|
124
|
+
|
|
125
|
+
export type MathFunction = (typeof FUNCTIONS)[number]
|
|
126
|
+
|
|
127
|
+
// キャッシュ用定数
|
|
128
|
+
export const CACHE_BOOLS = [true, false] as const
|
|
129
|
+
export const CACHE_INTS = [0, 1, 2, 3, 4, 5] as const
|
|
130
|
+
export const CACHE_FLOATS = [0.0, 1.0, 0.5, 2.0] as const
|
package/src/node/conv.ts
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { node } from '.'
|
|
2
|
+
import { getCachedBool, getCachedInt, getCachedFloat } from './cache'
|
|
3
|
+
import { is } from '../utils/helpers'
|
|
4
|
+
import type { X } from './types'
|
|
5
|
+
|
|
6
|
+
// JavaScript値をノードに変換
|
|
7
|
+
export const convertToNode = (x: unknown): X => {
|
|
8
|
+
if (is.bol(x)) return getCachedBool(x)
|
|
9
|
+
if (is.num(x)) {
|
|
10
|
+
if (is.int(x)) return getCachedInt(x)
|
|
11
|
+
return getCachedFloat(x)
|
|
12
|
+
}
|
|
13
|
+
if (is.arr(x)) return convertArrayToNode(x)
|
|
14
|
+
if (is.obj(x)) return convertObjectToNode(x)
|
|
15
|
+
return node('float', 0)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// 配列をベクトル/行列ノードに変換
|
|
19
|
+
const convertArrayToNode = (array: number[]): X => {
|
|
20
|
+
const len = array.length
|
|
21
|
+
if (len === 2) return node('vec2', array)
|
|
22
|
+
if (len === 3) return node('vec3', array)
|
|
23
|
+
if (len === 4) return node('vec4', array)
|
|
24
|
+
if (len === 9) return node('mat3', array)
|
|
25
|
+
if (len === 16) return node('mat4', array)
|
|
26
|
+
return node('float', array[0])
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// オブジェクトをノードに変換
|
|
30
|
+
const convertObjectToNode = (obj: any): X => {
|
|
31
|
+
// カラーオブジェクトの検出
|
|
32
|
+
if ('r' in obj && 'g' in obj && 'b' in obj) {
|
|
33
|
+
const arr = [obj.r, obj.g, obj.b]
|
|
34
|
+
if ('a' in obj) arr.push(obj.a)
|
|
35
|
+
return node('color', arr)
|
|
36
|
+
}
|
|
37
|
+
// ベクトルライクオブジェクトの検出
|
|
38
|
+
if ('x' in obj && 'y' in obj) {
|
|
39
|
+
const arr = [obj.x, obj.y]
|
|
40
|
+
if ('z' in obj) arr.push(obj.z)
|
|
41
|
+
if ('w' in obj) arr.push(obj.w)
|
|
42
|
+
return convertArrayToNode(arr)
|
|
43
|
+
}
|
|
44
|
+
return node('float', 0)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 型変換関数
|
|
48
|
+
export const float = (x: unknown): X => {
|
|
49
|
+
if (is.num(x)) return getCachedFloat(x)
|
|
50
|
+
return node('float', Number(x))
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const int = (x: unknown): X => {
|
|
54
|
+
if (is.num(x) && Number.isInteger(x)) return getCachedInt(x)
|
|
55
|
+
return node('int', Math.floor(Number(x)))
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const bool = (x: unknown): X => {
|
|
59
|
+
return getCachedBool(Boolean(x))
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const vec2 = (x?: any, y?: any): X => {
|
|
63
|
+
if (is.und(x)) return node('vec2', [0, 0])
|
|
64
|
+
if (is.und(y)) {
|
|
65
|
+
if (is.arr(x)) return node('vec2', x.slice(0, 2))
|
|
66
|
+
if (is.obj(x) && 'x' in x && 'y' in x) return node('vec2', [x.x, x.y])
|
|
67
|
+
return node('vec2', [Number(x), Number(x)])
|
|
68
|
+
}
|
|
69
|
+
return node('vec2', [Number(x), Number(y)])
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export const vec3 = (x?: any, y?: any, z?: any): X => {
|
|
73
|
+
if (is.und(x)) return node('vec3', [0, 0, 0])
|
|
74
|
+
if (is.und(y)) {
|
|
75
|
+
if (is.arr(x)) return node('vec3', x.slice(0, 3))
|
|
76
|
+
if (is.obj(x) && 'x' in x && 'y' in x && 'z' in x) return node('vec3', [x.x, x.y, x.z])
|
|
77
|
+
return node('vec3', [Number(x), Number(x), Number(x)])
|
|
78
|
+
}
|
|
79
|
+
if (z === undefined) {
|
|
80
|
+
return node('vec3', [Number(x), Number(y), 0])
|
|
81
|
+
}
|
|
82
|
+
return node('vec3', [Number(x), Number(y), Number(z)])
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export const vec4 = (x?: any, y?: any, z?: any, w?: any): X => {
|
|
86
|
+
if (is.und(x)) return node('vec4', [0, 0, 0, 1])
|
|
87
|
+
if (is.und(y)) {
|
|
88
|
+
if (is.arr(x)) return node('vec4', x.slice(0, 4))
|
|
89
|
+
if (is.obj(x) && 'x' in x && 'y' in x && 'z' in x && 'w' in x) return node('vec4', [x.x, x.y, x.z, x.w])
|
|
90
|
+
return node('vec4', [Number(x), Number(x), Number(x), 1])
|
|
91
|
+
}
|
|
92
|
+
return node('vec4', [Number(x), Number(y), Number(z), Number(w)])
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const color = (r?: any, g?: any, b?: any): X => {
|
|
96
|
+
if (r === undefined) return node('color', [1, 1, 1])
|
|
97
|
+
|
|
98
|
+
// 16進数カラーの処理
|
|
99
|
+
if (is.str(r) && r.startsWith('#')) {
|
|
100
|
+
const hex = r.slice(1)
|
|
101
|
+
const num = parseInt(hex, 16)
|
|
102
|
+
return node('color', [((num >> 16) & 255) / 255, ((num >> 8) & 255) / 255, (num & 255) / 255])
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// 数値カラーの処理
|
|
106
|
+
if (is.num(r) && g === undefined && b === undefined) {
|
|
107
|
+
return node('color', [((r >> 16) & 255) / 255, ((r >> 8) & 255) / 255, (r & 255) / 255])
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return vec3(r, g, b)
|
|
111
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { float } from './conv'
|
|
2
|
+
import { node } from './node'
|
|
3
|
+
import { uniform } from './uniform'
|
|
4
|
+
import { is } from '../utils/helpers'
|
|
5
|
+
import type { X, FunctionNode, ConditionalNode } from './types'
|
|
6
|
+
export type { X, FunctionNode, ConditionalNode }
|
|
7
|
+
export * from './cache'
|
|
8
|
+
export * from './const'
|
|
9
|
+
export * from './conv'
|
|
10
|
+
export * from './node'
|
|
11
|
+
export * from './types'
|
|
12
|
+
export * from './uniform'
|
|
13
|
+
|
|
14
|
+
// 関数定義
|
|
15
|
+
export const Fn = (jsFunc: Function): FunctionNode => {
|
|
16
|
+
const functionNode = (...args: any[]) => {
|
|
17
|
+
const inputs = args.map((arg) => {
|
|
18
|
+
if (is.obj(arg) && 'type' in arg && arg.type) return arg
|
|
19
|
+
return float(arg)
|
|
20
|
+
})
|
|
21
|
+
const result = jsFunc(inputs)
|
|
22
|
+
return result || float(0)
|
|
23
|
+
}
|
|
24
|
+
functionNode.call = (inputs: X[]) => jsFunc(inputs)
|
|
25
|
+
return functionNode as FunctionNode
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 条件分岐
|
|
29
|
+
export const If = (condition: X, callback: () => void): ConditionalNode => {
|
|
30
|
+
callback()
|
|
31
|
+
|
|
32
|
+
const conditionalNode = {
|
|
33
|
+
ElseIf(newCondition: X, newCallback: () => void): ConditionalNode {
|
|
34
|
+
newCallback()
|
|
35
|
+
return conditionalNode
|
|
36
|
+
},
|
|
37
|
+
Else(elseCallback: () => void) {
|
|
38
|
+
elseCallback()
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return conditionalNode
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 組み込み変数
|
|
46
|
+
export const gl_FragCoord = node('vec4', undefined)
|
|
47
|
+
export const gl_Position = node('vec4', undefined)
|
|
48
|
+
export const iTime = uniform(0.0)
|
|
49
|
+
export const iResolution = uniform([1920, 1080])
|
|
50
|
+
export const iMouse = uniform([0, 0])
|
|
51
|
+
|
|
52
|
+
// 数学関数
|
|
53
|
+
export const abs = (x: X) => x.abs()
|
|
54
|
+
export const sin = (x: X) => x.sin()
|
|
55
|
+
export const cos = (x: X) => x.cos()
|
|
56
|
+
export const tan = (x: X) => x.tan()
|
|
57
|
+
export const sqrt = (x: X) => x.sqrt()
|
|
58
|
+
export const floor = (x: X) => x.floor()
|
|
59
|
+
export const ceil = (x: X) => x.ceil()
|
|
60
|
+
export const fract = (x: X) => x.fract()
|
|
61
|
+
export const normalize = (x: X) => x.normalize()
|
|
62
|
+
export const length = (x: X) => x.length()
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @TODO FIX
|
|
66
|
+
export const min = (a: X, b: X) => {
|
|
67
|
+
return node('float', undefined, {
|
|
68
|
+
mathFunction: 'min',
|
|
69
|
+
children: [a as any, b as any],
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const max = (a: X, b: X) => {
|
|
74
|
+
return node('float', undefined, {
|
|
75
|
+
mathFunction: 'max',
|
|
76
|
+
children: [a as any, b as any],
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const mix = (a: X, b: X, t: X) => {
|
|
81
|
+
return node('float', undefined, {
|
|
82
|
+
mathFunction: 'mix',
|
|
83
|
+
children: [a as any, b as any, t as any],
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export const clamp = (x: X, min: X, max: X) => {
|
|
88
|
+
return node('float', undefined, {
|
|
89
|
+
mathFunction: 'clamp',
|
|
90
|
+
children: [x as any, min as any, max as any],
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
*/
|