playcanvas
Version:
PlayCanvas WebGL game engine
242 lines (239 loc) • 11.8 kB
JavaScript
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 };