import { Vector3D } from '@awayjs/core';
import { ShaderRegisterCache, ShaderRegisterElement } from '@awayjs/stage';
import { ShaderBase, AnimationRegisterData } from '@awayjs/renderer';
import { ParticleProperties } from '../data/ParticleProperties';
import { ParticlePropertiesMode } from '../data/ParticlePropertiesMode';
import { ParticleRotationalVelocityState } from '../states/ParticleRotationalVelocityState';
import { ParticleAnimationSet } from '../ParticleAnimationSet';
import { AnimatorBase } from '../AnimatorBase';
import { ParticleNodeBase } from './ParticleNodeBase';
/**
* A particle animation node used to set the starting rotational velocity of a particle.
*/
export class ParticleRotationalVelocityNode extends ParticleNodeBase {
/** @private */
public _iRotationalVelocity: Vector3D;
/**
* Reference for rotational velocity node properties on a single particle (when in local property mode).
* Expects a Vector3D object representing the rotational velocity around an axis of the particle.
*/
public static ROTATIONALVELOCITY_VECTOR3D: string = 'RotationalVelocityVector3D';
/**
* Creates a new ParticleRotationalVelocityNode
*
* @param mode Defines whether the mode of operation acts on local properties of a particle or global properties of the node.
*/
constructor(mode: number, rotationalVelocity: Vector3D = null) {
super('ParticleRotationalVelocity', mode, 4);
this._pStateClass = ParticleRotationalVelocityState;
this._iRotationalVelocity = rotationalVelocity || new Vector3D();
}
/**
* @inheritDoc
*/
public getAGALVertexCode(shader: ShaderBase, animationSet: ParticleAnimationSet, registerCache: ShaderRegisterCache, animationRegisterData: AnimationRegisterData): string {
const rotationRegister: ShaderRegisterElement = (this._pMode == ParticlePropertiesMode.GLOBAL) ? registerCache.getFreeVertexConstant() : registerCache.getFreeVertexAttribute();
animationRegisterData.setRegisterIndex(this, ParticleRotationalVelocityState.ROTATIONALVELOCITY_INDEX, rotationRegister.index);
const nrmVel: ShaderRegisterElement = registerCache.getFreeVertexVectorTemp();
registerCache.addVertexTempUsages(nrmVel, 1);
const xAxis: ShaderRegisterElement = registerCache.getFreeVertexVectorTemp();
registerCache.addVertexTempUsages(xAxis, 1);
const temp: ShaderRegisterElement = registerCache.getFreeVertexVectorTemp();
registerCache.addVertexTempUsages(temp, 1);
const Rtemp: ShaderRegisterElement = new ShaderRegisterElement(temp.regName, temp.index);
let R_rev: ShaderRegisterElement = registerCache.getFreeVertexVectorTemp();
R_rev = new ShaderRegisterElement(R_rev.regName, R_rev.index);
const cos: ShaderRegisterElement = new ShaderRegisterElement(Rtemp.regName, Rtemp.index, 3);
const sin: ShaderRegisterElement = new ShaderRegisterElement(R_rev.regName, R_rev.index, 3);
registerCache.removeVertexTempUsage(nrmVel);
registerCache.removeVertexTempUsage(xAxis);
registerCache.removeVertexTempUsage(temp);
let code: string = '';
code += 'mov ' + nrmVel + '.xyz,' + rotationRegister + '.xyz\n';
code += 'mov ' + nrmVel + '.w,' + animationRegisterData.vertexZeroConst + '\n';
code += 'mul ' + cos + ',' + animationRegisterData.vertexTime + ',' + rotationRegister + '.w\n';
code += 'sin ' + sin + ',' + cos + '\n';
code += 'cos ' + cos + ',' + cos + '\n';
code += 'mul ' + Rtemp + '.xyz,' + sin + ',' + nrmVel + '.xyz\n';
code += 'mul ' + R_rev + '.xyz,' + sin + ',' + nrmVel + '.xyz\n';
code += 'neg ' + R_rev + '.xyz,' + R_rev + '.xyz\n';
//nrmVel and xAxis are used as temp register
code += 'crs ' + nrmVel + '.xyz,' + Rtemp + '.xyz,' + animationRegisterData.scaleAndRotateTarget + '.xyz\n';
code += 'mul ' + xAxis + '.xyz,' + cos + ',' + animationRegisterData.scaleAndRotateTarget + '.xyz\n';
code += 'add ' + nrmVel + '.xyz,' + nrmVel + '.xyz,' + xAxis + '.xyz\n';
code += 'dp3 ' + xAxis + '.w,' + Rtemp + '.xyz,' + animationRegisterData.scaleAndRotateTarget + '.xyz\n';
code += 'neg ' + nrmVel + '.w,' + xAxis + '.w\n';
code += 'crs ' + Rtemp + '.xyz,' + nrmVel + '.xyz,' + R_rev + '.xyz\n';
//use cos as R_rev.w
code += 'mul ' + xAxis + '.xyzw,' + nrmVel + '.xyzw,' + cos + '\n';
code += 'add ' + Rtemp + '.xyz,' + Rtemp + '.xyz,' + xAxis + '.xyz\n';
code += 'mul ' + xAxis + '.xyz,' + nrmVel + '.w,' + R_rev + '.xyz\n';
code += 'add ' + animationRegisterData.scaleAndRotateTarget + '.xyz,' + Rtemp + '.xyz,' + xAxis + '.xyz\n';
const len: number = animationRegisterData.rotationRegisters.length;
for (let i: number = 0; i < len; i++) {
code += 'mov ' + nrmVel + '.xyz,' + rotationRegister + '.xyz\n';
code += 'mov ' + nrmVel + '.w,' + animationRegisterData.vertexZeroConst + '\n';
code += 'mul ' + cos + ',' + animationRegisterData.vertexTime + ',' + rotationRegister + '.w\n';
code += 'sin ' + sin + ',' + cos + '\n';
code += 'cos ' + cos + ',' + cos + '\n';
code += 'mul ' + Rtemp + '.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,' + Rtemp + '.xyz,' + animationRegisterData.rotationRegisters[i] + '.xyz\n';
code += 'mul ' + xAxis + '.xyz,' + cos + ',' + animationRegisterData.rotationRegisters[i] + '\n';
code += 'add ' + nrmVel + '.xyz,' + nrmVel + '.xyz,' + xAxis + '.xyz\n';
code += 'dp3 ' + xAxis + '.w,' + Rtemp + '.xyz,' + animationRegisterData.rotationRegisters[i] + '\n';
code += 'neg ' + nrmVel + '.w,' + xAxis + '.w\n';
code += 'crs ' + Rtemp + '.xyz,' + nrmVel + '.xyz,' + R_rev + '.xyz\n';
code += 'mul ' + xAxis + '.xyzw,' + nrmVel + '.xyzw,' + cos + '\n';
code += 'add ' + Rtemp + '.xyz,' + Rtemp + '.xyz,' + xAxis + '.xyz\n';
code += 'mul ' + xAxis + '.xyz,' + nrmVel + '.w,' + R_rev + '.xyz\n';
code += 'add ' + animationRegisterData.rotationRegisters[i] + ',' + Rtemp + '.xyz,' + xAxis + '.xyz\n';
}
return code;
}
/**
* @inheritDoc
*/
public getAnimationState(animator: AnimatorBase): ParticleRotationalVelocityState {
return animator.getAnimationState(this);
}
/**
* @inheritDoc
*/
public _iGeneratePropertyOfOneParticle(param: ParticleProperties): void {
//(Vector3d.x,Vector3d.y,Vector3d.z) is rotation axis,Vector3d.w is cycle duration
const rotate: Vector3D = param[ParticleRotationalVelocityNode.ROTATIONALVELOCITY_VECTOR3D];
if (!rotate)
throw (new Error('there is no ' + ParticleRotationalVelocityNode.ROTATIONALVELOCITY_VECTOR3D + ' in param!'));
if (rotate.length <= 0)
rotate.z = 1; //set the default direction
else
rotate.normalize();
this._pOneData[0] = rotate.x;
this._pOneData[1] = rotate.y;
this._pOneData[2] = rotate.z;
if (rotate.w <= 0)
throw (new Error('the cycle duration must greater than zero'));
// it's used as angle/2 in agal
this._pOneData[3] = Math.PI / rotate.w;
}
}