Skip to content

Commit 9d2fd2f

Browse files
[NPE] Particles directed cone emitter (#17446)
Adds direction support for ConeShapeBlock. DirectionRandomizer will be used unless both direction1 and direction2 are assigned. PG to test: #UL4WC0#117 <img width="1018" height="861" alt="image" src="https://github.com/user-attachments/assets/ea552c78-f28b-4d96-8833-f11f4d771b3a" />
1 parent f8ed2c7 commit 9d2fd2f

File tree

3 files changed

+71
-16
lines changed

3 files changed

+71
-16
lines changed

packages/dev/core/src/Particles/EmitterTypes/coneParticleEmitter.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,11 +249,19 @@ export class ConeDirectedParticleEmitter extends ConeParticleEmitter {
249249
* Called by the particle System when the direction is computed for the created particle.
250250
* @param worldMatrix is the world matrix of the particle system
251251
* @param directionToUpdate is the direction vector to update with the result
252+
* @param particle is the particle we are computed the position for
253+
* @param isLocal defines if the direction should be set in local space
252254
*/
253-
public override startDirectionFunction(worldMatrix: Matrix, directionToUpdate: Vector3): void {
255+
public override startDirectionFunction(worldMatrix: Matrix, directionToUpdate: Vector3, particle: Particle, isLocal: boolean): void {
254256
const randX = RandomRange(this.direction1.x, this.direction2.x);
255257
const randY = RandomRange(this.direction1.y, this.direction2.y);
256258
const randZ = RandomRange(this.direction1.z, this.direction2.z);
259+
260+
if (isLocal) {
261+
directionToUpdate.copyFromFloats(randX, randY, randZ);
262+
return;
263+
}
264+
257265
Vector3.TransformNormalFromFloatsToRef(randX, randY, randZ, worldMatrix, directionToUpdate);
258266
}
259267

packages/dev/core/src/Particles/Node/Blocks/Emitters/coneShapeBlock.ts

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { _CreateLocalPositionData } from "./emitters.functions";
1717
export class ConeShapeBlock extends NodeParticleBlock implements IShapeBlock {
1818
/**
1919
* Gets or sets a boolean indicating if the system should emit only from the spawn point
20+
* DirectionRandomizer will be used for the particles initial direction unless both direction1 and direction2 are connected.
2021
*/
2122
@editableInPropertyPage("Emit from spawn point only", PropertyTypeForEdition.Boolean, "ADVANCED", { embedded: true, notifiers: { rebuild: true } })
2223
public emitFromSpawnPointOnly = false;
@@ -34,6 +35,8 @@ export class ConeShapeBlock extends NodeParticleBlock implements IShapeBlock {
3435
this.registerInput("radiusRange", NodeParticleBlockConnectionPointTypes.Float, true, 1);
3536
this.registerInput("heightRange", NodeParticleBlockConnectionPointTypes.Float, true, 1);
3637
this.registerInput("directionRandomizer", NodeParticleBlockConnectionPointTypes.Float, true, 0);
38+
this.registerInput("direction1", NodeParticleBlockConnectionPointTypes.Vector3, true);
39+
this.registerInput("direction2", NodeParticleBlockConnectionPointTypes.Vector3, true);
3740
this.registerOutput("output", NodeParticleBlockConnectionPointTypes.Particle);
3841
}
3942

@@ -87,6 +90,20 @@ export class ConeShapeBlock extends NodeParticleBlock implements IShapeBlock {
8790
return this._inputs[5];
8891
}
8992

93+
/**
94+
* Gets the direction1 input component
95+
*/
96+
public get direction1(): NodeParticleConnectionPoint {
97+
return this._inputs[6];
98+
}
99+
100+
/**
101+
* Gets the direction2 input component
102+
*/
103+
public get direction2(): NodeParticleConnectionPoint {
104+
return this._inputs[7];
105+
}
106+
90107
/**
91108
* Gets the output component
92109
*/
@@ -105,21 +122,37 @@ export class ConeShapeBlock extends NodeParticleBlock implements IShapeBlock {
105122
state.particleContext = particle;
106123
state.systemContext = system;
107124

108-
const directionRandomizer = this.directionRandomizer.getConnectedValue(state) as number;
109-
110-
const direction = particle.position.subtract(state.emitterPosition!).normalize();
111-
const randX = RandomRange(0, directionRandomizer);
112-
const randY = RandomRange(0, directionRandomizer);
113-
const randZ = RandomRange(0, directionRandomizer);
114-
direction.x += randX;
115-
direction.y += randY;
116-
direction.z += randZ;
117-
direction.normalize();
118-
119-
if (system.isLocal) {
120-
particle.direction.copyFromFloats(direction.x, direction.y, direction.z);
125+
// We always use directionRandomizer unless both directions are connected
126+
if (this.direction1.isConnected === false || this.direction2.isConnected === false) {
127+
const directionRandomizer = this.directionRandomizer.getConnectedValue(state) as number;
128+
129+
const direction = particle.position.subtract(state.emitterPosition!).normalize();
130+
const randX = RandomRange(0, directionRandomizer);
131+
const randY = RandomRange(0, directionRandomizer);
132+
const randZ = RandomRange(0, directionRandomizer);
133+
direction.x += randX;
134+
direction.y += randY;
135+
direction.z += randZ;
136+
direction.normalize();
137+
138+
if (system.isLocal) {
139+
particle.direction.copyFromFloats(direction.x, direction.y, direction.z);
140+
} else {
141+
Vector3.TransformNormalFromFloatsToRef(direction.x, direction.y, direction.z, state.emitterWorldMatrix!, particle.direction);
142+
}
121143
} else {
122-
Vector3.TransformNormalFromFloatsToRef(direction.x, direction.y, direction.z, state.emitterWorldMatrix!, particle.direction);
144+
const direction1 = this.direction1.getConnectedValue(state) as Vector3;
145+
const direction2 = this.direction2.getConnectedValue(state) as Vector3;
146+
147+
const randX = RandomRange(direction1.x, direction2.x);
148+
const randY = RandomRange(direction1.y, direction2.y);
149+
const randZ = RandomRange(direction1.z, direction2.z);
150+
151+
if (system.isLocal) {
152+
particle.direction.copyFromFloats(randX, randY, randZ);
153+
} else {
154+
Vector3.TransformNormalFromFloatsToRef(randX, randY, randZ, state.emitterWorldMatrix!, particle.direction);
155+
}
123156
}
124157

125158
particle._initialDirection = particle.direction.clone();

packages/dev/core/src/Particles/Node/nodeParticleSystemSet.helper.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import type { ColorGradient } from "core/Misc";
66
import type { ParticleSystem } from "core/Particles/particleSystem";
77
import type { IParticleSystem } from "core/Particles/IParticleSystem";
88
import type { BoxParticleEmitter } from "core/Particles/EmitterTypes/boxParticleEmitter";
9-
import type { ConeParticleEmitter } from "core/Particles/EmitterTypes/coneParticleEmitter";
9+
import type { ConeDirectedParticleEmitter, ConeParticleEmitter } from "core/Particles/EmitterTypes/coneParticleEmitter";
1010
import type { CustomParticleEmitter } from "core/Particles/EmitterTypes/customParticleEmitter";
1111
import type { CylinderDirectedParticleEmitter, CylinderParticleEmitter } from "core/Particles/EmitterTypes/cylinderParticleEmitter";
1212
import type { HemisphericParticleEmitter } from "core/Particles/EmitterTypes/hemisphericParticleEmitter";
@@ -197,6 +197,20 @@ function _CreateEmitterShapeBlock(oldSystem: IParticleSystem): IShapeBlock {
197197
_CreateAndConnectInput("Direction Randomizer", source.directionRandomizer, target.directionRandomizer);
198198
break;
199199
}
200+
case "ConeDirectedParticleEmitter": {
201+
const source = emitter as ConeDirectedParticleEmitter;
202+
shapeBlock = new ConeShapeBlock("Cone Shape");
203+
204+
const target = shapeBlock as ConeShapeBlock;
205+
target.emitFromSpawnPointOnly = source.emitFromSpawnPointOnly;
206+
_CreateAndConnectInput("Radius", source.radius, target.radius);
207+
_CreateAndConnectInput("Angle", source.angle, target.angle);
208+
_CreateAndConnectInput("Radius Range", source.radiusRange, target.radiusRange);
209+
_CreateAndConnectInput("Height Range", source.heightRange, target.heightRange);
210+
_CreateAndConnectInput("Direction 1", source.direction1, target.direction1);
211+
_CreateAndConnectInput("Direction 2", source.direction2, target.direction2);
212+
break;
213+
}
200214
case "CustomParticleEmitter": {
201215
const source = emitter as CustomParticleEmitter;
202216
shapeBlock = new CustomShapeBlock("Custom Shape");

0 commit comments

Comments
 (0)