import { ShaderRegisterCache, ShaderRegisterElement } from '@awayjs/stage'; import { ShaderBase, AnimationRegisterData } from '@awayjs/renderer'; import { ParticleProperties } from '../data/ParticleProperties'; import { ParticlePropertiesMode } from '../data/ParticlePropertiesMode'; import { ParticleTimeState } from '../states/ParticleTimeState'; import { ParticleAnimationSet } from '../ParticleAnimationSet'; import { AnimatorBase } from '../AnimatorBase'; import { ParticleNodeBase } from './ParticleNodeBase'; /** * A particle animation node used as the base node for timekeeping inside a particle. Automatically added to a particle animation set on instatiation. */ export class ParticleTimeNode extends ParticleNodeBase { /** @private */ public _iUsesDuration: boolean; /** @private */ public _iUsesDelay: boolean; /** @private */ public _iUsesLooping: boolean; /** * Creates a new ParticleTimeNode * * @param [optional] usesDuration Defines whether the node uses the duration data in the static properties to determine how long a particle is visible for. Defaults to false. * @param [optional] usesDelay Defines whether the node uses the delay data in the static properties to determine how long a particle is hidden for. Defaults to false. Requires usesDuration to be true. * @param [optional] usesLooping Defines whether the node creates a looping timeframe for each particle determined by the startTime, duration and delay data in the static properties function. Defaults to false. Requires usesLooping to be true. */ constructor(usesDuration: boolean = false, usesLooping: boolean = false, usesDelay: boolean = false) { super('ParticleTime', ParticlePropertiesMode.LOCAL_STATIC, 4, 0); this._pStateClass = ParticleTimeState; this._iUsesDuration = usesDuration; this._iUsesLooping = usesLooping; this._iUsesDelay = usesDelay; } /** * @inheritDoc */ public getAGALVertexCode(shader: ShaderBase, animationSet: ParticleAnimationSet, registerCache: ShaderRegisterCache, animationRegisterData: AnimationRegisterData): string { const timeStreamRegister: ShaderRegisterElement = registerCache.getFreeVertexAttribute(); //timeStreamRegister.x is start,timeStreamRegister.y is during time animationRegisterData.setRegisterIndex(this, ParticleTimeState.TIME_STREAM_INDEX, timeStreamRegister.index); const timeConst: ShaderRegisterElement = registerCache.getFreeVertexConstant(); animationRegisterData.setRegisterIndex(this, ParticleTimeState.TIME_CONSTANT_INDEX, timeConst.index); let code: string = ''; code += 'sub ' + animationRegisterData.vertexTime + ',' + timeConst + ',' + timeStreamRegister + '.x\n'; //if time=0,set the position to zero. const temp: ShaderRegisterElement = registerCache.getFreeVertexSingleTemp(); code += 'sge ' + temp + ',' + animationRegisterData.vertexTime + ',' + animationRegisterData.vertexZeroConst + '\n'; code += 'mul ' + animationRegisterData.scaleAndRotateTarget + '.xyz,' + animationRegisterData.scaleAndRotateTarget + '.xyz,' + temp + '\n'; if (this._iUsesDuration) { if (this._iUsesLooping) { const div: ShaderRegisterElement = registerCache.getFreeVertexSingleTemp(); if (this._iUsesDelay) { code += 'div ' + div + ',' + animationRegisterData.vertexTime + ',' + timeStreamRegister + '.z\n'; code += 'frc ' + div + ',' + div + '\n'; code += 'mul ' + animationRegisterData.vertexTime + ',' + div + ',' + timeStreamRegister + '.z\n'; code += 'slt ' + div + ',' + animationRegisterData.vertexTime + ',' + timeStreamRegister + '.y\n'; code += 'mul ' + animationRegisterData.scaleAndRotateTarget + '.xyz,' + animationRegisterData.scaleAndRotateTarget + '.xyz,' + div + '\n'; } else { code += 'mul ' + div + ',' + animationRegisterData.vertexTime + ',' + timeStreamRegister + '.w\n'; code += 'frc ' + div + ',' + div + '\n'; code += 'mul ' + animationRegisterData.vertexTime + ',' + div + ',' + timeStreamRegister + '.y\n'; } } else { const sge: ShaderRegisterElement = registerCache.getFreeVertexSingleTemp(); code += 'sge ' + sge + ',' + timeStreamRegister + '.y,' + animationRegisterData.vertexTime + '\n'; code += 'mul ' + animationRegisterData.scaleAndRotateTarget + '.xyz,' + animationRegisterData.scaleAndRotateTarget + '.xyz,' + sge + '\n'; } } code += 'mul ' + animationRegisterData.vertexLife + ',' + animationRegisterData.vertexTime + ',' + timeStreamRegister + '.w\n'; return code; } /** * @inheritDoc */ public getAnimationState(animator: AnimatorBase): ParticleTimeState { return animator.getAnimationState(this); } /** * @inheritDoc */ public _iGeneratePropertyOfOneParticle(param: ParticleProperties): void { this._pOneData[0] = param.startTime; this._pOneData[1] = param.duration; this._pOneData[2] = param.delay + param.duration; this._pOneData[3] = 1 / param.duration; } }