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