Browse Source

webGLMesh

master
blobt 3 years ago
parent
commit
2e3aef939c
  1. 722
      src/render/webgl/WebGLMesh.ts

722
src/render/webgl/WebGLMesh.ts

@ -1,3 +1,725 @@
import { GLAttribBits, GLAttribState, GLAttribOffsetMap } from "./WebGLAttribState";
import { vec2, vec3, vec4, mat4 } from "../math";
import { TypedArrayList } from "../ds/TypedArrayList";
import { GLProgram } from "./WebGLProgram";
import { GLTexture } from "./WebGLTexture"
export abstract class GLMeshBase {
/**
* @var WebGL渲染上下文
*/
public gl: WebGLRenderingContext;
/**
* @var 7
*/
public drawMode: number
/**
* @var
*/
protected _attribState: GLAttribBits;
/**
* @var stride字节数
*/
protected _attribStride: number;
/**
* @var VAO
*/
protected _vao: OES_vertex_array_object;
/**
* @var VAO对象
*/
protected _vaoTarget: WebGLVertexArrayObjectOES;
public constructor(gl: WebGLRenderingContext, attribState: GLAttribBits, drawMode: number = gl.TRIANGLES) {
this.gl = gl;
//创建 VAO
let vao: OES_vertex_array_object | null = this.gl.getExtension("OES_vertex_array_object");
if (vao === null) {
throw new Error("Not support OES_vertex_array_object");
}
this._vao = vao;
let vaoTarget: WebGLVertexArrayObjectOES | null = this._vao.createVertexArrayOES();
if (vaoTarget === null) {
throw new Error("Not support WebGLVertexArrayObjectOES");
}
this._vaoTarget = vaoTarget;
this._attribState = attribState;
this._attribStride = GLAttribState.getVertexBytesStride(this._attribState);
this.drawMode = drawMode;
}
/**
* vao
*/
public bind(): void {
this._vao.bindVertexArrayOES(this._vaoTarget);
}
/**
* vao
*/
public unbind(): void {
this._vao.bindVertexArrayOES(null);
}
/**
* @var
*/
public get vertexStride(): number {
return this._attribStride;
}
}
export class GLStaticMesh extends GLMeshBase {
/**
* @var
*/
protected _vbo: WebGLBuffer;
/**
* @var
*/
protected _vertCount: number = 0;
/**
* @var
*/
protected _ibo: WebGLBuffer | null = null;
/**
* @var
*/
protected _indexCount: number = 0;
/**
* @var
*/
public mins: vec3;
/**
* @var
*/
public maxs: vec3;
public constructor(gl: WebGLRenderingContext, attribState: GLAttribBits, vbo: Float32Array | ArrayBuffer, ibo: Uint16Array | null = null, drawMode: number = gl.TRIANGLES) {
super(gl, attribState, drawMode);
//要先绑定vao才能使用vao来管理vbo
this.bind();
//创建vbo
let vb: WebGLBuffer | null = gl.createBuffer();
if (vb === null) {
throw new Error("create vbo failed");
}
this._vbo = vbo;
//绑定vbo并载入顶点数据
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this._vbo);
this.gl.bufferData(this.gl.ARRAY_BUFFER, vbo, this.gl.STATIC_DRAW);
// 然后计算出交错存储的顶点属性attribOffsetMap相关的值
let offsetMap: GLAttribOffsetMap = GLAttribState.getInterleaveLayoutAttribOffsetMap(this._attribState);
//计算顶点数量
this._vertCount = vbo.byteLength / offsetMap[GLAttribState.ATTRIBSTRIDE];
// 使用VAO后,我们只要初始化时设置一次setAttribVertexArrayPointer和setAttribVertexArrayState就行了
// 当我们后续调用基类的bind方法绑定VAO对象后,VAO会自动处理顶点地址绑定和顶点属性寄存器开启相关操作,这就简化了很多操作
GLAttribState.setAttribVertexArrayPointer(gl, offsetMap);
GLAttribState.setAttribVertexArrayState(gl, this._attribState);
this.setIBO(ibo);
this.unbind();
this.mins = new vec3();
this.maxs = new vec3();
}
/**
* ibo
* @param ibo
* @returns
*/
protected setIBO(ibo: Uint16Array | null): void {
if (ibo === null) {
return;
}
this._ibo = this.gl.createBuffer();
if (this._ibo === null) {
throw new Error("create ibo failed!");
}
//绑定ibo 并赋值
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this._ibo);
this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, ibo, this.gl.STATIC_DRAW);
this._indexCount = ibo.length;
}
/**
*
*/
public draw(): void {
this.bind();
if (this._ibo !== null) {
// 如果有IBO,使用drawElements方法绘制静态网格对象
this.gl.drawElements(this.drawMode, this._indexCount, this.gl.UNSIGNED_SHORT, 0);
} else {
// 如果没有IBO,则使用drawArrays方法绘制静态网格对象
this.gl.drawArrays(this.drawMode, 0, this._vertCount);
}
this.unbind();
}
/**
* 1drawElement的offset是以字节为单位的count是以索引个数为单位的
* 2drawRange内部并没有调用bind和unbind方法drawRange方法的话
* mesh.bind(); // 绑定VAO
* mesh.drawRange( 2, 5); // 调用drawRange方法
* mesh.unbind(); // 解绑VAO
*
* drawRange绘制从offset开始count个索引
* @param offset
* @param count
*/
public drawRange(offset: number, count: number) {
if (this._ibo !== null) {
this.gl.drawElements(this.drawMode, count, this.gl.UNSIGNED_SHORT, offset);
} else {
this.gl.drawArrays(this.drawMode, offset, count);
}
}
}
export class GLIndexedStaticMesh extends GLStaticMesh {
private _indices: TypedArrayList<Uint16Array>;
public constructor(gl: WebGLRenderingContext, attribState: GLAttribBits, vbo: Float32Array | ArrayBuffer, drawMode: number = gl.TRIANGLES) {
super(gl, attribState, vbo, null, drawMode);
this._indices = new TypedArrayList<Uint16Array>(Uint16Array, 90);
}
/**
*
* @param idx
* @returns
*/
public addIndex(idx: number): GLIndexedStaticMesh {
this._indices.push(idx);
this._indexCount = this._indices.length;
return this;
}
/**
*
* @returns
*/
public clearIndices(): GLIndexedStaticMesh {
this._indices.clear();
this._indexCount = 0;
return this;
}
/**
* ibo
* @param ibo
*/
protected setIBO(ibo: Uint16Array | null): void {
this._ibo = this.gl.createBuffer();
if (this._ibo === null) {
throw new Error("create ibo falied!");
}
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this._ibo);
}
/**
*
*/
public draw(): void {
this.bind();
if (this._ibo !== null) {
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this._ibo);
this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, this._indexCount, this.gl.UNSIGNED_SHORT);
this.gl.drawElements(this.drawMode, this._indexCount, this.gl.UNSIGNED_SHORT, 0);
} else {
this.gl.drawArrays(this.drawMode, 0, this._vertCount);
}
this.unbind();
}
/**
*
* @param offset
* @param count
*/
public drawRange(offset: number, count: number) {
if (this._ibo !== null) {
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this._ibo);
this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, this._indices.subArray(), this._indexCount);
this.gl.drawElements(this.drawMode, count, this.gl.UNSIGNED_SHORT, offset);
} else {
this.gl.drawArrays(this.drawMode, offset, count);
}
}
}
export enum EVertexLayout {
INTERLEAVED,
SEQUENCED,
SEPARATED
}
export class GLMeshBuilder extends GLMeshBase {
private static SEQUENCED: string = "SEQUENCED";
private static INTERLEAVED: string = "INVERLEAVED";
/**
* @var
*/
private _layout: EVertexLayout;
/* 为了简单,目前只支持顶点位置坐标,纹理坐标,颜色,法线这四种顶点属性格式 */
/**
* @var
*/
private _color: vec4 = new vec4([0, 0, 0, 0]);
/**
* @var
*/
private _texCoord: vec2 = new vec2([0, 0]);
/**
* @var
*/
private _normal: vec3 = new vec3([0, 0, 1]);
/**
* @var
*/
private _hasColor: boolean;
/**
* @var texcoord
*/
private _hasTexcoord: boolean;
/**
* @var 线
*/
private _hasNormal: boolean;
/**
* @var
*/
private _lists: { [key: string]: TypedArrayList<Float32Array> } = {}
/**
* VBO
*/
private _buffers: { [key: string]: WebGLBuffer } = {};
/**
*
*/
private _vertCount: number = 0;
/**
* @var
*/
public program: GLProgram;
/**
* @var
*/
public texture: WebGLTexture | null;
/**
* @var ibo
*/
private _ibo: WebGLBuffer | null;
/**
* @var
*/
private _indexCount: number = -1;
/**
*
* @param tex
*/
public setTexture(tex: GLTexture): void {
this.texture = tex;
}
/**
* ibo数据
* @param data
*/
public setIBO(data: Uint16Array): void {
this._ibo = this.gl.createBuffer();
if (this._ibo === null) {
throw new Error("create ibo falied!");
}
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this._ibo);
this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, data, this.gl.STATIC_DRAW);
this._indexCount = data.length;
}
public constructor(gl: WebGLRenderingContext, state: GLAttribBits, program: GLProgram, texture: GLTexture | null = null, layout: EVertexLayout = EVertexLayout.INTERLEAVED) {
super(gl, state);
this._hasColor = GLAttribState.hasColor(this._attribState);
this._hasTexcoord = GLAttribState.hasTexCoord_0(this._attribState);
this._hasNormal = GLAttribState.hasNormal(this._attribState);
this._ibo = null;
this._layout = layout;
this.program = program;
this.texture = texture;
//先绑定vao对象
this.bind();
let buffer: WebGLBuffer | null = this.gl.createBuffer();
if (buffer === null) {
throw new Error("create webgl buffer failed!");
}
if (this._layout === EVertexLayout.INTERLEAVED) {
//interleaved 使用一个arraylist 和一个 顶点缓存,调用的是GLAttribState.getInterleavedLayoutAttribOffsetMap方法
this._lists[GLMeshBuilder.INTERLEAVED] = new TypedArrayList<Float32Array>(Float32Array);
this._buffers[GLMeshBuilder.INTERLEAVED] = buffer;
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);
let map: GLAttribOffsetMap = GLAttribState.getInterleaveLayoutAttribOffsetMap(this._attribState);
GLAttribState.setAttribVertexArrayPointer(this.gl, map);
GLAttribState.setAttribVertexArrayState(this.gl, this._attribState);
} else if (this._layout === EVertexLayout.SEQUENCED) {
//sequenced 使用n个arraylist和一个顶点缓存
this._lists[GLAttribState.POSITION_NAME] = new TypedArrayList<Float32Array>(Float32Array);
if (this._hasColor) {
this._lists[GLAttribState.COLOR_NAME] = new TypedArrayList<Float32Array>(Float32Array);
}
if (this._hasTexcoord) {
this._lists[GLAttribState.TEXCOORD_NAME] = new TypedArrayList<Float32Array>(Float32Array);
}
if (this._hasNormal) {
this._lists[GLAttribState.NORMAL_NAME] = new TypedArrayList<Float32Array>(Float32Array);
}
buffer = this.gl.createBuffer();
if (buffer === null) {
throw new Error("create webgl buffer failed!");
}
this._buffers[GLMeshBuilder.SEQUENCED] = buffer;
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);
//因为是动态的,seqnenced 没法预先设置指针,但是可以预先设置顶点状态
GLAttribState.setAttribVertexArrayState(this.gl, this._attribState);
} else {
//seperated 使用n个arraylist和n个顶点缓存
this._lists[GLAttribState.POSITION_NAME] = new TypedArrayList<Float32Array>(Float32Array);
this._buffers[GLAttribState.POSITION_NAME] = buffer;
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);
this.gl.vertexAttribPointer(GLAttribState.POSITION_LOCATION, 3, gl.FLOAT, false, 0, 0);
this.gl.enableVertexAttribArray(GLAttribState.POSITION_LOCATION);
if (this._hasColor) {
this._lists[GLAttribState.COLOR_NAME] = new TypedArrayList<Float32Array>(Float32Array);
buffer = this.gl.createBuffer();
if (buffer === null) {
throw new Error("create webgl buffer failed!");
}
this._buffers[GLAttribState.COLOR_NAME] = buffer;
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);
this.gl.vertexAttribPointer(GLAttribState.COLOR_LOCATION, 4, gl.FLOAT, false, 0, 0);
this.gl.enableVertexAttribArray(GLAttribState.COLOR_LOCATION);
}
if (this._hasTexcoord) {
this._lists[GLAttribState.TEXCOORD_NAME] = new TypedArrayList<Float32Array>(Float32Array);
buffer = this.gl.createBuffer();
if (buffer === null) {
throw new Error("create webgl buffer failed!");
}
this._buffers[GLAttribState.TEXCOORD_NAME] = buffer;
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);
this.gl.vertexAttribPointer(GLAttribState.TEXCOORD_BIT, 2, gl.FLOAT, false, 0, 0);
this.gl.enableVertexAttribArray(GLAttribState.TEXCOORD_BIT);
}
if (this._hasNormal) {
this._lists[GLAttribState.NORMAL_NAME] = new TypedArrayList<Float32Array>(Float32Array);
buffer = this.gl.createBuffer();
if (buffer === null) {
throw new Error("create webgl buffer failed!");
}
this._buffers[GLAttribState.NORMAL_NAME] = buffer;
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);
this.gl.vertexAttribPointer(GLAttribState.NORMAL_LOCATION, 3, gl.FLOAT, false, 0, 0);
this.gl.enableVertexAttribArray(GLAttribState.NORMAL_LOCATION);
}
}
this.unbind();
}
/**
*
* @param r
* @param g
* @param b
* @param a
* @returns
*/
public color(r: number, g: number, b: number, a: number = 1.0): GLMeshBuilder {
if (this._hasColor) {
this._color.r = r;
this._color.g = g;
this._color.b = b;
this._color.a = a;
}
return this;
}
/**
* texcoord
* @param u
* @param v
* @returns
*
*/
public texcoord(u: number, v: number): GLMeshBuilder {
if (this._hasTexcoord) {
this._texCoord.x = u;
this._texCoord.y = v;
}
return this;
}
/**
*
* @param x
* @param y
* @param z
* @returns
*/
public normal(x: number, y: number, z: number): GLMeshBuilder {
if (this._hasNormal) {
this._normal.x = x;
this._normal.y = y;
this._normal.z = z;
}
return this;
}
/**
* vertex
* @param x
* @param y
* @param z
* @returns
*/
public vertex(x: number, y: number, z: number): GLMeshBuilder {
if (this._layout === EVertexLayout.INTERLEAVED) {
let list: TypedArrayList<Float32Array> = this._lists[GLMeshBuilder.INTERLEAVED];
list.push(x);
list.push(y);
list.push(z);
if (this._hasTexcoord) {
list.push(this._texCoord.x);
list.push(this._texCoord.y)
}
if (this._hasNormal) {
list.push(this._normal.x);
list.push(this._normal.y);
list.push(this._normal.z);
}
if (this._hasColor) {
list.push(this._color.r);
list.push(this._color.g);
list.push(this._color.b);
list.push(this._color.a);
}
} else { //sequenced separated 都是多个ArrayList
let list: TypedArrayList<Float32Array> = this._lists[GLAttribState.POSITION_NAME];
list.push(x);
list.push(y);
list.push(z);
if (this._hasTexcoord) {
list = this._lists[GLAttribState.TEXCOORD_NAME];
list.push(this._texCoord.x);
list.push(this._texCoord.y);
}
if (this._hasNormal) {
list = this._lists[GLAttribState.NORMAL_NAME];
list.push(this._normal.x);
list.push(this._normal.y);
list.push(this._normal.z);
}
if (this._hasColor) {
list = this._lists[GLAttribState.COLOR_NAME];
list.push(this._color.r);
list.push(this._color.g);
list.push(this._color.b);
list.push(this._color.a);
}
}
//更新顶点数量
this._vertCount++;
return this;
}
/**
* begin方法this指针
* @param drawMode
*/
public begin(drawMode: number = this.gl.TRIANGLES): GLMeshBuilder {
this.drawMode = drawMode;
this._vertCount = 0; //清空顶点
if (this._layout === EVertexLayout.INTERLEAVED) {
let list: TypedArrayList<Float32Array> = this._lists[GLMeshBuilder.INTERLEAVED];
list.clear();
} else {
let list: TypedArrayList<Float32Array> = this._lists[GLMeshBuilder.INTERLEAVED];
list.clear();
if (this._hasTexcoord) {
list = this._lists[GLAttribState.TEXCOORD_NAME];
list.clear();
}
if (this._hasNormal) {
list = this._lists[GLAttribState.NORMAL_NAME];
list.clear();
}
if (this._hasColor) {
list = this._lists[GLAttribState.COLOR_NAME];
list.clear();
}
}
return this;
}
public end(mvp: mat4): void {
this.program.bind();
//载入MVPMatrix uniform变量
this.program.setMatrix4(GLProgram.MVPMatrix, mvp);
//载入texture
if (this.texture !== null) {
this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);
this.program.loadSampler();
}
//绑定vao
this.bind();
if (this._layout === EVertexLayout.INTERLEAVED) {
//获取数据源
let list: TypedArrayList<Float32Array> = this._lists[GLMeshBuilder.INTERLEAVED];
//获取vbo
let buffer: WebGLBuffer = this._buffers[GLMeshBuilder.INTERLEAVED];
//绑定vbo 上传渲染数据到vbo
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);
this.gl.bufferData(this.gl.ARRAY_BUFFER, list.subArray(), this.gl.DYNAMIC_DRAW);
} else if (this._layout === EVertexLayout.SEQUENCED) {
let buffer: WebGLBuffer = this._buffers[GLMeshBuilder.SEQUENCED];
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);
this.gl.bufferData(this.gl.ARRAY_BUFFER, this._attribStride * this._vertCount, this.gl.DYNAMIC_DRAW);
let list: TypedArrayList<Float32Array> = this._lists[GLAttribState.POSITION_NAME];
this.gl.bufferSubData(this.gl.ARRAY_BUFFER, 0, list.subArray());
let map: GLAttribOffsetMap = GLAttribState.getSequenceLayoutAttribOffsetMap(this._attribState, this._vertCount);
if (this._hasTexcoord) {
list = this._lists[GLAttribState.TEXCOORD_NAME];
this.gl.bufferSubData(this.gl.ARRAY_BUFFER, map[GLAttribState.TEXCOORD_NAME], list.subArray());
}
if (this._hasNormal) {
list = this._lists[GLAttribState.NORMAL_NAME];
this.gl.bufferSubData(this.gl.ARRAY_BUFFER, map[GLAttribState.NORMAL_NAME], list.subArray());
}
if (this._hasColor) {
list = this._lists[GLAttribState.COLOR_NAME];
this.gl.bufferSubData(this.gl.ARRAY_BUFFER, map[GLAttribState.COLOR_NAME], list.subArray());
}
//每次都要重新计算和绑定顶点属性的首地址
GLAttribState.setAttribVertexArrayPointer(this.gl, map);
} else {
let buffer: WebGLBuffer = this._buffers[GLAttribState.POSITION_NAME];
let list: TypedArrayList<Float32Array> = this._lists[GLAttribState.POSITION_NAME];
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);
this.gl.bufferData(this.gl.ARRAY_BUFFER, list.subArray(), this.gl.DYNAMIC_DRAW);
if (this._hasTexcoord) {
buffer = this._buffers[GLAttribState.TEXCOORD_NAME];
list = this._lists[GLAttribState.TEXCOORD_NAME];
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);
this.gl.bufferData(this.gl.ARRAY_BUFFER, list.subArray(), this.gl.DYNAMIC_DRAW);
}
if (this._hasNormal) {
buffer = this._buffers[GLAttribState.NORMAL_NAME];
list = this._lists[GLAttribState.NORMAL_NAME];
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);
this.gl.bufferData(this.gl.ARRAY_BUFFER, list.subArray(), this.gl.DYNAMIC_DRAW);
}
if (this._hasColor) {
buffer = this._buffers[GLAttribState.COLOR_NAME];
list = this._lists[GLAttribState.COLOR_NAME];
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);
this.gl.bufferData(this.gl.ARRAY_BUFFER, list.subArray(), this.gl.DYNAMIC_DRAW);
}
}
if (this._ibo !== null) {
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this._ibo);
this.gl.drawElements(this.drawMode, this._indexCount, this.gl.UNSIGNED_SHORT, 0);
} else {
this.gl.drawArrays(this.drawMode, 0, this._vertCount);
}
this.unbind();
this.program.unbind();
}
}
Loading…
Cancel
Save