TkAstral3D/packages/sdk/lib/core/objects/ParticleEmitter.ts
2025-10-04 23:36:07 +08:00

934 lines
29 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 用于代理粒子发射器的空对象,以便于进行场景树显示及控制操作
* @author ErSan
* @email mlt131220@163.com
* @date 2025-02-14 16:00:00
*/
import * as THREE from 'three';
import * as Particle from '@/core/libs/three-nebula';
import {ParticleSystem} from '@/core/viewer/modules/ParticleSystem';
import {ObjectLoader} from '@/core/loader/ObjectLoader';
import {useAddSignal, useDispatchSignal, useRemoveSignal} from '@/hooks';
/**
* 获取默认粒子配置
* @description 以函数调用的方式返回,避免在模块外被引用
*/
export const getDefaultParticleConfig = (): IParticle.Config => ({
attr: {
position: { x: 0, y: 0, z: 0 },
rotation: { x: 0, y: 0, z: 0 },
scale: 1,
totalEmitTimes: Infinity,
damping: 0.006,
life: Infinity,
numPan: {
min: 1,
max: 1
},
timePan: {
a: 0.1,
b: 0.1
}
},
init: {
mass: {
min: 1,
max: 1,
center: true,
isEnabled: false
},
life: {
min: 1,
max: 1,
center: true,
isEnabled: false
},
radius: {
width: 1,
height: 1,
center: false,
isEnabled: false
},
rotation: {
x: 0,
y: 0,
z: 0,
useEmitterRotation: false,
isEnabled: false
},
position: {
isEnabled: false,
zone: null
},
velocity: {
isEnabled: false,
velocity: null
},
body: {
isEnabled: false,
body: {
type: 'Sprite',
uuid: '',
}
}
},
behaviour: {
color: {
isEnabled: false,
colorA: "#002a4f",
colorB: "#0029FF",
life: Infinity,
easing: 'easeLinear',
},
scale: {
isEnabled: false,
scaleA: 1,
scaleB: 1,
life: Infinity,
easing: 'easeLinear',
},
alpha: {
isEnabled: false,
alphaA: 1,
alphaB: 1,
life: Infinity,
easing: 'easeLinear',
},
force: {
isEnabled: false,
fx: 0,
fy: 0,
fz: 0,
life: Infinity,
easing: 'easeLinear',
},
rotate: {
isEnabled: false,
x: 0,
y: 0,
z: 0,
life: Infinity,
easing: 'easeLinear',
},
randomDrift: {
isEnabled: false,
driftX: 0,
driftY: 0,
driftZ: 0,
delay: 0.03,
life: Infinity,
easing: 'easeLinear',
},
spring: {
isEnabled: false,
x: 0,
y: 0,
z: 0,
spring: 0.1,
friction: 0.98,
life: Infinity,
easing: 'easeLinear',
},
attraction: {
x: 0,
y: 0,
z: 0,
force: 100,
radius: 1000,
life: Infinity,
easing: 'easeLinear',
isEnabled: false
},
collision: {
useMass: false,
life: Infinity,
easing: 'easeLinear',
isEnabled: false
}
},
})
let _handleAddToParticleSystemFn, _handleParticleCreatedFn;
class ParticleEmitter extends THREE.Object3D {
emitter: Particle.Emitter;
isEmitterProxy = true;
constructor(emitter: Particle.Emitter) {
super();
// @ts-ignore
this.type = 'Particle';
this.emitter = emitter;
this.syncProperties();
this.proxyProperties();
this.initEvent();
}
initEvent(){
/**
* 需要做粒子的选中,选中时定位到这个粒子发射器的代理对象上
* 如果后续不需要做粒子选中了,就把下方代码删除
* 对应的particleSystemAddEmitter signal也删除
*/
_handleAddToParticleSystemFn = this.handleAddToParticleSystem.bind(this);
useAddSignal('particleSystemAddEmitter', _handleAddToParticleSystemFn)
this.emitter.particles.forEach(particle => {
if (!particle.target) return;
particle.target.proxy = this;
})
_handleParticleCreatedFn = this.handleParticleCreated.bind(this);
this.emitter.parent?.eventDispatcher.addEventListener('PARTICLE_CREATED', _handleParticleCreatedFn, true)
}
/**
* 监听粒子创建
* @param particle
*/
handleParticleCreated(particle){
if (!this.emitter?.particles) return;
if (!this.emitter.particles.includes(particle)) return;
if (!particle.target) return;
particle.target.proxy = this;
}
/**
* 添加到粒子系统时
* @param _emitter
*/
handleAddToParticleSystem(_emitter: Particle.Emitter){
if (_emitter === this.emitter) {
this.emitter.parent?.eventDispatcher.addEventListener('PARTICLE_CREATED', _handleParticleCreatedFn,true)
}
}
/**
* 同步粒子发射器的属性到组中
*/
syncProperties() {
this.position.set(this.emitter.position.x, this.emitter.position.y, this.emitter.position.z);
// 粒子发射器的缩放是统一的,无法从三个轴分开设置
this.scale.set(this.emitter.scale, this.emitter.scale, this.emitter.scale);
this.rotation.set(this.emitter.rotation.x, this.emitter.rotation.y, this.emitter.rotation.z);
this.updateMatrixWorld(true);
}
/**
* 拦截重要属性的 setter 方法,同步到粒子发射器中
*/
proxyProperties() {
// 重写 position 的 setter 方法
let _position = new THREE.Vector3().copy(this.position);
Object.defineProperty(this.position, 'x', {
get: () => _position.x,
set: (value: number) => {
_position.setX(value);
this.emitter.position.x = value;
},
configurable: true,
enumerable: true
});
Object.defineProperty(this.position, 'y', {
get: () => _position.y,
set: (value: number) => {
_position.setY(value);
this.emitter.position.y = value;
},
configurable: true,
enumerable: true
});
Object.defineProperty(this.position, 'z', {
get: () => _position.z,
set: (value: number) => {
_position.setZ(value);
this.emitter.position.z = value;
},
configurable: true,
enumerable: true
});
// 重写 rotation 的 setter 方法
let _rotation = this.rotation.clone();
Object.defineProperty(this.rotation, '_x', {
get: () => _rotation.x,
set: (value: number) => {
_rotation.x = value;
// this.emitter.rotation.x = value * THREE.MathUtils.RAD2DEG;
this.emitter.rotation.x = value;
},
configurable: true,
enumerable: true
});
Object.defineProperty(this.rotation, '_y', {
get: () => _rotation.y,
set: (value: number) => {
_rotation.y = value;
this.emitter.rotation.y = value;
},
configurable: true,
enumerable: true
});
Object.defineProperty(this.rotation, '_z', {
get: () => _rotation.z,
set: (value: number) => {
_rotation.z = value;
this.emitter.rotation.z = value;
},
configurable: true,
enumerable: true
});
// 重写 scale 的 setter 方法
let _scale = this.scale.clone();
Object.defineProperty(this.scale, 'x', {
get: () => _scale.x,
set: (value: number) => {
_scale.setX(value);
// 获取scale三轴中的最小值应用到粒子发射器
this.emitter.scale = Math.min(_scale.x, _scale.y, _scale.z);
},
configurable: true,
enumerable: true
});
Object.defineProperty(this.scale, 'y', {
get: () => _scale.y,
set: (value: number) => {
_scale.setY(value);
// 获取scale三轴中的最小值应用到粒子发射器
this.emitter.scale = Math.min(_scale.x, _scale.y, _scale.z);
},
configurable: true,
enumerable: true
});
Object.defineProperty(this.scale, 'z', {
get: () => _scale.z,
set: (value: number) => {
_scale.setZ(value);
// 获取scale三轴中的最小值应用到粒子发射器
this.emitter.scale = Math.min(_scale.x, _scale.y, _scale.z);
},
configurable: true,
enumerable: true
});
// 重写 visible 的 setter 方法
let _visible = this.visible, _totalEmitTimes = this.emitter.totalEmitTimes;
Object.defineProperty(this, 'visible', {
get: () => _visible,
set: (value: boolean) => {
_visible = value;
// 发射器上不存在直接控制显隐的属性遍历粒子对象设置显隐影响瞬时性能故使用emitter.totalEmitTimes控制显隐
this.emitter.totalEmitTimes = value ? _totalEmitTimes : 0;
},
configurable: true,
enumerable: true
})
}
/**
* 获取emitter的json配置
*/
getEmitterJSON() {
const emitter: IParticle.Config = getDefaultParticleConfig();
emitter.attr = {
position: JSON.parse(JSON.stringify(this.emitter.position)),
rotation: JSON.parse(JSON.stringify(this.emitter.rotation)),
scale: this.emitter.scale,
totalEmitTimes: this.emitter.totalEmitTimes,
damping: this.emitter.damping,
life: this.emitter.life,
numPan: {
min: this.emitter.rate.numPan.a,
max: this.emitter.rate.numPan.b,
},
timePan: {
a: this.emitter.rate.timePan.a,
b: this.emitter.rate.timePan.b,
}
};
this.emitter.initializers.forEach(initializer => {
switch (initializer.type) {
case "Mass":
emitter.init.mass.isEnabled = initializer.isEnabled;
emitter.init.mass.min = initializer.massPan.a;
emitter.init.mass.max = initializer.massPan.b;
emitter.init.mass.center = initializer.massPan._center;
break;
case "Life":
emitter.init.life.isEnabled = initializer.isEnabled;
emitter.init.life.min = initializer.lifePan.a;
emitter.init.life.max = initializer.lifePan.b;
emitter.init.life.center = initializer.lifePan._center;
break;
case "Radius":
emitter.init.radius.isEnabled = initializer.isEnabled;
emitter.init.radius.width = initializer.radius.a;
emitter.init.radius.height = initializer.radius.b;
emitter.init.radius.center = initializer.radius._center;
break;
case "Rotation":
emitter.init.rotation.isEnabled = initializer.isEnabled;
emitter.init.rotation.x = initializer.rotation.x;
emitter.init.rotation.y = initializer.rotation.y;
emitter.init.rotation.z = initializer.rotation.z;
emitter.init.rotation.useEmitterRotation = initializer.useEmitterRotation;
break;
case "Position":
emitter.init.position.isEnabled = initializer.isEnabled;
emitter.init.position.zone = (function () {
const zone = initializer.zones[0];
switch (zone.type) {
case 'PointZone':
return {
type: 'PointZone',
x: zone.x,
y: zone.y,
z: zone.z
}
case 'LineZone':
return {
type: 'LineZone',
x1: zone.x1,
y1: zone.y1,
z1: zone.z1,
x2: zone.x2,
y2: zone.y2,
z2: zone.z2,
}
case 'BoxZone':
return {
type: 'BoxZone',
depth: zone.depth,
height: zone.height,
width: zone.width,
x: zone.x,
y: zone.y,
z: zone.z
}
case 'SphereZone':
return {
type: 'SphereZone',
radius: zone.radius,
x: zone.x,
y: zone.y,
z: zone.z
}
default:
return null;
}
})()
break;
case 'RadialVelocity':
emitter.init.velocity.isEnabled = initializer.isEnabled;
emitter.init.velocity.velocity = {
type: 'RadialVelocity',
radius: initializer.radiusPan.a,
x: initializer.dir.x,
y: initializer.dir.y,
z: initializer.dir.z,
theta: initializer.tha * 180 / Math.PI,
}
break;
case "PolarVelocity":
emitter.init.velocity.isEnabled = initializer.isEnabled;
emitter.init.velocity.velocity = {
type: 'PolarVelocity',
radius: initializer._polar.radius,
theta: initializer._polar.theta * 180 / Math.PI,
phi: initializer._polar.phi * 180 / Math.PI,
tha: initializer.tha * 180 / Math.PI,
}
break;
case 'VectorVelocity':
emitter.init.velocity.isEnabled = initializer.isEnabled;
emitter.init.velocity.velocity = {
type: 'VectorVelocity',
x: initializer.dir.x,
y: initializer.dir.y,
z: initializer.dir.z,
theta: initializer.tha * 180 / Math.PI,
}
break;
case "Body":
emitter.init.body.isEnabled = initializer.isEnabled;
emitter.init.body.body = {
type: initializer.body.items[0].type === 'Sprite' ? 'Sprite' : initializer.body.items[0].type === 'Points' ? 'Point' : 'Mesh',
uuid: this.uuid
}
break;
}
});
this.emitter.behaviours.forEach(behaviour => {
switch (behaviour.type) {
case "Color":
emitter.behaviour.color.isEnabled = behaviour.isEnabled;
emitter.behaviour.color.colorA = behaviour.colorA.colors[0];
emitter.behaviour.color.colorB = behaviour.colorB.colors[0];
emitter.behaviour.color.life = behaviour._life;
emitter.behaviour.color.easing = behaviour.easing.name;
break;
case "Scale":
emitter.behaviour.scale.isEnabled = behaviour.isEnabled;
emitter.behaviour.scale.scaleA = behaviour.scaleA.a;
emitter.behaviour.scale.scaleB = behaviour.scaleB.a;
emitter.behaviour.scale.life = behaviour._life;
emitter.behaviour.scale.easing = behaviour.easing.name;
break;
case "Alpha":
emitter.behaviour.alpha.isEnabled = behaviour.isEnabled;
emitter.behaviour.alpha.alphaA = behaviour.alphaA.a;
emitter.behaviour.alpha.alphaB = behaviour.alphaB.a;
emitter.behaviour.alpha.life = behaviour._life;
emitter.behaviour.alpha.easing = behaviour.easing.name;
break;
case "Force":
emitter.behaviour.force.isEnabled = behaviour.isEnabled;
emitter.behaviour.force.fx = behaviour.force.x / 100;
emitter.behaviour.force.fy = behaviour.force.y / 100;
emitter.behaviour.force.fz = behaviour.force.z / 100;
emitter.behaviour.force.life = behaviour._life;
emitter.behaviour.force.easing = behaviour.easing.name;
break;
case "Rotate":
emitter.behaviour.rotate.isEnabled = behaviour.isEnabled;
emitter.behaviour.rotate.x = behaviour.x.a * 180 / Math.PI;
emitter.behaviour.rotate.y = behaviour.y.a * 180 / Math.PI;
emitter.behaviour.rotate.z = behaviour.z.a * 180 / Math.PI;
emitter.behaviour.rotate.life = behaviour._life;
emitter.behaviour.rotate.easing = behaviour.easing.name;
break;
case "RandomDrift":
emitter.behaviour.randomDrift.isEnabled = behaviour.isEnabled;
emitter.behaviour.randomDrift.driftX = behaviour.randomForce.x / 100;
emitter.behaviour.randomDrift.driftY = behaviour.randomForce.y / 100;
emitter.behaviour.randomDrift.driftZ = behaviour.randomForce.z / 100;
emitter.behaviour.randomDrift.delay = behaviour.delayPan.a;
emitter.behaviour.randomDrift.life = behaviour._life;
emitter.behaviour.randomDrift.easing = behaviour.easing.name;
break;
case "Spring":
emitter.behaviour.spring.isEnabled = behaviour.isEnabled;
emitter.behaviour.spring.x = behaviour.pos.x;
emitter.behaviour.spring.y = behaviour.pos.y;
emitter.behaviour.spring.z = behaviour.pos.z;
emitter.behaviour.spring.spring = behaviour.spring;
emitter.behaviour.spring.friction = behaviour.friction;
emitter.behaviour.spring.life = behaviour._life;
emitter.behaviour.spring.easing = behaviour.easing.name;
break;
case "Attraction":
emitter.behaviour.attraction.isEnabled = behaviour.isEnabled;
emitter.behaviour.attraction.x = behaviour.targetPosition.x;
emitter.behaviour.attraction.y = behaviour.targetPosition.y;
emitter.behaviour.attraction.z = behaviour.targetPosition.z;
emitter.behaviour.attraction.force = behaviour.force / 100;
emitter.behaviour.attraction.radius = behaviour.radius;
emitter.behaviour.attraction.life = behaviour._life;
emitter.behaviour.attraction.easing = behaviour.easing.name;
break;
case "Collision":
emitter.behaviour.collision.isEnabled = behaviour.isEnabled;
emitter.behaviour.collision.useMass = behaviour.useMass;
emitter.behaviour.collision.life = behaviour._life;
emitter.behaviour.collision.easing = behaviour.easing.name;
break;
}
});
return emitter;
}
/**
* 从json配置解析
*/
static fromJSON(json: IParticle.Object3DJSON) {
const emitterConfig = json.emitter.config;
const emitter = new Particle.Emitter({
position: new Particle.Vector3D(
emitterConfig.attr.position.x,
emitterConfig.attr.position.y,
emitterConfig.attr.position.z
),
rotation: new Particle.Vector3D(
emitterConfig.attr.rotation.x,
emitterConfig.attr.rotation.y,
emitterConfig.attr.rotation.z
),
scale: emitterConfig.attr.scale,
life: emitterConfig.attr.life
});
emitter.totalEmitTimes = emitterConfig.attr.totalEmitTimes;
emitter.damping = emitterConfig.attr.damping;
emitter.rate = new Particle.Rate(
new Particle.Span(emitterConfig.attr.numPan.min, emitterConfig.attr.numPan.max),
new Particle.Span(emitterConfig.attr.timePan.a, emitterConfig.attr.timePan.b)
);
emitter.emit();
// 还原initializers
json.emitter.useInitializers.forEach(initializer => {
switch (initializer) {
case "Mass":
emitter.addInitializer(
new Particle.Mass(
emitterConfig.init.mass.min,
emitterConfig.init.mass.max,
emitterConfig.init.mass.center,
emitterConfig.init.mass.isEnabled
)
);
break;
case "Life":
emitter.addInitializer(
new Particle.Life(
emitterConfig.init.life.min,
emitterConfig.init.life.max,
emitterConfig.init.life.center,
emitterConfig.init.life.isEnabled
)
);
break;
case "Radius":
emitter.addInitializer(
new Particle.Radius(
emitterConfig.init.radius.width,
emitterConfig.init.radius.height,
emitterConfig.init.radius.center,
emitterConfig.init.radius.isEnabled
)
);
break;
case "Rotation":
emitter.addInitializer(
new Particle.Rotation(
emitterConfig.init.rotation.x,
emitterConfig.init.rotation.y,
emitterConfig.init.rotation.z,
emitterConfig.init.rotation.useEmitterRotation,
emitterConfig.init.rotation.isEnabled
)
);
break;
case "Position":
let position = new Particle.Position();
emitter.addInitializer(position);
let zone;
const zoneData = emitterConfig.init.position.zone;
switch (zoneData?.type) {
case 'PointZone':
zone = new Particle.PointZone(zoneData.x, zoneData.y, zoneData.z);
break;
case 'LineZone':
zone = new Particle.LineZone(
zoneData.x1,
zoneData.y1,
zoneData.z1,
zoneData.x2,
zoneData.y2,
zoneData.z2,
);
break;
case 'BoxZone':
zone = new Particle.BoxZone(
zoneData.x,
zoneData.y,
zoneData.z,
zoneData.width,
zoneData.height,
zoneData.depth,
);
break;
case 'SphereZone':
zone = new Particle.SphereZone(
zoneData.x,
zoneData.y,
zoneData.z,
zoneData.radius
);
break;
}
if (!zone) return;
position.addZone(zone);
break;
case "RadialVelocity": {
const velocity = emitterConfig.init.velocity.velocity as IParticle.RadialVelocity;
emitter.addInitializer(
new Particle.RadialVelocity(
velocity.radius,
new Particle.Vector3D(
velocity.x,
velocity.y,
velocity.z
),
velocity.theta,
emitterConfig.init.velocity.isEnabled
)
);
}
break;
case "PolarVelocity": {
const velocity = emitterConfig.init.velocity.velocity as IParticle.PolarVelocity;
emitter.addInitializer(
new Particle.PolarVelocity(
new Particle.Polar3D(velocity.radius, velocity.theta * Math.PI / 180, velocity.phi * Math.PI / 180),
velocity.tha,
emitterConfig.init.velocity.isEnabled
)
);
}
break;
case "VectorVelocity": {
const velocity = emitterConfig.init.velocity.velocity as IParticle.VectorVelocity;
emitter.addInitializer(
new Particle.VectorVelocity(
new Particle.Vector3D(velocity.x, velocity.y, velocity.z),
velocity.theta,
emitterConfig.init.velocity.isEnabled
)
);
}
break;
case "Body":
if (!emitterConfig.init.body.body) break;
switch (emitterConfig.init.body.body.type) {
case "Sprite":
case "Mesh":
new ObjectLoader().parse(json.emitter.bodyObjectJSON, (object3D => {
emitter.addInitializer(
new Particle.Body(
object3D,
null,
null,
emitterConfig.init.body.isEnabled
)
);
ParticleSystem.Body3DMap.set(json.uuid, object3D);
}))
break;
case "Point":
emitter.addInitializer(
new Particle.Body(
ParticleSystem.PointBody.clone(),
null,
null,
emitterConfig.init.body.isEnabled
)
);
break;
}
break;
}
})
// 还原behaviours
json.emitter.useBehaviours.forEach(behaviour => {
switch (behaviour) {
case "Color":
emitter.addBehaviour(
new Particle.Color(
emitterConfig.behaviour.color.colorA,
emitterConfig.behaviour.color.colorB,
emitterConfig.behaviour.color.life,
Particle.ease[emitterConfig.behaviour.color.easing],
emitterConfig.behaviour.color.isEnabled
)
)
break;
case "Scale":
emitter.addBehaviour(
new Particle.Scale(
emitterConfig.behaviour.scale.scaleA,
emitterConfig.behaviour.scale.scaleB,
emitterConfig.behaviour.scale.life,
Particle.ease[emitterConfig.behaviour.scale.easing],
emitterConfig.behaviour.scale.isEnabled
)
)
break;
case "Alpha":
emitter.addBehaviour(
new Particle.Alpha(
emitterConfig.behaviour.alpha.alphaA,
emitterConfig.behaviour.alpha.alphaB,
emitterConfig.behaviour.alpha.life,
Particle.ease[emitterConfig.behaviour.alpha.easing],
emitterConfig.behaviour.alpha.isEnabled
)
)
break;
case "Force":
emitter.addBehaviour(
new Particle.Force(
emitterConfig.behaviour.force.fx,
emitterConfig.behaviour.force.fy,
emitterConfig.behaviour.force.fz,
emitterConfig.behaviour.force.life,
Particle.ease[emitterConfig.behaviour.force.easing],
emitterConfig.behaviour.force.isEnabled
)
)
break;
case "Rotate":
emitter.addBehaviour(
new Particle.Rotate(
emitterConfig.behaviour.rotate.x,
emitterConfig.behaviour.rotate.y,
emitterConfig.behaviour.rotate.z,
emitterConfig.behaviour.rotate.life,
Particle.ease[emitterConfig.behaviour.rotate.easing],
emitterConfig.behaviour.rotate.isEnabled
)
)
break;
case "RandomDrift":
emitter.addBehaviour(
new Particle.RandomDrift(
emitterConfig.behaviour.randomDrift.driftX,
emitterConfig.behaviour.randomDrift.driftY,
emitterConfig.behaviour.randomDrift.driftZ,
emitterConfig.behaviour.randomDrift.delay,
emitterConfig.behaviour.randomDrift.life,
Particle.ease[emitterConfig.behaviour.randomDrift.easing],
emitterConfig.behaviour.randomDrift.isEnabled
)
)
break;
case "Spring":
emitter.addBehaviour(
new Particle.Spring(
emitterConfig.behaviour.spring.x,
emitterConfig.behaviour.spring.y,
emitterConfig.behaviour.spring.z,
emitterConfig.behaviour.spring.spring,
emitterConfig.behaviour.spring.friction,
emitterConfig.behaviour.spring.life,
Particle.ease[emitterConfig.behaviour.spring.easing],
emitterConfig.behaviour.spring.isEnabled
)
)
break;
case "Attraction":
emitter.addBehaviour(
new Particle.Attraction(
new Particle.Vector3D(
emitterConfig.behaviour.attraction.x,
emitterConfig.behaviour.attraction.y,
emitterConfig.behaviour.attraction.z
),
emitterConfig.behaviour.attraction.force,
emitterConfig.behaviour.attraction.radius,
emitterConfig.behaviour.attraction.life,
Particle.ease[emitterConfig.behaviour.attraction.easing],
emitterConfig.behaviour.attraction.isEnabled
)
)
break;
case "Collision":
emitter.addBehaviour(
new Particle.Collision(
emitter,
emitterConfig.behaviour.collision.useMass,
() => { },
emitterConfig.behaviour.collision.life,
Particle.ease[emitterConfig.behaviour.collision.easing],
emitterConfig.behaviour.collision.isEnabled
)
)
break;
}
})
useDispatchSignal("emitterAdd2ParticleSystem",emitter,json.emitter.system)
const particleEmitter = new ParticleEmitter(emitter);
particleEmitter.name = json.name;
particleEmitter.uuid = json.uuid;
return particleEmitter;
}
/**
* 获取json配置
*/
toJSON(meta?: THREE.JSONMeta) {
const superJSON = super.toJSON(meta).object;
// @ts-ignore
superJSON.matrix = undefined;
// @ts-ignore
delete superJSON.matrix;
// 父级toJSON调用子级toJSON时只会保留object对象主要信息都需要放在这
const object: IParticle.Object3DJSON = {
uuid: this.uuid,
type: this.type,
name: this.name,
emitter: {
config:this.getEmitterJSON(),
system: this.emitter.parent.name,
useInitializers: this.emitter.initializers.map(initializer => initializer.type),
bodyObjectJSON: ParticleSystem.Body3DMap.get(this.uuid)?.toJSON() || null,
useBehaviours: this.emitter.behaviours.map(behaviour => behaviour.type)
},
children: [],
};
if (this.children.length > 0) {
object.children = [];
for (let i = 0; i < this.children.length; i++) {
//@ts-ignore
object.children.push(this.children[i].toJSON(meta).object);
}
}
return {
metadata: {
version: 4.6,
type: 'Object',
generator: 'ParticleEmitter.toJSON'
},
object: Object.assign(superJSON, object),
} as any;
}
/**
* 销毁
*/
dispose(){
// 手动销毁所有粒子模型对象发射器的destroy方法不会进行销毁
this.emitter.particles && this.emitter.particles.forEach(p => {
if (!p.target) return;
p.target.removeFromParent();
})
useRemoveSignal('particleSystemAddEmitter', _handleAddToParticleSystemFn);
_handleAddToParticleSystemFn = null;
this.emitter.parent?.eventDispatcher.removeEventListener('PARTICLE_CREATED', _handleParticleCreatedFn);
_handleParticleCreatedFn = null;
this.emitter.destroy();
}
}
export default ParticleEmitter;