FireParticle.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. // import { vertexShader, fragmentShader } from './shaders'
  2. // import Tween from './tween'
  3. import * as THREE from "../../../../../libs/three.js/build/three.module.js";
  4. import { vertexShader, fragmentShader } from './shader.js'
  5. let ONE_SPRITE_ROW_LENGTH = 0.25 //对应着色器的0.25
  6. let texture
  7. const getTexture = ()=>{
  8. if(!texture){
  9. texture = new THREE.TextureLoader().load( Potree.resourcePath+'/textures/fire.png')
  10. }
  11. return texture
  12. }
  13. const boxGeo = new THREE.BoxBufferGeometry(1,1,1,1);
  14. const boxMat = new THREE.MeshBasicMaterial({wireframe:true, color:"#ffffff"})
  15. class FireParticle extends THREE.Points{
  16. constructor (prop) {
  17. super()
  18. for ( var key in prop ){
  19. this[key] = prop[key]
  20. }
  21. this.strength = this.strength || 1
  22. this.radius = prop.radius || 1;
  23. this.height = prop.height || 5;
  24. this.computeParams()
  25. this.geometry = this.createGeometry( this.radius, this.height, this.particleCount );
  26. if(this.color == void 0) this.color = 0xff3200
  27. this.createMaterial( ); //小蓝火:0x00338f
  28. //---?:
  29. this.velocity = new THREE.Vector3()
  30. this.acceleration = new THREE.Vector3()
  31. this.angle = 0
  32. this.angleVelocity = 0
  33. this.angleAcceleration = 0
  34. this.size = 16
  35. this.opacity = 1
  36. this.age = 0
  37. this.alive = 0
  38. this.sizeTween = null
  39. this.colorTween = null
  40. this.opacityTween = null
  41. this.setSize({viewport:viewer.mainViewport})
  42. this.setFov(viewer.fov)
  43. let setSize = (e)=>{
  44. if(e.viewport.name != "MainView")return
  45. this.setSize(e)
  46. }
  47. let setFov = (e)=>{
  48. this.setFov(e.fov)
  49. }
  50. viewer.addEventListener('resize',setSize)
  51. viewer.addEventListener('fov_changed',setFov)
  52. this.addEventListener('dispose',()=>{
  53. viewer.removeEventListener('resize',setSize)
  54. viewer.removeEventListener('fov_changed',setFov)
  55. })
  56. }
  57. computeParams(){
  58. let length = (this.curve ? this.curve.wholeLength : 0) + this.radius * 2 //加上首尾的半径
  59. const minSize = 0.3, maxSize = 3, minRadiusBound = 0.3, maxRadiusBound = 10;
  60. this.size = minSize + (maxSize - minSize) * THREE.Math.smoothstep(this.radius, minRadiusBound, maxRadiusBound);
  61. //console.log('fire material particle size:', size )
  62. this.particleCount = Math.ceil( length * Math.sqrt(this.strength * this.height ) * this.radius / (this.size * this.size) * 25 )
  63. //console.log('fire particleCount',this.particleCount)
  64. }
  65. getPointsForBound(){
  66. return this.boundPoints; //可以用于expand实时bound的点, 不含particle的size等边距
  67. }
  68. getBound(points){ // points为生成点(圆心)
  69. this.boundPoints = []
  70. let boundingBox = new THREE.Box3()
  71. let margin = this.size * 0.13 + 0.3;
  72. points.forEach(bottom=>{
  73. let top = bottom.clone()
  74. top.z += this.height
  75. boundingBox.expandByPoint(bottom);
  76. boundingBox.expandByPoint(top);
  77. this.boundPoints.push(bottom,top)
  78. })
  79. let xyExpand = this.radius+margin
  80. boundingBox.expandByVector(new THREE.Vector3(xyExpand,xyExpand,margin))
  81. this.boundingBox = boundingBox
  82. /* if(!this.debugBox){
  83. this.debugBox = new THREE.Mesh(boxGeo, boxMat)
  84. this.add(this.debugBox)
  85. }
  86. this.debugBox.scale.copy(boundingBox.getSize(new THREE.Vector3))
  87. this.debugBox.position.copy(boundingBox.getCenter(new THREE.Vector3)) */
  88. }
  89. createGeometry( radius, height, particleCount){
  90. let geometry = new THREE.BufferGeometry()
  91. let count , points
  92. if(this.positions.length>1){
  93. const spaceDis = 0.2;//间隔距离
  94. count = Math.ceil(this.curve.wholeLength / spaceDis) + 1
  95. //console.log('count', count)
  96. points = this.curve.getSpacedPoints( count ); //得到的数量会比count多一个
  97. count = points.length
  98. //得到的点不太均匀,两端容易点少。
  99. this.getBound(points)
  100. }else{
  101. this.getBound(this.positions)
  102. }
  103. var position = new Float32Array(particleCount * 3);
  104. var randam = new Float32Array(particleCount);
  105. var sprite = new Float32Array(particleCount);
  106. var centerHeight = new Float32Array(particleCount);
  107. for (var i = 0; i < particleCount; i++) {
  108. var center = new THREE.Vector3().copy(this.positions.length>1 ? points[Math.floor(i/particleCount * count)] : this.positions[0])
  109. centerHeight[i] = center.z
  110. if (i === 0) {
  111. // to avoid going out of Frustum
  112. position[i * 3 + 0] = center.x;
  113. position[i * 3 + 1] = center.y;
  114. position[i * 3 + 2] = center.z;
  115. }else{
  116. var r = Math.sqrt(Math.random()) * radius;
  117. var angle = Math.random() * 2 * Math.PI;
  118. position[i * 3 + 0] = center.x + Math.cos(angle) * r;
  119. position[i * 3 + 1] = center.y + Math.sin(angle) * r;
  120. position[i * 3 + 2] = center.z + (radius - r) / radius * height/2 + height/2; //不太明白这句为什么能达到height高度
  121. sprite[i] = 0.25 * (Math.random() * 4 | 0);
  122. randam[i] = Math.random();
  123. //center在底部
  124. }
  125. }
  126. geometry.setAttribute('centerHeight', new THREE.BufferAttribute(centerHeight, 1));
  127. geometry.setAttribute('position', new THREE.BufferAttribute(position, 3));
  128. geometry.setAttribute('randam', new THREE.BufferAttribute(randam, 1));
  129. geometry.setAttribute('sprite', new THREE.BufferAttribute(sprite, 1));
  130. return geometry;
  131. }
  132. updateGeometry(){
  133. this.computeParams()
  134. this.geometry.dispose()
  135. this.geometry = this.createGeometry( this.radius, this.height, this.particleCount )
  136. this.material.uniforms.size.value = this.size
  137. }
  138. createMaterial(){
  139. const material = new THREE.ShaderMaterial( {
  140. uniforms:{
  141. color: { type: "c", value: new THREE.Color(this.color) },
  142. size: { type: "f", value: this.size},
  143. u_sampler: { type: "t", value: getTexture() },
  144. time: { type: "f", value: 0.0 },
  145. heightOfNearPlane: { type: "f", value:0}, //相对far ,以确保画面缩放时点的大小也会缩放
  146. height :{ type: "f", value:this.height} ,
  147. },
  148. vertexShader,
  149. fragmentShader,
  150. blending: THREE.AdditiveBlending, //加法融合模式 glBlendFunc(GL_ONE, GL_ONE)
  151. depthTest: true,
  152. depthWrite: false,
  153. transparent: true
  154. } );
  155. this.material = material
  156. this.setPerspective(this.fov, this.screenHeight)
  157. }
  158. setSize(e){
  159. let viewport = e.viewport
  160. this.screenHeight = viewport.resolution.y
  161. this.setPerspective(this.fov, this.screenHeight)
  162. }
  163. setFov(fov){
  164. this.fov = fov
  165. this.setPerspective(this.fov, this.screenHeight)
  166. }
  167. setPerspective(fov, height){
  168. //this.uniforms.heightOfNearPlane.value = Math.abs(height / (2 * Math.tan(THREE.Math.degToRad(fov * 0.5))));
  169. let far = Math.abs(height / (2 * Math.tan(THREE.Math.degToRad(fov * 0.5))));
  170. this.material.uniforms.heightOfNearPlane.value = far
  171. }
  172. update(delta){
  173. if(!Potree.Utils.getObjVisiByReason(this,'force')){//被手动隐藏了
  174. return
  175. }
  176. if(!Potree.Utils.isInsideFrustum(this.boundingBox, viewer.scene.getActiveCamera())){
  177. Potree.Utils.updateVisible(this,'isInsideFrustum', false ) //不在视野范围
  178. //console.log('unvi')
  179. return
  180. }else{
  181. Potree.Utils.updateVisible(this,'isInsideFrustum', true )
  182. }
  183. delta *= 1//更改速度
  184. this.material.uniforms.time.value = (this.material.uniforms.time.value + delta) % 1;
  185. }
  186. dispose(){
  187. this.geometry.dispose();
  188. this.material.dispose();
  189. this.dispatchEvent('dispose')
  190. }
  191. }
  192. export default FireParticle