import * as THREE from "../../../../libs/three.js/build/three.module.js"; import { vertexShader, fragmentShader } from './shader.js' import Tween from './Tween.js' import Particle from './Particle.js' import {util} from './Util.js' import { Shape } from './const.js' let particleTexture const getTexture = ()=>{ if(!particleTexture){ particleTexture = new THREE.TextureLoader().load( Potree.resourcePath+'/textures/explode.png') } return particleTexture } const defaults = { position: new THREE.Vector3(0, 0 , 4), positionShape: Shape.SPHERE, positionRange : new THREE.Vector3(1,1,1), //cube positionRadius: 3, //sphere velocityShape: Shape.SPHERE, velocity: new THREE.Vector3(0.3,0.3, 2), //cube velocityRange: new THREE.Vector3(1, 1, 2), speed : 1, //sphere speedRange : 10, size: 0.4, sizeRange: 2, //sizeTween: new Tween( [0, 0.05, 0.3, 0.45], [0, 1, 3, 0.1] ), sizeTween: new Tween( ), color : new THREE.Vector3(1.0, 1.0, 1.0), colorRange : new THREE.Vector3(0.0, 0.0, 0.0), colorTween : new Tween(), opacity : 1.0, opacityRange : 0.0 , opacityTween: new Tween( [0, 0.05, 0.3, 0.45], [1, 1, 0.5, 0] ), blendMode: THREE.AdditiveBlending, acceleration : 0.1, accelerationRange : 0.5, angle : 0, angleRange : 0, angleVelocity : 0, angleVelocityRange : 0, angleAcceleration : 0, angleAccelerationRange : 0, particlesPerSecond: 10, particleDeathAge: 1, } class ExplodeParticle extends THREE.Points{ constructor(params) { super() this.particles = [] this.age = 0 this.alive = true this.deathAge = 60 this.loop = true viewer.on('pageVisible', (state)=>{ if(state){//重新一个个放出粒子,否则会一股脑儿全部出来,因为同时大于粒子周期了一起重新生成出现。 setTimeout(()=>{//会先update一次delta为pageUnvisile的时间才触发 //归零 console.log('归零') this.age = 0; this.geometry.dispose() this.createParticles() },1) } }) this.blendMode = THREE.NormalBlending this.setParameters(params) this.createParticles() } setParameters(params) { for ( var key in defaults ){ let value = params[key] != void 0 ? params[key] : defaults[ key ] if(key == 'position') this.position.copy(value) else if(value instanceof Array && value[0] instanceof Array ) this[ key ] = new Tween(...value) else this[ key ] = value; } //Object.assign(this, params) this.particles = [] this.age = 0.0 this.alive = true this.particleCount = this.particlesPerSecond * Math.min(this.particleDeathAge, this.deathAge) this.geometry = new THREE.BufferGeometry() this.material = new THREE.ShaderMaterial({ uniforms: { u_sampler: { value: this.texture || getTexture() } }, vertexShader, fragmentShader, transparent: true, alphaTest: 0.5, depthTest: false, blending: THREE.AdditiveBlending }) } createParticles() { const count = this.particleCount const positionArray = new Float32Array(count * 3) const colorArray = new Float32Array(count * 3) const sizeArray = new Float32Array(count) const angleArray = new Float32Array(count) const opacityArray = new Float32Array(count) const visibleArray = new Float32Array(count) for(let i = 0; i < count; i++) { const particle = this.createParticle() positionArray[i*3] = particle.position.x positionArray[i*3+1] = particle.position.y positionArray[i*3+2] = particle.position.z colorArray[i*3] = particle.color.r colorArray[i*3+1] = particle.color.g colorArray[i*3+2] = particle.color.b sizeArray[i] = particle.size angleArray[i] = particle.angel opacityArray[i] = particle.opacity visibleArray[i] = particle.alive this.particles[i] = particle } this.geometry.setAttribute('position', new THREE.BufferAttribute(positionArray, 3)) this.geometry.setAttribute('color', new THREE.BufferAttribute(colorArray, 3)) this.geometry.setAttribute('angle', new THREE.BufferAttribute(angleArray, 1)) this.geometry.setAttribute('size', new THREE.BufferAttribute(sizeArray, 1)) this.geometry.setAttribute('visible', new THREE.BufferAttribute(visibleArray, 1)) this.geometry.setAttribute('opacity', new THREE.BufferAttribute(opacityArray, 1)) this.material.blending = this.blendMode if(this.blendMode != THREE.NormalBlending) { this.material.depthTest = false } } createParticle() { const particle = new Particle() particle.sizeTween = this.sizeTween particle.colorTween = this.colorTween particle.opacityTween = this.opacityTween particle.deathAge = this.particleDeathAge if(this.positionShape == Shape.CUBE) { particle.position = util.randomVector3(new THREE.Vector3, this.positionRange) } if(this.positionShape == Shape.SPHERE) { const z = 2 * Math.random() - 1 const t = Math.PI * 2 * Math.random() const r = Math.sqrt(1 - z*z) const vec3 = new THREE.Vector3(r * Math.cos(t), r * Math.sin(t), z) particle.position = vec3.multiplyScalar(this.positionRadius) } if(this.velocityShape == Shape.CUBE) { particle.velocity = util.randomVector3(this.velocity, this.velocityRange) } if(this.velocityShape == Shape.SPHERE) { const direction = particle.position.clone() const speed = util.randomValue(this.speed, this.speedRange) particle.velocity = direction.normalize().multiplyScalar(speed) } particle.acceleration = util.randomValue(this.acceleration, this.accelerationRange) particle.angle = util.randomValue(this.angle, this.angleRange) particle.angleVelocity = util.randomValue(this.angleVelocity, this.angleVelocityRange) particle.angleAcceleration = util.randomValue(this.angleAcceleration, this.angleAccelerationRange) particle.size = util.randomValue(this.size, this.sizeRange) const color = util.randomVector3(this.color, this.colorRange) particle.color = new THREE.Color().setHSL(color.x, color.y, color.z) particle.opacity = util.randomValue(this.opacity, this.opacityRange) particle.age = 0 return particle } update(dt) { dt *= 0.5 const recycleIndices = [] const positionArray = this.geometry.attributes.position.array const opacityArray = this.geometry.attributes.opacity.array const visibleArray = this.geometry.attributes.visible.array const colorArray = this.geometry.attributes.color.array const angleArray = this.geometry.attributes.angle.array const sizeArray = this.geometry.attributes.size.array for(let i = 0; i < this.particleCount; i++) { const particle = this.particles[i] if(particle.alive) { particle.update(dt) if(particle.age > this.particleDeathAge) { particle.alive = 0.0 recycleIndices.push(i) } positionArray[i*3] = particle.position.x positionArray[i*3+1] = particle.position.y positionArray[i*3+2] = particle.position.z colorArray[i*3] = particle.color.r colorArray[i*3+1] = particle.color.g colorArray[i*3+2] = particle.color.b visibleArray[i] = particle.alive opacityArray[i] = particle.opacity angleArray[i] = particle.angle sizeArray[i] = particle.size } } this.geometry.attributes.size.needsUpdate = true this.geometry.attributes.color.needsUpdate = true this.geometry.attributes.angle.needsUpdate = true this.geometry.attributes.visible.needsUpdate = true this.geometry.attributes.opacity.needsUpdate = true this.geometry.attributes.position.needsUpdate = true if(!this.alive) return if(this.age < this.particleDeathAge) { let startIndex = Math.round(this.particlesPerSecond * (this.age + 0)) let endIndex = Math.round(this.particlesPerSecond * (this.age + dt)) if(endIndex > this.particleCount) { endIndex = this.particleCount } for(let i = startIndex; i < endIndex; i++) { this.particles[i].alive = 1.0 } } for(let j = 0;j < recycleIndices.length; j++) { let i = recycleIndices[j] this.particles[i] = this.createParticle() this.particles[i].alive = 1.0 positionArray[i*3] = this.particles[i].position.x positionArray[i*3+1] = this.particles[i].position.y positionArray[i*3+2] = this.particles[i].position.z } this.geometry.attributes.position.needsUpdate = true this.age += dt if(this.age > this.deathAge && !this.loop) { this.alive = false } } } export default ExplodeParticle