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