Skip to content

Commit 55aea71

Browse files
deltakoshDavid Catuhe
and
David Catuhe
authored
Particle attractors (#16541)
Co-authored-by: David Catuhe <[email protected]>
1 parent 13d0e6d commit 55aea71

File tree

7 files changed

+206
-104
lines changed

7 files changed

+206
-104
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import type { Nullable } from "core/types";
2+
import type { Particle } from "../particle";
3+
import type { ThinParticleSystem } from "../thinParticleSystem";
4+
5+
/** @internal */
6+
export interface _IExecutionQueueItem {
7+
/** @internal */
8+
process: (particle: Particle, system: ThinParticleSystem) => void;
9+
/** @internal */
10+
previousItem: Nullable<_IExecutionQueueItem>;
11+
/** @internal */
12+
nextItem: Nullable<_IExecutionQueueItem>;
13+
}
14+
15+
/** @internal */
16+
export function _ConnectBefore(newOne: _IExecutionQueueItem, activeOne: _IExecutionQueueItem) {
17+
newOne.previousItem = activeOne.previousItem;
18+
newOne.nextItem = activeOne;
19+
if (activeOne.previousItem) {
20+
activeOne.previousItem.nextItem = newOne;
21+
}
22+
activeOne.previousItem = newOne;
23+
}
24+
25+
/** @internal */
26+
export function _ConnectAfter(newOne: _IExecutionQueueItem, activeOne: _IExecutionQueueItem) {
27+
newOne.previousItem = activeOne;
28+
newOne.nextItem = activeOne.nextItem;
29+
if (activeOne.nextItem) {
30+
activeOne.nextItem.previousItem = newOne;
31+
}
32+
activeOne.nextItem = newOne;
33+
}
34+
35+
/** @internal */
36+
export function _RemoveFromQueue(item: _IExecutionQueueItem) {
37+
if (item.previousItem) {
38+
item.previousItem.nextItem = item.nextItem;
39+
}
40+
if (item.nextItem) {
41+
item.nextItem.previousItem = item.previousItem;
42+
}
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { Vector3 } from "core/Maths/math.vector";
2+
import type { Particle } from "./particle";
3+
import type { ThinParticleSystem } from "./thinParticleSystem";
4+
5+
const toAttractor: Vector3 = Vector3.Zero();
6+
const force: Vector3 = Vector3.Zero();
7+
const scaledForce: Vector3 = Vector3.Zero();
8+
9+
/**
10+
* Class representing an attractor in a particle system.
11+
* #DEZ79M#31
12+
*/
13+
export class Attractor {
14+
/**
15+
* Gets or sets the strength of the attractor.
16+
* A positive value attracts particles, while a negative value repels them.
17+
*/
18+
public strength = 0.0;
19+
20+
/**
21+
* Gets or sets the position of the attractor in 3D space.
22+
*/
23+
public position = Vector3.Zero();
24+
25+
/** @internal */
26+
public _processParticle(particle: Particle, system: ThinParticleSystem) {
27+
this.position.subtractToRef(particle.position, toAttractor);
28+
const distanceSquared = toAttractor.lengthSquared() + 1; // Avoid going under 1.0
29+
toAttractor.normalize().scaleToRef(this.strength / distanceSquared, force);
30+
31+
force.scaleToRef(system._tempScaledUpdateSpeed, scaledForce);
32+
33+
particle.direction.addInPlace(scaledForce); // Update particle velocity
34+
}
35+
}

packages/dev/core/src/Particles/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export * from "./solidParticleSystem";
1818
export * from "./cloudPoint";
1919
export * from "./pointsCloudSystem";
2020
export * from "./subEmitter";
21+
export * from "./attractor";
2122

2223
export * from "../Shaders/particles.fragment";
2324
export * from "../Shaders/particles.vertex";

packages/dev/core/src/Particles/particleSystem.ts

+50
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ import {
3232
CreatePointEmitter,
3333
CreateSphereEmitter,
3434
} from "./particleSystem.functions";
35+
import type { Attractor } from "./attractor";
36+
import type { _IExecutionQueueItem } from "./Queue/executionQueue";
37+
import { _ConnectAfter, _RemoveFromQueue } from "./Queue/executionQueue";
3538

3639
/**
3740
* This represents a particle system in Babylon.
@@ -88,6 +91,53 @@ export class ParticleSystem extends ThinParticleSystem {
8891
return particleEmitter;
8992
}
9093

94+
private _attractors: Attractor[] = [];
95+
private _attractorUpdate: Nullable<_IExecutionQueueItem> = null;
96+
97+
/**
98+
* The list of attractors used to change the direction of the particles in the system.
99+
* Please note that this is a copy of the internal array. If you want to modify it, please use the addAttractor and removeAttractor methods.
100+
*/
101+
public get attractors(): Attractor[] {
102+
return this._attractors.slice(0);
103+
}
104+
105+
/**
106+
* Add an attractor to the particle system. Attractors are used to change the direction of the particles in the system.
107+
* @param attractor The attractor to add to the particle system
108+
*/
109+
public addAttractor(attractor: Attractor): void {
110+
this._attractors.push(attractor);
111+
112+
if (this._attractors.length === 1) {
113+
this._attractorUpdate = {
114+
process: (particle: Particle) => {
115+
for (const attractor of this._attractors) {
116+
attractor._processParticle(particle, this);
117+
}
118+
},
119+
previousItem: null,
120+
nextItem: null,
121+
};
122+
_ConnectAfter(this._attractorUpdate, this._directionProcessing!);
123+
}
124+
}
125+
126+
/**
127+
* Removes an attractor from the particle system. Attractors are used to change the direction of the particles in the system.
128+
* @param attractor The attractor to remove from the particle system
129+
*/
130+
public removeAttractor(attractor: Attractor): void {
131+
const index = this._attractors.indexOf(attractor);
132+
if (index !== -1) {
133+
this._attractors.splice(index, 1);
134+
}
135+
136+
if (this._attractors.length === 0) {
137+
_RemoveFromQueue(this._attractorUpdate!);
138+
}
139+
}
140+
91141
/**
92142
* Creates a Hemisphere Emitter for the particle system (emits along the hemisphere radius)
93143
* @param radius The radius of the hemisphere to emit from

0 commit comments

Comments
 (0)