import { ShaderRegisterCache, ShaderRegisterElement } from '@awayjs/stage'; import { ShaderBase, AnimationRegisterData } from '@awayjs/renderer'; import { ParticlePropertiesMode } from '../data/ParticlePropertiesMode'; import { ParticleRotateToHeadingState } from '../states/ParticleRotateToHeadingState'; import { ParticleAnimationSet } from '../ParticleAnimationSet'; import { AnimatorBase } from '../AnimatorBase'; import { ParticleNodeBase } from './ParticleNodeBase'; /** * A particle animation node used to control the rotation of a particle to match its heading vector. */ export class ParticleRotateToHeadingNode extends ParticleNodeBase { /** * Creates a new ParticleBillboardNode */ constructor() { super('ParticleRotateToHeading', ParticlePropertiesMode.GLOBAL, 0, 3); this._pStateClass = ParticleRotateToHeadingState; } /** * @inheritDoc */ public getAGALVertexCode(shader: ShaderBase, animationSet: ParticleAnimationSet, registerCache: ShaderRegisterCache, animationRegisterData: AnimationRegisterData): string { let code: string = ''; const len: number = animationRegisterData.rotationRegisters.length; let i: number; if (animationSet.hasBillboard) { const temp1: ShaderRegisterElement = registerCache.getFreeVertexVectorTemp(); registerCache.addVertexTempUsages(temp1, 1); const temp2: ShaderRegisterElement = registerCache.getFreeVertexVectorTemp(); registerCache.addVertexTempUsages(temp2, 1); const temp3: ShaderRegisterElement = registerCache.getFreeVertexVectorTemp(); const rotationMatrixRegister: ShaderRegisterElement = registerCache.getFreeVertexConstant(); animationRegisterData.setRegisterIndex(this, ParticleRotateToHeadingState.MATRIX_INDEX, rotationMatrixRegister.index); registerCache.getFreeVertexConstant(); registerCache.getFreeVertexConstant(); registerCache.getFreeVertexConstant(); registerCache.removeVertexTempUsage(temp1); registerCache.removeVertexTempUsage(temp2); //process the velocity code += 'm33 ' + temp1 + '.xyz,' + animationRegisterData.velocityTarget + '.xyz,' + rotationMatrixRegister + '\n'; code += 'mov ' + temp3 + ',' + animationRegisterData.vertexZeroConst + '\n'; code += 'mov ' + temp3 + '.xy,' + temp1 + '.xy\n'; code += 'nrm ' + temp3 + '.xyz,' + temp3 + '.xyz\n'; //temp3.x=cos,temp3.y=sin //only process z axis code += 'mov ' + temp2 + ',' + animationRegisterData.vertexZeroConst + '\n'; code += 'mov ' + temp2 + '.x,' + temp3 + '.y\n'; code += 'mov ' + temp2 + '.y,' + temp3 + '.x\n'; code += 'mov ' + temp1 + ',' + animationRegisterData.vertexZeroConst + '\n'; code += 'mov ' + temp1 + '.x,' + temp3 + '.x\n'; code += 'neg ' + temp1 + '.y,' + temp3 + '.y\n'; code += 'mov ' + temp3 + ',' + animationRegisterData.vertexZeroConst + '\n'; code += 'mov ' + temp3 + '.z,' + animationRegisterData.vertexOneConst + '\n'; code += 'm33 ' + animationRegisterData.scaleAndRotateTarget + '.xyz,' + animationRegisterData.scaleAndRotateTarget + '.xyz,' + temp1 + '\n'; for (i = 0; i < len; i++) code += 'm33 ' + animationRegisterData.rotationRegisters[i] + '.xyz,' + animationRegisterData.rotationRegisters[i] + ',' + temp1 + '\n'; } else { const nrmVel: ShaderRegisterElement = registerCache.getFreeVertexVectorTemp(); registerCache.addVertexTempUsages(nrmVel, 1); const xAxis: ShaderRegisterElement = registerCache.getFreeVertexVectorTemp(); registerCache.addVertexTempUsages(xAxis, 1); const R: ShaderRegisterElement = registerCache.getFreeVertexVectorTemp(); registerCache.addVertexTempUsages(R, 1); const R_rev: ShaderRegisterElement = registerCache.getFreeVertexVectorTemp(); const cos: ShaderRegisterElement = new ShaderRegisterElement(R.regName, R.index, 3); const sin: ShaderRegisterElement = new ShaderRegisterElement(R_rev.regName, R_rev.index, 3); const cos2: ShaderRegisterElement = new ShaderRegisterElement(nrmVel.regName, nrmVel.index, 3); const tempSingle: ShaderRegisterElement = sin; registerCache.removeVertexTempUsage(nrmVel); registerCache.removeVertexTempUsage(xAxis); registerCache.removeVertexTempUsage(R); code += 'mov ' + xAxis + '.x,' + animationRegisterData.vertexOneConst + '\n'; code += 'mov ' + xAxis + '.yz,' + animationRegisterData.vertexZeroConst + '\n'; code += 'nrm ' + nrmVel + '.xyz,' + animationRegisterData.velocityTarget + '.xyz\n'; code += 'dp3 ' + cos2 + ',' + nrmVel + '.xyz,' + xAxis + '.xyz\n'; code += 'crs ' + nrmVel + '.xyz,' + xAxis + '.xyz,' + nrmVel + '.xyz\n'; code += 'nrm ' + nrmVel + '.xyz,' + nrmVel + '.xyz\n'; //use R as temp to judge if nrm is (0,0,0). //if nrm is (0,0,0) ,change it to (0,0,1). code += 'dp3 ' + R + '.x,' + nrmVel + '.xyz,' + nrmVel + '.xyz\n'; code += 'sge ' + R + '.x,' + animationRegisterData.vertexZeroConst + ',' + R + '.x\n'; code += 'add ' + nrmVel + '.z,' + R + '.x,' + nrmVel + '.z\n'; code += 'add ' + tempSingle + ',' + cos2 + ',' + animationRegisterData.vertexOneConst + '\n'; code += 'div ' + tempSingle + ',' + tempSingle + ',' + animationRegisterData.vertexTwoConst + '\n'; code += 'sqt ' + cos + ',' + tempSingle + '\n'; code += 'sub ' + tempSingle + ',' + animationRegisterData.vertexOneConst + ',' + cos2 + '\n'; code += 'div ' + tempSingle + ',' + tempSingle + ',' + animationRegisterData.vertexTwoConst + '\n'; code += 'sqt ' + sin + ',' + tempSingle + '\n'; code += 'mul ' + R + '.xyz,' + sin + ',' + nrmVel + '.xyz\n'; //use cos as R.w code += 'mul ' + R_rev + '.xyz,' + sin + ',' + nrmVel + '.xyz\n'; code += 'neg ' + R_rev + '.xyz,' + R_rev + '.xyz\n'; //use cos as R_rev.w //nrmVel and xAxis are used as temp register code += 'crs ' + nrmVel + '.xyz,' + R + '.xyz,' + animationRegisterData.scaleAndRotateTarget + '.xyz\n'; //use cos as R.w code += 'mul ' + xAxis + '.xyz,' + cos + ',' + animationRegisterData.scaleAndRotateTarget + '.xyz\n'; code += 'add ' + nrmVel + '.xyz,' + nrmVel + '.xyz,' + xAxis + '.xyz\n'; code += 'dp3 ' + xAxis + '.w,' + R + '.xyz,' + animationRegisterData.scaleAndRotateTarget + '.xyz\n'; code += 'neg ' + nrmVel + '.w,' + xAxis + '.w\n'; code += 'crs ' + R + '.xyz,' + nrmVel + '.xyz,' + R_rev + '.xyz\n'; //code += "mul " + xAxis + ".xyzw," + nrmVel + ".xyzw," +R_rev + ".w\n"; code += 'mul ' + xAxis + '.xyzw,' + nrmVel + '.xyzw,' + cos + '\n'; code += 'add ' + R + '.xyz,' + R + '.xyz,' + xAxis + '.xyz\n'; code += 'mul ' + xAxis + '.xyz,' + nrmVel + '.w,' + R_rev + '.xyz\n'; code += 'add ' + animationRegisterData.scaleAndRotateTarget + '.xyz,' + R + '.xyz,' + xAxis + '.xyz\n'; for (i = 0; i < len; i++) { //just repeat the calculate above //because of the limited registers, no need to optimise code += 'mov ' + xAxis + '.x,' + animationRegisterData.vertexOneConst + '\n'; code += 'mov ' + xAxis + '.yz,' + animationRegisterData.vertexZeroConst + '\n'; code += 'nrm ' + nrmVel + '.xyz,' + animationRegisterData.velocityTarget + '.xyz\n'; code += 'dp3 ' + cos2 + ',' + nrmVel + '.xyz,' + xAxis + '.xyz\n'; code += 'crs ' + nrmVel + '.xyz,' + xAxis + '.xyz,' + nrmVel + '.xyz\n'; code += 'nrm ' + nrmVel + '.xyz,' + nrmVel + '.xyz\n'; code += 'dp3 ' + R + '.x,' + nrmVel + '.xyz,' + nrmVel + '.xyz\n'; code += 'sge ' + R + '.x,' + animationRegisterData.vertexZeroConst + ',' + R + '.x\n'; code += 'add ' + nrmVel + '.z,' + R + '.x,' + nrmVel + '.z\n'; code += 'add ' + tempSingle + ',' + cos2 + ',' + animationRegisterData.vertexOneConst + '\n'; code += 'div ' + tempSingle + ',' + tempSingle + ',' + animationRegisterData.vertexTwoConst + '\n'; code += 'sqt ' + cos + ',' + tempSingle + '\n'; code += 'sub ' + tempSingle + ',' + animationRegisterData.vertexOneConst + ',' + cos2 + '\n'; code += 'div ' + tempSingle + ',' + tempSingle + ',' + animationRegisterData.vertexTwoConst + '\n'; code += 'sqt ' + sin + ',' + tempSingle + '\n'; code += 'mul ' + R + '.xyz,' + sin + ',' + nrmVel + '.xyz\n'; code += 'mul ' + R_rev + '.xyz,' + sin + ',' + nrmVel + '.xyz\n'; code += 'neg ' + R_rev + '.xyz,' + R_rev + '.xyz\n'; code += 'crs ' + nrmVel + '.xyz,' + R + '.xyz,' + animationRegisterData.rotationRegisters[i] + '.xyz\n'; code += 'mul ' + xAxis + '.xyz,' + cos + ',' + animationRegisterData.rotationRegisters[i] + '.xyz\n'; code += 'add ' + nrmVel + '.xyz,' + nrmVel + '.xyz,' + xAxis + '.xyz\n'; code += 'dp3 ' + xAxis + '.w,' + R + '.xyz,' + animationRegisterData.rotationRegisters[i] + '.xyz\n'; code += 'neg ' + nrmVel + '.w,' + xAxis + '.w\n'; code += 'crs ' + R + '.xyz,' + nrmVel + '.xyz,' + R_rev + '.xyz\n'; code += 'mul ' + xAxis + '.xyzw,' + nrmVel + '.xyzw,' + cos + '\n'; code += 'add ' + R + '.xyz,' + R + '.xyz,' + xAxis + '.xyz\n'; code += 'mul ' + xAxis + '.xyz,' + nrmVel + '.w,' + R_rev + '.xyz\n'; code += 'add ' + animationRegisterData.rotationRegisters[i] + '.xyz,' + R + '.xyz,' + xAxis + '.xyz\n'; } } return code; } /** * @inheritDoc */ public getAnimationState(animator: AnimatorBase): ParticleRotateToHeadingState { return animator.getAnimationState(this); } /** * @inheritDoc */ public _iProcessAnimationSetting(particleAnimationSet: ParticleAnimationSet): void { particleAnimationSet.needVelocity = true; } }