UNPKG

playcanvas

Version:

Open-source WebGL/WebGPU 3D engine for the web

155 lines (154 loc) 5.22 kB
import { SEMANTIC_POSITION } from "../platform/graphics/constants.js"; import { drawQuadWithShader } from "./graphics/quad-render-utils.js"; import { RenderTarget } from "../platform/graphics/render-target.js"; import { ShaderUtils } from "./shader-lib/shader-utils.js"; import { BlendState } from "../platform/graphics/blend-state.js"; class MorphInstance { constructor(morph) { this.morph = morph; morph.incRefCount(); this.device = morph.device; const maxNumTargets = morph._targets.length; this.shader = this._createShader(maxNumTargets); this._weights = []; this._weightMap = /* @__PURE__ */ new Map(); for (let v = 0; v < morph._targets.length; v++) { const target = morph._targets[v]; if (target.name) { this._weightMap.set(target.name, v); } this.setWeight(v, target.defaultWeight); } this._shaderMorphWeights = new Float32Array(maxNumTargets); this._shaderMorphIndex = new Uint32Array(maxNumTargets); const 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]); const halfSize = morph.aabb.halfExtents; this._aabbSize = new Float32Array([halfSize.x * 4, halfSize.y * 4, halfSize.z * 4]); const 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"); this.morphTextureId = this.device.scope.resolve("morphTexture"); this.morphFactor = this.device.scope.resolve("morphFactor[0]"); this.morphIndex = this.device.scope.resolve("morphIndex[0]"); this.countId = this.device.scope.resolve("count"); this.zeroTextures = false; } destroy() { this.shader = null; const morph = this.morph; if (morph) { this.morph = null; morph.decRefCount(); if (morph.refCount < 1) { morph.destroy(); } } this.rtPositions?.destroy(); this.rtPositions = null; this.texturePositions?.destroy(); this.texturePositions = null; this.rtNormals?.destroy(); this.rtNormals = null; this.textureNormals?.destroy(); this.textureNormals = null; } clone() { return new MorphInstance(this.morph); } _getWeightIndex(key) { if (typeof key === "string") { const index = this._weightMap.get(key); if (index === void 0) { } return index; } return key; } getWeight(key) { const index = this._getWeightIndex(key); return this._weights[index]; } setWeight(key, weight) { const index = this._getWeightIndex(key); this._weights[index] = weight; this._dirty = true; } _createShader(maxCount) { const defines = /* @__PURE__ */ new Map(); defines.set("{MORPH_TEXTURE_MAX_COUNT}", maxCount); if (this.morph.intRenderFormat) defines.set("MORPH_INT", ""); const outputType = this.morph.intRenderFormat ? "uvec4" : "vec4"; return ShaderUtils.createShader(this.device, { uniqueName: `TextureMorphShader_${maxCount}-${this.morph.intRenderFormat ? "int" : "float"}`, attributes: { vertex_position: SEMANTIC_POSITION }, vertexChunk: "morphVS", fragmentChunk: "morphPS", fragmentDefines: defines, fragmentOutputTypes: [outputType] }); } _updateTextureRenderTarget(renderTarget, activeCount, isPos) { const { morph, device } = this; this.setAabbUniforms(isPos); this.morphTextureId.setValue(isPos ? morph.targetsTexturePositions : morph.targetsTextureNormals); device.setBlendState(BlendState.NOBLEND); this.countId.setValue(activeCount); this.morphFactor.setValue(this._shaderMorphWeights); this.morphIndex.setValue(this._shaderMorphIndex); drawQuadWithShader(device, renderTarget, this.shader); } _updateTextureMorph(activeCount) { const device = this.device; if (activeCount > 0 || !this.zeroTextures) { if (this.rtPositions) { this._updateTextureRenderTarget(this.rtPositions, activeCount, true); } if (this.rtNormals) { this._updateTextureRenderTarget(this.rtNormals, activeCount, false); } this.zeroTextures = activeCount === 0; } } setAabbUniforms(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; const targets = this.morph._targets; const epsilon = 1e-5; const weights = this._shaderMorphWeights; const indices = this._shaderMorphIndex; let activeCount = 0; for (let i = 0; i < targets.length; i++) { if (Math.abs(this.getWeight(i)) > epsilon) { weights[activeCount] = this.getWeight(i); indices[activeCount] = i; activeCount++; } } this._updateTextureMorph(activeCount); } } export { MorphInstance };