import { ShaderRegisterCache, ShaderRegisterElement } from '@awayjs/stage'; import { ShaderBase, AnimationRegisterData } from '@awayjs/renderer'; import { ParticlePropertiesMode } from '../data/ParticlePropertiesMode'; import { ParticleFollowState } from '../states/ParticleFollowState'; import { ParticleAnimationSet } from '../ParticleAnimationSet'; import { AnimatorBase } from '../AnimatorBase'; import { ParticleNodeBase } from './ParticleNodeBase'; /** * A particle animation node used to create a follow behaviour on a particle system. */ export class ParticleFollowNode extends ParticleNodeBase { /** @private */ public _iUsesPosition: boolean; /** @private */ public _iUsesRotation: boolean; /** @private */ public _iSmooth: boolean; /** * Creates a new ParticleFollowNode * * @param [optional] usesPosition Defines wehether the individual particle reacts to the position of the target. * @param [optional] usesRotation Defines wehether the individual particle reacts to the rotation of the target. * @param [optional] smooth Defines wehether the state calculate the interpolated value. */ constructor(usesPosition: boolean = true, usesRotation: boolean = true, smooth: boolean = false) { super('ParticleFollow', ParticlePropertiesMode.LOCAL_DYNAMIC, (usesPosition && usesRotation) ? 6 : 3, ParticleAnimationSet.POST_PRIORITY); this._pStateClass = ParticleFollowState; this._iUsesPosition = usesPosition; this._iUsesRotation = usesRotation; this._iSmooth = smooth; } /** * @inheritDoc */ public getAGALVertexCode(shader: ShaderBase, animationSet: ParticleAnimationSet, registerCache: ShaderRegisterCache, animationRegisterData: AnimationRegisterData): string { //TODO: use Quaternion to implement this function let code: string = ''; if (this._iUsesRotation) { const rotationAttribute: ShaderRegisterElement = registerCache.getFreeVertexAttribute(); animationRegisterData.setRegisterIndex(this, ParticleFollowState.FOLLOW_ROTATION_INDEX, rotationAttribute.index); const temp1: ShaderRegisterElement = registerCache.getFreeVertexVectorTemp(); registerCache.addVertexTempUsages(temp1, 1); const temp2: ShaderRegisterElement = registerCache.getFreeVertexVectorTemp(); registerCache.addVertexTempUsages(temp2, 1); const temp3: ShaderRegisterElement = registerCache.getFreeVertexVectorTemp(); let temp4: ShaderRegisterElement; if (animationSet.hasBillboard) { registerCache.addVertexTempUsages(temp3, 1); temp4 = registerCache.getFreeVertexVectorTemp(); } registerCache.removeVertexTempUsage(temp1); registerCache.removeVertexTempUsage(temp2); if (animationSet.hasBillboard) registerCache.removeVertexTempUsage(temp3); const len: number = animationRegisterData.rotationRegisters.length; let i: number; //x axis code += 'mov ' + temp1 + ',' + animationRegisterData.vertexZeroConst + '\n'; code += 'mov ' + temp1 + '.x,' + animationRegisterData.vertexOneConst + '\n'; code += 'mov ' + temp3 + ',' + animationRegisterData.vertexZeroConst + '\n'; code += 'sin ' + temp3 + '.y,' + rotationAttribute + '.x\n'; code += 'cos ' + temp3 + '.z,' + rotationAttribute + '.x\n'; code += 'mov ' + temp2 + '.x,' + animationRegisterData.vertexZeroConst + '\n'; code += 'mov ' + temp2 + '.y,' + temp3 + '.z\n'; code += 'neg ' + temp2 + '.z,' + temp3 + '.y\n'; if (animationSet.hasBillboard) code += 'm33 ' + temp4 + '.xyz,' + animationRegisterData.positionTarget + '.xyz,' + temp1 + '\n'; else { 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'; } //y axis code += 'mov ' + temp1 + ',' + animationRegisterData.vertexZeroConst + '\n'; code += 'cos ' + temp1 + '.x,' + rotationAttribute + '.y\n'; code += 'sin ' + temp1 + '.z,' + rotationAttribute + '.y\n'; code += 'mov ' + temp2 + ',' + animationRegisterData.vertexZeroConst + '\n'; code += 'mov ' + temp2 + '.y,' + animationRegisterData.vertexOneConst + '\n'; code += 'mov ' + temp3 + ',' + animationRegisterData.vertexZeroConst + '\n'; code += 'neg ' + temp3 + '.x,' + temp1 + '.z\n'; code += 'mov ' + temp3 + '.z,' + temp1 + '.x\n'; if (animationSet.hasBillboard) code += 'm33 ' + temp4 + '.xyz,' + temp4 + '.xyz,' + temp1 + '\n'; else { 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'; } //z axis code += 'mov ' + temp2 + ',' + animationRegisterData.vertexZeroConst + '\n'; code += 'sin ' + temp2 + '.x,' + rotationAttribute + '.z\n'; code += 'cos ' + temp2 + '.y,' + rotationAttribute + '.z\n'; code += 'mov ' + temp1 + ',' + animationRegisterData.vertexZeroConst + '\n'; code += 'mov ' + temp1 + '.x,' + temp2 + '.y\n'; code += 'neg ' + temp1 + '.y,' + temp2 + '.x\n'; code += 'mov ' + temp3 + ',' + animationRegisterData.vertexZeroConst + '\n'; code += 'mov ' + temp3 + '.z,' + animationRegisterData.vertexOneConst + '\n'; if (animationSet.hasBillboard) { code += 'm33 ' + temp4 + '.xyz,' + temp4 + '.xyz,' + temp1 + '\n'; code += 'sub ' + temp4 + '.xyz,' + temp4 + '.xyz,' + animationRegisterData.positionTarget + '.xyz\n'; code += 'add ' + animationRegisterData.scaleAndRotateTarget + '.xyz,' + temp4 + '.xyz,' + animationRegisterData.scaleAndRotateTarget + '.xyz\n'; } else { 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'; } } if (this._iUsesPosition) { const positionAttribute: ShaderRegisterElement = registerCache.getFreeVertexAttribute(); animationRegisterData.setRegisterIndex(this, ParticleFollowState.FOLLOW_POSITION_INDEX, positionAttribute.index); code += 'add ' + animationRegisterData.scaleAndRotateTarget + '.xyz,' + positionAttribute + ',' + animationRegisterData.scaleAndRotateTarget + '.xyz\n'; } return code; } /** * @inheritDoc */ public getAnimationState(animator: AnimatorBase): ParticleFollowState { return animator.getAnimationState(this); } }