@bigmistqke/view.gl 0.1.0 → 0.1.2
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/.claude/settings.local.json +2 -1
- package/.github/ISSUE_TEMPLATE/bug-report.yml +31 -0
- package/.github/ISSUE_TEMPLATE/feature-request.yml +27 -0
- package/.github/workflows/codeql.yml +46 -0
- package/.github/workflows/deploy-page.yml +38 -0
- package/.github/workflows/format.yml +40 -0
- package/README.md +92 -35
- package/dev/404.html +19 -0
- package/dev/dist/404.html +19 -0
- package/dev/dist/assets/bezier-DuLTb2d1.js +294 -0
- package/dev/dist/assets/game_of_life-D7qHdFcv.js +202 -0
- package/dev/dist/assets/grid-Ie22U_9K.js +173 -0
- package/dev/dist/assets/index-0qrXMlfv.js +109 -0
- package/dev/dist/assets/index-C2N3u8wj.css +12 -0
- package/dev/dist/assets/main-BZ1N6pp6.js +32 -0
- package/dev/dist/assets/overview-DAxtMIw5.js +78 -0
- package/dev/dist/assets/pix_sim-DIhzM_vu.js +501 -0
- package/dev/dist/assets/symbol-BouRL-Ni.js +65 -0
- package/dev/dist/assets/tag-C_bg62fD.js +516 -0
- package/dev/dist/assets/tetris-IMnkSi9p.js +344 -0
- package/dev/dist/assets/utils-CvCgsM08.js +50 -0
- package/dev/dist/index.html +51 -0
- package/dev/index.html +39 -10
- package/dev/{bezier.ts → src/examples/bezier.ts} +1 -1
- package/dev/{grid.ts → src/examples/grid.ts} +1 -1
- package/dev/{pix_sim.ts → src/examples/pix_sim.ts} +112 -97
- package/dev/src/examples/symbol.ts +79 -0
- package/dev/{tetris.ts → src/examples/tetris.ts} +1 -0
- package/dev/src/index.css +12 -0
- package/dev/src/index.ts +69 -0
- package/dev/{utils.ts → src/utils.ts} +8 -2
- package/dev/vite.config.ts +9 -1
- package/dist/index.d.mts +18 -0
- package/dist/index.mjs +324 -0
- package/dist/index.mjs.map +1 -0
- package/dist/tag.d.mts +97 -0
- package/dist/tag.mjs +162 -0
- package/dist/tag.mjs.map +1 -0
- package/dist/types-DoFAJoh-.d.mts +266 -0
- package/package.json +24 -10
- package/src/index.ts +39 -17
- package/src/tag.ts +47 -39
- package/src/types.ts +24 -24
- package/src/{utils.ts → utils/index.ts} +39 -14
- package/src/utils/object.ts +27 -0
- package/test/index.test.ts +1017 -0
- package/test/mocks/webgl.ts +248 -0
- package/test/setup.ts +13 -0
- package/test/tag.test.ts +790 -0
- package/vitest.config.ts +15 -0
- /package/dev/{game_of_life.ts → src/examples/game_of_life.ts} +0 -0
- /package/dev/{index.ts → src/examples/overview.ts} +0 -0
|
@@ -2,6 +2,7 @@ import { FramebufferOptions } from 'src/types'
|
|
|
2
2
|
import { createFramebuffer } from 'src/utils'
|
|
3
3
|
import { view } from 'view.gl'
|
|
4
4
|
import { attribute, compile, glsl, uniform } from 'view.gl/tag'
|
|
5
|
+
import { createElement } from '../utils'
|
|
5
6
|
|
|
6
7
|
export const MATERIALS = {
|
|
7
8
|
SAND: {
|
|
@@ -21,126 +22,140 @@ export const MATERIALS = {
|
|
|
21
22
|
const MATERIAL_SIZE = Object.keys(MATERIALS).length
|
|
22
23
|
|
|
23
24
|
let playing = false
|
|
24
|
-
let currentMaterial = MATERIALS.SAND.id
|
|
25
|
-
|
|
26
|
-
const canvas = document.createElement('canvas')
|
|
27
|
-
const gl = canvas.getContext('webgl2', { antialias: false })!
|
|
28
|
-
if (!gl) {
|
|
29
|
-
throw new Error('WebGL not supported')
|
|
30
|
-
}
|
|
25
|
+
let currentMaterial: (typeof MATERIALS)[keyof typeof MATERIALS]['id'] = MATERIALS.SAND.id
|
|
31
26
|
|
|
32
27
|
// Create UI container
|
|
33
|
-
const container =
|
|
34
|
-
|
|
28
|
+
const container = createElement('div', {
|
|
29
|
+
style: 'display: flex; flex-direction: column; align-items: center; gap: 10px;',
|
|
30
|
+
parentElement: document.body,
|
|
31
|
+
})
|
|
35
32
|
|
|
36
33
|
// Create material buttons
|
|
37
|
-
const buttonContainer =
|
|
38
|
-
|
|
34
|
+
const buttonContainer = createElement('div', {
|
|
35
|
+
style: 'display: flex; gap: 10px; margin-bottom: 10px;',
|
|
36
|
+
parentElement: container,
|
|
37
|
+
})
|
|
39
38
|
|
|
40
39
|
Object.entries(MATERIALS).forEach(([name, material]) => {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
createElement('button', {
|
|
41
|
+
textContent: name.toLowerCase(),
|
|
42
|
+
'data-name': name,
|
|
43
|
+
'data-id': material.id.toString(),
|
|
44
|
+
style: `padding: 5px 10px;
|
|
45
45
|
font-size: 14px;
|
|
46
|
-
font-family:
|
|
46
|
+
font-family: arial;
|
|
47
47
|
cursor: pointer;
|
|
48
|
+
color: black;
|
|
48
49
|
background: ${
|
|
49
50
|
currentMaterial === material.id
|
|
50
51
|
? `rgb(${material.color.map(c => Math.floor(c * 255)).join(',')})`
|
|
51
|
-
: '
|
|
52
|
+
: `rgba(${material.color.map(c => Math.floor(c * 255)).join(',')}, 0.75)`
|
|
52
53
|
};
|
|
53
54
|
border-color: ${name === 'SAND' ? 'black' : 'white'};
|
|
54
55
|
border: 2px solid rgb(${material.color.map(c => Math.floor(c * 255)).join(',')});
|
|
55
56
|
border-radius: 2px;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
57
|
+
`,
|
|
58
|
+
onclick() {
|
|
59
|
+
currentMaterial = material.id
|
|
60
|
+
// Update all button borders
|
|
61
|
+
buttonContainer.querySelectorAll('button').forEach(btn => {
|
|
62
|
+
const name = btn.getAttribute('data-name') as keyof typeof MATERIALS
|
|
63
|
+
const id = btn.getAttribute('data-id')
|
|
64
|
+
if (id === currentMaterial.toString()) {
|
|
65
|
+
btn.style.background = `rgb(${MATERIALS[name].color
|
|
66
|
+
.map(c => Math.floor(c * 255))
|
|
67
|
+
.join(',')})`
|
|
68
|
+
} else {
|
|
69
|
+
btn.style.background = `rgba(${MATERIALS[name].color
|
|
70
|
+
.map(c => Math.floor(c * 255))
|
|
71
|
+
.join(',')}, 0.75)`
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
},
|
|
75
|
+
parentElement: buttonContainer,
|
|
76
|
+
})
|
|
68
77
|
})
|
|
69
78
|
|
|
70
|
-
container.appendChild(canvas)
|
|
71
|
-
container.appendChild(buttonContainer)
|
|
72
|
-
document.body.append(container)
|
|
73
|
-
|
|
74
79
|
function spray() {
|
|
75
80
|
return Math.floor(Math.random() * 8) * (Math.random() < 0.5 ? 1 : -1)
|
|
76
81
|
}
|
|
77
82
|
|
|
78
|
-
canvas
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
83
|
+
const canvas = createElement('canvas', {
|
|
84
|
+
parentElement: container,
|
|
85
|
+
onmousedown() {
|
|
86
|
+
const rect = canvas.getBoundingClientRect()
|
|
87
|
+
const controller = new AbortController()
|
|
88
|
+
// playing = false
|
|
89
|
+
window.addEventListener(
|
|
90
|
+
'mousemove',
|
|
91
|
+
event => {
|
|
92
|
+
const x = Math.floor((event.clientX - rect.left + spray()) * (canvas.width / rect.width))
|
|
93
|
+
const y = Math.floor(
|
|
94
|
+
(rect.bottom - event.clientY + spray()) * (canvas.height / rect.height),
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
const dimensions = [8, 8] as const
|
|
98
|
+
|
|
99
|
+
// Encode selected material using bit encoding
|
|
100
|
+
const data = new Uint8Array(
|
|
101
|
+
(function* () {
|
|
102
|
+
for (let i = 0; i < dimensions[0] * dimensions[1]; i++) {
|
|
103
|
+
yield (currentMaterial & 1) * 255 // Red channel (bit 0)
|
|
104
|
+
yield ((currentMaterial >> 1) & 1) * 255 // Green channel (bit 1)
|
|
105
|
+
yield ((currentMaterial >> 2) & 1) * 255 // Blue channel (bit 2)
|
|
106
|
+
yield 255 // Alpha channel
|
|
107
|
+
}
|
|
108
|
+
})(),
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
// Update both read and write textures
|
|
112
|
+
gl.bindTexture(gl.TEXTURE_2D, read.texture)
|
|
113
|
+
gl.texSubImage2D(
|
|
114
|
+
gl.TEXTURE_2D,
|
|
115
|
+
0,
|
|
116
|
+
Math.max(0, Math.min(WIDTH - dimensions[0], x)),
|
|
117
|
+
Math.max(0, Math.min(HEIGHT - dimensions[1], y)),
|
|
118
|
+
dimensions[0],
|
|
119
|
+
dimensions[1],
|
|
120
|
+
gl.RGBA,
|
|
121
|
+
gl.UNSIGNED_BYTE,
|
|
122
|
+
data,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
gl.bindTexture(gl.TEXTURE_2D, write.texture)
|
|
126
|
+
gl.texSubImage2D(
|
|
127
|
+
gl.TEXTURE_2D,
|
|
128
|
+
0,
|
|
129
|
+
Math.max(0, Math.min(WIDTH - dimensions[0], x)),
|
|
130
|
+
Math.max(0, Math.min(HEIGHT - dimensions[1], y)),
|
|
131
|
+
dimensions[0],
|
|
132
|
+
dimensions[1],
|
|
133
|
+
gl.RGBA,
|
|
134
|
+
gl.UNSIGNED_BYTE,
|
|
135
|
+
data,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
render()
|
|
139
|
+
},
|
|
140
|
+
controller,
|
|
141
|
+
)
|
|
142
|
+
window.addEventListener(
|
|
143
|
+
'mouseup',
|
|
144
|
+
() => {
|
|
145
|
+
controller.abort()
|
|
146
|
+
// playing = true
|
|
147
|
+
// animate()
|
|
148
|
+
},
|
|
149
|
+
controller,
|
|
150
|
+
)
|
|
151
|
+
},
|
|
142
152
|
})
|
|
143
153
|
|
|
154
|
+
const gl = canvas.getContext('webgl2', { antialias: false })!
|
|
155
|
+
if (!gl) {
|
|
156
|
+
throw new Error('WebGL not supported')
|
|
157
|
+
}
|
|
158
|
+
|
|
144
159
|
canvas.width = 256 * 2
|
|
145
160
|
canvas.height = 256 * 2
|
|
146
161
|
const WIDTH = canvas.width
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { view } from 'view.gl'
|
|
2
|
+
import { attribute, compile, glsl, uniform } from 'view.gl/tag'
|
|
3
|
+
import { createElement } from '../utils'
|
|
4
|
+
|
|
5
|
+
const canvas = createElement('canvas', {
|
|
6
|
+
width: window.innerWidth,
|
|
7
|
+
height: window.innerHeight,
|
|
8
|
+
parentElement: document.body,
|
|
9
|
+
style: 'width: 100%; height: 100%',
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
new ResizeObserver(() => {
|
|
13
|
+
canvas.width = window.innerWidth
|
|
14
|
+
canvas.height = window.innerHeight
|
|
15
|
+
gl.viewport(0, 0, canvas.width, canvas.height)
|
|
16
|
+
draw(performance.now())
|
|
17
|
+
}).observe(canvas)
|
|
18
|
+
|
|
19
|
+
const gl = canvas.getContext('webgl2')!
|
|
20
|
+
gl.viewport(0, 0, canvas.width, canvas.height)
|
|
21
|
+
|
|
22
|
+
const vertex = glsl`
|
|
23
|
+
precision mediump float;
|
|
24
|
+
|
|
25
|
+
${attribute.vec2('a_vertex')}
|
|
26
|
+
${uniform.float('u_time')}
|
|
27
|
+
|
|
28
|
+
void main() {
|
|
29
|
+
float s = sin(u_time);
|
|
30
|
+
float c = cos(u_time);
|
|
31
|
+
mat2 rotation = mat2(
|
|
32
|
+
c, -s,
|
|
33
|
+
s, c
|
|
34
|
+
);
|
|
35
|
+
vec2 position = a_vertex * rotation;
|
|
36
|
+
gl_Position = vec4(position, 0.0, 1.0);
|
|
37
|
+
}`
|
|
38
|
+
|
|
39
|
+
const u_color = Symbol('color')
|
|
40
|
+
|
|
41
|
+
const fragment = glsl`
|
|
42
|
+
precision mediump float;
|
|
43
|
+
|
|
44
|
+
${uniform.vec3(u_color)}
|
|
45
|
+
|
|
46
|
+
void main() {
|
|
47
|
+
gl_FragColor = vec4(${u_color}, 1.0);
|
|
48
|
+
}`
|
|
49
|
+
|
|
50
|
+
const { program, schema } = compile(gl, vertex, fragment)
|
|
51
|
+
const { attributes, uniforms } = view(gl, program, schema)
|
|
52
|
+
|
|
53
|
+
gl.useProgram(program)
|
|
54
|
+
|
|
55
|
+
uniforms[u_color].set(0, 255, 0)
|
|
56
|
+
|
|
57
|
+
// Create triangle vertex buffer
|
|
58
|
+
attributes.a_vertex
|
|
59
|
+
.set(
|
|
60
|
+
new Float32Array([
|
|
61
|
+
// top
|
|
62
|
+
0.0, 1.0,
|
|
63
|
+
// bottom left
|
|
64
|
+
-1.0, -1.0,
|
|
65
|
+
// bottom right
|
|
66
|
+
1.0, -1.0,
|
|
67
|
+
]),
|
|
68
|
+
)
|
|
69
|
+
.bind()
|
|
70
|
+
|
|
71
|
+
function draw(delta: number) {
|
|
72
|
+
requestAnimationFrame(draw)
|
|
73
|
+
|
|
74
|
+
// Set uniform
|
|
75
|
+
uniforms.u_time.set(delta / 1_000)
|
|
76
|
+
|
|
77
|
+
gl.drawArraysInstanced(gl.TRIANGLES, 0, 3, 100)
|
|
78
|
+
}
|
|
79
|
+
requestAnimationFrame(draw)
|
package/dev/src/index.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import css from './index.css?url'
|
|
2
|
+
import { createElement } from './utils'
|
|
3
|
+
|
|
4
|
+
const iframe = document.querySelector('iframe')!
|
|
5
|
+
const nav = document.querySelector('nav')!
|
|
6
|
+
|
|
7
|
+
createElement('h3', { innerText: 'Examples', parentElement: nav })
|
|
8
|
+
|
|
9
|
+
const urls = Object.fromEntries(
|
|
10
|
+
Object.entries(import.meta.glob('./examples/*.ts', { eager: false })).map(([key, entry]) => {
|
|
11
|
+
const name = key.split('/').pop()!.replace('.ts', '')
|
|
12
|
+
const chunk = entry.toString().match(/import\(['"]([^'"]+)['"]/)![1]!
|
|
13
|
+
return [name, chunk] as const
|
|
14
|
+
}),
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
Object.entries(urls).forEach(([name, localUrl]) => {
|
|
18
|
+
createElement('button', {
|
|
19
|
+
onclick(event) {
|
|
20
|
+
event.preventDefault()
|
|
21
|
+
load(name, localUrl)
|
|
22
|
+
},
|
|
23
|
+
innerHTML: name,
|
|
24
|
+
parentElement: nav,
|
|
25
|
+
'data-route': name,
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
function load(name: string, url: string) {
|
|
30
|
+
// Use hash routing instead of pushState for GitHub Pages compatibility
|
|
31
|
+
window.location.hash = name
|
|
32
|
+
|
|
33
|
+
URL.revokeObjectURL(iframe.src)
|
|
34
|
+
|
|
35
|
+
nav.querySelectorAll('button').forEach(button => {
|
|
36
|
+
if (button.getAttribute('data-route') === name) {
|
|
37
|
+
button.classList.add('active')
|
|
38
|
+
} else {
|
|
39
|
+
button.classList.remove('active')
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
iframe.src = URL.createObjectURL(
|
|
44
|
+
new Blob(
|
|
45
|
+
[
|
|
46
|
+
`<head>
|
|
47
|
+
<link rel="stylesheet" href="${new URL(css, import.meta.url).href}"></link>
|
|
48
|
+
<script type="module" src="${new URL(url, import.meta.url).href}"></script></head>`,
|
|
49
|
+
],
|
|
50
|
+
{
|
|
51
|
+
type: 'text/html',
|
|
52
|
+
},
|
|
53
|
+
),
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Handle hash-based routing
|
|
58
|
+
const hash = window.location.hash.slice(1) // Remove #
|
|
59
|
+
if (hash && hash in urls) {
|
|
60
|
+
load(hash, urls[hash]!)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Listen for hash changes
|
|
64
|
+
window.addEventListener('hashchange', () => {
|
|
65
|
+
const hash = window.location.hash.slice(1)
|
|
66
|
+
if (hash && hash in urls) {
|
|
67
|
+
load(hash, urls[hash]!)
|
|
68
|
+
}
|
|
69
|
+
})
|
|
@@ -48,11 +48,17 @@ export function createElement<T extends keyof HTMLElementTagNameMap>(
|
|
|
48
48
|
{
|
|
49
49
|
parentElement,
|
|
50
50
|
...options
|
|
51
|
-
}: Partial<Omit<HTMLElementTagNameMap[T], 'style'> & { style: string }>
|
|
51
|
+
}: Partial<Omit<HTMLElementTagNameMap[T], 'style'> & { style: string }> &
|
|
52
|
+
Record<`data-${string}`, string> = {},
|
|
52
53
|
) {
|
|
53
54
|
const element = document.createElement(tag)
|
|
55
|
+
|
|
54
56
|
for (const key in options) {
|
|
55
|
-
|
|
57
|
+
if (key.startsWith('data-')) {
|
|
58
|
+
element.setAttribute(key, options[key])
|
|
59
|
+
} else {
|
|
60
|
+
element[key] = options[key]
|
|
61
|
+
}
|
|
56
62
|
}
|
|
57
63
|
|
|
58
64
|
;(parentElement ?? document.body).appendChild(element)
|
package/dev/vite.config.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { defineConfig } from 'vite'
|
|
|
2
2
|
import tsconfigPaths from 'vite-tsconfig-paths'
|
|
3
3
|
|
|
4
4
|
export default defineConfig({
|
|
5
|
-
base: '
|
|
5
|
+
base: process.env.GITHUB_ACTIONS ? 'https://bigmistqke.github.io/view.gl/' : undefined,
|
|
6
6
|
assetsInclude: ['assets/**/*'],
|
|
7
7
|
plugins: [
|
|
8
8
|
tsconfigPaths(),
|
|
@@ -38,5 +38,13 @@ export default defineConfig({
|
|
|
38
38
|
build: {
|
|
39
39
|
target: 'esnext',
|
|
40
40
|
minify: false,
|
|
41
|
+
rollupOptions: {
|
|
42
|
+
input: {
|
|
43
|
+
main: './dev/index.html',
|
|
44
|
+
index: './dev/src/index.ts', // Separate entry for index.ts
|
|
45
|
+
utils: './dev/src/utils.ts', // Separate entry for utils.ts
|
|
46
|
+
404: './dev/404.html',
|
|
47
|
+
},
|
|
48
|
+
},
|
|
41
49
|
},
|
|
42
50
|
})
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { V as ViewSchema, G as GL, a as ViewOptions, b as View, U as UniformSchema, c as UniformView, A as AttributeSchema, d as AttributeView, I as InterleavedAttributeSchema, e as InterleavedAttributeView, B as BufferSchema, f as BufferView } from './types-DoFAJoh-.mjs';
|
|
2
|
+
|
|
3
|
+
declare function toID(key: string | number | symbol): any;
|
|
4
|
+
/**********************************************************************************/
|
|
5
|
+
/**********************************************************************************/
|
|
6
|
+
declare function view<T extends ViewSchema>(gl: GL, program: WebGLProgram, schema: T, options?: ViewOptions): View<T>;
|
|
7
|
+
/**********************************************************************************/
|
|
8
|
+
/**********************************************************************************/
|
|
9
|
+
declare function uniformView<T extends UniformSchema>(gl: GL, program: WebGLProgram, schema: T): UniformView<T>;
|
|
10
|
+
declare function attributeView<T extends AttributeSchema>(gl: GL, program: WebGLProgram, schema: T, { signal }?: ViewOptions): AttributeView<T>;
|
|
11
|
+
/**********************************************************************************/
|
|
12
|
+
/**********************************************************************************/
|
|
13
|
+
declare function interleavedAttributeView<T extends InterleavedAttributeSchema>(gl: GL, program: WebGLProgram, schema: T, { signal }?: ViewOptions): InterleavedAttributeView<T>;
|
|
14
|
+
/**********************************************************************************/
|
|
15
|
+
/**********************************************************************************/
|
|
16
|
+
declare function bufferView<T extends BufferSchema>(gl: GL, schema: T, { signal }?: ViewOptions): BufferView<T>;
|
|
17
|
+
|
|
18
|
+
export { attributeView, bufferView, interleavedAttributeView, toID, uniformView, view };
|