mdx-m3-viewer
Version:
A browser WebGL model viewer. Mainly focused on models of the games Warcraft 3 and Starcraft 2.
131 lines (110 loc) • 5.2 kB
text/typescript
import MdlxModel from '../../../parsers/mdlx/model';
import ShaderProgram from '../../gl/program';
import War3MapViewer from './viewer';
/**
* A static terrain model.
*/
export default class TerrainModel {
viewer: War3MapViewer;
vertexBuffer: WebGLBuffer;
faceBuffer: WebGLBuffer;
normalsOffset: number;
uvsOffset: number;
elements: number;
locationAndTextureBuffer: WebGLBuffer;
texturesOffset: number;
instances: number;
vao: WebGLVertexArrayObjectOES | null;
constructor(viewer: War3MapViewer, arrayBuffer: ArrayBuffer, locations: number[], textures: number[], shader: ShaderProgram) {
let gl = viewer.gl;
let webgl = viewer.webgl;
let instancedArrays = <ANGLE_instanced_arrays>webgl.extensions.ANGLE_instanced_arrays
let vertexArrayObject = <OES_vertex_array_object>webgl.extensions.OES_vertex_array_object;
let parser = new MdlxModel(arrayBuffer);
let geoset = parser.geosets[0];
let vertices = geoset.vertices;
let normals = geoset.normals;
let uvs = geoset.uvSets[0];
let faces = geoset.faces;
let normalsOffset = vertices.byteLength;
let uvsOffset = normalsOffset + normals.byteLength;
let vao = null;
let attribs = shader.attribs;
if (vertexArrayObject) {
vao = vertexArrayObject.createVertexArrayOES();
vertexArrayObject.bindVertexArrayOES(vao);
}
let vertexBuffer = <WebGLBuffer>gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, uvsOffset + uvs.byteLength, gl.STATIC_DRAW);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, vertices);
gl.bufferSubData(gl.ARRAY_BUFFER, normalsOffset, normals);
gl.bufferSubData(gl.ARRAY_BUFFER, uvsOffset, uvs);
if (vertexArrayObject) {
gl.vertexAttribPointer(attribs.a_position, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(attribs.a_position);
gl.vertexAttribPointer(attribs.a_normal, 3, gl.FLOAT, false, 0, normalsOffset);
gl.enableVertexAttribArray(attribs.a_normal);
gl.vertexAttribPointer(attribs.a_uv, 2, gl.FLOAT, false, 0, uvsOffset);
gl.enableVertexAttribArray(attribs.a_uv);
}
let texturesOffset = locations.length * 4;
let locationAndTextureBuffer = <WebGLBuffer>gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, locationAndTextureBuffer);
gl.bufferData(gl.ARRAY_BUFFER, texturesOffset + textures.length, gl.STATIC_DRAW);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, new Float32Array(locations));
gl.bufferSubData(gl.ARRAY_BUFFER, texturesOffset, new Uint8Array(textures));
if (vertexArrayObject) {
gl.vertexAttribPointer(attribs.a_instancePosition, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(attribs.a_instancePosition);
instancedArrays.vertexAttribDivisorANGLE(attribs.a_instancePosition, 1);
gl.vertexAttribPointer(attribs.a_instanceTexture, 1, gl.UNSIGNED_BYTE, false, 0, texturesOffset);
gl.enableVertexAttribArray(attribs.a_instanceTexture);
instancedArrays.vertexAttribDivisorANGLE(attribs.a_instanceTexture, 1);
}
let faceBuffer = <WebGLBuffer>gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, faceBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW);
if (vertexArrayObject) {
vertexArrayObject.bindVertexArrayOES(null);
}
this.viewer = viewer;
this.vertexBuffer = vertexBuffer;
this.faceBuffer = faceBuffer;
this.normalsOffset = normalsOffset;
this.uvsOffset = uvsOffset;
this.elements = faces.length;
this.locationAndTextureBuffer = locationAndTextureBuffer;
this.texturesOffset = texturesOffset;
this.instances = locations.length / 3;
this.vao = vao;
}
render(shader: ShaderProgram) {
let viewer = this.viewer;
let gl = viewer.gl;
let webgl = viewer.webgl;
let instancedArrays = <ANGLE_instanced_arrays>webgl.extensions.ANGLE_instanced_arrays
let vertexArrayObject = <OES_vertex_array_object>webgl.extensions.OES_vertex_array_object;
let attribs = shader.attribs;
if (vertexArrayObject) {
vertexArrayObject.bindVertexArrayOES(this.vao);
} else {
// Locations and textures.
gl.bindBuffer(gl.ARRAY_BUFFER, this.locationAndTextureBuffer);
gl.vertexAttribPointer(attribs.a_instancePosition, 3, gl.FLOAT, false, 0, 0);
gl.vertexAttribPointer(attribs.a_instanceTexture, 1, gl.UNSIGNED_BYTE, false, 0, this.texturesOffset);
// Vertices.
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
gl.vertexAttribPointer(attribs.a_position, 3, gl.FLOAT, false, 0, 0);
gl.vertexAttribPointer(attribs.a_normal, 3, gl.FLOAT, false, 0, this.normalsOffset);
gl.vertexAttribPointer(attribs.a_uv, 2, gl.FLOAT, false, 0, this.uvsOffset);
// Faces.
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.faceBuffer);
}
// Draw.
instancedArrays.drawElementsInstancedANGLE(gl.TRIANGLES, this.elements, gl.UNSIGNED_SHORT, 0, this.instances);
if (vertexArrayObject) {
vertexArrayObject.bindVertexArrayOES(null);
}
}
}