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;
}
}