636 lines
32 KiB
JavaScript
636 lines
32 KiB
JavaScript
|
(function (exports, Laya) {
|
|||
|
'use strict';
|
|||
|
|
|||
|
class ParticleSetting {
|
|||
|
constructor() {
|
|||
|
this.textureName = null;
|
|||
|
this.textureCount = 1;
|
|||
|
this.maxPartices = 100;
|
|||
|
this.duration = 1;
|
|||
|
this.ageAddScale = 0;
|
|||
|
this.emitterVelocitySensitivity = 1;
|
|||
|
this.minStartSize = 100;
|
|||
|
this.maxStartSize = 100;
|
|||
|
this.minEndSize = 100;
|
|||
|
this.maxEndSize = 100;
|
|||
|
this.minHorizontalVelocity = 0;
|
|||
|
this.maxHorizontalVelocity = 0;
|
|||
|
this.minVerticalVelocity = 0;
|
|||
|
this.maxVerticalVelocity = 0;
|
|||
|
this.endVelocity = 1;
|
|||
|
this.gravity = new Float32Array([0, 0, 0]);
|
|||
|
this.minRotateSpeed = 0;
|
|||
|
this.maxRotateSpeed = 0;
|
|||
|
this.minStartRadius = 0;
|
|||
|
this.maxStartRadius = 0;
|
|||
|
this.minEndRadius = 0;
|
|||
|
this.maxEndRadius = 0;
|
|||
|
this.minHorizontalStartRadian = 0;
|
|||
|
this.maxHorizontalStartRadian = 0;
|
|||
|
this.minVerticalStartRadian = 0;
|
|||
|
this.maxVerticalStartRadian = 0;
|
|||
|
this.useEndRadian = true;
|
|||
|
this.minHorizontalEndRadian = 0;
|
|||
|
this.maxHorizontalEndRadian = 0;
|
|||
|
this.minVerticalEndRadian = 0;
|
|||
|
this.maxVerticalEndRadian = 0;
|
|||
|
this.minStartColor = new Float32Array([1, 1, 1, 1]);
|
|||
|
this.maxStartColor = new Float32Array([1, 1, 1, 1]);
|
|||
|
this.minEndColor = new Float32Array([1, 1, 1, 1]);
|
|||
|
this.maxEndColor = new Float32Array([1, 1, 1, 1]);
|
|||
|
this.colorComponentInter = false;
|
|||
|
this.disableColor = false;
|
|||
|
this.blendState = 0;
|
|||
|
this.emitterType = "null";
|
|||
|
this.emissionRate = 0;
|
|||
|
this.pointEmitterPosition = new Float32Array([0, 0, 0]);
|
|||
|
this.pointEmitterPositionVariance = new Float32Array([0, 0, 0]);
|
|||
|
this.pointEmitterVelocity = new Float32Array([0, 0, 0]);
|
|||
|
this.pointEmitterVelocityAddVariance = new Float32Array([0, 0, 0]);
|
|||
|
this.boxEmitterCenterPosition = new Float32Array([0, 0, 0]);
|
|||
|
this.boxEmitterSize = new Float32Array([0, 0, 0]);
|
|||
|
this.boxEmitterVelocity = new Float32Array([0, 0, 0]);
|
|||
|
this.boxEmitterVelocityAddVariance = new Float32Array([0, 0, 0]);
|
|||
|
this.sphereEmitterCenterPosition = new Float32Array([0, 0, 0]);
|
|||
|
this.sphereEmitterRadius = 1;
|
|||
|
this.sphereEmitterVelocity = 0;
|
|||
|
this.sphereEmitterVelocityAddVariance = 0;
|
|||
|
this.ringEmitterCenterPosition = new Float32Array([0, 0, 0]);
|
|||
|
this.ringEmitterRadius = 30;
|
|||
|
this.ringEmitterVelocity = 0;
|
|||
|
this.ringEmitterVelocityAddVariance = 0;
|
|||
|
this.ringEmitterUp = 2;
|
|||
|
this.positionVariance = new Float32Array([0, 0, 0]);
|
|||
|
}
|
|||
|
static checkSetting(setting) {
|
|||
|
var key;
|
|||
|
for (key in ParticleSetting._defaultSetting) {
|
|||
|
if (!(key in setting)) {
|
|||
|
setting[key] = ParticleSetting._defaultSetting[key];
|
|||
|
}
|
|||
|
}
|
|||
|
setting.endVelocity = +setting.endVelocity;
|
|||
|
setting.gravity[0] = +setting.gravity[0];
|
|||
|
setting.gravity[1] = +setting.gravity[1];
|
|||
|
setting.gravity[2] = +setting.gravity[2];
|
|||
|
}
|
|||
|
}
|
|||
|
ParticleSetting._defaultSetting = new ParticleSetting();
|
|||
|
|
|||
|
class ParticleTemplateBase {
|
|||
|
constructor() {
|
|||
|
}
|
|||
|
addParticleArray(position, velocity) {
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class ParticleData {
|
|||
|
constructor() {
|
|||
|
}
|
|||
|
static Create(settings, position, velocity, time) {
|
|||
|
var particleData = new ParticleData();
|
|||
|
particleData.position = position;
|
|||
|
Laya.MathUtil.scaleVector3(velocity, settings.emitterVelocitySensitivity, ParticleData._tempVelocity);
|
|||
|
var horizontalVelocity = Laya.MathUtil.lerp(settings.minHorizontalVelocity, settings.maxHorizontalVelocity, Math.random());
|
|||
|
var horizontalAngle = Math.random() * Math.PI * 2;
|
|||
|
ParticleData._tempVelocity[0] += horizontalVelocity * Math.cos(horizontalAngle);
|
|||
|
ParticleData._tempVelocity[2] += horizontalVelocity * Math.sin(horizontalAngle);
|
|||
|
ParticleData._tempVelocity[1] += Laya.MathUtil.lerp(settings.minVerticalVelocity, settings.maxVerticalVelocity, Math.random());
|
|||
|
particleData.velocity = ParticleData._tempVelocity;
|
|||
|
particleData.startColor = ParticleData._tempStartColor;
|
|||
|
particleData.endColor = ParticleData._tempEndColor;
|
|||
|
var i;
|
|||
|
if (settings.disableColor) {
|
|||
|
for (i = 0; i < 3; i++) {
|
|||
|
particleData.startColor[i] = 1;
|
|||
|
particleData.endColor[i] = 1;
|
|||
|
}
|
|||
|
particleData.startColor[i] = Laya.MathUtil.lerp(settings.minStartColor[i], settings.maxStartColor[i], Math.random());
|
|||
|
particleData.endColor[i] = Laya.MathUtil.lerp(settings.minEndColor[i], settings.maxEndColor[i], Math.random());
|
|||
|
}
|
|||
|
else {
|
|||
|
if (settings.colorComponentInter) {
|
|||
|
for (i = 0; i < 4; i++) {
|
|||
|
particleData.startColor[i] = Laya.MathUtil.lerp(settings.minStartColor[i], settings.maxStartColor[i], Math.random());
|
|||
|
particleData.endColor[i] = Laya.MathUtil.lerp(settings.minEndColor[i], settings.maxEndColor[i], Math.random());
|
|||
|
}
|
|||
|
}
|
|||
|
else {
|
|||
|
Laya.MathUtil.lerpVector4(settings.minStartColor, settings.maxStartColor, Math.random(), particleData.startColor);
|
|||
|
Laya.MathUtil.lerpVector4(settings.minEndColor, settings.maxEndColor, Math.random(), particleData.endColor);
|
|||
|
}
|
|||
|
}
|
|||
|
particleData.sizeRotation = ParticleData._tempSizeRotation;
|
|||
|
var sizeRandom = Math.random();
|
|||
|
particleData.sizeRotation[0] = Laya.MathUtil.lerp(settings.minStartSize, settings.maxStartSize, sizeRandom);
|
|||
|
particleData.sizeRotation[1] = Laya.MathUtil.lerp(settings.minEndSize, settings.maxEndSize, sizeRandom);
|
|||
|
particleData.sizeRotation[2] = Laya.MathUtil.lerp(settings.minRotateSpeed, settings.maxRotateSpeed, Math.random());
|
|||
|
particleData.radius = ParticleData._tempRadius;
|
|||
|
var radiusRandom = Math.random();
|
|||
|
particleData.radius[0] = Laya.MathUtil.lerp(settings.minStartRadius, settings.maxStartRadius, radiusRandom);
|
|||
|
particleData.radius[1] = Laya.MathUtil.lerp(settings.minEndRadius, settings.maxEndRadius, radiusRandom);
|
|||
|
particleData.radian = ParticleData._tempRadian;
|
|||
|
particleData.radian[0] = Laya.MathUtil.lerp(settings.minHorizontalStartRadian, settings.maxHorizontalStartRadian, Math.random());
|
|||
|
particleData.radian[1] = Laya.MathUtil.lerp(settings.minVerticalStartRadian, settings.maxVerticalStartRadian, Math.random());
|
|||
|
var useEndRadian = settings.useEndRadian;
|
|||
|
particleData.radian[2] = useEndRadian ? Laya.MathUtil.lerp(settings.minHorizontalEndRadian, settings.maxHorizontalEndRadian, Math.random()) : particleData.radian[0];
|
|||
|
particleData.radian[3] = useEndRadian ? Laya.MathUtil.lerp(settings.minVerticalEndRadian, settings.maxVerticalEndRadian, Math.random()) : particleData.radian[1];
|
|||
|
particleData.durationAddScale = settings.ageAddScale * Math.random();
|
|||
|
particleData.time = time;
|
|||
|
return particleData;
|
|||
|
}
|
|||
|
}
|
|||
|
ParticleData._tempVelocity = new Float32Array(3);
|
|||
|
ParticleData._tempStartColor = new Float32Array(4);
|
|||
|
ParticleData._tempEndColor = new Float32Array(4);
|
|||
|
ParticleData._tempSizeRotation = new Float32Array(3);
|
|||
|
ParticleData._tempRadius = new Float32Array(2);
|
|||
|
ParticleData._tempRadian = new Float32Array(4);
|
|||
|
|
|||
|
class ParticleTemplateWebGL extends ParticleTemplateBase {
|
|||
|
constructor(parSetting) {
|
|||
|
super();
|
|||
|
this._floatCountPerVertex = 29;
|
|||
|
this._firstActiveElement = 0;
|
|||
|
this._firstNewElement = 0;
|
|||
|
this._firstFreeElement = 0;
|
|||
|
this._firstRetiredElement = 0;
|
|||
|
this._currentTime = 0;
|
|||
|
this.settings = parSetting;
|
|||
|
}
|
|||
|
reUse(context, pos) {
|
|||
|
return 0;
|
|||
|
}
|
|||
|
initialize() {
|
|||
|
var floatStride = 0;
|
|||
|
this._vertices = this._mesh._vb.getFloat32Array();
|
|||
|
floatStride = this._mesh._stride / 4;
|
|||
|
var bufi = 0;
|
|||
|
var bufStart = 0;
|
|||
|
for (var i = 0; i < this.settings.maxPartices; i++) {
|
|||
|
var random = Math.random();
|
|||
|
var cornerYSegement = this.settings.textureCount ? 1.0 / this.settings.textureCount : 1.0;
|
|||
|
var cornerY;
|
|||
|
for (cornerY = 0; cornerY < this.settings.textureCount; cornerY += cornerYSegement) {
|
|||
|
if (random < cornerY + cornerYSegement)
|
|||
|
break;
|
|||
|
}
|
|||
|
this._vertices[bufi++] = -1;
|
|||
|
this._vertices[bufi++] = -1;
|
|||
|
this._vertices[bufi++] = 0;
|
|||
|
this._vertices[bufi++] = cornerY;
|
|||
|
bufi = (bufStart += floatStride);
|
|||
|
this._vertices[bufi++] = 1;
|
|||
|
this._vertices[bufi++] = -1;
|
|||
|
this._vertices[bufi++] = 1;
|
|||
|
this._vertices[bufi++] = cornerY;
|
|||
|
bufi = bufStart += floatStride;
|
|||
|
this._vertices[bufi++] = 1;
|
|||
|
this._vertices[bufi++] = 1;
|
|||
|
this._vertices[bufi++] = 1;
|
|||
|
this._vertices[bufi++] = cornerY + cornerYSegement;
|
|||
|
bufi = bufStart += floatStride;
|
|||
|
this._vertices[bufi++] = -1;
|
|||
|
this._vertices[bufi++] = 1;
|
|||
|
this._vertices[bufi++] = 0;
|
|||
|
this._vertices[bufi++] = cornerY + cornerYSegement;
|
|||
|
bufi = bufStart += floatStride;
|
|||
|
}
|
|||
|
}
|
|||
|
update(elapsedTime) {
|
|||
|
this._currentTime += elapsedTime / 1000;
|
|||
|
this.retireActiveParticles();
|
|||
|
this.freeRetiredParticles();
|
|||
|
if (this._firstActiveElement == this._firstFreeElement)
|
|||
|
this._currentTime = 0;
|
|||
|
if (this._firstRetiredElement == this._firstActiveElement)
|
|||
|
this._drawCounter = 0;
|
|||
|
}
|
|||
|
retireActiveParticles() {
|
|||
|
const epsilon = 0.0001;
|
|||
|
var particleDuration = this.settings.duration;
|
|||
|
while (this._firstActiveElement != this._firstNewElement) {
|
|||
|
var offset = this._firstActiveElement * this._floatCountPerVertex * 4;
|
|||
|
var index = offset + 28;
|
|||
|
var particleAge = this._currentTime - this._vertices[index];
|
|||
|
particleAge *= (1.0 + this._vertices[offset + 27]);
|
|||
|
if (particleAge + epsilon < particleDuration)
|
|||
|
break;
|
|||
|
this._vertices[index] = this._drawCounter;
|
|||
|
this._firstActiveElement++;
|
|||
|
if (this._firstActiveElement >= this.settings.maxPartices)
|
|||
|
this._firstActiveElement = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
freeRetiredParticles() {
|
|||
|
while (this._firstRetiredElement != this._firstActiveElement) {
|
|||
|
var age = this._drawCounter - this._vertices[this._firstRetiredElement * this._floatCountPerVertex * 4 + 28];
|
|||
|
if (age < 3)
|
|||
|
break;
|
|||
|
this._firstRetiredElement++;
|
|||
|
if (this._firstRetiredElement >= this.settings.maxPartices)
|
|||
|
this._firstRetiredElement = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
addNewParticlesToVertexBuffer() {
|
|||
|
}
|
|||
|
addParticleArray(position, velocity) {
|
|||
|
var nextFreeParticle = this._firstFreeElement + 1;
|
|||
|
if (nextFreeParticle >= this.settings.maxPartices)
|
|||
|
nextFreeParticle = 0;
|
|||
|
if (nextFreeParticle === this._firstRetiredElement)
|
|||
|
return;
|
|||
|
var particleData = ParticleData.Create(this.settings, position, velocity, this._currentTime);
|
|||
|
var startIndex = this._firstFreeElement * this._floatCountPerVertex * 4;
|
|||
|
for (var i = 0; i < 4; i++) {
|
|||
|
var j, offset;
|
|||
|
for (j = 0, offset = 4; j < 3; j++)
|
|||
|
this._vertices[startIndex + i * this._floatCountPerVertex + offset + j] = particleData.position[j];
|
|||
|
for (j = 0, offset = 7; j < 3; j++)
|
|||
|
this._vertices[startIndex + i * this._floatCountPerVertex + offset + j] = particleData.velocity[j];
|
|||
|
for (j = 0, offset = 10; j < 4; j++)
|
|||
|
this._vertices[startIndex + i * this._floatCountPerVertex + offset + j] = particleData.startColor[j];
|
|||
|
for (j = 0, offset = 14; j < 4; j++)
|
|||
|
this._vertices[startIndex + i * this._floatCountPerVertex + offset + j] = particleData.endColor[j];
|
|||
|
for (j = 0, offset = 18; j < 3; j++)
|
|||
|
this._vertices[startIndex + i * this._floatCountPerVertex + offset + j] = particleData.sizeRotation[j];
|
|||
|
for (j = 0, offset = 21; j < 2; j++)
|
|||
|
this._vertices[startIndex + i * this._floatCountPerVertex + offset + j] = particleData.radius[j];
|
|||
|
for (j = 0, offset = 23; j < 4; j++)
|
|||
|
this._vertices[startIndex + i * this._floatCountPerVertex + offset + j] = particleData.radian[j];
|
|||
|
this._vertices[startIndex + i * this._floatCountPerVertex + 27] = particleData.durationAddScale;
|
|||
|
this._vertices[startIndex + i * this._floatCountPerVertex + 28] = particleData.time;
|
|||
|
}
|
|||
|
this._firstFreeElement = nextFreeParticle;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
var parvs = "attribute vec4 a_CornerTextureCoordinate;\r\nattribute vec3 a_Position;\r\nattribute vec3 a_Velocity;\r\nattribute vec4 a_StartColor;\r\nattribute vec4 a_EndColor;\r\nattribute vec3 a_SizeRotation;\r\nattribute vec2 a_Radius;\r\nattribute vec4 a_Radian;\r\nattribute float a_AgeAddScale;\r\nattribute float a_Time;\r\n\r\nvarying vec4 v_Color;\r\nvarying vec2 v_TextureCoordinate;\r\n\r\nuniform float u_CurrentTime;\r\nuniform float u_Duration;\r\nuniform float u_EndVelocity;\r\nuniform vec3 u_Gravity;\r\n\r\nuniform vec2 size;\r\nuniform mat4 u_mmat;\r\n\r\nvec4 ComputeParticlePosition(in vec3 position, in vec3 velocity,in float age,in float normalizedAge)\r\n{\r\n\r\n float startVelocity = length(velocity);//起始标量速度\r\n float endVelocity = startVelocity * u_EndVelocity;//结束标量速度\r\n\r\n float velocityIntegral = startVelocity * normalizedAge +(endVelocity - startVelocity) * normalizedAge *normalizedAge/2.0;//计算当前速度的标量(单位空间),vt=v0*t+(1/2)*a*(t^2)\r\n \r\n vec3 addPosition = normalize(velocity) * velocityIntegral * u_Duration;//计算受自身速度影响的位置,转换标量到矢量 \r\n addPosition += u_Gravity * age * normalizedAge;//计算受重力影响的位置\r\n \r\n float radius=mix(a_Radius.x, a_Radius.y, normalizedAge); //计算粒子受半径和角度影响(无需计算角度和半径时,可用宏定义优化屏蔽此计算)\r\n float radianHorizontal =mix(a_Radian.x,a_Radian.z,normalizedAge);\r\n float radianVertical =mix(a_Radian.y,a_Radian.w,normalizedAge);\r\n \r\n float r =cos(radianVertical)* radius;\r\n addPosition.y += sin(radianVertical) * radius;\r\n\t\r\n addPosition.x += cos(radianHorizontal) *r;\r\n addPosition.z += sin(radianHorizontal) *r;\r\n \r\n addPosition.y=-addPosition.y;//2D粒子位置更新需要取负,2D粒子坐标系Y轴正向朝上\r\n position+=addPosition;\r\n return vec4(position,1.0);\r\n}\r\n\r\nfloat ComputeParticleSize(in float startSize,in float endSize, in float normalizedAge)\r\n{ \r\n float size = mix(startSize, endSize, normalizedAge);\r\n return size;\r\n}\r\n\r\nmat2 ComputeParticleRotation(in float rot,in float age)\r\n{ \r\n float rotation =rot * age;\r\n //计算2x2旋转矩阵.\r\n float c = cos(rotation);\r\n float s = sin(rotation);\r\n return mat2(c, -s, s, c);\r\n}\r\n\r\nvec4 ComputeParticleColor(in vec4 startColor,in vec4 endColor,in float normalizedAge)\r\n{\r\n\tvec4 color=mix(startColor,endColor,normalizedAge);\r\n //硬编码设置,使粒子淡入很快,淡出很慢,6.7的缩放因子把置归一在0到1之间,可以谷歌x*(1-x)*(1-x)*6.7的制图表\r\n color.a *= normalizedAge * (1.0-normalizedAge) * (1.0-normalizedAge) * 6.7;\r\n \r\n return color;\r\n}\r\n\r\nvoid main()\r\n{\r\n float age = u_CurrentTime - a_Time;\r\n age *= 1.0 + a_AgeAddScale;\r\n float normalizedAge = clamp(age / u_Duration,0.0,1.0);\r\n gl_Position = ComputeParticlePosition(a_Position, a_Velocity, age, normalizedAge);//计算粒子位置\r\n float pSize = ComputeParticleSize(a_SizeRotation.x,a_SizeRotation.y, normalizedAge);\r\n mat2 rotation = ComputeParticleRotation(a_SizeRotation.z, age);\r\n\t\r\n mat4 mat=u_mmat;\r\n gl_Position=vec4((mat*gl_Position).xy,0.0,1.0);\r\n gl_Position.xy += (rotation*a_CornerTextureCoordinate.xy) * pSize*vec2(mat[0][0],mat[1][1]);\r\n gl_Position=vec4((gl_Position.x/size.x-0.5)*2.0,(0.5-gl_Position.y/size.y)*2.0,0.0,1.0);\r\n \r\n v_Color = ComputeParticleColor(a_StartColor,a_EndColor, normalizedAge);\r\n v_TextureCoordinate =a_CornerTextureCoordinate.zw;\r\n}\r\n\r\n";
|
|||
|
|
|||
|
var parps = "#if defined(GL_FRAGMENT_PRECISION_HIGH)\r\nprecision highp float;\r\n#else\r\nprecision mediump float;\r\n#endif\r\n\r\nvarying vec4 v_Color;\r\nvarying vec2 v_TextureCoordinate;\r\nuniform sampler2D u_texture;\r\n\r\nvoid main()\r\n{\t\r\n\tgl_FragColor=texture2D(u_texture,v_TextureCoordinate)*v_Color;\r\n\tgl_FragColor.xyz *= v_Color.w;\r\n}";
|
|||
|
|
|||
|
class ParticleShader extends Laya.Shader {
|
|||
|
constructor() {
|
|||
|
super(parvs, parps, "ParticleShader", null, ['a_CornerTextureCoordinate', 0, 'a_Position', 1, 'a_Velocity', 2, 'a_StartColor', 3,
|
|||
|
'a_EndColor', 4, 'a_SizeRotation', 5, 'a_Radius', 6, 'a_Radian', 7, 'a_AgeAddScale', 8, 'a_Time', 9]);
|
|||
|
}
|
|||
|
}
|
|||
|
ParticleShader.vs = parvs;
|
|||
|
ParticleShader.ps = parps;
|
|||
|
|
|||
|
class ParticleShaderValue extends Laya.Value2D {
|
|||
|
constructor() {
|
|||
|
super(0, 0);
|
|||
|
if (!ParticleShaderValue.pShader) {
|
|||
|
ParticleShaderValue.pShader = new ParticleShader();
|
|||
|
}
|
|||
|
}
|
|||
|
upload() {
|
|||
|
var size = this.size;
|
|||
|
size[0] = Laya.RenderState2D.width;
|
|||
|
size[1] = Laya.RenderState2D.height;
|
|||
|
this.alpha = this.ALPHA * Laya.RenderState2D.worldAlpha;
|
|||
|
ParticleShaderValue.pShader.upload(this);
|
|||
|
}
|
|||
|
}
|
|||
|
ParticleShaderValue.pShader = null;
|
|||
|
|
|||
|
class ParticleTemplate2D extends ParticleTemplateWebGL {
|
|||
|
constructor(parSetting) {
|
|||
|
super(parSetting);
|
|||
|
this.x = 0;
|
|||
|
this.y = 0;
|
|||
|
this.sv = new ParticleShaderValue();
|
|||
|
this._key = {};
|
|||
|
var _this = this;
|
|||
|
Laya.ILaya.loader.load(this.settings.textureName, Laya.Handler.create(null, function (texture) {
|
|||
|
_this.texture = texture;
|
|||
|
}), null, Laya.Loader.IMAGE);
|
|||
|
this.sv.u_Duration = this.settings.duration;
|
|||
|
this.sv.u_Gravity = this.settings.gravity;
|
|||
|
this.sv.u_EndVelocity = this.settings.endVelocity;
|
|||
|
this._blendFn = Laya.BlendMode.fns[parSetting.blendState];
|
|||
|
this._mesh = Laya.MeshParticle2D.getAMesh(this.settings.maxPartices);
|
|||
|
this.initialize();
|
|||
|
}
|
|||
|
getRenderType() { return -111; }
|
|||
|
releaseRender() { }
|
|||
|
addParticleArray(position, velocity) {
|
|||
|
position[0] += this.x;
|
|||
|
position[1] += this.y;
|
|||
|
super.addParticleArray(position, velocity);
|
|||
|
}
|
|||
|
addNewParticlesToVertexBuffer() {
|
|||
|
var _vertexBuffer2D = this._mesh._vb;
|
|||
|
_vertexBuffer2D.clear();
|
|||
|
_vertexBuffer2D.append(this._vertices);
|
|||
|
var start;
|
|||
|
if (this._firstNewElement < this._firstFreeElement) {
|
|||
|
start = this._firstNewElement * 4 * this._floatCountPerVertex * 4;
|
|||
|
_vertexBuffer2D.subUpload(start, start, start + (this._firstFreeElement - this._firstNewElement) * 4 * this._floatCountPerVertex * 4);
|
|||
|
}
|
|||
|
else {
|
|||
|
start = this._firstNewElement * 4 * this._floatCountPerVertex * 4;
|
|||
|
_vertexBuffer2D.subUpload(start, start, start + (this.settings.maxPartices - this._firstNewElement) * 4 * this._floatCountPerVertex * 4);
|
|||
|
if (this._firstFreeElement > 0) {
|
|||
|
_vertexBuffer2D.setNeedUpload();
|
|||
|
_vertexBuffer2D.subUpload(0, 0, this._firstFreeElement * 4 * this._floatCountPerVertex * 4);
|
|||
|
}
|
|||
|
}
|
|||
|
this._firstNewElement = this._firstFreeElement;
|
|||
|
}
|
|||
|
renderSubmit() {
|
|||
|
if (this.texture && this.texture.getIsReady()) {
|
|||
|
this.update(Laya.ILaya.timer._delta);
|
|||
|
this.sv.u_CurrentTime = this._currentTime;
|
|||
|
if (this._firstNewElement != this._firstFreeElement) {
|
|||
|
this.addNewParticlesToVertexBuffer();
|
|||
|
}
|
|||
|
this.blend();
|
|||
|
if (this._firstActiveElement != this._firstFreeElement) {
|
|||
|
var gl = Laya.WebGLContext.mainContext;
|
|||
|
this._mesh.useMesh(gl);
|
|||
|
this.sv.u_texture = this.texture._getSource();
|
|||
|
this.sv.upload();
|
|||
|
if (this._firstActiveElement < this._firstFreeElement) {
|
|||
|
gl.drawElements(gl.TRIANGLES, (this._firstFreeElement - this._firstActiveElement) * 6, gl.UNSIGNED_SHORT, this._firstActiveElement * 6 * 2);
|
|||
|
}
|
|||
|
else {
|
|||
|
Laya.WebGLContext.mainContext.drawElements(gl.TRIANGLES, (this.settings.maxPartices - this._firstActiveElement) * 6, gl.UNSIGNED_SHORT, this._firstActiveElement * 6 * 2);
|
|||
|
if (this._firstFreeElement > 0)
|
|||
|
gl.drawElements(gl.TRIANGLES, this._firstFreeElement * 6, gl.UNSIGNED_SHORT, 0);
|
|||
|
}
|
|||
|
Laya.Stat.renderBatches++;
|
|||
|
}
|
|||
|
this._drawCounter++;
|
|||
|
}
|
|||
|
return 1;
|
|||
|
}
|
|||
|
updateParticleForNative() {
|
|||
|
if (this.texture && this.texture.getIsReady()) {
|
|||
|
this.update(Laya.ILaya.timer._delta);
|
|||
|
this.sv.u_CurrentTime = this._currentTime;
|
|||
|
if (this._firstNewElement != this._firstFreeElement) {
|
|||
|
this._firstNewElement = this._firstFreeElement;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
getMesh() {
|
|||
|
return this._mesh;
|
|||
|
}
|
|||
|
getConchMesh() {
|
|||
|
return this._conchMesh;
|
|||
|
}
|
|||
|
getFirstNewElement() {
|
|||
|
return this._firstNewElement;
|
|||
|
}
|
|||
|
getFirstFreeElement() {
|
|||
|
return this._firstFreeElement;
|
|||
|
}
|
|||
|
getFirstActiveElement() {
|
|||
|
return this._firstActiveElement;
|
|||
|
}
|
|||
|
getFirstRetiredElement() {
|
|||
|
return this._firstRetiredElement;
|
|||
|
}
|
|||
|
setFirstFreeElement(_value) {
|
|||
|
this._firstFreeElement = _value;
|
|||
|
}
|
|||
|
setFirstNewElement(_value) {
|
|||
|
this._firstNewElement = _value;
|
|||
|
}
|
|||
|
addDrawCounter() {
|
|||
|
this._drawCounter++;
|
|||
|
}
|
|||
|
blend() {
|
|||
|
if (Laya.BlendMode.activeBlendFunction !== this._blendFn) {
|
|||
|
var gl = Laya.WebGLContext.mainContext;
|
|||
|
gl.enable(gl.BLEND);
|
|||
|
this._blendFn(gl);
|
|||
|
Laya.BlendMode.activeBlendFunction = this._blendFn;
|
|||
|
}
|
|||
|
}
|
|||
|
dispose() {
|
|||
|
this._mesh.releaseMesh();
|
|||
|
}
|
|||
|
}
|
|||
|
ParticleTemplate2D.activeBlendType = -1;
|
|||
|
|
|||
|
class EmitterBase {
|
|||
|
constructor() {
|
|||
|
this._frameTime = 0;
|
|||
|
this._emissionRate = 60;
|
|||
|
this._emissionTime = 0;
|
|||
|
this.minEmissionTime = 1 / 60;
|
|||
|
}
|
|||
|
set particleTemplate(particleTemplate) {
|
|||
|
this._particleTemplate = particleTemplate;
|
|||
|
}
|
|||
|
set emissionRate(_emissionRate) {
|
|||
|
if (_emissionRate <= 0)
|
|||
|
return;
|
|||
|
this._emissionRate = _emissionRate;
|
|||
|
(_emissionRate > 0) && (this.minEmissionTime = 1 / _emissionRate);
|
|||
|
}
|
|||
|
get emissionRate() {
|
|||
|
return this._emissionRate;
|
|||
|
}
|
|||
|
start(duration = Number.MAX_VALUE) {
|
|||
|
if (this._emissionRate != 0)
|
|||
|
this._emissionTime = duration;
|
|||
|
}
|
|||
|
stop() {
|
|||
|
this._emissionTime = 0;
|
|||
|
}
|
|||
|
clear() {
|
|||
|
this._emissionTime = 0;
|
|||
|
}
|
|||
|
emit() {
|
|||
|
}
|
|||
|
advanceTime(passedTime = 1) {
|
|||
|
this._emissionTime -= passedTime;
|
|||
|
if (this._emissionTime < 0)
|
|||
|
return;
|
|||
|
this._frameTime += passedTime;
|
|||
|
if (this._frameTime < this.minEmissionTime)
|
|||
|
return;
|
|||
|
while (this._frameTime > this.minEmissionTime) {
|
|||
|
this._frameTime -= this.minEmissionTime;
|
|||
|
this.emit();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class Emitter2D extends EmitterBase {
|
|||
|
constructor(_template) {
|
|||
|
super();
|
|||
|
this.template = _template;
|
|||
|
}
|
|||
|
set template(template) {
|
|||
|
this._particleTemplate = template;
|
|||
|
if (!template) {
|
|||
|
this._emitFun = null;
|
|||
|
this.setting = null;
|
|||
|
this._posRange = null;
|
|||
|
}
|
|||
|
this.setting = template.settings;
|
|||
|
this._posRange = this.setting.positionVariance;
|
|||
|
if (this._particleTemplate instanceof ParticleTemplate2D) {
|
|||
|
this._emitFun = this.webGLEmit;
|
|||
|
}
|
|||
|
}
|
|||
|
get template() {
|
|||
|
return this._particleTemplate;
|
|||
|
}
|
|||
|
emit() {
|
|||
|
super.emit();
|
|||
|
if (this._emitFun != null)
|
|||
|
this._emitFun();
|
|||
|
}
|
|||
|
getRandom(value) {
|
|||
|
return (Math.random() * 2 - 1) * value;
|
|||
|
}
|
|||
|
webGLEmit() {
|
|||
|
var pos = new Float32Array(3);
|
|||
|
pos[0] = this.getRandom(this._posRange[0]);
|
|||
|
pos[1] = this.getRandom(this._posRange[1]);
|
|||
|
pos[2] = this.getRandom(this._posRange[2]);
|
|||
|
var v = new Float32Array(3);
|
|||
|
v[0] = 0;
|
|||
|
v[1] = 0;
|
|||
|
v[2] = 0;
|
|||
|
this._particleTemplate.addParticleArray(pos, v);
|
|||
|
}
|
|||
|
canvasEmit() {
|
|||
|
var pos = new Float32Array(3);
|
|||
|
pos[0] = this.getRandom(this._posRange[0]);
|
|||
|
pos[1] = this.getRandom(this._posRange[1]);
|
|||
|
pos[2] = this.getRandom(this._posRange[2]);
|
|||
|
var v = new Float32Array(3);
|
|||
|
v[0] = 0;
|
|||
|
v[1] = 0;
|
|||
|
v[2] = 0;
|
|||
|
this._particleTemplate.addParticleArray(pos, v);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class Particle2D extends Laya.Sprite {
|
|||
|
constructor(setting) {
|
|||
|
super();
|
|||
|
this._matrix4 = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
|
|||
|
this.autoPlay = true;
|
|||
|
this.customRenderEnable = true;
|
|||
|
if (setting)
|
|||
|
this.setParticleSetting(setting);
|
|||
|
}
|
|||
|
set url(url) {
|
|||
|
this.load(url);
|
|||
|
}
|
|||
|
load(url) {
|
|||
|
Laya.ILaya.loader.load(url, Laya.Handler.create(this, this.setParticleSetting), null, Laya.ILaya.Loader.JSON);
|
|||
|
}
|
|||
|
setParticleSetting(setting) {
|
|||
|
if (!setting)
|
|||
|
return this.stop();
|
|||
|
ParticleSetting.checkSetting(setting);
|
|||
|
this.customRenderEnable = true;
|
|||
|
this._particleTemplate = new ParticleTemplate2D(setting);
|
|||
|
this.graphics._saveToCmd(null, Laya.DrawParticleCmd.create(this._particleTemplate));
|
|||
|
if (!this._emitter) {
|
|||
|
this._emitter = new Emitter2D(this._particleTemplate);
|
|||
|
}
|
|||
|
else {
|
|||
|
this._emitter.template = this._particleTemplate;
|
|||
|
}
|
|||
|
if (this.autoPlay) {
|
|||
|
this.emitter.start();
|
|||
|
this.play();
|
|||
|
}
|
|||
|
}
|
|||
|
get emitter() {
|
|||
|
return this._emitter;
|
|||
|
}
|
|||
|
play() {
|
|||
|
Laya.ILaya.timer.frameLoop(1, this, this._loop);
|
|||
|
}
|
|||
|
stop() {
|
|||
|
Laya.ILaya.timer.clear(this, this._loop);
|
|||
|
}
|
|||
|
_loop() {
|
|||
|
this.advanceTime(1 / 60);
|
|||
|
}
|
|||
|
advanceTime(passedTime = 1) {
|
|||
|
if (this._canvasTemplate) {
|
|||
|
this._canvasTemplate.advanceTime(passedTime);
|
|||
|
}
|
|||
|
if (this._emitter) {
|
|||
|
this._emitter.advanceTime(passedTime);
|
|||
|
}
|
|||
|
}
|
|||
|
customRender(context, x, y) {
|
|||
|
this._matrix4[0] = context._curMat.a;
|
|||
|
this._matrix4[1] = context._curMat.b;
|
|||
|
this._matrix4[4] = context._curMat.c;
|
|||
|
this._matrix4[5] = context._curMat.d;
|
|||
|
this._matrix4[12] = context._curMat.tx;
|
|||
|
this._matrix4[13] = context._curMat.ty;
|
|||
|
var sv = this._particleTemplate.sv;
|
|||
|
sv.u_mmat = this._matrix4;
|
|||
|
if (this._canvasTemplate) {
|
|||
|
this._canvasTemplate.render(context, x, y);
|
|||
|
}
|
|||
|
}
|
|||
|
destroy(destroyChild = true) {
|
|||
|
if (this._particleTemplate instanceof ParticleTemplate2D)
|
|||
|
this._particleTemplate.dispose();
|
|||
|
super.destroy(destroyChild);
|
|||
|
}
|
|||
|
}
|
|||
|
Laya.ClassUtils.regClass("laya.particle.Particle2D", Particle2D);
|
|||
|
Laya.ClassUtils.regClass("Laya.Particle2D", Particle2D);
|
|||
|
Laya.ILaya.regClass(Particle2D);
|
|||
|
|
|||
|
class ParticleEmitter {
|
|||
|
constructor(templet, particlesPerSecond, initialPosition) {
|
|||
|
this._timeLeftOver = 0;
|
|||
|
this._tempVelocity = new Float32Array([0, 0, 0]);
|
|||
|
this._tempPosition = new Float32Array([0, 0, 0]);
|
|||
|
this._templet = templet;
|
|||
|
this._timeBetweenParticles = 1.0 / particlesPerSecond;
|
|||
|
this._previousPosition = initialPosition;
|
|||
|
}
|
|||
|
update(elapsedTime, newPosition) {
|
|||
|
elapsedTime = elapsedTime / 1000;
|
|||
|
if (elapsedTime > 0) {
|
|||
|
Laya.MathUtil.subtractVector3(newPosition, this._previousPosition, this._tempVelocity);
|
|||
|
Laya.MathUtil.scaleVector3(this._tempVelocity, 1 / elapsedTime, this._tempVelocity);
|
|||
|
var timeToSpend = this._timeLeftOver + elapsedTime;
|
|||
|
var currentTime = -this._timeLeftOver;
|
|||
|
while (timeToSpend > this._timeBetweenParticles) {
|
|||
|
currentTime += this._timeBetweenParticles;
|
|||
|
timeToSpend -= this._timeBetweenParticles;
|
|||
|
Laya.MathUtil.lerpVector3(this._previousPosition, newPosition, currentTime / elapsedTime, this._tempPosition);
|
|||
|
this._templet.addParticleArray(this._tempPosition, this._tempVelocity);
|
|||
|
}
|
|||
|
this._timeLeftOver = timeToSpend;
|
|||
|
}
|
|||
|
this._previousPosition[0] = newPosition[0];
|
|||
|
this._previousPosition[1] = newPosition[1];
|
|||
|
this._previousPosition[2] = newPosition[2];
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
exports.Emitter2D = Emitter2D;
|
|||
|
exports.EmitterBase = EmitterBase;
|
|||
|
exports.Particle2D = Particle2D;
|
|||
|
exports.ParticleData = ParticleData;
|
|||
|
exports.ParticleEmitter = ParticleEmitter;
|
|||
|
exports.ParticleSetting = ParticleSetting;
|
|||
|
exports.ParticleShader = ParticleShader;
|
|||
|
exports.ParticleShaderValue = ParticleShaderValue;
|
|||
|
exports.ParticleTemplate2D = ParticleTemplate2D;
|
|||
|
exports.ParticleTemplateBase = ParticleTemplateBase;
|
|||
|
exports.ParticleTemplateWebGL = ParticleTemplateWebGL;
|
|||
|
|
|||
|
}(window.Laya = window.Laya || {}, Laya));
|