UNPKG

playcanvas

Version:

PlayCanvas WebGL game engine

229 lines (226 loc) 7.52 kB
import { SHADERLANGUAGE_WGSL, SHADERLANGUAGE_GLSL, SEMANTIC_POSITION, BLENDEQUATION_ADD, BLENDMODE_ONE } from '../platform/graphics/constants.js'; import { drawQuadWithShader } from './graphics/quad-render-utils.js'; import { RenderTarget } from '../platform/graphics/render-target.js'; import { createShaderFromCode } from './shader-lib/utils.js'; import { BlendState } from '../platform/graphics/blend-state.js'; import { shaderChunks } from './shader-lib/chunks/chunks.js'; import { shaderChunksWGSL } from './shader-lib/chunks-wgsl/chunks-wgsl.js'; var blendStateAdditive = new BlendState(true, BLENDEQUATION_ADD, BLENDMODE_ONE, BLENDMODE_ONE); class MorphInstance { destroy() { this.shader = null; var morph = this.morph; if (morph) { this.morph = null; morph.decRefCount(); if (morph.refCount < 1) { morph.destroy(); } } if (this.rtPositions) { this.rtPositions.destroy(); this.rtPositions = null; } if (this.texturePositions) { this.texturePositions.destroy(); this.texturePositions = null; } if (this.rtNormals) { this.rtNormals.destroy(); this.rtNormals = null; } if (this.textureNormals) { this.textureNormals.destroy(); this.textureNormals = null; } } clone() { return new MorphInstance(this.morph); } _getWeightIndex(key) { if (typeof key === 'string') { var index = this._weightMap.get(key); return index; } return key; } getWeight(key) { var index = this._getWeightIndex(key); return this._weights[index]; } setWeight(key, weight) { var index = this._getWeightIndex(key); this._weights[index] = weight; this._dirty = true; } _getShader(count) { var shader = this.shaderCache[count]; if (!shader) { var wgsl = this.device.isWebGPU; var chunks = wgsl ? shaderChunksWGSL : shaderChunks; var defines = new Map(); defines.set('MORPH_TEXTURE_COUNT', count); defines.set('{MORPH_TEXTURE_COUNT}', count); if (this.morph.intRenderFormat) defines.set('MORPH_INT', ''); var includes = new Map(); includes.set('morphDeclarationPS', chunks.morphDeclarationPS); includes.set('morphEvaluationPS', chunks.morphEvaluationPS); var outputType = this.morph.intRenderFormat ? 'uvec4' : 'vec4'; shader = createShaderFromCode(this.device, chunks.morphVS, chunks.morphPS, "textureMorph" + count, { vertex_position: SEMANTIC_POSITION }, { shaderLanguage: wgsl ? SHADERLANGUAGE_WGSL : SHADERLANGUAGE_GLSL, fragmentIncludes: includes, fragmentDefines: defines, fragmentOutputTypes: [ outputType ] }); this.shaderCache[count] = shader; } return shader; } _updateTextureRenderTarget(renderTarget, srcTextureName, isPos) { var device = this.device; var submitBatch = (usedCount, blending)=>{ this.morphFactor.setValue(this._shaderMorphWeights); device.setBlendState(blending ? blendStateAdditive : BlendState.NOBLEND); var shader = this._getShader(usedCount); drawQuadWithShader(device, renderTarget, shader); }; this.setAabbUniforms(isPos); var usedCount = 0; var blending = false; var count = this._activeTargets.length; for(var i = 0; i < count; i++){ var activeTarget = this._activeTargets[i]; var tex = activeTarget.target[srcTextureName]; if (tex) { this["morphBlendTex" + usedCount].setValue(tex); this._shaderMorphWeights[usedCount] = activeTarget.weight; usedCount++; if (usedCount >= this.maxSubmitCount) { submitBatch(usedCount, blending); usedCount = 0; blending = true; } } } if (usedCount > 0 || count === 0 && !this.zeroTextures) { submitBatch(usedCount, blending); } } _updateTextureMorph() { this.device; if (this._activeTargets.length > 0 || !this.zeroTextures) { if (this.rtPositions) { this._updateTextureRenderTarget(this.rtPositions, 'texturePositions', true); } if (this.rtNormals) { this._updateTextureRenderTarget(this.rtNormals, 'textureNormals', false); } this.zeroTextures = this._activeTargets.length === 0; } } setAabbUniforms(isPos) { if (isPos === void 0) isPos = true; this.aabbSizeId.setValue(isPos ? this._aabbSize : this._aabbNrmSize); this.aabbMinId.setValue(isPos ? this._aabbMin : this._aabbNrmMin); } prepareRendering(device) { this.setAabbUniforms(); } update() { this._dirty = false; var targets = this.morph._targets; var activeCount = 0; var epsilon = 0.00001; for(var i = 0; i < targets.length; i++){ var absWeight = Math.abs(this.getWeight(i)); if (absWeight > epsilon) { if (this._activeTargets.length <= activeCount) { this._activeTargets[activeCount] = {}; } var activeTarget = this._activeTargets[activeCount++]; activeTarget.absWeight = absWeight; activeTarget.weight = this.getWeight(i); activeTarget.target = targets[i]; } } this._activeTargets.length = activeCount; if (this.morph.intRenderFormat) { if (this._activeTargets.length > this.maxSubmitCount) { this._activeTargets.sort((l, r)=>{ return l.absWeight < r.absWeight ? 1 : r.absWeight < l.absWeight ? -1 : 0; }); this._activeTargets.length = this.maxSubmitCount; } } this._updateTextureMorph(); } constructor(morph){ this.shaderCache = []; this.morph = morph; morph.incRefCount(); this.device = morph.device; this._weights = []; this._weightMap = new Map(); for(var v = 0; v < morph._targets.length; v++){ var target = morph._targets[v]; if (target.name) { this._weightMap.set(target.name, v); } this.setWeight(v, target.defaultWeight); } this._activeTargets = []; this.maxSubmitCount = this.device.maxTextures; this._shaderMorphWeights = new Float32Array(this.maxSubmitCount); var createRT = (name, textureVar)=>{ this[textureVar] = morph._createTexture(name, morph._renderTextureFormat); return new RenderTarget({ colorBuffer: this[textureVar], depth: false }); }; if (morph.morphPositions) { this.rtPositions = createRT('MorphRTPos', 'texturePositions'); } if (morph.morphNormals) { this.rtNormals = createRT('MorphRTNrm', 'textureNormals'); } this._textureParams = new Float32Array([ morph.morphTextureWidth, morph.morphTextureHeight ]); var halfSize = morph.aabb.halfExtents; this._aabbSize = new Float32Array([ halfSize.x * 4, halfSize.y * 4, halfSize.z * 4 ]); var min = morph.aabb.getMin(); this._aabbMin = new Float32Array([ min.x * 2, min.y * 2, min.z * 2 ]); this._aabbNrmSize = new Float32Array([ 2, 2, 2 ]); this._aabbNrmMin = new Float32Array([ -1, -1, -1 ]); this.aabbSizeId = this.device.scope.resolve('aabbSize'); this.aabbMinId = this.device.scope.resolve('aabbMin'); for(var i = 0; i < this.maxSubmitCount; i++){ this["morphBlendTex" + i] = this.device.scope.resolve("morphBlendTex" + i); } this.morphFactor = this.device.scope.resolve('morphFactor[0]'); this.zeroTextures = false; } } export { MorphInstance };