UNPKG

ts-game-engine

Version:

Simple WebGL game/render engine written in TypeScript

130 lines (129 loc) 6.49 kB
"use strict"; 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;