123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475 |
- 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 sphereGeo = new THREE.SphereBufferGeometry(1, 10,4);
- const sphereMat = new THREE.MeshBasicMaterial({wireframe:true, color:"#ffffff"})
- const defaults = {
- position: new THREE.Vector3(0,0,1),
- positionShape: Shape.SPHERE,
- positionRange: new THREE.Vector3(1,1,1),
- //cube
- radius: 1.3,
- //sphere
- velocityShape: Shape.SPHERE,
- velocity: new THREE.Vector3(0,0,2),
- //cube
- velocityRange: new THREE.Vector3(0,0,3),
-
- //sphere
- speed: 0.4,
- speedRange: 1,
- size: 0.4,
- sizeRange: 2,
- //sizeTween: new Tween( [0, 0.05, 0.3, 0.45], [0, 1, 3, 0.1] ),
- sizeTween: [[0, 0.04, 0.2, 1],[0.1, 1, 6, 8]] ,
- color: new THREE.Vector3(1.0,1.0,1.0),
- colorRange: new THREE.Vector3(0,0,0),
- colorTween: new Tween(),
- opacity: 1.0,
- opacityRange: 0.0,
- opacityTween: new Tween([0, 0.06, 0.3, 0.8, 1],[0, 1, 0.3, 0.05, 0]),
- blendMode: THREE.AdditiveBlending,
- acceleration: 0.5,
- accelerationRange: 0,
- angle: 0,
- angleRange: 0,
- angleVelocity: 0,
- angleVelocityRange: 0,
- angleAcceleration: 0,
- angleAccelerationRange: 0,
-
- strength:1,
-
- //particlesPerSecond: 8,
- particleDeathAge: 0.7 ,
- recycleTimes : 3 , //每个粒子在一次爆炸后循环次数,循环完毕进入particleSpaceTime,等待下一次爆炸.
- //爆炸时长: particleDeathAge * (recycleTimes+1)
- particleSpaceTime: 3, //间隔
-
-
- }
- class ExplodeParticle extends THREE.Points {
- constructor(params) {
- super()
-
- this.age = 0
- this.alive = true
- //this.deathAge = 60
- this.loop = true
- this.blendMode = THREE.NormalBlending
- this.setParameters(params)
- this.createParticles()
- this.frustumCulled = false//似乎是禁止相机裁剪,否则会在某些角度消失。但是会不会更耗性能呢?
- //------------------------------------
- this.setSize({viewport:viewer.mainViewport})
- this.setFov(viewer.fov)
-
- let setSize = (e)=>{
- if(e.viewport.name != "MainView")return
- this.setSize(e)
- }
- let setFov = (e)=>{
- this.setFov(e.fov)
- }
-
- /* let reStart = (e)=>{
- if(e.v){//重新一个个放出粒子,否则会一股脑儿全部出来,因为同时大于粒子周期了一起重新生成出现。
- setTimeout(()=>{//会先update一次delta为pageUnvisile的时间才触发
- //console.log('归零')
- //this.reStart()
- },1)
- }
- } */
- viewer.addEventListener('resize',setSize)
- viewer.addEventListener('fov_changed',setFov)
- //viewer.addEventListener('pageVisible', reStart)
-
- this.addEventListener('dispose',()=>{
- viewer.removeEventListener('resize',setSize)
- viewer.removeEventListener('fov_changed',setFov)
- //viewer.removeEventListener('pageVisible', reStart)
- })
-
-
- }
-
-
- computeParams(){
- if(this.curve){
- this.position.copy(this.curve.points[0])
- }
-
-
-
- const minSize = 0.8, maxSize = 10, minRadiusBound = 0.2, maxRadiusBound = 20;
- let size = minSize + (maxSize - minSize) * THREE.Math.smoothstep(this.radius*this.strength , minRadiusBound, maxRadiusBound);
-
- this.sizeTween.values = this.defaultSizeTween.values.map(e=> e*size)
-
- this.particleCount = Math.ceil( this.strength * this.radius * 5 /* * this.radius * this.radius */ )
-
- this.speed = defaults.speed * this.radius;
- this.speedRange = defaults.speedRange * this.radius;
-
- console.log(this.particleCount)
-
- {
- this.boundPoints = []
- this.boundPoints.push(this.position.clone())
- let maxSize = this.sizeTween.values.slice().sort((a,b)=>b-a)[0]
- let margin = maxSize * 0.35 + 0.5;
- let scale = this.radius+margin
- let sphere = new THREE.Sphere(this.position, scale)//加上防止剪裁
- this.boundingSphere = sphere //虽然还是会有一些后续移动的会超出
- this.boundingBox = new THREE.Box3().setFromCenterAndSize(this.position, new THREE.Vector3(scale*2,scale*2,scale*2))
- /* if(!this.debugSphere){
- this.debugSphere = new THREE.Mesh(sphereGeo, sphereMat)
- this.add(this.debugSphere)
- }
- this.debugSphere.scale.set(scale,scale,scale) */
- }
- }
- getPointsForBound(){
- return this.boundPoints; //可以用于expand实时bound的点, 不含particle的size等边距
- }
- reStart(){
- this.age = 0;
-
- this.createParticles()
- }
- setParameters(params) {
-
- params = $.extend({}, defaults, params)
-
-
- for (var key in params) {
- let value = params[key]
- if (key == 'position')
- this.position.copy(value)
- else if (value instanceof Array && value[0]instanceof Array){
- this[key] = new Tween(...value)
- }else if(value instanceof THREE.Vector3 || value instanceof THREE.Color){
- this[ key ] = value.clone()
- }else{
- this[key] = value;
- }
- }
-
-
- this.defaultSizeTween = this.sizeTween.clone()
- //Object.assign(this, params)
- this.particles = []
- this.age = 0.0
- this.alive = true
-
- this.geometry = new THREE.BufferGeometry()
- this.computeParams()
- this.material = new THREE.ShaderMaterial({
- uniforms: {
- u_sampler: {
- value: this.texture || getTexture()
- },
- heightOfNearPlane: {
- type: "f",
- value: 0
- }//相对far ,以确保画面缩放时点的大小也会缩放
- },
- vertexShader,
- fragmentShader,
- transparent: true,
- alphaTest: 0.5,
- depthTest: this.blendMode == THREE.NormalBlending,
- blending: this.blendMode
- })
-
-
- }
- createParticles() {
- this.particles = []
- 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))
-
- }
-
-
-
-
- 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.radius) */
- const y = 2 * Math.random() - 1
- const t = Math.PI * 2 * Math.random();
- const r = Math.sqrt(1 - y * y);
- const vec3 = new THREE.Vector3(r * Math.cos(t),y,r * Math.sin(t));
- particle.position = vec3.multiplyScalar(this.radius)
- }
- if (this.velocityShape == Shape.CUBE) {
- particle.velocity = util.randomVector3(this.velocity, this.velocityRange)
- }
- if (this.velocityShape == Shape.SPHERE) {
- const direction = new THREE.Vector3().addVectors(particle.position, new THREE.Vector3(0,0,this.radius*2))//向上升?
- 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)
-
- return particle
- }
- update(dt) {
- if(!viewer.getObjVisiByReason(this,'force')){//被手动隐藏了
- return
- }
- if(this.delayStartTime>0){ // 爆炸延迟
- return this.delayStartTime -= dt
- }
-
-
-
- if(!Potree.Utils.isInsideFrustum(this.boundingSphere, viewer.scene.getActiveCamera())){
- viewer.updateVisible(this,'isInsideFrustum', false ) //不在视野范围
- return
- }else{
- viewer.updateVisible(this,'isInsideFrustum', true )
- }
- //const timeRatio = 0.5
- if(dt > 1){
- console.log('update dt>1', dt)
- }
- //dt *= timeRatio
- let particleDeathAge = this.particleDeathAge/* * timeRatio */
- let particleSpaceTime = this.particleSpaceTime /* * timeRatio */
- const recycleIndices = []
- const recycleAges = []
- const recycleRebornCount = []
-
- 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 > particleDeathAge) {
- particle.alive = 0.0
- if(particle.rebornCount >= this.recycleTimes){
- particle.deadAge = particle.age - particleDeathAge //已死亡时间
- }else{//直接循环
- recycleIndices.push(i)
- recycleAges.push(/* ( */particle.age - particleDeathAge/* )%(this.particleDeathAge ) */)
- recycleRebornCount.push(particle.rebornCount+1)
- }
-
- }
- 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
- }else{
- if(particle.rebornCount >= this.recycleTimes){
- if(particle.age > particleDeathAge) {//其他已经死亡的粒子的时间继续增加
- particle.deadAge += dt
- }
- }
- }
-
-
-
- if (particle.rebornCount >= this.recycleTimes && particle.age > particleDeathAge) {//已经死亡
- if(particle.deadAge >= particleSpaceTime){//死亡时间超过设定的间隔时间后重启
- recycleIndices.push(i)
- let wholeTime = particleDeathAge * (this.recycleTimes+1) + particleSpaceTime
- recycleAges.push((particle.deadAge - particleSpaceTime)% wholeTime ) //剩余时间就是重生后的age
- recycleRebornCount.push(0)
- }
- }
-
- }
-
-
-
- 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 < particleDeathAge) {
- let startIndex = Math.round(this.particleCount * (this.age + 0)/ particleDeathAge)
- let endIndex = Math.round(this.particleCount * (this.age + dt)/ particleDeathAge)
- 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 //出生
- this.particles[i].age = recycleAges[j]
- this.particles[i].rebornCount= recycleRebornCount[j]
- /* if(this.particles[i].age < particleDeathAge){
- 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
- visibleArray[i] = particle.alive?
- } */
- }
- this.geometry.attributes.position.needsUpdate = true
- this.age += dt
- if (this.age > this.deathAge && !this.loop) {
- this.alive = false
- }
- }
- setSize(e) {
- let viewport = e.viewport
- this.screenHeight = viewport.resolution.y
- this.setPerspective(this.fov, this.screenHeight)
- }
- setFov(fov) {
- this.fov = fov
- this.setPerspective(this.fov, this.screenHeight)
- }
- setPerspective(fov, height) {
- //this.uniforms.heightOfNearPlane.value = Math.abs(height / (2 * Math.tan(THREE.Math.degToRad(fov * 0.5))));
- let far = Math.abs(height / (2 * Math.tan(THREE.Math.degToRad(fov * 0.5))));
- this.material.uniforms.heightOfNearPlane.value = far
- }
- updateGeometry(){
- this.computeParams()
- this.reStart()
- }
- dispose(){
- this.geometry.dispose();
- this.material.dispose();
- this.dispatchEvent('dispose')
- }
- }
- export default ExplodeParticle
|