UNPKG

playcanvas

Version:

PlayCanvas WebGL game engine

242 lines (239 loc) 11.8 kB
import { SEMANTIC_POSITION, PRIMITIVE_TRISTRIP, SEMANTIC_COLOR } from '../../platform/graphics/constants.js'; import { BLEND_NORMAL } from '../constants.js'; import { GraphNode } from '../graph-node.js'; import { Mesh } from '../mesh.js'; import { MeshInstance } from '../mesh-instance.js'; import { ShaderMaterial } from '../materials/shader-material.js'; import { shaderChunks } from '../shader-lib/chunks/chunks.js'; import { ImmediateBatches } from './immediate-batches.js'; import { Vec3 } from '../../core/math/vec3.js'; import { ChunkUtils } from '../shader-lib/chunk-utils.js'; var tempPoints = []; var vec = new Vec3(); var lineShaderDesc = { uniqueName: 'ImmediateLine', vertexCode: "\n attribute vec3 vertex_position;\n attribute vec4 vertex_color;\n uniform mat4 matrix_model;\n uniform mat4 matrix_viewProjection;\n varying vec4 color;\n void main(void) {\n color = vertex_color;\n gl_Position = matrix_viewProjection * matrix_model * vec4(vertex_position, 1);\n }\n ", fragmentCode: '\n #include "gammaPS"\n varying vec4 color;\n void main(void) {\n gl_FragColor = vec4(gammaCorrectOutput(decodeGamma(color.rgb)), color.a);\n }\n ', attributes: { vertex_position: SEMANTIC_POSITION, vertex_color: SEMANTIC_COLOR } }; class Immediate { // creates material for line rendering createMaterial(depthTest) { var material = new ShaderMaterial(lineShaderDesc); material.blendType = BLEND_NORMAL; material.depthTest = depthTest; material.update(); return material; } // material for line rendering with depth testing on get materialDepth() { if (!this._materialDepth) { this._materialDepth = this.createMaterial(true); } return this._materialDepth; } // material for line rendering with depth testing off get materialNoDepth() { if (!this._materialNoDepth) { this._materialNoDepth = this.createMaterial(false); } return this._materialNoDepth; } // returns a batch for rendering lines to a layer with required depth testing state getBatch(layer, depthTest) { // get batches for the layer var batches = this.batchesMap.get(layer); if (!batches) { batches = new ImmediateBatches(this.device); this.batchesMap.set(layer, batches); } // add it for rendering this.allBatches.add(batches); // get batch for the material var material = depthTest ? this.materialDepth : this.materialNoDepth; return batches.getBatch(material, layer); } getShaderDesc(id, fragment) { if (!this.shaderDescs.has(id)) { // shared vertex shader for textured quad rendering var vertex = "\n attribute vec2 vertex_position;\n uniform mat4 matrix_model;\n varying vec2 uv0;\n void main(void) {\n gl_Position = matrix_model * vec4(vertex_position, 0, 1);\n uv0 = vertex_position.xy + 0.5;\n }\n "; this.shaderDescs.set(id, { uniqueName: "DebugShader:" + id, vertexCode: vertex, fragmentCode: fragment, attributes: { vertex_position: SEMANTIC_POSITION } }); } return this.shaderDescs.get(id); } // shader used to display texture getTextureShaderDesc(encoding) { var decodeFunc = ChunkUtils.decodeFunc(encoding); return this.getShaderDesc("textureShader-" + encoding, /* glsl */ '\n #include "gammaPS"\n varying vec2 uv0;\n uniform sampler2D colorMap;\n void main (void) {\n vec3 linearColor = ' + decodeFunc + "(texture2D(colorMap, uv0));\n gl_FragColor = vec4(gammaCorrectOutput(linearColor), 1);\n }\n "); } // shader used to display infilterable texture sampled using texelFetch getUnfilterableTextureShaderDesc() { return this.getShaderDesc('textureShaderUnfilterable', "\n varying vec2 uv0;\n uniform highp sampler2D colorMap;\n void main (void) {\n ivec2 uv = ivec2(uv0 * textureSize(colorMap, 0));\n gl_FragColor = vec4(texelFetch(colorMap, uv, 0).xyz, 1);\n }\n "); } // shader used to display depth texture getDepthTextureShaderDesc() { return this.getShaderDesc('depthTextureShader', /* glsl */ "\n " + shaderChunks.screenDepthPS + '\n #include "gammaPS"\n varying vec2 uv0;\n void main() {\n float depth = getLinearScreenDepth(getImageEffectUV(uv0)) * camera_params.x;\n gl_FragColor = vec4(gammaCorrectOutput(vec3(depth)), 1.0);\n }\n '); } // creates mesh used to render a quad getQuadMesh() { if (!this.quadMesh) { this.quadMesh = new Mesh(this.device); this.quadMesh.setPositions([ -0.5, -0.5, 0, 0.5, -0.5, 0, -0.5, 0.5, 0, 0.5, 0.5, 0 ]); this.quadMesh.update(PRIMITIVE_TRISTRIP); } return this.quadMesh; } // Draw mesh at this frame drawMesh(material, matrix, mesh, meshInstance, layer) { // create a mesh instance for the mesh if needed if (!meshInstance) { var graphNode = this.getGraphNode(matrix); meshInstance = new MeshInstance(mesh, material, graphNode); } // add the mesh instance to an array per layer, they get added to layers before rendering var layerMeshInstances = this.layerMeshInstances.get(layer); if (!layerMeshInstances) { layerMeshInstances = []; this.layerMeshInstances.set(layer, layerMeshInstances); } layerMeshInstances.push(meshInstance); } drawWireAlignedBox(min, max, color, depthTest, layer, mat) { if (mat) { var mulPoint = (x, y, z)=>{ vec.set(x, y, z); mat.transformPoint(vec, vec); tempPoints.push(vec.x, vec.y, vec.z); }; mulPoint(min.x, min.y, min.z); mulPoint(min.x, max.y, min.z); mulPoint(min.x, max.y, min.z); mulPoint(max.x, max.y, min.z); mulPoint(max.x, max.y, min.z); mulPoint(max.x, min.y, min.z); mulPoint(max.x, min.y, min.z); mulPoint(min.x, min.y, min.z); mulPoint(min.x, min.y, max.z); mulPoint(min.x, max.y, max.z); mulPoint(min.x, max.y, max.z); mulPoint(max.x, max.y, max.z); mulPoint(max.x, max.y, max.z); mulPoint(max.x, min.y, max.z); mulPoint(max.x, min.y, max.z); mulPoint(min.x, min.y, max.z); mulPoint(min.x, min.y, min.z); mulPoint(min.x, min.y, max.z); mulPoint(min.x, max.y, min.z); mulPoint(min.x, max.y, max.z); mulPoint(max.x, max.y, min.z); mulPoint(max.x, max.y, max.z); mulPoint(max.x, min.y, min.z); mulPoint(max.x, min.y, max.z); } else { tempPoints.push(min.x, min.y, min.z, min.x, max.y, min.z, min.x, max.y, min.z, max.x, max.y, min.z, max.x, max.y, min.z, max.x, min.y, min.z, max.x, min.y, min.z, min.x, min.y, min.z, min.x, min.y, max.z, min.x, max.y, max.z, min.x, max.y, max.z, max.x, max.y, max.z, max.x, max.y, max.z, max.x, min.y, max.z, max.x, min.y, max.z, min.x, min.y, max.z, min.x, min.y, min.z, min.x, min.y, max.z, min.x, max.y, min.z, min.x, max.y, max.z, max.x, max.y, min.z, max.x, max.y, max.z, max.x, min.y, min.z, max.x, min.y, max.z); } var batch = this.getBatch(layer, depthTest); batch.addLinesArrays(tempPoints, color); tempPoints.length = 0; } drawWireSphere(center, radius, color, numSegments, depthTest, layer) { var step = 2 * Math.PI / numSegments; var angle = 0; for(var i = 0; i < numSegments; i++){ var sin0 = Math.sin(angle); var cos0 = Math.cos(angle); angle += step; var sin1 = Math.sin(angle); var cos1 = Math.cos(angle); tempPoints.push(center.x + radius * sin0, center.y, center.z + radius * cos0); tempPoints.push(center.x + radius * sin1, center.y, center.z + radius * cos1); tempPoints.push(center.x + radius * sin0, center.y + radius * cos0, center.z); tempPoints.push(center.x + radius * sin1, center.y + radius * cos1, center.z); tempPoints.push(center.x, center.y + radius * sin0, center.z + radius * cos0); tempPoints.push(center.x, center.y + radius * sin1, center.z + radius * cos1); } var batch = this.getBatch(layer, depthTest); batch.addLinesArrays(tempPoints, color); tempPoints.length = 0; } getGraphNode(matrix) { var graphNode = new GraphNode('ImmediateDebug'); graphNode.worldTransform = matrix; graphNode._dirtyWorld = graphNode._dirtyNormal = false; return graphNode; } // This is called just before the layer is rendered to allow lines for the layer to be added from inside // the frame getting rendered onPreRenderLayer(layer, visibleList, transparent) { // update line batches for the specified sub-layer this.batchesMap.forEach((batches, batchLayer)=>{ if (batchLayer === layer) { batches.onPreRender(visibleList, transparent); } }); // only update meshes once for each layer (they're not per sub-layer at the moment) if (!this.updatedLayers.has(layer)) { this.updatedLayers.add(layer); // add mesh instances for specified layer to visible list var meshInstances = this.layerMeshInstances.get(layer); if (meshInstances) { for(var i = 0; i < meshInstances.length; i++){ visibleList.push(meshInstances[i]); } meshInstances.length = 0; } } } // called after the frame was rendered, clears data onPostRender() { // clean up line batches this.allBatches.forEach((batch)=>batch.clear()); this.allBatches.clear(); // all batches need updating next frame this.updatedLayers.clear(); } constructor(device){ this.shaderDescs = new Map(); this.device = device; this.quadMesh = null; this.textureShader = null; this.depthTextureShader = null; this.cubeLocalPos = null; this.cubeWorldPos = null; // map of Layer to ImmediateBatches, storing line batches for a layer this.batchesMap = new Map(); // set of all batches that were used in the frame this.allBatches = new Set(); // set of all layers updated during this frame this.updatedLayers = new Set(); // line materials this._materialDepth = null; this._materialNoDepth = null; // map of meshes instances added to a layer. The key is layer, the value is an array of mesh instances this.layerMeshInstances = new Map(); } } export { Immediate };