import { PointShapeEmitter, SphereShapeEmitter, BoxShapeEmitter } from '../Shape/emitter' import { FactorGradient, BasicGradientMethod, ColorGradient } from '../Util/Gradient' import { IParticleData, ParticleSchema } from './ParticleInterface' const xrFrameSystem = wx.getXrFrameSystem(); // 注册粒子系统的effect xrFrameSystem.registerEffect('custom-particle-effect', scene => scene.createEffect({ name: "custom-particle-effect", properties: [{ "key": 'u_baseColorFactor', "type": 3, "default": [1, 1, 1, 1] }], images: [{ key: 'u_baseColorMap', default: 'white', macro: 'WX_USE_BASECOLORMAP' }, { key: 'u_rampColorMap', default: 'white', macro: 'WX_USE_RAMPCOLORMAP' }], defaultRenderQueue: 3000, passes: [{ "renderStates": { cullOn: false, blendOn: true, depthWrite: false }, lightMode: "ForwardBase", useMaterialRenderStates: true, shaders: [0, 1] }], shaders: [`#version 100 precision mediump float; uniform highp mat4 u_view; uniform highp mat4 u_projection; uniform highp mat4 u_world; attribute vec3 a_position; attribute highp vec2 a_texCoord; attribute vec3 a_offset; attribute vec4 a_color; attribute float a_angle; #ifdef WX_BILLBOARDMODE_STRETCHED attribute vec3 a_direction; attribute float a_lengthScale; #endif #ifdef WX_USE_RAMPCOLORMAP attribute vec4 a_rampPos; #endif #ifdef WX_SPRITESHEET attribute vec4 a_cellPosition; #endif varying highp vec2 v_Uv; varying highp vec4 v_Color; #ifdef WX_USE_RAMPCOLORMAP varying highp vec4 v_rampData; #endif uniform mat4 u_viewInverse; #ifdef WX_BILLBOARDMODE_Y vec3 rotate(vec3 yaxis,vec3 rotatedOffset) { vec3 xaxis=normalize(cross(vec3(0.,1.0,0.),yaxis)); vec3 zaxis=normalize(cross(yaxis,xaxis)); vec3 row0=vec3(xaxis.x,xaxis.y,xaxis.z); vec3 row1=vec3(yaxis.x,yaxis.y,yaxis.z); vec3 row2=vec3(zaxis.x,zaxis.y,zaxis.z); mat3 rotMatrix= mat3(row0,row1,row2); vec3 alignedOffset=rotMatrix*rotatedOffset; return alignedOffset; } #endif #ifdef WX_BILLBOARDMODE_STRETCHED vec3 rotateAlign(vec3 toCamera, vec3 rotatedOffset) { vec3 normalizedToCamera = normalize(toCamera); vec3 normalizedCrossVec = normalize(cross(normalize(a_direction), normalizedToCamera)); vec3 normalizedCrossResult = normalize(cross(normalizedToCamera, normalizedCrossVec)) * a_lengthScale; vec3 row0 = normalizedCrossVec; vec3 row1 = normalizedCrossResult; vec3 row2 = normalizedToCamera; mat3 rotMatrix = mat3(row0, row1, row2); vec3 alignedOffset = rotMatrix * rotatedOffset; return alignedOffset; } #endif vec3 extractScale(mat4 matrix){ vec3 scale; scale.x = length(vec4(matrix[0][0],matrix[0][1],matrix[0][2],matrix[0][3])); scale.y = length(vec4(matrix[1][0],matrix[1][1],matrix[1][2],matrix[1][3])); scale.z = length(vec4(matrix[2][0],matrix[2][1],matrix[2][2],matrix[2][3])); return scale; } void main() { #ifdef WX_SPRITESHEET vec2 uvOffset = vec2(a_texCoord.x * a_cellPosition.z ,a_texCoord.y * a_cellPosition.w); v_Uv = vec2(a_cellPosition.x, a_cellPosition.y) + uvOffset; #else v_Uv = a_texCoord; #endif vec4 localPosition = vec4(a_position, 1.0); vec4 cameraPosition; vec3 rotatedOffset; vec3 viewPos; v_Color = a_color; #ifdef WX_BILLBOARDMODE_Y rotatedOffset.x = a_offset.x * cos(a_angle) - a_offset.y * sin(a_angle); rotatedOffset.z = a_offset.x * sin(a_angle) + a_offset.y * cos(a_angle); rotatedOffset.y = 0.0; vec3 yaxis = a_position - vec3(u_viewInverse[3][0], u_viewInverse[3][1], u_viewInverse[3][2]); yaxis.y = 0.0; vec3 alignedOffset = rotate(yaxis, rotatedOffset); cameraPosition = u_view * u_world * vec4(localPosition.xyz + alignedOffset.xyz, 1.0); viewPos = cameraPosition.xyz; #elif defined(WX_BILLBOARDMODE_STRETCHED) rotatedOffset.x = a_offset.x * cos(a_angle) - a_offset.y * sin(a_angle); rotatedOffset.y = a_offset.x * sin(a_angle) + a_offset.y * cos(a_angle); rotatedOffset.z = 0.0; vec3 toCamera = a_position - vec3(u_viewInverse[3][0], u_viewInverse[3][1], u_viewInverse[3][2]); vec3 alignedOffset = rotateAlign(toCamera, rotatedOffset); cameraPosition = u_view * u_world * vec4(localPosition.xyz + alignedOffset.xyz, 1.0); viewPos = cameraPosition.xyz; #elif defined(WX_BILLBOARD) vec3 scale = extractScale(u_world); rotatedOffset.x = (a_offset.x * cos(a_angle) - a_offset.y * sin(a_angle)) * scale.x; rotatedOffset.y = (a_offset.x * sin(a_angle) + a_offset.y * cos(a_angle)) * scale.y; rotatedOffset.z = 0.0; cameraPosition = u_view * u_world * localPosition + vec4(rotatedOffset, 1.0); viewPos = cameraPosition.xyz; #elif defined(WX_RENDERMESH) rotatedOffset.x = a_offset.x * cos(a_angle) - a_offset.z * sin(a_angle); rotatedOffset.z = a_offset.x * sin(a_angle) + a_offset.z * cos(a_angle); rotatedOffset.y = a_offset.y; cameraPosition = u_view * u_world * vec4(localPosition.xyz + rotatedOffset.xyz, 1.0); viewPos = cameraPosition.xyz; #else rotatedOffset.x = a_offset.x * cos(a_angle) - a_offset.y * sin(a_angle); rotatedOffset.y = a_offset.x * sin(a_angle) + a_offset.y * cos(a_angle); rotatedOffset.z = 0.0; cameraPosition = u_view * u_world * vec4(localPosition.xyz + rotatedOffset.xyz, 1.0); viewPos = cameraPosition.xyz; #endif #ifdef WX_USE_RAMPCOLORMAP v_rampData = a_rampPos; #endif gl_Position = u_projection * vec4(viewPos, 1.0); }`, `#version 100 precision mediump float; precision highp int; varying highp vec2 v_Uv; varying highp vec4 v_Color; #ifdef WX_USE_RAMPCOLORMAP varying highp vec4 v_rampData; uniform sampler2D u_rampColorMap; #endif uniform highp vec4 u_baseColorFactor; #ifdef WX_USE_BASECOLORMAP uniform sampler2D u_baseColorMap; #endif void main() { vec4 baseColor; #ifdef WX_USE_BASECOLORMAP vec4 textureColor = texture2D(u_baseColorMap, v_Uv) * u_baseColorFactor; baseColor = textureColor * v_Color; #else baseColor = v_Color; #endif #ifdef WX_USE_RAMPCOLORMAP float alpha=baseColor.a; float remappedIndex=clamp((alpha-v_rampData.x)/v_rampData.y,0.0,1.0); vec4 rampColor = texture2D(u_rampColorMap,vec2(1.0-remappedIndex,0.)); baseColor.rgb *= rampColor.rgb; baseColor.a=clamp((alpha*rampColor.a-v_rampData.z)/v_rampData.w,0.0,1.0); #endif #ifdef WX_PP_ACTIVE // removeGammaCorrection gl_FragData[0] = vec4(pow(baseColor.rgb, vec3(2.2)), baseColor.a); #else gl_FragData[0] = baseColor; #endif gl_FragData[0] = baseColor; } `], })); /** * BillBoard渲染模式。 */ export const enum BillBoardMode { BILLBOARDMODE_DEFAULT = 0, BILLBOARDMODE_Y = 1, BILLBOARDMODE_STRETCHED = 2 } export default class BasicParticle extends xrFrameSystem.Component { public readonly schema: xrFrameSystem.IComponentSchema = ParticleSchema; protected _data: IParticleData; // 当前粒子系统所在场景 protected particleScene: xrFrameSystem.Scene; // 当前粒子系统的元素 protected particleEl: xrFrameSystem.Element; // 现有粒子对象池 protected _instances: ParticleInstance[] = new Array(); // 粒子对象池 protected _stockInstances: ParticleInstance[] = new Array(); // 粒子总容量 protected _capacity: number = 1; // 粒子延时启动的时间 protected _delay: number = 0; // 粒子生命周期的更新速率 protected _updateSpeed: number = 0.01; // 粒子系统的生命周期 protected _stopDuration: number = 0; // 粒子发射速率 protected _emitRate: number = 10; // 重力大小 protected _gravity: number = 0; protected _preWarmCycles: number = 0; protected _preWarmStepOffset: number = 5; protected _particleEmitterType: string; protected _particleEmitter: BasicShapeEmitter = null; protected _particleEmitterProperties; protected particleStride: number; protected particleVertexSize: number; protected byteStride: number; protected ParticleAttributes; protected _burstCount: number = 0; protected _burstTime: number = 0; // -1 = Infinity protected _burstCycle: number = -1; // -1 = invalid protected _burstInterval: number = 1; protected _burstCountTime: number = 0; protected _burstCountCycle: number = 0; protected _burstCountInterval: number = 0; protected _minLifeTime: number = 1; protected _maxLifeTime: number = 1; protected _minScaleX: number = 1; protected _maxScaleX: number = 1; protected _minScaleY: number = 1; protected _maxScaleY: number = 1; protected _minSize: number = 0.3; protected _maxSize: number = 0.3; protected _minSpeed: number = 1; protected _maxSpeed: number = 1; protected _particleLengthScale: number = 1; // the left edge of color at the begin of particle's life cycle protected _startColor: number[] = [1, 1, 1, 1]; // the right edge of color at the begin of particle's life cycle protected _startColor2: number[] = null; // the color at the end of particle's life cycle protected _endColor: number[] = null; protected _sizeGradients; protected _alphaGradients; protected _colorRemapGradients; protected _speedScaleGradients; protected _limitSpeedGradients; protected _speedDampenFactor: number; protected _dragGradients; protected _useSpriteSheet: boolean = false; protected _startSpriteCellIndex: number = 0; protected _endSpriteCellIndex: number = 0; protected _useRandomSpriteCellIndex: boolean = false; protected _useSpriteCellLoop: boolean = true; protected _spriteChangeSpeed: number = 1; protected _spriteFrameInfo: xrFrameSystem.Vector4[]; protected _spriteNameToCellIndex: Map; protected _textureData: xrFrameSystem.Texture = null; protected _atlasObj: xrFrameSystem.Atlas = null; protected _atlasTexture: xrFrameSystem.Texture; protected _mesh: xrFrameSystem.MeshRendererComponent; protected _sourceMaterial: xrFrameSystem.Material; protected _material: xrFrameSystem.Material; protected _trs: xrFrameSystem.Transform; protected _renderMesh: Geometry = null; protected _vertexCount: number; protected _vertexData: Float32Array; protected _indexData: UInt32Array; protected _eachIndexSize: number; protected _indexSize: number = 0; protected _vertexSize: number = 0; // 默认,粒子使用billboard, 始终面向相机 protected _useBillboard: boolean = true; protected _useRenderMesh: boolean = false; protected _billboardMode: number = BillBoardMode.BILLBOARDMODE_DEFAULT; protected _useRampGradients: boolean = false; protected _rampGradients; protected _rampGradientsTexture: xrFrameSystem.Texture; protected _colorGradients; protected _vertexLayoutDirty: boolean = false; protected _startAngle: number = 0; protected _startAngle2: number = 0; protected _minAngularSpeed: number = 0; protected _maxAngularSpeed: number = 0; protected _subEmitters: any[] = null; protected _emitterPosition: xrFrameSystem.Vector3 = xrFrameSystem.Vector3.createFromNumber(0, 0, 0); get material() { return this._material; } get useBillboard() { return this._useBillboard; } set useBillboard(value) { if (this.useBillboard != value) { this._useBillboard = value; this._vertexLayoutDirty = true; } } get useRampGradients() { return this._useRampGradients; } set useRampGradients(value) { if (this._useRampGradients != value) { this._useRampGradients = value; this._vertexLayoutDirty = true; } } get billboardMode() { return this._billboardMode; } set billboardMode(value) { if (this._billboardMode != value) { this._billboardMode = value; this._vertexLayoutDirty = true; } } get useSpriteSheet() { return this._useSpriteSheet; } set useSpriteSheet(value) { if (this._useSpriteSheet != value) { this._useSpriteSheet = value; this._vertexLayoutDirty = true; } } get useRandomSpriteCellIndex() { return this._useRandomSpriteCellIndex; } get useSpriteCellLoop() { return this._useSpriteCellLoop; } get spriteChangeSpeed() { return this._spriteChangeSpeed; } get emitterPosition() { return this._emitterPosition; } set emitterPosition(value) { this._emitterPosition = value; } /** * 设置粒子的顶点属性数据 */ protected _parseAttribute() { this.ParticleAttributes = []; this.ParticleAttributes.push( { name: "a_position", format: xrFrameSystem.EVertexFormat.FLOAT3, offset: 0, usage: xrFrameSystem.EVertexLayoutUsage.POSITION }, { name: "a_texCoord", format: xrFrameSystem.EVertexFormat.FLOAT2, offset: 12, usage: xrFrameSystem.EVertexLayoutUsage.UV0 }, { name: "a_offset", format: xrFrameSystem.EVertexFormat.FLOAT3, offset: 20, // 自定义用途此处可指定为xrFrameSystem.EVertexLayoutUsage.CUSTOM usage: xrFrameSystem.EVertexLayoutUsage.CUSTOM }, { name: "a_color", format: xrFrameSystem.EVertexFormat.FLOAT4, offset: 32, usage: xrFrameSystem.EVertexLayoutUsage.COLOR }, { name: "a_angle", format: xrFrameSystem.EVertexFormat.FLOAT, offset: 48, usage: xrFrameSystem.EVertexLayoutUsage.CUSTOM } ); var count = 52; // 根据是否使用billboard,动态增添属性 if (this._useBillboard && this._billboardMode == BillBoardMode.BILLBOARDMODE_STRETCHED) { this.ParticleAttributes.push({ name: "a_direction", format: xrFrameSystem.EVertexFormat.FLOAT3, offset: count, usage: xrFrameSystem.EVertexLayoutUsage.CUSTOM }) count += 3 * 4; this.ParticleAttributes.push({ name: "a_lengthScale", format: xrFrameSystem.EVertexFormat.FLOAT, offset: count, usage: xrFrameSystem.EVertexLayoutUsage.CUSTOM }) count += 4; } if (this._useRampGradients) { this.ParticleAttributes.push({ name: "a_rampPos", format: xrFrameSystem.EVertexFormat.FLOAT4, offset: count, usage: xrFrameSystem.EVertexLayoutUsage.CUSTOM }) count += 4 * 4; } if (this.useSpriteSheet) { this.ParticleAttributes.push({ name: "a_cellPosition", format: xrFrameSystem.EVertexFormat.FLOAT4, offset: count, usage: xrFrameSystem.EVertexLayoutUsage.CUSTOM }) count += 4 * 4; } // 每一个粒子的顶点属性偏移量 this.particleStride = count; // 每一个属性大小背后的字节偏移量 this.byteStride = 4; // 每一个粒子使用的顶点元素偏移量 this.particleVertexSize = this.particleStride / this.byteStride; } /** * 设置粒子系统的内置粒子effect。 */ protected createMaterial() { let newMaterial = null; newMaterial = new xrFrameSystem.Material(this.particleScene); // 此处初始化指定的custom-particle-effect,因为前面有注册过对应名字的effect newMaterial.initByEffect(this.particleScene.assets.getAsset("effect", 'custom-particle-effect')); return newMaterial; } // 注册 Geometry 信息 public _registerGeometry() { // 构建顶点属性布局 const vl = this.particleScene.createVertexLayout({ attributes: this.ParticleAttributes, stride: this.particleStride }); const vb = new Float32Array(this._vertexData); const ib = new Uint16Array(this._indexData); this._geometry = this.particleScene.createGeometry(vl, vb, ib); // 指定为子网格0号 this._geometry.addSubMesh(ib.length, 0, 0); } /** * 创建一个点发射器。 * @param {Vector3} direction1 粒子运动方向左区间 * @param {Vector3} direction2 粒子运动方向右区间 * @return {PointShapeEmitter} 点发射器 */ public createPointEmitter(direction1: Vector3, direction2: Vector3) { var particleEmitter = new PointShapeEmitter(); particleEmitter.direction = direction1; particleEmitter.direction2 = direction2; this._particleEmitter = particleEmitter; this._particleEmitterType = "PointShape"; return particleEmitter; }; /** * 创建一个箱形发射器。 * @param {Vector3} direction1 粒子运动方向左区间 * @param {Vector3} direction2 粒子运动方向右区间 * @param {Vector3} minEmitBox 粒子生成位置最小允许坐标 * @param {Vector3} maxEmitBox 粒子生成位置最大允许坐标 * @return {BoxShapeEmitter} 箱形发射器 */ public createBoxEmitter(direction1: Vector3, direction2: Vector3, minEmitBox: Vector3, maxEmitBox: Vector3) { var particleEmitter = new BoxShapeEmitter(); particleEmitter.direction = direction1; particleEmitter.direction2 = direction2; particleEmitter.minEmitBox = minEmitBox; particleEmitter.maxEmitBox = maxEmitBox; this._particleEmitter = particleEmitter; this._particleEmitterType = "BoxShape"; return particleEmitter; }; /** * 创建一个球形发射器。 * @param {number} radius 球形半径 * @param {number} radiusRange 球形区域内的覆盖范围[0-1] * @param {number} arc 粒子在球形内生成的角度区间[0-360] * @param {number} randomizeDirection 粒子运动方向偏离程度[0-1] * @return {SphereShapeEmitter} 球形发射器 */ public createSphereEmitter(radius: number, radiusRange: number, arc: number, randomizeDirection: number) { var particleEmitter = new SphereShapeEmitter(); particleEmitter.radius = radius; particleEmitter.radiusRange = radiusRange; particleEmitter.arc = arc; particleEmitter.randomizeDirection = randomizeDirection; this._particleEmitter = particleEmitter; this._particleEmitterType = "SphereShape"; return particleEmitter; }; protected _parseProperties(data: IParticleData) { var useNoise: boolean = false; var useAtlasFrame: boolean = false; var atlasFrameArray: string[]; for (var k in data) { var v = data[k] switch (k) { case 'capacity': this._capacity = v; break; case 'renderMode': switch (v) { case 'off': this.useBillboard = false; break; case 'default': this.useBillboard = true; this.billboardMode = BillBoardMode.BILLBOARDMODE_DEFAULT; break; case 'y': this.useBillboard = true; this.billboardMode = BillBoardMode.BILLBOARDMODE_Y; break; case 'stretched': this.useBillboard = true; this.billboardMode = BillBoardMode.BILLBOARDMODE_STRETCHED; break; case 'mesh': this.useBillboard = false; this._useRenderMesh = true; break; default: break; } break; case 'mesh': if (v == undefined) break; this._renderMesh = v; break; case 'renderModel': if (v == undefined) break; this.processRenderModel(v); break; case 'delay': this._delay = v; break; case 'burstCount': this._burstCount = v; break; case 'burstTime': this._burstTime = v; break; case 'burstCycle': this._burstCycle = v; break; case 'burstInterval': this._burstInterval = v; break; case 'stopDuration': this._stopDuration = v; break; case 'updateSpeed': this._updateSpeed = v; break; case 'emitRate': this._emitRate = v; break; case 'gravity': this._gravity = v; break; case 'prewarmCycles': this._preWarmCycles = v; break; case 'particleLengthScale': this._particleLengthScale = v; break; case "speedDampen": this._speedDampenFactor = v; break; case "texture": if (v == undefined) break; if (typeof v == 'string') { this._textureData = this.particleScene.assets.getAsset('texture', v); } else { this._textureData = v; } break; case "useNoise": if (v == undefined) break; useNoise = v; break; case 'atlas': if (v == undefined) break; this._useSpriteSheet = true; this._startSpriteCellIndex = 0; var count = 0; this._spriteFrameInfo = new Array(); this._spriteNameToCellIndex = new Map(); var value: Atlas = v; this._atlasObj = value; this._atlasTexture = value.texture; for (var k in value.frames) { this._spriteNameToCellIndex.set(k, count); this._spriteFrameInfo.push(value.getUVST(k)); count++; } this._endSpriteCellIndex = count; break; case 'atlasFrames': useAtlasFrame = true; atlasFrameArray = v; break; case "atlasLoop": this._useSpriteCellLoop = v; break; case "atlasSpeed": this._spriteChangeSpeed = v; break; case 'atlasRandom': this._useRandomSpriteCellIndex = v; break; case "angle": this._startAngle = this._startAngle2 = v[0]; if (v[1]) this._startAngle2 = v[1]; break; case 'lifeTime': this._minLifeTime = this._maxLifeTime = v[0]; if (v[1]) this._maxLifeTime = v[1]; break; case 'scaleX': this._minScaleX = this._maxScaleX = v[0]; if (v[1]) this._maxScaleX = v[1]; break; case 'scaleY': this._minScaleY = this._maxScaleY = v[0]; if (v[1]) this._maxScaleY = v[1]; break; case 'size': this._minSize = this._maxSize = v[0]; if (v[1]) this._maxSize = v[1]; break; case 'speed': this._minSpeed = this._maxSpeed = v[0]; if (v[1]) this._maxSpeed = v[1]; break; case 'angularSpeed': this._minAngularSpeed = this._maxAngularSpeed = v[0]; if (v[1]) this._maxAngularSpeed = v[1]; break; case 'emitterType': this._particleEmitterType = v; break; case 'emitterProps': this._particleEmitterProperties = v; break; case 'emitterPosition': this._emitterPosition.x = v[0]; this._emitterPosition.y = v[1]; this._emitterPosition.z = v[2]; break; case 'startColor': for (var i = 0; i < 4; i++) { this._startColor[i] = v[i]; } break; case 'sizeChange': v.forEach(([key, value]) => { this.addSizeGradient(parseFloat(key), parseFloat(value)); }) break; case 'speedChange': v.forEach(([key, value]) => { this.addSpeedScaleGradient(parseFloat(key), parseFloat(value)); }) break; case 'colorChange': v.forEach(([key, value]) => { var valueArray = value.split(' ') this.addColorGradient(parseFloat(key), xrFrameSystem.Vector4.createFromNumber( parseFloat(valueArray[0]), parseFloat(valueArray[1]), parseFloat(valueArray[2]), parseFloat(valueArray[3]))); }) break; case 'startColor2': this._startColor2 = this._startColor2 || [0, 0, 0, 0]; for (var i = 0; i < 4; i++) { this._startColor2[i] = v[i]; } break; case 'endColor': this._endColor = this._endColor || [0, 0, 0, 0]; for (var i = 0; i < 4; i++) { this._endColor[i] = v[i]; } break; } } if (useAtlasFrame) { if (!this._atlasObj) { console.log("未取得图集信息"); } else { var count = 0; this._startSpriteCellIndex = 0; this._spriteFrameInfo = new Array(); this._spriteNameToCellIndex = new Map(); for (var name in this._atlasObj.frames) { atlasFrameArray.forEach((v, k) => { if (name == v) { this._spriteNameToCellIndex.set(name, count); this._spriteFrameInfo.push(this._atlasObj.getUVST(name)); count++; } }) } this._endSpriteCellIndex = count; } } } protected processRenderModel(value){ for(const mesh of value.model.meshes) { this._renderMesh = mesh.subMeshes[0].geometry } this.useBillboard = false; this._useRenderMesh = true; } protected _chooseEmitterProcess() { if (this._particleEmitter != null) { return; } //Add cache to optimize on onUpdate cycle else { switch (this._particleEmitterType) { case "SphereShape": this._particleEmitter = new SphereShapeEmitter(); break; case "BoxShape": this._particleEmitter = new BoxShapeEmitter(); break; default: this._particleEmitter = new PointShapeEmitter(); break; } } this._particleEmitter.setProperty(this._particleEmitterProperties); } protected _createVertexBuffers() { if (this._useRenderMesh) { const layout = this._renderMesh.getVertexLayout(); const stride = layout.stride; const vertexBuffer = this._renderMesh._getRawVertexBuffer(); this._vertexCount = vertexBuffer .byteLength / stride; this._vertexSize = this._capacity * this._vertexCount; this._vertexData = new Float32Array(this.particleVertexSize * this._vertexSize); } else { //*4 是因为每一个粒子面片由上下左右四个点构成 this._vertexSize = this._capacity * 4; // 这里构建顶点数据数组的长度,为顶点属性的元素数乘以顶点总数 this._vertexData = new Float32Array(this.particleVertexSize * this._vertexSize); } } protected _createIndexBuffer() { if (this._useRenderMesh) { const indexBuffer = this._renderMesh._getRawIndiceBuffer(); this._eachIndexSize = indexBuffer.byteLength / 2; this._indexSize = this._capacity * this._eachIndexSize; this._indexData = new Uint16Array(this._indexSize); for (let i = 0; i < this._capacity; i++) { var firstIndex = i * this._eachIndexSize; for (let j = 0; j < this._eachIndexSize; j++) { this._indexData[firstIndex + j] = indexBuffer[j] + i * this._vertexCount; } } }else{ // 粒子索引数量 this._indexSize = this._capacity * 6; this._indexData = new Uint16Array(this._indexSize); for (let i = 0; i < this._capacity; i++) { this._indexData[i * 6] = i * 4; this._indexData[i * 6 + 1] = i * 4 + 1; this._indexData[i * 6 + 2] = i * 4 + 2; this._indexData[i * 6 + 3] = i * 4; this._indexData[i * 6 + 4] = i * 4 + 2; this._indexData[i * 6 + 5] = i * 4 + 3; } } } // 创建材质与mesh网络 protected _createMesh() { this._material = this.createMaterial() this._mesh = this.particleEl.getComponent(xrFrameSystem.Mesh); if (this._mesh == undefined) { this._mesh = this.particleEl.addComponent(xrFrameSystem.Mesh, { geometry: this._geometry, material: this._material, meshCount: this._renderMesh ? this._renderMesh.getSubMeshCount() : 1, }); } } //往顶点数据数组里设置值,分别为四个点设置偏移与uv值 protected _appendParticleVertices(offset, instance: ParticleInstance = null) { if (this._useRenderMesh) { const vertexBufferView = this._renderMesh._getRawVertexBuffer(); const layout = this._renderMesh.vertexLayout; const stride = layout.stride; for (var i = 0; i < this._vertexCount; i++) { const aUV = layout.getConfigByName("a_texCoord"); const aPosition = layout.getConfigByName("a_position"); const realStride = stride / 4; const posBase = i * realStride + aPosition.offset / 4; const uvBase = i * realStride + aUV.offset / 4; this._appendParticleVertex(offset++, instance, vertexBufferView[posBase + 0], vertexBufferView[posBase + 1], vertexBufferView[posBase + 2], vertexBufferView[uvBase + 0], vertexBufferView[uvBase + 1]); } }else{ this._appendParticleVertex(offset++, instance, -1, -1, 0, 0, 1); this._appendParticleVertex(offset++, instance, 1, -1, 0, 1, 1); this._appendParticleVertex(offset++, instance, 1, 1, 0, 1, 0); this._appendParticleVertex(offset++, instance, -1, 1, 0, 0, 0); } }; protected _appendParticleVertex(index, instance: ParticleInstance, offsetX, offsetY, offsetZ, u, v) { var offset = index * this.particleVertexSize; this._vertexData[offset++] = instance.position.x; this._vertexData[offset++] = instance.position.y; this._vertexData[offset++] = instance.position.z; this._vertexData[offset++] = u; this._vertexData[offset++] = v; //Offset this._vertexData[offset++] = offsetX * instance.size * instance.scale.x; this._vertexData[offset++] = offsetY * instance.size * instance.scale.y; this._vertexData[offset++] = offsetZ * instance.size; //Color this._vertexData[offset++] = instance.color.x; this._vertexData[offset++] = instance.color.y; this._vertexData[offset++] = instance.color.z; this._vertexData[offset++] = instance.color.w; //Rotation this._vertexData[offset++] = instance.angle * Math.PI / 180; if (this._useBillboard && this._billboardMode == BillBoardMode.BILLBOARDMODE_STRETCHED) { this._vertexData[offset++] = instance.direction.x; this._vertexData[offset++] = instance.direction.y; this._vertexData[offset++] = instance.direction.z; this._vertexData[offset++] = this._particleLengthScale; } //RampColor if (this._useRampGradients) { this._vertexData[offset++] = instance.rampPos.x; this._vertexData[offset++] = instance.rampPos.y; this._vertexData[offset++] = 0; this._vertexData[offset++] = 1; } //SpriteSheet if (this.useSpriteSheet) { var cellIndex = ~~instance.cellIndex; this._vertexData[offset++] = this._spriteFrameInfo[cellIndex].z; this._vertexData[offset++] = this._spriteFrameInfo[cellIndex].w; this._vertexData[offset++] = this._spriteFrameInfo[cellIndex].x; this._vertexData[offset++] = this._spriteFrameInfo[cellIndex].y; } }; // 更新材质上挂载的effect的属性信息 protected _setMeshData(material: Material, uniforms?: [string, string][], states?: [string, string][] ) { if (!uniforms && !states) { this._material = material; } else if (this._sourceMaterial !== material || this._sourceMaterial === this._material) { this._material = material.clone(); } if (this._textureData) { this._material.setTexture('u_baseColorMap', this._textureData); } if (this._atlasTexture) { this._material.setTexture('u_baseColorMap', this._atlasTexture); } if (this._useRampGradients) { if (this._rampGradientsTexture) this._material.setTexture('u_rampColorMap', this._rampGradientsTexture); } if (this.useSpriteSheet) { this._material.setMacro("WX_SPRITESHEET", true); } else { this._material.setMacro("WX_SPRITESHEET", false); } if (this._useRenderMesh) { this._material.setMacro("WX_RENDERMESH", true); } else { this._material.setMacro("WX_RENDERMESH", false); } if (this._useBillboard) { this._material.setMacros({ "WX_BILLBOARD": true }); switch (this._billboardMode) { case BillBoardMode.BILLBOARDMODE_Y: this._material.setMacros({ "WX_BILLBOARDMODE_Y": true }); break; case BillBoardMode.BILLBOARDMODE_STRETCHED: this._material.setMacros({ "WX_BILLBOARDMODE_STRETCHED": true }); break; case BillBoardMode.BILLBOARDMODE_DEFAULT: this._material.setMacros({ "WX_BILLBOARDMODE_DEFAULT": true }); break; default: break; } } else { this._material.setMacro("WX_BILLBOARD", false); this._material.setMacro("WX_BILLBOARDMODE_Y", false); this._material.setMacro("WX_BILLBOARDMODE_STRETCHED", false); this._material.setMacro("WX_BILLBOARDMODE_DEFAULT", false); } if (uniforms) { this._material._parseUniforms(uniforms); } if (states) { this._material._parseRenderStates(states); } this._sourceMaterial = material; const vb = new Float32Array(this._vertexData); const ib = new Uint16Array(this._indexData); // 更新顶点与索引信息 this._mesh.geometry.uploadVertexBuffer(0, vb); this._mesh.geometry.uploadIndexBuffer(0, ib); } /** * 添加粒子运动过程中的颜色变化规则。 * @param {number} gradient 指定所处粒子生命周期的阶段 * @param {Vector4} color1 指定粒子颜色的左区间 * @param {Vector4} color2 指定粒子颜色的右区间 */ public addColorGradient(gradient: number, color1: Vector4, color2?: Vector4) { if (!this._colorGradients) { this._colorGradients = []; } var colorGradient = new ColorGradient(gradient, color1, color2); this._colorGradients.push(colorGradient); this._colorGradients.sort(function (a, b) { if (a.gradient < b.gradient) { return -1; } else if (a.gradient > b.gradient) { return 1; } return 0; }); }; /** * 添加粒子运动过程中的速度变化规则。 * @param {number} gradient 指定所处粒子生命周期的阶段 * @param {Vector4} speed 指定粒子速度的左区间 * @param {Vector4} speed2 指定粒子速度的右区间 */ public addSpeedScaleGradient(gradient: number, speed: number, speed2?: number) { if (!this._speedScaleGradients) { this._speedScaleGradients = []; } this.addFactorGradient(this._speedScaleGradients, gradient, speed, speed2); } /** * 添加粒子运动过程中的速度限制规则。 * @param {number} gradient 指定所处粒子生命周期的阶段 * @param {number} limitSpeed 指定粒子限制速度的左区间 * @param {number} limitSpeed2 指定粒子限制速度的右区间 */ public addLimitSpeedGradient(gradient: number, limitSpeed: number, limitSpeed2?: number) { if (!this._limitSpeedGradients) { this._limitSpeedGradients = []; } this.addFactorGradient(this._limitSpeedGradients, gradient, limitSpeed, limitSpeed2); } /** * 添加粒子运动过程中的阻力规则。 * @param {number} gradient 指定所处粒子生命周期的阶段 * @param {number} speed 指定粒子受到的阻力大小的左区间[0-1] * @param {number} speed2 指定粒子受到的阻力大小的右区间[0-1] */ public addDragGradient(gradient: number, drag: number, drag2?: number) { if (!this._dragGradients) { this._dragGradients = []; } this.addFactorGradient(this._dragGradients, gradient, drag, drag2); } /** * 添加粒子运动过程中的透明度变化规则。 * @param {number} gradient 指定所处粒子生命周期的阶段 * @param {number} alpha 指定粒子颜色透明度的左区间[0-1] * @param {number} alpha2 指定粒子颜色透明度的右区间[0-1] */ public addAlphaGradient(gradient: number, alpha: number, alpha2?: number) { if (!this._alphaGradients) { this._alphaGradients = []; } this.addFactorGradient(this._alphaGradients, gradient, alpha, alpha2); } /** * 添加粒子运动过程中的尺寸变化规则。 * @param {number} gradient 指定所处粒子生命周期的阶段 * @param {number} size 指定粒子尺寸的左区间 * @param {number} size2 指定粒子尺寸的右区间 */ public addSizeGradient(gradient: number, size: number, size2?: number) { if (!this._sizeGradients) { this._sizeGradients = []; } this.addFactorGradient(this._sizeGradients, gradient, size, size2); } /** * 添加粒子运动过程中的透明度变化范围。 * @param {number} gradient 指定所处粒子生命周期的阶段 * @param {number} min 指定粒子透明度值的左区间 * @param {number} max 指定粒子透明度值的右区间 */ public addColorRemapGradient(gradient: number, min: number, max?: number) { if (!this._colorRemapGradients) { this._colorRemapGradients = []; } this.addFactorGradient(this._colorRemapGradients, gradient, min, max); } /** * 将存储不同时间段相关属性系数的数组按时间点从小到大进行排序。 * @param {Array} factorGradients 存储不同时间段相关属性系数的数组 * @param {number} gradient 一般代表粒子所处生命周期的阶段 * @param {number} factor 左区间值 * @param {number} factor2 右区间值 */ protected addFactorGradient(factorGradients, gradient, factor, factor2) { var newGradient = new FactorGradient(gradient, factor, factor2); factorGradients.push(newGradient); factorGradients.sort(function (a, b) { if (a.gradient < b.gradient) { return -1; } else if (a.gradient > b.gradient) { return 1; } return 0; }); } /** * 添加粒子运动过程中的根据透明度影响的颜色变化规则,将通过颜色变化图纹理进行采样。 * @param {number} gradient 指定粒子颜色变化图的具体位置,对应具体值应为(1-alpha) * @param {number} color 指定该位置的颜色 */ public addRampGradient(gradient, color) { if (!this._rampGradients) { this._rampGradients = []; } var rampGradient = new Color3Gradient(gradient, color); this._rampGradients.push(rampGradient); this._rampGradients.sort(function (a, b) { if (a.gradient < b.gradient) { return -1; } else if (a.gradient > b.gradient) { return 1; } return 0; }); this.createRampGradientTexture(); } /** * 根据颜色变化数组,生成对应的颜色变化纹理 */ protected createRampGradientTexture() { var data = new Uint8Array(256 * 4); var tmpColor: Vector3; var loop = (x: number) => { var ratio = x / 256; BasicGradientMethod.GetCurrentGradient(ratio, this._rampGradients, function (currentGradient, nextGradient, lerp) { tmpColor = currentGradient.color.lerp(nextGradient.color, lerp); data[x * 4] = tmpColor.x * 255; data[x * 4 + 1] = tmpColor.y * 255; data[x * 4 + 2] = tmpColor.z * 255; data[x * 4 + 3] = 255; }); } for (var x = 0; x < 256; x++) { loop(x); } this._rampGradientsTexture = this.particleScene.createTexture({ width: 256, height: 1, source: [data], pixelFormat: ETextureFormat.RGBA8 }) } /** * 根据数组进行插值 */ protected lerpNumberArrayToVector(vector, numberArray1, numberArray2, step, length = 4) { let result: number[] = new Array(length); for (var i = 0; i < length; i++) result[i] = numberArray1[i] + (numberArray2[i] - numberArray1[i]) * step; vector.setValue(...result); } }