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/node/node.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { OPERATORS, FUNCTIONS, SWIZZLES } from './const'
|
|
2
|
+
import { is } from '../utils/helpers'
|
|
3
|
+
import type { NodeType } from './const'
|
|
4
|
+
import type { Node, ProxyCallback, X } from './types'
|
|
5
|
+
|
|
6
|
+
let nodeIdCounter = 0
|
|
7
|
+
|
|
8
|
+
// ノードIDを生成
|
|
9
|
+
const generateNodeId = () => `node_${++nodeIdCounter}`
|
|
10
|
+
|
|
11
|
+
// ノードを作成
|
|
12
|
+
export const createNode = (type: NodeType, value?: any, options?: Partial<Node>): Node => {
|
|
13
|
+
return {
|
|
14
|
+
id: generateNodeId(),
|
|
15
|
+
type,
|
|
16
|
+
value,
|
|
17
|
+
children: [],
|
|
18
|
+
...options,
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const isSwizzleProperty = (key = '') => SWIZZLES.includes(key as any)
|
|
23
|
+
const isOperatorMethod = (key = '') => OPERATORS.includes(key as any)
|
|
24
|
+
const isMathMethod = (key = '') => FUNCTIONS.includes(key as any)
|
|
25
|
+
|
|
26
|
+
// Proxyハンドラーを作成
|
|
27
|
+
const createNodeProxy = (node: Node, callback?: (info: ProxyCallback) => X) => {
|
|
28
|
+
const get = (_target: unknown, key: unknown) => {
|
|
29
|
+
if (!is.str(key) || key === 'then') return void 0
|
|
30
|
+
|
|
31
|
+
if (key === 'id') return node.id
|
|
32
|
+
if (key === 'type') return node.type
|
|
33
|
+
if (key === 'value') return node.value
|
|
34
|
+
if (key === 'property') return node.property
|
|
35
|
+
|
|
36
|
+
// swizzle prooerty
|
|
37
|
+
if (isSwizzleProperty(key))
|
|
38
|
+
return createNodeProxy(
|
|
39
|
+
createNode(getSwizzleType(key), undefined, {
|
|
40
|
+
parent: node,
|
|
41
|
+
property: key,
|
|
42
|
+
}),
|
|
43
|
+
callback
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
// 演算子メソッド
|
|
47
|
+
if (isOperatorMethod(key))
|
|
48
|
+
return (...args: any[]) => {
|
|
49
|
+
return createNodeProxy(
|
|
50
|
+
createNode(node.type, undefined, {
|
|
51
|
+
operator: key as any,
|
|
52
|
+
children: [node, ...args],
|
|
53
|
+
}),
|
|
54
|
+
callback
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 数学関数メソッド
|
|
59
|
+
if (isMathMethod(key))
|
|
60
|
+
return (...args: any[]) => {
|
|
61
|
+
return createNodeProxy(
|
|
62
|
+
createNode(getMathReturnType(key, node.type), undefined, {
|
|
63
|
+
mathFunction: key as any,
|
|
64
|
+
children: [node, ...args],
|
|
65
|
+
}),
|
|
66
|
+
callback
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return callback?.({ path: [key], args: [] })
|
|
71
|
+
}
|
|
72
|
+
const apply = (_target: unknown, _thisArg: unknown, args: any) => {
|
|
73
|
+
return callback?.({ path: [], args })
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return new Proxy(() => {}, { get, apply }) as X
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// スウィズルの戻り値型を取得
|
|
80
|
+
const getSwizzleType = (swizzle: string): NodeType => {
|
|
81
|
+
if (swizzle.length === 1) return 'float'
|
|
82
|
+
if (swizzle.length === 2) return 'vec2'
|
|
83
|
+
if (swizzle.length === 3) return 'vec3'
|
|
84
|
+
if (swizzle.length === 4) return 'vec4'
|
|
85
|
+
return 'float'
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 数学関数の戻り値型を取得
|
|
89
|
+
const getMathReturnType = (func: string, inputType: NodeType): NodeType => {
|
|
90
|
+
if (func === 'length') return 'float'
|
|
91
|
+
if (func === 'normalize') return inputType
|
|
92
|
+
if (func === 'toVar') return inputType
|
|
93
|
+
return inputType
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 公開API
|
|
97
|
+
export const node = (type: NodeType, value?: any, options?: Partial<Node>) => {
|
|
98
|
+
const nodeInstance = createNode(type, value, options)
|
|
99
|
+
return createNodeProxy(nodeInstance)
|
|
100
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { NodeType, Operator, MathFunction, Swillzes } from './const'
|
|
2
|
+
|
|
3
|
+
// ノードの基本インターフェース
|
|
4
|
+
export interface Node {
|
|
5
|
+
id: string
|
|
6
|
+
type: NodeType
|
|
7
|
+
value?: any
|
|
8
|
+
property?: string
|
|
9
|
+
parent?: Node
|
|
10
|
+
children?: Node[]
|
|
11
|
+
operator?: Operator
|
|
12
|
+
mathFunction?: MathFunction
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Proxyハンドラーのコールバック型
|
|
16
|
+
export interface ProxyCallback {
|
|
17
|
+
path: string[]
|
|
18
|
+
args: any[]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ノード作成関数の型
|
|
22
|
+
export type NodeCreator = (value?: any) => X
|
|
23
|
+
|
|
24
|
+
// 演算子メソッドの型
|
|
25
|
+
export interface OperatorMethods {
|
|
26
|
+
add(x: X | number): X
|
|
27
|
+
sub(x: X | number): X
|
|
28
|
+
mul(x: X | number): X
|
|
29
|
+
div(x: X | number): X
|
|
30
|
+
mod(x: X | number): X
|
|
31
|
+
equal(x: X | number): X
|
|
32
|
+
notEqual(x: X | number): X
|
|
33
|
+
lessThan(x: X | number): X
|
|
34
|
+
lessThanEqual(x: X | number): X
|
|
35
|
+
greaterThan(x: X | number): X
|
|
36
|
+
greaterThanEqual(x: X | number): X
|
|
37
|
+
and(x: X): X
|
|
38
|
+
or(x: X): X
|
|
39
|
+
not(): X
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 数学関数メソッドの型
|
|
43
|
+
export interface MathMethods {
|
|
44
|
+
abs(): X
|
|
45
|
+
acos(): X
|
|
46
|
+
asin(): X
|
|
47
|
+
atan(): X
|
|
48
|
+
ceil(): X
|
|
49
|
+
cos(): X
|
|
50
|
+
floor(): X
|
|
51
|
+
fract(): X
|
|
52
|
+
length(): X
|
|
53
|
+
normalize(): X
|
|
54
|
+
sin(): X
|
|
55
|
+
sqrt(): X
|
|
56
|
+
tan(): X
|
|
57
|
+
toVar(): X
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// 全てのswizzleパターンをまとめる型
|
|
61
|
+
|
|
62
|
+
// スウィズルプロパティの型
|
|
63
|
+
export type SwizzleProperties = {
|
|
64
|
+
[k in Swillzes]: X
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ノードProxy型
|
|
68
|
+
export interface X extends MathMethods, OperatorMethods, SwizzleProperties {
|
|
69
|
+
readonly id: string
|
|
70
|
+
readonly type: NodeType
|
|
71
|
+
readonly value: any
|
|
72
|
+
readonly property: string
|
|
73
|
+
(...args: any[]): X
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ユニフォーム変数の型
|
|
77
|
+
export interface UniformNode extends X {
|
|
78
|
+
set(value: any): void
|
|
79
|
+
onObjectUpdate(callback: (context: any) => any): UniformNode
|
|
80
|
+
onRenderUpdate(callback: (context: any) => any): UniformNode
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// 関数定義の型
|
|
84
|
+
export interface FunctionNode {
|
|
85
|
+
(...args: any[]): X
|
|
86
|
+
call(x: X[]): X
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 条件分岐の型
|
|
90
|
+
export interface ConditionalNode {
|
|
91
|
+
ElseIf(condition: X, callback: () => void): ConditionalNode
|
|
92
|
+
Else(callback: () => void): void
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// WebGL/WebGPU変換コンテキスト
|
|
96
|
+
export interface ConversionContext {
|
|
97
|
+
target: 'webgl' | 'webgpu'
|
|
98
|
+
nodes: Map<string, Node>
|
|
99
|
+
variables: Map<string, string>
|
|
100
|
+
functions: Map<string, string>
|
|
101
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { node } from './node'
|
|
2
|
+
import { is } from '../utils/helpers'
|
|
3
|
+
import type { UniformNode } from './types'
|
|
4
|
+
import type { NodeType } from './const'
|
|
5
|
+
|
|
6
|
+
// ユニフォーム更新コンテキスト
|
|
7
|
+
interface UpdateContext {
|
|
8
|
+
object?: any
|
|
9
|
+
camera?: any
|
|
10
|
+
renderer?: any
|
|
11
|
+
scene?: any
|
|
12
|
+
time?: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// ユニフォーム値の型を推定
|
|
16
|
+
const inferUniformType = (value: any): NodeType => {
|
|
17
|
+
if (is.num(value)) return 'float'
|
|
18
|
+
if (is.bol(value)) return 'bool'
|
|
19
|
+
if (is.arr(value)) {
|
|
20
|
+
const len = value.length
|
|
21
|
+
if (len === 2) return 'vec2'
|
|
22
|
+
if (len === 3) return 'vec3'
|
|
23
|
+
if (len === 4) return 'vec4'
|
|
24
|
+
if (len === 9) return 'mat3'
|
|
25
|
+
if (len === 16) return 'mat4'
|
|
26
|
+
}
|
|
27
|
+
if (is.obj(value) && 'r' in value && 'g' in value && 'b' in value) return 'color'
|
|
28
|
+
return 'float'
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ユニフォーム変数を作成
|
|
32
|
+
export const uniform = (initialValue: any): UniformNode => {
|
|
33
|
+
const type = inferUniformType(initialValue)
|
|
34
|
+
let currentValue = initialValue
|
|
35
|
+
let objectUpdateCallback: ((context: UpdateContext) => any) | null = null
|
|
36
|
+
let renderUpdateCallback: ((context: UpdateContext) => any) | null = null
|
|
37
|
+
|
|
38
|
+
const baseNode = node(type, currentValue) as any
|
|
39
|
+
|
|
40
|
+
// 値を設定
|
|
41
|
+
const set = (value: any) => {
|
|
42
|
+
currentValue = value
|
|
43
|
+
baseNode.value = value
|
|
44
|
+
}
|
|
45
|
+
// オブジェクト更新時のコールバックを設定
|
|
46
|
+
const onObjectUpdate = (callback: (context: UpdateContext) => any): UniformNode => {
|
|
47
|
+
objectUpdateCallback = callback
|
|
48
|
+
return uniformNode
|
|
49
|
+
}
|
|
50
|
+
// レンダー更新時のコールバックを設定
|
|
51
|
+
const onRenderUpdate = (callback: (context: UpdateContext) => any): UniformNode => {
|
|
52
|
+
renderUpdateCallback = callback
|
|
53
|
+
return uniformNode
|
|
54
|
+
}
|
|
55
|
+
// 内部更新関数(外部から呼び出される)
|
|
56
|
+
const _updateFromContext = (context: UpdateContext) => {
|
|
57
|
+
if (objectUpdateCallback) {
|
|
58
|
+
const newValue = objectUpdateCallback(context)
|
|
59
|
+
if (newValue !== undefined) set(newValue)
|
|
60
|
+
}
|
|
61
|
+
if (renderUpdateCallback) {
|
|
62
|
+
const newValue = renderUpdateCallback(context)
|
|
63
|
+
if (newValue !== undefined) set(newValue)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// UniformNodeインターフェースを実装
|
|
67
|
+
const uniformNode = Object.create(baseNode)
|
|
68
|
+
uniformNode.set = set
|
|
69
|
+
uniformNode.onObjectUpdate = onObjectUpdate
|
|
70
|
+
uniformNode.onRenderUpdate = onRenderUpdate
|
|
71
|
+
uniformNode._updateFromContext = _updateFromContext
|
|
72
|
+
uniformNode.isUniform = true
|
|
73
|
+
return uniformNode as UniformNode
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// 組み込みユニフォーム変数
|
|
77
|
+
export const iTime = uniform(0.0)
|
|
78
|
+
export const iPrevTime = uniform(0.0)
|
|
79
|
+
export const iDeltaTime = uniform(0.0)
|
|
80
|
+
export const iResolution = uniform([1920, 1080])
|
|
81
|
+
export const iMouse = uniform([0, 0])
|
|
82
|
+
|
|
83
|
+
// ユニフォーム変数の更新(レンダーループで呼び出される)
|
|
84
|
+
export const updateUniforms = (context: UpdateContext) => {
|
|
85
|
+
// 時間の更新
|
|
86
|
+
if (context.time !== undefined) {
|
|
87
|
+
const prevTime = iTime.value || 0
|
|
88
|
+
iTime.set(context.time)
|
|
89
|
+
iPrevTime.set(prevTime)
|
|
90
|
+
iDeltaTime.set(context.time - prevTime)
|
|
91
|
+
}
|
|
92
|
+
}
|
package/src/react.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import { createGL, isGL } from './index'
|
|
3
|
+
import type { GL } from './types'
|
|
4
|
+
export * from './index'
|
|
5
|
+
|
|
6
|
+
export const useGL = (props: Partial<GL> = {}) => {
|
|
7
|
+
return useState(() => {
|
|
8
|
+
const gl = isGL(props) ? props : createGL(props)
|
|
9
|
+
gl.ref = (el: HTMLCanvasElement | null) => {
|
|
10
|
+
if (el) {
|
|
11
|
+
gl.el = el
|
|
12
|
+
gl.mount()
|
|
13
|
+
} else gl.clean()
|
|
14
|
+
}
|
|
15
|
+
return gl
|
|
16
|
+
})[0]
|
|
17
|
+
}
|
package/src/solid.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createGL, isGL } from './index'
|
|
2
|
+
import type { GL } from './types'
|
|
3
|
+
export * from './index'
|
|
4
|
+
|
|
5
|
+
export const onGL = (props?: Partial<GL>) => {
|
|
6
|
+
const gl = isGL(props) ? props : createGL(props)
|
|
7
|
+
gl.ref = (el: HTMLCanvasElement | null) => {
|
|
8
|
+
if (el) {
|
|
9
|
+
gl.el = el
|
|
10
|
+
gl.mount()
|
|
11
|
+
} else gl.clean()
|
|
12
|
+
}
|
|
13
|
+
return gl
|
|
14
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { EventState } from 'reev'
|
|
2
|
+
import type { Fun, Queue, Frame } from 'refr'
|
|
3
|
+
import type { X } from './node'
|
|
4
|
+
export type { Fun, Queue, Frame }
|
|
5
|
+
export type GPUContext = any // GPUCanvasContext https://developer.mozilla.org/en-US/docs/Web/API/GPUCanvasContext
|
|
6
|
+
export type GPUDevice = any //
|
|
7
|
+
export type GPUBuffer = any //
|
|
8
|
+
export type GPUPipeline = any //
|
|
9
|
+
export type Uniform = number | number[]
|
|
10
|
+
export type Attribute = number[]
|
|
11
|
+
export type Attributes = Record<string, Attribute>
|
|
12
|
+
export type Uniforms = Record<string, Uniform>
|
|
13
|
+
export type PrecisionMode = 'highp' | 'mediump' | 'lowp'
|
|
14
|
+
export type GLClearMode = 'COLOR_BUFFER_BIT' | 'DEPTH_BUFFER_BIT' | 'STENCIL_BUFFER_BIT'
|
|
15
|
+
export type GLDrawType = 'UNSIGNED_BYTE' | 'UNSIGNED_SHORT' | 'UNSIGNED_INT'
|
|
16
|
+
export type GLDrawMode =
|
|
17
|
+
| 'POINTS'
|
|
18
|
+
| 'LINE_STRIP'
|
|
19
|
+
| 'LINE_LOOP'
|
|
20
|
+
| 'LINES'
|
|
21
|
+
| 'TRIANGLE_STRIP'
|
|
22
|
+
| 'TRIANGLE_FAN'
|
|
23
|
+
| 'TRIANGLES'
|
|
24
|
+
|
|
25
|
+
export interface WebGLState {
|
|
26
|
+
context: WebGLRenderingContext
|
|
27
|
+
program: WebGLProgram
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface WebGPUState {
|
|
31
|
+
device: GPUDevice
|
|
32
|
+
context: GPUContext
|
|
33
|
+
pipeline: GPUPipeline
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type GL = EventState<{
|
|
37
|
+
/**
|
|
38
|
+
* initial value
|
|
39
|
+
*/
|
|
40
|
+
isNative: boolean
|
|
41
|
+
isWebGL: boolean
|
|
42
|
+
isLoop: boolean
|
|
43
|
+
isGL: true
|
|
44
|
+
width: number
|
|
45
|
+
height: number
|
|
46
|
+
size: [number, number]
|
|
47
|
+
mouse: [number, number]
|
|
48
|
+
count: number
|
|
49
|
+
el: HTMLCanvasElement
|
|
50
|
+
vs: string | X
|
|
51
|
+
fs: string | X
|
|
52
|
+
vert: string | X
|
|
53
|
+
frag: string | X
|
|
54
|
+
vertex: string | X
|
|
55
|
+
fragment: string | X
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* core state
|
|
59
|
+
*/
|
|
60
|
+
webgpu: WebGPUState
|
|
61
|
+
webgl: WebGLState
|
|
62
|
+
queue: Queue
|
|
63
|
+
frame: Frame
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* events
|
|
67
|
+
*/
|
|
68
|
+
ref?: any
|
|
69
|
+
init(): void
|
|
70
|
+
loop(): void
|
|
71
|
+
mount(): void
|
|
72
|
+
clean(): void
|
|
73
|
+
render(): void
|
|
74
|
+
resize(e?: Event): void
|
|
75
|
+
mousemove(e: Event): void
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* setter
|
|
79
|
+
*/
|
|
80
|
+
_uniform?(key: string, value: Uniform, isMatrix?: boolean): GL
|
|
81
|
+
uniform(key: string, value: Uniform, isMatrix?: boolean): GL
|
|
82
|
+
uniform(target: { [key: string]: Uniform }): GL
|
|
83
|
+
_texture?(key: string, value: string): GL
|
|
84
|
+
texture(key: string, value: string): GL
|
|
85
|
+
texture(target: { [key: string]: string }): GL
|
|
86
|
+
_attribute?(key: string, value: Attribute, iboValue?: Attribute): GL
|
|
87
|
+
attribute(key: string, value: Attribute, iboValue?: Attribute): GL
|
|
88
|
+
attribute(target: { [key: string]: Attribute }): GL
|
|
89
|
+
}>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export const is = {
|
|
2
|
+
arr: Array.isArray,
|
|
3
|
+
bol: (a: unknown): a is boolean => typeof a === 'boolean',
|
|
4
|
+
str: (a: unknown): a is string => typeof a === 'string',
|
|
5
|
+
num: (a: unknown): a is number => typeof a === 'number',
|
|
6
|
+
int: (a: unknown): a is number => Number.isInteger(a),
|
|
7
|
+
fun: (a: unknown): a is Function => typeof a === 'function',
|
|
8
|
+
und: (a: unknown): a is undefined => typeof a === 'undefined',
|
|
9
|
+
nul: (a: unknown): a is null => a === null,
|
|
10
|
+
set: (a: unknown): a is Set<unknown> => a instanceof Set,
|
|
11
|
+
map: (a: unknown): a is Map<unknown, unknown> => a instanceof Map,
|
|
12
|
+
obj: (a: unknown): a is object => !!a && a.constructor.name === 'Object',
|
|
13
|
+
nan: (a: unknown): a is number => typeof a === 'number' && Number.isNaN(a),
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* each
|
|
18
|
+
*/
|
|
19
|
+
type EachFn<Value, Key, This> = (this: This, value: Value, key: Key) => void
|
|
20
|
+
type Eachable<Value = any, Key = any, This = any> = {
|
|
21
|
+
forEach(cb: EachFn<Value, Key, This>, ctx?: This): void
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const each = <Value, Key, This>(obj: Eachable<Value, Key, This>, fn: EachFn<Value, Key, This>) => obj.forEach(fn)
|
|
25
|
+
|
|
26
|
+
export const flush = <Value extends Function, Key, This>(obj: Eachable<Value, Key, This>, ...args: any[]) => {
|
|
27
|
+
each(obj, (f) => f(...args))
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* other
|
|
32
|
+
*/
|
|
33
|
+
export const replace = (x = '', from = '_', to = '/') => x.split(from).join(to)
|
|
34
|
+
export const ext = (src = '.pdf') => src.split('.').pop()?.toLowerCase() ?? ''
|
|
35
|
+
export const fig = (x = 0) => `${x}`.split('.')[1]?.length ?? 0
|
|
36
|
+
export const dig = (x = 0) => `${x}`.split('.')[0]?.length - (x < 0 ? 1 : 0)
|
|
37
|
+
export const sig = (value = 0, digit = -2) => {
|
|
38
|
+
digit *= -1
|
|
39
|
+
digit = Math.pow(10, digit)
|
|
40
|
+
value *= digit
|
|
41
|
+
value = Math.round(value)
|
|
42
|
+
value /= digit
|
|
43
|
+
return value
|
|
44
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { GPUContext, GPUDevice, GPUPipeline } from '../types'
|
|
2
|
+
|
|
3
|
+
export const initWebGPUDevice = async (el: HTMLCanvasElement) => {
|
|
4
|
+
const gpu = (navigator as any).gpu
|
|
5
|
+
if (!gpu) return null
|
|
6
|
+
const adapter = await gpu.requestAdapter()
|
|
7
|
+
if (!adapter) return null
|
|
8
|
+
const device = await adapter.requestDevice()
|
|
9
|
+
const context = el.getContext('webgpu') as GPUContext
|
|
10
|
+
if (!context) return null
|
|
11
|
+
const format = gpu.getPreferredCanvasFormat()
|
|
12
|
+
context.configure({ device, format })
|
|
13
|
+
return { device, context, format }
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const defaultVertexWGSL = `
|
|
17
|
+
@vertex
|
|
18
|
+
fn main(@builtin(vertex_index) vertex_index: u32) -> @builtin(position) vec4f {
|
|
19
|
+
let x = f32(vertex_index % 2u) * 4.0 - 1.0;
|
|
20
|
+
let y = f32(vertex_index / 2u) * 4.0 - 1.0;
|
|
21
|
+
return vec4f(x, y, 0.0, 1.0);
|
|
22
|
+
}
|
|
23
|
+
`
|
|
24
|
+
|
|
25
|
+
const defaultFragmentWGSL = `
|
|
26
|
+
@fragment
|
|
27
|
+
fn main(@builtin(position) position: vec4f) -> @location(0) vec4f {
|
|
28
|
+
return vec4f(position.xy / vec2f(1280, 800), 0.0, 1.0);
|
|
29
|
+
}
|
|
30
|
+
`
|
|
31
|
+
|
|
32
|
+
export const createRenderPipeline = (
|
|
33
|
+
device: GPUDevice,
|
|
34
|
+
format: string,
|
|
35
|
+
vs = defaultVertexWGSL,
|
|
36
|
+
fs = defaultFragmentWGSL,
|
|
37
|
+
buffers: any[]
|
|
38
|
+
) => {
|
|
39
|
+
return device.createRenderPipeline({
|
|
40
|
+
vertex: {
|
|
41
|
+
module: device.createShaderModule({ code: vs.trim() }),
|
|
42
|
+
entryPoint: 'main',
|
|
43
|
+
buffers,
|
|
44
|
+
},
|
|
45
|
+
fragment: {
|
|
46
|
+
module: device.createShaderModule({ code: fs.trim() }),
|
|
47
|
+
entryPoint: 'main',
|
|
48
|
+
targets: [{ format }],
|
|
49
|
+
},
|
|
50
|
+
layout: 'auto',
|
|
51
|
+
primitive: { topology: 'triangle-list' },
|
|
52
|
+
}) as GPUPipeline
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const createDescriptor = (c: GPUContext) => {
|
|
56
|
+
return {
|
|
57
|
+
colorAttachments: [
|
|
58
|
+
{
|
|
59
|
+
view: c.getCurrentTexture().createView(),
|
|
60
|
+
clearValue: { r: 0, g: 0, b: 0, a: 1 },
|
|
61
|
+
loadOp: 'clear',
|
|
62
|
+
storeOp: 'store',
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export const alignTo256 = (size: number) => Math.ceil(size / 256) * 256
|
|
68
|
+
|
|
69
|
+
export const createUniformBuffer = (device: GPUDevice, size: number) => {
|
|
70
|
+
return device.createBuffer({ size: alignTo256(size), usage: 0x40 | 0x4 }) as Buffer
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const createVertexBuffer = (device: GPUDevice, value: number[]) => {
|
|
74
|
+
const array = new Float32Array(value)
|
|
75
|
+
const buffer = device.createBuffer({ size: array.byteLength, usage: 0x20 | 0x4 })
|
|
76
|
+
device.queue.writeBuffer(buffer, 0, array)
|
|
77
|
+
return buffer as Buffer
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const createBindGroup = (device: GPUDevice, pipeline: GPUPipeline, entries: any[]) => {
|
|
81
|
+
const layout = pipeline.getBindGroupLayout(0)
|
|
82
|
+
return device.createBindGroup({ layout, entries })
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export const updateBindGroup = (
|
|
86
|
+
device: GPUDevice,
|
|
87
|
+
pipeline: GPUPipeline,
|
|
88
|
+
uniformBuffer: Buffer,
|
|
89
|
+
textures: any = {},
|
|
90
|
+
sampler: any = null
|
|
91
|
+
) => {
|
|
92
|
+
const entries = [{ binding: 0, resource: { buffer: uniformBuffer } }]
|
|
93
|
+
let binding = 1
|
|
94
|
+
Object.values(textures).forEach((texture: any) => {
|
|
95
|
+
entries.push({ binding: binding++, resource: texture.createView() })
|
|
96
|
+
})
|
|
97
|
+
if (sampler && Object.keys(textures).length > 0) entries.push({ binding: binding++, resource: sampler })
|
|
98
|
+
return createBindGroup(device, pipeline, entries)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export const createUniform = (device: GPUDevice, buffer: any, data: Float32Array, offset = 0) => {
|
|
102
|
+
device.queue.writeBuffer(buffer, offset, data)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export const createDeviceTexture = (device: GPUDevice, image: HTMLImageElement) => {
|
|
106
|
+
const texture = device.createTexture({
|
|
107
|
+
size: { width: image.width, height: image.height },
|
|
108
|
+
format: 'rgba8unorm',
|
|
109
|
+
usage: 0x4 | 0x2,
|
|
110
|
+
})
|
|
111
|
+
device.queue.copyExternalImageToTexture(
|
|
112
|
+
{ source: image },
|
|
113
|
+
{ texture },
|
|
114
|
+
{ width: image.width, height: image.height }
|
|
115
|
+
)
|
|
116
|
+
return texture
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export const createSampler = (device: GPUDevice) => {
|
|
120
|
+
return device.createSampler({
|
|
121
|
+
magFilter: 'linear',
|
|
122
|
+
minFilter: 'linear',
|
|
123
|
+
addressModeU: 'clamp-to-edge',
|
|
124
|
+
addressModeV: 'clamp-to-edge',
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export const getDefaultVertices = () => new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1])
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
export const defaultVertexGLSL = /* cpp */ `
|
|
2
|
+
#version 300 es
|
|
3
|
+
void main() {
|
|
4
|
+
float x = float(gl_VertexID % 2) * 4.0 - 1.0;
|
|
5
|
+
float y = float(gl_VertexID / 2) * 4.0 - 1.0;
|
|
6
|
+
gl_Position = vec4(x, y, 0.0, 1.0);
|
|
7
|
+
}
|
|
8
|
+
`
|
|
9
|
+
|
|
10
|
+
export const defaultFragmentGLSL = /* cpp */ `
|
|
11
|
+
#version 300 es
|
|
12
|
+
precision mediump float;
|
|
13
|
+
uniform vec2 iResolution;
|
|
14
|
+
out vec4 fragColor;
|
|
15
|
+
void main() {
|
|
16
|
+
fragColor = vec4(fract(gl_FragCoord.xy / iResolution), 0, 1);
|
|
17
|
+
}
|
|
18
|
+
`
|
|
19
|
+
|
|
20
|
+
export const createShader = (c: WebGLRenderingContext, source: string, type: number) => {
|
|
21
|
+
const shader = c.createShader(type)
|
|
22
|
+
if (!shader) throw new Error('Failed to create shader')
|
|
23
|
+
c.shaderSource(shader, source.trim())
|
|
24
|
+
c.compileShader(shader)
|
|
25
|
+
if (c.getShaderParameter(shader, c.COMPILE_STATUS)) return shader
|
|
26
|
+
const error = c.getShaderInfoLog(shader)
|
|
27
|
+
c.deleteShader(shader)
|
|
28
|
+
throw new Error(`Could not compile shader: ${error}`)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const createProgram = (c: WebGLRenderingContext, vs = defaultVertexGLSL, fs = defaultFragmentGLSL) => {
|
|
32
|
+
const pg = c.createProgram()
|
|
33
|
+
c.attachShader(pg, createShader(c, vs, c.VERTEX_SHADER))
|
|
34
|
+
c.attachShader(pg, createShader(c, fs, c.FRAGMENT_SHADER))
|
|
35
|
+
c.linkProgram(pg)
|
|
36
|
+
if (c.getProgramParameter(pg, c.LINK_STATUS)) return pg
|
|
37
|
+
const error = c.getProgramInfoLog(pg)
|
|
38
|
+
c.deleteProgram(pg)
|
|
39
|
+
throw new Error(`Could not link pg: ${error}`)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const createVbo = (c: WebGLRenderingContext, data: number[]) => {
|
|
43
|
+
const buffer = c.createBuffer()
|
|
44
|
+
c.bindBuffer(c.ARRAY_BUFFER, buffer)
|
|
45
|
+
c.bufferData(c.ARRAY_BUFFER, new Float32Array(data), c.STATIC_DRAW)
|
|
46
|
+
c.bindBuffer(c.ARRAY_BUFFER, null)
|
|
47
|
+
return buffer
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const createIbo = (c: WebGLRenderingContext, data: number[]) => {
|
|
51
|
+
const buffer = c.createBuffer()
|
|
52
|
+
c.bindBuffer(c.ELEMENT_ARRAY_BUFFER, buffer)
|
|
53
|
+
c.bufferData(c.ELEMENT_ARRAY_BUFFER, new Int16Array(data), c.STATIC_DRAW)
|
|
54
|
+
c.bindBuffer(c.ELEMENT_ARRAY_BUFFER, null)
|
|
55
|
+
return buffer
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const createAttrib = (
|
|
59
|
+
c: WebGLRenderingContext,
|
|
60
|
+
stride: number,
|
|
61
|
+
location: any,
|
|
62
|
+
vbo: WebGLBuffer,
|
|
63
|
+
ibo?: WebGLBuffer
|
|
64
|
+
) => {
|
|
65
|
+
c.bindBuffer(c.ARRAY_BUFFER, vbo)
|
|
66
|
+
c.enableVertexAttribArray(location)
|
|
67
|
+
c.vertexAttribPointer(location, stride, c.FLOAT, false, 0, 0)
|
|
68
|
+
if (ibo) c.bindBuffer(c.ELEMENT_ARRAY_BUFFER, ibo)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const createTexture = (c: WebGLRenderingContext, img: HTMLImageElement) => {
|
|
72
|
+
const texture = c.createTexture()
|
|
73
|
+
c.bindTexture(c.TEXTURE_2D, texture)
|
|
74
|
+
c.texImage2D(c.TEXTURE_2D, 0, c.RGBA, c.RGBA, c.UNSIGNED_BYTE, img)
|
|
75
|
+
c.generateMipmap(c.TEXTURE_2D)
|
|
76
|
+
c.texParameteri(c.TEXTURE_2D, c.TEXTURE_MIN_FILTER, c.LINEAR)
|
|
77
|
+
c.texParameteri(c.TEXTURE_2D, c.TEXTURE_MAG_FILTER, c.LINEAR)
|
|
78
|
+
c.texParameteri(c.TEXTURE_2D, c.TEXTURE_WRAP_S, c.CLAMP_TO_EDGE)
|
|
79
|
+
c.texParameteri(c.TEXTURE_2D, c.TEXTURE_WRAP_T, c.CLAMP_TO_EDGE)
|
|
80
|
+
c.bindTexture(c.TEXTURE_2D, null)
|
|
81
|
+
return texture
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const activeTexture = (
|
|
85
|
+
c: WebGLRenderingContext,
|
|
86
|
+
location: WebGLUniformLocation | null,
|
|
87
|
+
unit: number,
|
|
88
|
+
texture: WebGLTexture
|
|
89
|
+
) => {
|
|
90
|
+
if (!location) return
|
|
91
|
+
c.uniform1i(location, unit)
|
|
92
|
+
c.activeTexture(c.TEXTURE0 + unit)
|
|
93
|
+
c.bindTexture(c.TEXTURE_2D, texture)
|
|
94
|
+
}
|