UNPKG

@thi.ng/webgl

Version:

WebGL & GLSL abstraction layer

177 lines (176 loc) 4.49 kB
import { BIT_SHIFTS, asGLType, typedArrayType } from "@thi.ng/api/typedarray"; import { isPlainObject } from "@thi.ng/checks/is-plain-object"; import { DrawMode } from "./api/model.js"; import { isGL2Context } from "./checks.js"; import { error } from "./error.js"; import { defShader } from "./shader.js"; class WebGLArrayBuffer { gl; buffer; target; mode; data; constructor(gl, data, target = gl.ARRAY_BUFFER, mode = gl.STATIC_DRAW, retain = false) { this.gl = gl; this.buffer = gl.createBuffer() || error("error creating WebGL buffer"); this.target = target; this.mode = mode; if (data) { this.set(data); if (retain) this.data = data; } } bind() { this.gl.bindBuffer(this.target, this.buffer); return true; } unbind() { this.gl.bindBuffer(this.target, null); return true; } release() { if (this.buffer) { this.gl.deleteBuffer(this.buffer); delete this.buffer; } return true; } /** * Re-applies retained data (from ctor arg) using * {@link WebGLArrayBuffer.set}. Presumably the underlying data has been * updated elsewhere, but needs to be reflected to WebGL. * * @remarks * If no data is retained, this method is a no-op. */ update() { if (this.data) this.set(this.data); } set(data, mode = this.mode) { this.bind(); this.gl.bufferData(this.target, data, mode); if (this.data) this.data = data; } setChunk(data, byteOffset = 0) { this.bind(); this.gl.bufferSubData(this.target, byteOffset, data); if (this.data) { this.data.set( data, byteOffset >>> BIT_SHIFTS[typedArrayType(data)] ); } } } const defBuffer = (gl, data, target = gl.ARRAY_BUFFER, mode = gl.STATIC_DRAW, retain = false) => new WebGLArrayBuffer(gl, data, target, mode, retain); const compileModel = (gl, spec, mode = gl.STATIC_DRAW) => { if (spec.attribPool) { spec.attribs = compileAttribPool( gl, spec.attribPool, void 0, gl.ARRAY_BUFFER, mode ); } else { __compileAttribs(gl, spec.attribs, mode); } if (spec.instancePool) { spec.instances = { attribs: compileAttribPool( gl, spec.instancePool, void 0, gl.ARRAY_BUFFER, mode ), num: spec.instancePool.capacity }; } else if (spec.instances) { __compileAttribs(gl, spec.instances.attribs, mode); } compileIndices(gl, spec.indices, mode); spec.mode == null && (spec.mode = DrawMode.TRIANGLES); if (isPlainObject(spec.shader)) { spec.shader = defShader(gl, spec.shader); } return spec; }; const __initBuffer = (gl, src, type, mode) => { if (src.buffer) { src.data && src.buffer.set(src.data); } else { src.buffer = new WebGLArrayBuffer(gl, src.data, type, mode, src.retain); } }; const __compileAttribs = (gl, attribs, mode) => { if (attribs) { for (let id in attribs) { __initBuffer(gl, attribs[id], gl.ARRAY_BUFFER, mode); } } return attribs; }; const compileIndices = (gl, index, mode = gl.STATIC_DRAW) => { if (index) { __initBuffer(gl, index, gl.ELEMENT_ARRAY_BUFFER, mode); } return index; }; const compileVAO = (gl, spec) => { if (spec.shader) { const isGL2 = isGL2Context(gl); const ext = !isGL2 ? gl.getExtension("OES_vertex_array_object") : null; if (isGL2 || ext) { let vao; if (isGL2) { vao = gl.createVertexArray(); gl.bindVertexArray(vao); } else { vao = ext.createVertexArrayOES(); ext.bindVertexArrayOES(vao); } !!vao && error("error creating VAO"); spec.shader.bindAttribs(spec.attribs); if (spec.indices) { spec.indices.buffer.bind(); } spec.shader.unbind(null); if (isGL2) { gl.bindVertexArray(null); } else { ext.bindVertexArrayOES(null); } return vao; } } }; const compileAttribPool = (gl, pool, ids, target = gl.ARRAY_BUFFER, mode = gl.STATIC_DRAW) => { const buf = defBuffer(gl, pool.bytes(), target, mode, true); const spec = {}; for (let id of ids || Object.keys(pool.specs)) { const attr = pool.specs[id]; spec[id] = { buffer: buf, size: attr.size, type: asGLType(attr.type), stride: pool.byteStride, offset: attr.byteOffset }; } return spec; }; export { WebGLArrayBuffer, compileAttribPool, compileIndices, compileModel, compileVAO, defBuffer };