mirror of
https://github.com/Terncode/pixel.horse.git
synced 2026-09-25 06:05:52 +02:00
157 lines
4.4 KiB
TypeScript
157 lines
4.4 KiB
TypeScript
import { timeStart, timeEnd } from '../../client/timing';
|
|
|
|
export interface VAOAttributes {
|
|
name: string;
|
|
buffer: WebGLBuffer;
|
|
size?: number;
|
|
type?: number;
|
|
normalized?: boolean;
|
|
stride?: number;
|
|
offset?: number;
|
|
divisor?: number;
|
|
}
|
|
|
|
export interface VAO {
|
|
gl: WebGLRenderingContext;
|
|
bind(): void;
|
|
unbind(): void;
|
|
dispose(): void;
|
|
update(attributes: VAOAttributes[], elements: WebGLBuffer, elementsType?: number): void;
|
|
draw(mode: number, count: number, offset?: number): void;
|
|
}
|
|
|
|
interface WebGL extends WebGLRenderingContext {
|
|
bindVertexArray?: any;
|
|
createVertexArray?: any;
|
|
deleteVertexArray?: any;
|
|
}
|
|
|
|
function extensionShim(gl: WebGL): OES_vertex_array_object {
|
|
return {
|
|
bindVertexArrayOES: gl.bindVertexArray.bind(gl),
|
|
createVertexArrayOES: gl.createVertexArray.bind(gl),
|
|
deleteVertexArrayOES: gl.deleteVertexArray.bind(gl),
|
|
} as any;
|
|
}
|
|
|
|
export function createVAO(gl: WebGL, attributes: VAOAttributes[], elements: WebGLBuffer, elementsType?: number): VAO {
|
|
const ext = gl.createVertexArray ? extensionShim(gl) : gl.getExtension('OES_vertex_array_object');
|
|
const handle = ext && ext.createVertexArrayOES();
|
|
const vao = (ext && handle) ? new VAONative(gl, ext, handle) : new VAOEmulated(gl);
|
|
vao.update(attributes, elements, elementsType);
|
|
return vao;
|
|
}
|
|
|
|
class VAONative implements VAO {
|
|
private useElements = false;
|
|
private elementsType: number;
|
|
private maxAttribs: number;
|
|
constructor(
|
|
public gl: WebGLRenderingContext,
|
|
private ext: OES_vertex_array_object,
|
|
public handle: WebGLVertexArrayObjectOES,
|
|
) {
|
|
this.elementsType = gl.UNSIGNED_SHORT;
|
|
this.maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
|
|
}
|
|
bind() {
|
|
this.ext.bindVertexArrayOES(this.handle);
|
|
}
|
|
unbind() {
|
|
this.ext.bindVertexArrayOES(null);
|
|
}
|
|
dispose() {
|
|
this.ext.deleteVertexArrayOES(this.handle);
|
|
this.handle = {};
|
|
}
|
|
update(attributes: VAOAttributes[], elements: WebGLBuffer | null, elementsType?: number) {
|
|
this.bind();
|
|
bindAttribs(this.gl, elements, attributes, this.maxAttribs);
|
|
this.unbind();
|
|
this.useElements = !!elements;
|
|
this.elementsType = elementsType || this.gl.UNSIGNED_SHORT;
|
|
}
|
|
draw(mode: number, count: number, offset = 0) {
|
|
TIMING && timeStart('VAONative.draw');
|
|
if (this.useElements) {
|
|
this.gl.drawElements(mode, count, this.elementsType, offset);
|
|
} else {
|
|
this.gl.drawArrays(mode, offset, count);
|
|
}
|
|
TIMING && timeEnd();
|
|
}
|
|
}
|
|
|
|
class VAOEmulated implements VAO {
|
|
private elements: WebGLBuffer | null = null;
|
|
private attributes: VAOAttributes[] | null = null;
|
|
private elementsType: number;
|
|
private maxAttribs: number;
|
|
constructor(public gl: WebGLRenderingContext) {
|
|
this.elementsType = gl.UNSIGNED_SHORT;
|
|
this.maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
|
|
}
|
|
bind() {
|
|
bindAttribs(this.gl, this.elements, this.attributes, this.maxAttribs);
|
|
}
|
|
update(attributes: VAOAttributes[], elements: WebGLBuffer | null, elementsType?: number) {
|
|
this.elements = elements;
|
|
this.attributes = attributes;
|
|
this.elementsType = elementsType || this.gl.UNSIGNED_SHORT;
|
|
}
|
|
dispose() {
|
|
}
|
|
unbind() {
|
|
}
|
|
draw(mode: number, count: number, offset = 0) {
|
|
TIMING && timeStart('VAOEmulated.draw');
|
|
if (this.elements) {
|
|
this.gl.drawElements(mode, count, this.elementsType, offset);
|
|
} else {
|
|
this.gl.drawArrays(mode, offset, count);
|
|
}
|
|
TIMING && timeEnd();
|
|
}
|
|
}
|
|
|
|
function bindAttribs(
|
|
gl: WebGLRenderingContext, elements: WebGLBuffer | null, attributes: VAOAttributes[] | null, maxAttribs: number
|
|
) {
|
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elements);
|
|
|
|
if (attributes) {
|
|
if (maxAttribs != null && attributes.length > maxAttribs) {
|
|
throw new Error(`Too many vertex attributes ${attributes.length}/${maxAttribs}`);
|
|
}
|
|
|
|
let i = 0;
|
|
|
|
for (; i < attributes.length; ++i) {
|
|
const attrib = attributes[i];
|
|
const buffer = attrib.buffer;
|
|
const size = attrib.size || 4;
|
|
const type = attrib.type || gl.FLOAT;
|
|
const normalized = !!attrib.normalized;
|
|
const stride = attrib.stride || 0;
|
|
const offset = attrib.offset || 0;
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
gl.enableVertexAttribArray(i);
|
|
gl.vertexAttribPointer(i, size, type, normalized, stride, offset);
|
|
|
|
if (attrib.divisor !== undefined) {
|
|
(gl as any).vertexAttribDivisor(i, attrib.divisor);
|
|
}
|
|
}
|
|
|
|
for (; i < maxAttribs; ++i) {
|
|
gl.disableVertexAttribArray(i);
|
|
}
|
|
} else {
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, null);
|
|
|
|
for (let i = 0; i < maxAttribs; ++i) {
|
|
gl.disableVertexAttribArray(i);
|
|
}
|
|
}
|
|
}
|