UNPKG

ts-game-engine

Version:

Simple WebGL game/render engine written in TypeScript

203 lines (202 loc) 9.54 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const Entity_1 = require("./Entity"); const Meshes_1 = require("../Meshes"); const Materials_1 = require("../Materials"); const Constants_1 = require("../Constants"); const Utils_1 = require("../Utils"); const WireBoxMesh_1 = require("../Meshes/WireBoxMesh"); const gl_matrix_1 = require("gl-matrix"); const BoundingBox_1 = require("../Math/BoundingBox"); class MeshRenderer extends Entity_1.Entity { constructor(scene, name, culling = true) { super(scene, name); this.aabbMeshDirty = false; this.aabbColor = gl_matrix_1.vec4.fromValues(0.1, 1, 0.45, 1); this.culling = culling; this.context = scene.Game.GraphicsSystem.Context; this.pipelineState = scene.Game.GraphicsSystem.PipelineState; let vao = this.context.createVertexArray(); if (vao === null) throw new Error("Unable to create Vertex Array Object."); this.vao = vao; Utils_1.Utils.DebugName(this.vao, `${this.Name} VAO`); this.aabb = new BoundingBox_1.BoundingBox(); this.Transform.OnTransformChange = () => this.UpdateAABB(); } get Mesh() { return this.mesh; } get Material() { return this.material; } get IsRenderable() { return this.mesh !== undefined && this.material !== undefined; } Dispose() { this.context.deleteVertexArray(this.vao); this.DisposeBoundsDebug(); } Render() { if (this.mesh === undefined) return; if (this.material === undefined) return; if (this.IsCulled()) 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.drawElements(this.mesh.MeshTopology, this.mesh.IndexCount, WebGL2RenderingContext.UNSIGNED_SHORT, 0); else this.context.drawArrays(this.mesh.MeshTopology, 0, this.mesh.VertexCount); this.RenderBoundsDebug(globalUniforms); } 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(); this.UpdateAABB(); this.CreateBoundsDebug(); } SetMaterial(material) { if (this.material === material) return; this.material = material; this.UpdateVAO(); } UpdateVAO() { if (this.mesh === undefined) return; if (this.material === undefined) 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; } } } GetGlobalUniformsObject() { return { modelMatrix: this.Transform.ModelMatrix, viewMatrix: this.Scene.Camera.ViewMatrix, projectionMatrix: this.Scene.Camera.ProjectionMatrix, viewDirectionProjectionInverseMatrix: this.Scene.Camera.ViewDirectionProjectionInverseMatrix, normalMatrix: this.Transform.NormalMatrix, viewPosition: this.Scene.Camera.Transform.Position, ambientLight: this.Scene.AmbientLight, pointLightsData: this.Scene.PointLightsData, pointLightsCount: this.Scene.PointLightsCount }; } // Bounds and Culling ----------------------------------------------------------------------------------------------------- UpdateAABB() { if (!this.culling) return; if (this.mesh === undefined) return; let points = this.mesh.BoundingBox.GetPoints(); this.aabb.min[0] = Number.MAX_VALUE; this.aabb.min[1] = Number.MAX_VALUE; this.aabb.min[2] = Number.MAX_VALUE; this.aabb.max[0] = -Number.MAX_VALUE; this.aabb.max[1] = -Number.MAX_VALUE; this.aabb.max[2] = -Number.MAX_VALUE; for (let p = 0; p < points.length; p++) { let point = gl_matrix_1.vec3.transformMat4(points[p], points[p], this.Transform.ModelMatrix); if (point[0] < this.aabb.min[0]) this.aabb.min[0] = point[0]; else if (point[0] > this.aabb.max[0]) this.aabb.max[0] = point[0]; if (point[1] < this.aabb.min[1]) this.aabb.min[1] = point[1]; else if (point[1] > this.aabb.max[1]) this.aabb.max[1] = point[1]; if (point[2] < this.aabb.min[2]) this.aabb.min[2] = point[2]; else if (point[2] > this.aabb.max[2]) this.aabb.max[2] = point[2]; } this.aabbMeshDirty = true; } IsCulled() { if (!this.culling) return false; return this.Scene.Camera.Frustum.CheckBoundsIntersection(this.aabb) === 1 /* Outside */; } CreateBoundsDebug() { if (!this.culling) return; // Create mesh if (this.aabbMesh !== undefined) { this.aabbMesh.Dispose(); } this.aabbMesh = new WireBoxMesh_1.WireBoxMesh(this.Scene, this.aabb, this.aabbColor); // Create material if (this.aabbMaterial === undefined) { this.aabbMaterial = new Materials_1.VertexColoredMaterial(this.Scene); } // Create VAO if (this.aabbVAO !== undefined) { this.context.deleteVertexArray(this.aabbVAO); } let vao = this.context.createVertexArray(); if (vao === null) throw new Error("Unable to create bounding box Vertex Array Object."); this.aabbVAO = vao; Utils_1.Utils.DebugName(this.aabbVAO, `${this.Name} bounding box VAO`); // Configure VAO this.pipelineState.CurrentVAO = this.aabbVAO; this.context.bindBuffer(WebGL2RenderingContext.ARRAY_BUFFER, this.aabbMesh.VertexBuffer); this.context.bindBuffer(WebGL2RenderingContext.ELEMENT_ARRAY_BUFFER, this.aabbMesh.IndexBuffer !== undefined ? this.aabbMesh.IndexBuffer : null); let stride = (Constants_1.FLOAT_SIZE * Meshes_1.VERTEX_POSITION_SIZE) + (Constants_1.FLOAT_SIZE * Meshes_1.VERTEX_COLOR_SIZE); this.context.vertexAttribPointer(Constants_1.POSITION_ATTRIBUTE_LOCATION, Meshes_1.VERTEX_POSITION_SIZE, WebGL2RenderingContext.FLOAT, false, stride, 0); this.context.enableVertexAttribArray(Constants_1.POSITION_ATTRIBUTE_LOCATION); this.context.vertexAttribPointer(Constants_1.COLOR_ATTRIBUTE_LOCATION, Meshes_1.VERTEX_COLOR_SIZE, WebGL2RenderingContext.FLOAT, false, stride, Constants_1.FLOAT_SIZE * Meshes_1.VERTEX_POSITION_SIZE); this.context.enableVertexAttribArray(Constants_1.COLOR_ATTRIBUTE_LOCATION); } RenderBoundsDebug(globalUniforms) { if (!this.culling) return; if (!this.Scene.Game.Settings.ShowBounds) return; if (this.aabbMesh === undefined) return; if (this.aabbMaterial === undefined) return; if (this.aabbVAO === undefined) return; if (this.aabbMeshDirty) { this.aabbMesh.UpdateBounds(this.aabb, this.aabbColor); this.aabbMeshDirty = false; } this.pipelineState.CurrentShader = this.aabbMaterial.Shader; globalUniforms.modelMatrix = gl_matrix_1.mat4.create(); // Override model matrix as we don't use it for AABB this.aabbMaterial.SetUniforms(globalUniforms); this.pipelineState.CurrentVAO = this.aabbVAO; if (this.aabbMesh.IndexBuffer) this.context.drawElements(this.aabbMesh.MeshTopology, this.aabbMesh.IndexCount, WebGL2RenderingContext.UNSIGNED_SHORT, 0); else this.context.drawArrays(this.aabbMesh.MeshTopology, 0, this.aabbMesh.VertexCount); } DisposeBoundsDebug() { if (this.aabbMesh !== undefined) this.aabbMesh.Dispose(); if (this.aabbVAO !== undefined) this.context.deleteVertexArray(this.aabbVAO); } } exports.MeshRenderer = MeshRenderer;