import { Vector3D } from '@awayjs/core'; import { ContextGLVertexBufferFormat } from '@awayjs/stage'; import { ShaderBase, _Render_RenderableBase, AnimationRegisterData, IRenderContainer } from '@awayjs/renderer'; import { ParticleAnimationData } from '../data/ParticleAnimationData'; import { AnimationElements } from '../data/AnimationElements'; import { ParticleFollowNode } from '../nodes/ParticleFollowNode'; import { ParticleAnimator } from '../ParticleAnimator'; import { ParticleStateBase } from './ParticleStateBase'; /** * ... */ export class ParticleFollowState extends ParticleStateBase { /** @private */ public static FOLLOW_POSITION_INDEX: number = 0; /** @private */ public static FOLLOW_ROTATION_INDEX: number = 1; private _particleFollowNode: ParticleFollowNode; private _followTarget: IRenderContainer; private _targetPos: Vector3D = new Vector3D(); private _targetEuler: Vector3D = new Vector3D(); private _prePos: Vector3D; private _preEuler: Vector3D; private _smooth: boolean; //temporary vector3D for calculation private _temp: Vector3D = new Vector3D(); constructor(animator: ParticleAnimator, particleFollowNode: ParticleFollowNode) { super(animator, particleFollowNode, true); this._particleFollowNode = particleFollowNode; this._smooth = particleFollowNode._iSmooth; } public get followTarget(): IRenderContainer { return this._followTarget; } public set followTarget(value: IRenderContainer) { this._followTarget = value; } public get smooth(): boolean { return this._smooth; } public set smooth(value: boolean) { this._smooth = value; } /** * @inheritDoc */ public setRenderState(shader: ShaderBase, renderable: _Render_RenderableBase, animationElements: AnimationElements, animationRegisterData: AnimationRegisterData): void { if (this._followTarget) { if (this._particleFollowNode._iUsesPosition) { this._targetPos.x = this._followTarget.transform.position.x / renderable.entity.node.container.transform.scale.x; this._targetPos.y = this._followTarget.transform.position.y / renderable.entity.node.container.transform.scale.y; this._targetPos.z = this._followTarget.transform.position.z / renderable.entity.node.container.transform.scale.z; } if (this._particleFollowNode._iUsesRotation) { this._targetEuler.x = this._followTarget.transform.rotation.x; this._targetEuler.y = this._followTarget.transform.rotation.y; this._targetEuler.z = this._followTarget.transform.rotation.z; } } //initialization if (!this._prePos) this._prePos = this._targetPos.clone(); if (!this._preEuler) this._preEuler = this._targetEuler.clone(); const currentTime: number = this._pTime / 1000; const previousTime: number = animationElements.previousTime; const deltaTime: number = currentTime - previousTime; const needProcess: boolean = previousTime != currentTime; if (this._particleFollowNode._iUsesPosition && this._particleFollowNode._iUsesRotation) { if (needProcess) this.processPositionAndRotation(currentTime, deltaTime, animationElements); animationElements.activateVertexBuffer(animationRegisterData.getRegisterIndex(this._pAnimationNode, ParticleFollowState.FOLLOW_POSITION_INDEX), this._particleFollowNode._iDataOffset, shader.stage, ContextGLVertexBufferFormat.FLOAT_3); animationElements.activateVertexBuffer(animationRegisterData.getRegisterIndex(this._pAnimationNode, ParticleFollowState.FOLLOW_ROTATION_INDEX), this._particleFollowNode._iDataOffset + 3, shader.stage, ContextGLVertexBufferFormat.FLOAT_3); } else if (this._particleFollowNode._iUsesPosition) { if (needProcess) this.processPosition(currentTime, deltaTime, animationElements); animationElements.activateVertexBuffer(animationRegisterData.getRegisterIndex(this._pAnimationNode, ParticleFollowState.FOLLOW_POSITION_INDEX), this._particleFollowNode._iDataOffset, shader.stage, ContextGLVertexBufferFormat.FLOAT_3); } else if (this._particleFollowNode._iUsesRotation) { if (needProcess) this.precessRotation(currentTime, deltaTime, animationElements); animationElements.activateVertexBuffer(animationRegisterData.getRegisterIndex(this._pAnimationNode, ParticleFollowState.FOLLOW_ROTATION_INDEX), this._particleFollowNode._iDataOffset, shader.stage, ContextGLVertexBufferFormat.FLOAT_3); } this._prePos.copyFrom(this._targetPos); this._targetEuler.copyFrom(this._targetEuler); animationElements.previousTime = currentTime; } private processPosition(currentTime: number, deltaTime: number, animationElements: AnimationElements): void { const data: Array = animationElements.animationParticles; const vertexData: Float32Array = animationElements.vertexData; let changed: boolean = false; const len: number = data.length; let interpolatedPos: Vector3D; let posVelocity: Vector3D; if (this._smooth) { posVelocity = this._prePos.subtract(this._targetPos); posVelocity.scaleBy(1 / deltaTime); } else interpolatedPos = this._targetPos; for (let i: number = 0; i < len; i++) { const k: number = (currentTime - data[i].startTime) / data[i].totalTime; const t: number = (k - Math.floor(k)) * data[i].totalTime; if (t - deltaTime <= 0) { let inc: number = data[i].startVertexIndex * animationElements.totalLenOfOneVertex + this._particleFollowNode._iDataOffset; if (this._smooth) { this._temp.copyFrom(posVelocity); this._temp.scaleBy(t); interpolatedPos = this._targetPos.add(this._temp); } if (vertexData[inc] != interpolatedPos.x || vertexData[inc + 1] != interpolatedPos.y || vertexData[inc + 2] != interpolatedPos.z) { changed = true; for (let j: number = 0; j < data[i].numVertices; j++) { vertexData[inc++] = interpolatedPos.x; vertexData[inc++] = interpolatedPos.y; vertexData[inc++] = interpolatedPos.z; } } } } if (changed) animationElements.invalidateBuffer(); } private precessRotation(currentTime: number, deltaTime: number, animationElements: AnimationElements): void { const data: Array = animationElements.animationParticles; const vertexData: Float32Array = animationElements.vertexData; let changed: boolean = false; const len: number = data.length; let interpolatedRotation: Vector3D; let rotationVelocity: Vector3D; if (this._smooth) { rotationVelocity = this._preEuler.subtract(this._targetEuler); rotationVelocity.scaleBy(1 / deltaTime); } else interpolatedRotation = this._targetEuler; for (let i: number = 0; i < len; i++) { const k: number = (currentTime - data[i].startTime) / data[i].totalTime; const t: number = (k - Math.floor(k)) * data[i].totalTime; if (t - deltaTime <= 0) { let inc: number = data[i].startVertexIndex * animationElements.totalLenOfOneVertex + this._particleFollowNode._iDataOffset; if (this._smooth) { this._temp.copyFrom(rotationVelocity); this._temp.scaleBy(t); interpolatedRotation = this._targetEuler.add(this._temp); } if (vertexData[inc] != interpolatedRotation.x || vertexData[inc + 1] != interpolatedRotation.y || vertexData[inc + 2] != interpolatedRotation.z) { changed = true; for (let j: number = 0; j < data[i].numVertices; j++) { vertexData[inc++] = interpolatedRotation.x; vertexData[inc++] = interpolatedRotation.y; vertexData[inc++] = interpolatedRotation.z; } } } } if (changed) animationElements.invalidateBuffer(); } private processPositionAndRotation(currentTime: number, deltaTime: number, animationElements: AnimationElements): void { const data: Array = animationElements.animationParticles; const vertexData: Float32Array = animationElements.vertexData; let changed: boolean = false; const len: number = data.length; let interpolatedPos: Vector3D; let interpolatedRotation: Vector3D; let posVelocity: Vector3D; let rotationVelocity: Vector3D; if (this._smooth) { posVelocity = this._prePos.subtract(this._targetPos); posVelocity.scaleBy(1 / deltaTime); rotationVelocity = this._preEuler.subtract(this._targetEuler); rotationVelocity.scaleBy(1 / deltaTime); } else { interpolatedPos = this._targetPos; interpolatedRotation = this._targetEuler; } for (let i: number = 0; i < len; i++) { const k: number = (currentTime - data[i].startTime) / data[i].totalTime; const t: number = (k - Math.floor(k)) * data[i].totalTime; if (t - deltaTime <= 0) { let inc: number = data[i].startVertexIndex * animationElements.totalLenOfOneVertex + this._particleFollowNode._iDataOffset; if (this._smooth) { this._temp.copyFrom(posVelocity); this._temp.scaleBy(t); interpolatedPos = this._targetPos.add(this._temp); this._temp.copyFrom(rotationVelocity); this._temp.scaleBy(t); interpolatedRotation = this._targetEuler.add(this._temp); } if (vertexData[inc] != interpolatedPos.x || vertexData[inc + 1] != interpolatedPos.y || vertexData[inc + 2] != interpolatedPos.z || vertexData[inc + 3] != interpolatedRotation.x || vertexData[inc + 4] != interpolatedRotation.y || vertexData[inc + 5] != interpolatedRotation.z) { changed = true; for (let j: number = 0; j < data[i].numVertices; j++) { vertexData[inc++] = interpolatedPos.x; vertexData[inc++] = interpolatedPos.y; vertexData[inc++] = interpolatedPos.z; vertexData[inc++] = interpolatedRotation.x; vertexData[inc++] = interpolatedRotation.y; vertexData[inc++] = interpolatedRotation.z; } } } } if (changed) animationElements.invalidateBuffer(); } }