123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- /**
- * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author Alexander Rose <alexander.rose@weirdbyte.de>
- */
- import { WebGLContext } from './context';
- import { ValueCell } from '../../mol-util';
- import { RenderableSchema } from '../renderable/schema';
- import { idFactory } from '../../mol-util/id-factory';
- import { assertUnreachable, ValueOf } from '../../mol-util/type-helpers';
- import { GLRenderingContext, isWebGL2 } from './compat';
- import { WebGLExtensions } from './extensions';
- import { WebGLState } from './state';
- const getNextBufferId = idFactory();
- export type UsageHint = 'static' | 'dynamic' | 'stream'
- export type DataType = 'uint8' | 'int8' | 'uint16' | 'int16' | 'uint32' | 'int32' | 'float32'
- export type BufferType = 'attribute' | 'elements' | 'uniform'
- export type DataTypeArrayType = {
- 'uint8': Uint8Array
- 'int8': Int8Array
- 'uint16': Uint16Array
- 'int16': Int16Array
- 'uint32': Uint32Array
- 'int32': Int32Array
- 'float32': Float32Array
- }
- export type ArrayType = ValueOf<DataTypeArrayType>
- export type ArrayKind = keyof DataTypeArrayType
- export function getUsageHint(gl: GLRenderingContext, usageHint: UsageHint) {
- switch (usageHint) {
- case 'static': return gl.STATIC_DRAW;
- case 'dynamic': return gl.DYNAMIC_DRAW;
- case 'stream': return gl.STREAM_DRAW;
- }
- }
- export function getDataType(gl: GLRenderingContext, dataType: DataType) {
- switch (dataType) {
- case 'uint8': return gl.UNSIGNED_BYTE;
- case 'int8': return gl.BYTE;
- case 'uint16': return gl.UNSIGNED_SHORT;
- case 'int16': return gl.SHORT;
- case 'uint32': return gl.UNSIGNED_INT;
- case 'int32': return gl.INT;
- case 'float32': return gl.FLOAT;
- default: assertUnreachable(dataType);
- }
- }
- function dataTypeFromArray(gl: GLRenderingContext, array: ArrayType) {
- if (array instanceof Uint8Array) {
- return gl.UNSIGNED_BYTE;
- } else if (array instanceof Int8Array) {
- return gl.BYTE;
- } else if (array instanceof Uint16Array) {
- return gl.UNSIGNED_SHORT;
- } else if (array instanceof Int16Array) {
- return gl.SHORT;
- } else if (array instanceof Uint32Array) {
- return gl.UNSIGNED_INT;
- } else if (array instanceof Int32Array) {
- return gl.INT;
- } else if (array instanceof Float32Array) {
- return gl.FLOAT;
- }
- assertUnreachable(array);
- }
- export function getBufferType(gl: GLRenderingContext, bufferType: BufferType) {
- switch (bufferType) {
- case 'attribute': return gl.ARRAY_BUFFER;
- case 'elements': return gl.ELEMENT_ARRAY_BUFFER;
- case 'uniform':
- if (isWebGL2(gl)) {
- return gl.UNIFORM_BUFFER;
- } else {
- throw new Error('WebGL2 is required for uniform buffers');
- }
- }
- }
- export interface Buffer {
- readonly id: number
- readonly _usageHint: number
- readonly _bufferType: number
- readonly _dataType: number
- readonly _bpe: number
- readonly length: number
- getBuffer: () => WebGLBuffer
- updateData: (array: ArrayType) => void
- updateSubData: (array: ArrayType, offset: number, count: number) => void
- reset: () => void
- destroy: () => void
- }
- export function getBuffer(gl: GLRenderingContext) {
- const buffer = gl.createBuffer();
- if (buffer === null) {
- throw new Error('Could not create WebGL buffer');
- }
- return buffer;
- }
- function createBuffer(gl: GLRenderingContext, array: ArrayType, usageHint: UsageHint, bufferType: BufferType): Buffer {
- let _buffer = getBuffer(gl);
- const _usageHint = getUsageHint(gl, usageHint);
- const _bufferType = getBufferType(gl, bufferType);
- const _dataType = dataTypeFromArray(gl, array);
- const _bpe = array.BYTES_PER_ELEMENT;
- const _length = array.length;
- function updateData(array: ArrayType) {
- gl.bindBuffer(_bufferType, _buffer);
- gl.bufferData(_bufferType, array, _usageHint);
- }
- updateData(array);
- let destroyed = false;
- return {
- id: getNextBufferId(),
- _usageHint,
- _bufferType,
- _dataType,
- _bpe,
- length: _length,
- getBuffer: () => _buffer,
- updateData,
- updateSubData: (array: ArrayType, offset: number, count: number) => {
- gl.bindBuffer(_bufferType, _buffer);
- if (count - offset === array.length) {
- gl.bufferSubData(_bufferType, 0, array);
- } else {
- gl.bufferSubData(_bufferType, offset * _bpe, array.subarray(offset, offset + count));
- }
- },
- reset: () => {
- _buffer = getBuffer(gl);
- updateData(array);
- },
- destroy: () => {
- if (destroyed) return;
- gl.deleteBuffer(_buffer);
- destroyed = true;
- }
- };
- }
- //
- export type AttributeItemSize = 1 | 2 | 3 | 4 | 16
- export type AttributeKind = 'float32'
- export function getAttribType(gl: GLRenderingContext, kind: AttributeKind, itemSize: AttributeItemSize) {
- switch (kind) {
- case 'float32':
- switch (itemSize) {
- case 1: return gl.FLOAT;
- case 2: return gl.FLOAT_VEC2;
- case 3: return gl.FLOAT_VEC3;
- case 4: return gl.FLOAT_VEC4;
- case 16: return gl.FLOAT_MAT4;
- }
- default:
- assertUnreachable(kind);
- }
- }
- export type AttributeDefs = {
- [k: string]: { kind: AttributeKind, itemSize: AttributeItemSize, divisor: number }
- }
- export type AttributeValues = { [k: string]: ValueCell<ArrayType> }
- export type AttributeBuffers = [string, AttributeBuffer][]
- export interface AttributeBuffer extends Buffer {
- bind: (location: number) => void
- }
- export function createAttributeBuffer<T extends ArrayType, S extends AttributeItemSize>(gl: GLRenderingContext, state: WebGLState, extensions: WebGLExtensions, array: T, itemSize: S, divisor: number, usageHint: UsageHint = 'dynamic'): AttributeBuffer {
- const { instancedArrays } = extensions;
- const buffer = createBuffer(gl, array, usageHint, 'attribute');
- const { _bufferType, _dataType, _bpe } = buffer;
- return {
- ...buffer,
- bind: (location: number) => {
- gl.bindBuffer(_bufferType, buffer.getBuffer());
- if (itemSize === 16) {
- for (let i = 0; i < 4; ++i) {
- state.enableVertexAttrib(location + i);
- gl.vertexAttribPointer(location + i, 4, _dataType, false, 4 * 4 * _bpe, i * 4 * _bpe);
- instancedArrays.vertexAttribDivisor(location + i, divisor);
- }
- } else {
- state.enableVertexAttrib(location);
- gl.vertexAttribPointer(location, itemSize, _dataType, false, 0, 0);
- instancedArrays.vertexAttribDivisor(location, divisor);
- }
- }
- };
- }
- export function createAttributeBuffers(ctx: WebGLContext, schema: RenderableSchema, values: AttributeValues) {
- const buffers: AttributeBuffers = [];
- Object.keys(schema).forEach(k => {
- const spec = schema[k];
- if (spec.type === 'attribute') {
- buffers[buffers.length] = [k, ctx.resources.attribute(values[k].ref.value, spec.itemSize, spec.divisor)];
- }
- });
- return buffers;
- }
- //
- export type ElementsType = Uint16Array | Uint32Array
- export type ElementsKind = 'uint16' | 'uint32'
- export interface ElementsBuffer extends Buffer {
- bind: () => void
- }
- export function createElementsBuffer(gl: GLRenderingContext, array: ElementsType, usageHint: UsageHint = 'static'): ElementsBuffer {
- const buffer = createBuffer(gl, array, usageHint, 'elements');
- return {
- ...buffer,
- bind: () => {
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer.getBuffer());
- }
- };
- }
|