ts-game-engine
Version:
Simple WebGL game/render engine written in TypeScript
130 lines (129 loc) • 6.49 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const Meshes_1 = require("../Meshes");
const Constants_1 = require("../Constants");
const MeshRenderer_1 = require("./MeshRenderer");
const Utils_1 = require("../Utils");
class MeshRendererInstanced extends MeshRenderer_1.MeshRenderer {
constructor(scene, name, maxInstances) {
super(scene, name, false);
this.maxInstances = maxInstances;
this.instances = 0;
this.instancedBuffers = new Map();
}
Dispose() {
for (let value of this.instancedBuffers.values()) {
this.context.deleteBuffer(value.buffer);
}
this.instancedBuffers.clear();
super.Dispose();
}
Render() {
if (this.mesh === undefined)
return;
if (this.material === undefined)
return;
if (this.instancedBuffers.size === 0)
return;
this.pipelineState.CurrentShader = this.material.Shader;
const globalUniforms = this.GetGlobalUniformsObject();
this.material.SetUniforms(globalUniforms);
this.pipelineState.CurrentVAO = this.vao;
if (this.mesh.IndexBuffer)
this.context.drawElementsInstanced(this.mesh.MeshTopology, this.mesh.IndexCount, WebGL2RenderingContext.UNSIGNED_SHORT, 0, this.instances);
else
this.context.drawArraysInstanced(this.mesh.MeshTopology, 0, this.mesh.VertexCount, this.instances);
}
SetMesh(mesh) {
if (this.mesh === mesh)
return;
if (!mesh.IsLoaded) {
console.error(`Trying to set a mesh into "${this.Name}" MeshRenderer while it still didn't finish loading.`);
return;
}
this.mesh = mesh;
this.UpdateVAO();
}
SetMatrices(instances, data) {
this.SetInstancedAttribute(Constants_1.INSTANCE_MATRIX_ATTRIBUTE, Constants_1.MATRIX_4X4_SIZE, instances, data);
}
SetInstancedAttribute(instancedAttribute, instanceDataSize, instances, data) {
if (this.material === undefined) {
console.warn("First set a material before calling SetInstancedProperty.");
return;
}
let location = this.material.Shader.InstancedAttributes.get(instancedAttribute);
if (location === undefined) {
console.warn(`Instanced attribute "${instancedAttribute}" does not exist in the current material.`);
return;
}
const maxBufferSize = this.maxInstances * instanceDataSize;
if (data.byteLength > maxBufferSize) {
console.warn(`Can't set instanced attribute "${instancedAttribute}" data that is bigger than its initial size (${data.byteLength} > ${maxBufferSize}).`);
return;
}
this.SetInstanceCount(instances);
let instancedBuffer = this.instancedBuffers.get(instancedAttribute);
if (instancedBuffer === undefined) {
const buffer = this.context.createBuffer();
if (buffer === null)
throw new Error("Unable to create instanced buffer.");
Utils_1.Utils.DebugName(buffer, `${instancedAttribute} instanced buffer`);
instancedBuffer = { location: location, buffer: buffer, bufferSize: maxBufferSize, instanceDataSize: instanceDataSize };
this.instancedBuffers.set(instancedAttribute, instancedBuffer);
this.context.bindBuffer(WebGL2RenderingContext.ARRAY_BUFFER, instancedBuffer.buffer);
this.context.bufferData(WebGL2RenderingContext.ARRAY_BUFFER, instancedBuffer.bufferSize, WebGL2RenderingContext.DYNAMIC_DRAW);
this.context.bufferSubData(WebGL2RenderingContext.ARRAY_BUFFER, 0, data);
this.UpdateVAO();
}
else {
this.context.bindBuffer(WebGL2RenderingContext.ARRAY_BUFFER, instancedBuffer.buffer);
this.context.bufferSubData(WebGL2RenderingContext.ARRAY_BUFFER, 0, data);
}
}
SetInstanceCount(instances) {
this.instances = instances;
}
UpdateVAO() {
if (this.mesh === undefined)
return;
if (this.material === undefined)
return;
if (this.instancedBuffers.size === 0)
return;
this.pipelineState.CurrentVAO = this.vao;
this.context.bindBuffer(WebGL2RenderingContext.ARRAY_BUFFER, this.mesh.VertexBuffer);
this.context.bindBuffer(WebGL2RenderingContext.ELEMENT_ARRAY_BUFFER, this.mesh.IndexBuffer !== undefined ? this.mesh.IndexBuffer : null);
const vertexFormat = this.mesh.VertexFormat;
let stride = 0;
for (let f = 0; f < Meshes_1.ATTRIBUTE_INFO.length; f++) {
if ((vertexFormat & Meshes_1.ATTRIBUTE_INFO[f].vertexFormat) !== 0)
stride += Constants_1.FLOAT_SIZE * Meshes_1.ATTRIBUTE_INFO[f].size;
}
let offset = 0;
for (let f = 0; f < Meshes_1.ATTRIBUTE_INFO.length; f++) {
if ((vertexFormat & Meshes_1.ATTRIBUTE_INFO[f].vertexFormat) !== 0) {
this.context.vertexAttribPointer(Meshes_1.ATTRIBUTE_INFO[f].location, Meshes_1.ATTRIBUTE_INFO[f].size, WebGL2RenderingContext.FLOAT, false, stride, offset);
this.context.enableVertexAttribArray(Meshes_1.ATTRIBUTE_INFO[f].location);
offset += Constants_1.FLOAT_SIZE * Meshes_1.ATTRIBUTE_INFO[f].size;
}
}
for (let instancedBuffer of this.instancedBuffers.values()) {
this.context.bindBuffer(WebGL2RenderingContext.ARRAY_BUFFER, instancedBuffer.buffer);
// For example, a matrix4 is actually 4 vec4
const amountOfVec4 = instancedBuffer.instanceDataSize / 16;
for (let i = 0; i < amountOfVec4; i++) {
this.context.vertexAttribPointer(instancedBuffer.location + i, 4, WebGL2RenderingContext.FLOAT, false, instancedBuffer.instanceDataSize, i * 16);
this.context.vertexAttribDivisor(instancedBuffer.location + i, 1);
this.context.enableVertexAttribArray(instancedBuffer.location + i);
}
}
}
// Bounds and Culling -----------------------------------------------------------------------------------------------------
UpdateAABB() {
}
IsCulled() {
return false;
}
}
exports.MeshRendererInstanced = MeshRendererInstanced;