ExplodeParticle.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. import * as THREE from "../../../../libs/three.js/build/three.module.js";
  2. import { vertexShader, fragmentShader } from './shader.js'
  3. import Tween from './Tween.js'
  4. import Particle from './Particle.js'
  5. import {util} from './Util.js'
  6. import { Shape } from './const.js'
  7. let particleTexture
  8. const getTexture = ()=>{
  9. if(!particleTexture){
  10. particleTexture = new THREE.TextureLoader().load( Potree.resourcePath+'/textures/explode.png')
  11. }
  12. return particleTexture
  13. }
  14. const defaults = {
  15. position: new THREE.Vector3(0, 0 , 4),
  16. positionShape: Shape.SPHERE,
  17. positionRange : new THREE.Vector3(1,1,1), //cube
  18. positionRadius: 3, //sphere
  19. velocityShape: Shape.SPHERE,
  20. velocity: new THREE.Vector3(0.3,0.3, 2), //cube
  21. velocityRange: new THREE.Vector3(1, 1, 2),
  22. speed : 1, //sphere
  23. speedRange : 10,
  24. size: 0.4,
  25. sizeRange: 2,
  26. //sizeTween: new Tween( [0, 0.05, 0.3, 0.45], [0, 1, 3, 0.1] ),
  27. sizeTween: new Tween( ),
  28. color : new THREE.Vector3(1.0, 1.0, 1.0),
  29. colorRange : new THREE.Vector3(0.0, 0.0, 0.0),
  30. colorTween : new Tween(),
  31. opacity : 1.0,
  32. opacityRange : 0.0 ,
  33. opacityTween: new Tween( [0, 0.05, 0.3, 0.45], [1, 1, 0.5, 0] ),
  34. blendMode: THREE.AdditiveBlending,
  35. acceleration : 0.1,
  36. accelerationRange : 0.5,
  37. angle : 0,
  38. angleRange : 0,
  39. angleVelocity : 0,
  40. angleVelocityRange : 0,
  41. angleAcceleration : 0,
  42. angleAccelerationRange : 0,
  43. particlesPerSecond: 10,
  44. particleDeathAge: 1,
  45. }
  46. class ExplodeParticle extends THREE.Points{
  47. constructor(params) {
  48. super()
  49. this.particles = []
  50. this.age = 0
  51. this.alive = true
  52. this.deathAge = 60
  53. this.loop = true
  54. viewer.on('pageVisible', (state)=>{
  55. if(state){//重新一个个放出粒子,否则会一股脑儿全部出来,因为同时大于粒子周期了一起重新生成出现。
  56. setTimeout(()=>{//会先update一次delta为pageUnvisile的时间才触发
  57. //归零
  58. console.log('归零')
  59. this.age = 0;
  60. this.geometry.dispose()
  61. this.createParticles()
  62. },1)
  63. }
  64. })
  65. this.blendMode = THREE.NormalBlending
  66. this.setParameters(params)
  67. this.createParticles()
  68. }
  69. setParameters(params) {
  70. for ( var key in defaults ){
  71. let value = params[key] != void 0 ? params[key] : defaults[ key ]
  72. if(key == 'position') this.position.copy(value)
  73. else if(value instanceof Array && value[0] instanceof Array ) this[ key ] = new Tween(...value)
  74. else this[ key ] = value;
  75. }
  76. //Object.assign(this, params)
  77. this.particles = []
  78. this.age = 0.0
  79. this.alive = true
  80. this.particleCount = this.particlesPerSecond * Math.min(this.particleDeathAge, this.deathAge)
  81. this.geometry = new THREE.BufferGeometry()
  82. this.material = new THREE.ShaderMaterial({
  83. uniforms: {
  84. u_sampler: { value: this.texture || getTexture() }
  85. },
  86. vertexShader,
  87. fragmentShader,
  88. transparent: true,
  89. alphaTest: 0.5,
  90. depthTest: false,
  91. blending: THREE.AdditiveBlending
  92. })
  93. }
  94. createParticles() {
  95. const count = this.particleCount
  96. const positionArray = new Float32Array(count * 3)
  97. const colorArray = new Float32Array(count * 3)
  98. const sizeArray = new Float32Array(count)
  99. const angleArray = new Float32Array(count)
  100. const opacityArray = new Float32Array(count)
  101. const visibleArray = new Float32Array(count)
  102. for(let i = 0; i < count; i++) {
  103. const particle = this.createParticle()
  104. positionArray[i*3] = particle.position.x
  105. positionArray[i*3+1] = particle.position.y
  106. positionArray[i*3+2] = particle.position.z
  107. colorArray[i*3] = particle.color.r
  108. colorArray[i*3+1] = particle.color.g
  109. colorArray[i*3+2] = particle.color.b
  110. sizeArray[i] = particle.size
  111. angleArray[i] = particle.angel
  112. opacityArray[i] = particle.opacity
  113. visibleArray[i] = particle.alive
  114. this.particles[i] = particle
  115. }
  116. this.geometry.setAttribute('position', new THREE.BufferAttribute(positionArray, 3))
  117. this.geometry.setAttribute('color', new THREE.BufferAttribute(colorArray, 3))
  118. this.geometry.setAttribute('angle', new THREE.BufferAttribute(angleArray, 1))
  119. this.geometry.setAttribute('size', new THREE.BufferAttribute(sizeArray, 1))
  120. this.geometry.setAttribute('visible', new THREE.BufferAttribute(visibleArray, 1))
  121. this.geometry.setAttribute('opacity', new THREE.BufferAttribute(opacityArray, 1))
  122. this.material.blending = this.blendMode
  123. if(this.blendMode != THREE.NormalBlending) {
  124. this.material.depthTest = false
  125. }
  126. }
  127. createParticle() {
  128. const particle = new Particle()
  129. particle.sizeTween = this.sizeTween
  130. particle.colorTween = this.colorTween
  131. particle.opacityTween = this.opacityTween
  132. particle.deathAge = this.particleDeathAge
  133. if(this.positionShape == Shape.CUBE) {
  134. particle.position = util.randomVector3(new THREE.Vector3, this.positionRange)
  135. }
  136. if(this.positionShape == Shape.SPHERE) {
  137. const z = 2 * Math.random() - 1
  138. const t = Math.PI * 2 * Math.random()
  139. const r = Math.sqrt(1 - z*z)
  140. const vec3 = new THREE.Vector3(r * Math.cos(t), r * Math.sin(t), z)
  141. particle.position = vec3.multiplyScalar(this.positionRadius)
  142. }
  143. if(this.velocityShape == Shape.CUBE) {
  144. particle.velocity = util.randomVector3(this.velocity, this.velocityRange)
  145. }
  146. if(this.velocityShape == Shape.SPHERE) {
  147. const direction = particle.position.clone()
  148. const speed = util.randomValue(this.speed, this.speedRange)
  149. particle.velocity = direction.normalize().multiplyScalar(speed)
  150. }
  151. particle.acceleration = util.randomValue(this.acceleration, this.accelerationRange)
  152. particle.angle = util.randomValue(this.angle, this.angleRange)
  153. particle.angleVelocity = util.randomValue(this.angleVelocity, this.angleVelocityRange)
  154. particle.angleAcceleration = util.randomValue(this.angleAcceleration, this.angleAccelerationRange)
  155. particle.size = util.randomValue(this.size, this.sizeRange)
  156. const color = util.randomVector3(this.color, this.colorRange)
  157. particle.color = new THREE.Color().setHSL(color.x, color.y, color.z)
  158. particle.opacity = util.randomValue(this.opacity, this.opacityRange)
  159. particle.age = 0
  160. return particle
  161. }
  162. update(dt) {
  163. dt *= 0.5
  164. const recycleIndices = []
  165. const positionArray = this.geometry.attributes.position.array
  166. const opacityArray = this.geometry.attributes.opacity.array
  167. const visibleArray = this.geometry.attributes.visible.array
  168. const colorArray = this.geometry.attributes.color.array
  169. const angleArray = this.geometry.attributes.angle.array
  170. const sizeArray = this.geometry.attributes.size.array
  171. for(let i = 0; i < this.particleCount; i++) {
  172. const particle = this.particles[i]
  173. if(particle.alive) {
  174. particle.update(dt)
  175. if(particle.age > this.particleDeathAge) {
  176. particle.alive = 0.0
  177. recycleIndices.push(i)
  178. }
  179. positionArray[i*3] = particle.position.x
  180. positionArray[i*3+1] = particle.position.y
  181. positionArray[i*3+2] = particle.position.z
  182. colorArray[i*3] = particle.color.r
  183. colorArray[i*3+1] = particle.color.g
  184. colorArray[i*3+2] = particle.color.b
  185. visibleArray[i] = particle.alive
  186. opacityArray[i] = particle.opacity
  187. angleArray[i] = particle.angle
  188. sizeArray[i] = particle.size
  189. }
  190. }
  191. this.geometry.attributes.size.needsUpdate = true
  192. this.geometry.attributes.color.needsUpdate = true
  193. this.geometry.attributes.angle.needsUpdate = true
  194. this.geometry.attributes.visible.needsUpdate = true
  195. this.geometry.attributes.opacity.needsUpdate = true
  196. this.geometry.attributes.position.needsUpdate = true
  197. if(!this.alive) return
  198. if(this.age < this.particleDeathAge) {
  199. let startIndex = Math.round(this.particlesPerSecond * (this.age + 0))
  200. let endIndex = Math.round(this.particlesPerSecond * (this.age + dt))
  201. if(endIndex > this.particleCount) {
  202. endIndex = this.particleCount
  203. }
  204. for(let i = startIndex; i < endIndex; i++) {
  205. this.particles[i].alive = 1.0
  206. }
  207. }
  208. for(let j = 0;j < recycleIndices.length; j++) {
  209. let i = recycleIndices[j]
  210. this.particles[i] = this.createParticle()
  211. this.particles[i].alive = 1.0
  212. positionArray[i*3] = this.particles[i].position.x
  213. positionArray[i*3+1] = this.particles[i].position.y
  214. positionArray[i*3+2] = this.particles[i].position.z
  215. }
  216. this.geometry.attributes.position.needsUpdate = true
  217. this.age += dt
  218. if(this.age > this.deathAge && !this.loop) {
  219. this.alive = false
  220. }
  221. }
  222. }
  223. export default ExplodeParticle