Skip to content

Commit e10ee9b

Browse files
[NPE] Particles velocity gradient (#17468)
- Adds support for converting velocity gradients - Adds block for contextual directionScale PG to test: #VEAFKK#1 <img width="2579" height="1104" alt="image" src="https://github.com/user-attachments/assets/ae6adbaa-682a-4140-b051-fcd3d6e9d197" />
1 parent 4d0ff4e commit e10ee9b

File tree

8 files changed

+144
-46
lines changed

8 files changed

+144
-46
lines changed

packages/dev/core/src/Particles/Node/Blocks/particleInputBlock.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ export class ParticleInputBlock extends NodeParticleBlock {
150150
case NodeParticleContextualSources.Angle:
151151
case NodeParticleContextualSources.AgeGradient:
152152
case NodeParticleContextualSources.Size:
153+
case NodeParticleContextualSources.DirectionScale:
153154
this._type = NodeParticleBlockConnectionPointTypes.Float;
154155
break;
155156
case NodeParticleContextualSources.SpriteCellEnd:

packages/dev/core/src/Particles/Node/Enums/nodeParticleContextualSources.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,6 @@ export enum NodeParticleContextualSources {
4343
LocalPositionUpdated = 0x0018,
4444
/** Size */
4545
Size = 0x0019,
46+
/** Direction Scale */
47+
DirectionScale = 0x0020,
4648
}

packages/dev/core/src/Particles/Node/nodeParticleBuildState.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import type { Scene } from "core/scene";
2-
import type { NodeParticleConnectionPoint } from "./nodeParticleBlockConnectionPoint";
3-
import { NodeParticleContextualSources } from "./Enums/nodeParticleContextualSources";
4-
import type { Particle } from "../particle";
52
import type { Nullable } from "core/types";
6-
import { NodeParticleBlockConnectionPointTypes } from "./Enums/nodeParticleBlockConnectionPointTypes";
7-
import { Vector2, Vector3 } from "core/Maths/math.vector";
8-
import type { ThinParticleSystem } from "../thinParticleSystem";
9-
import { Color4 } from "core/Maths/math.color";
10-
import { NodeParticleSystemSources } from "./Enums/nodeParticleSystemSources";
113
import type { AbstractMesh } from "core/Meshes/abstractMesh";
4+
import type { Particle } from "core/Particles/particle";
5+
import type { ThinParticleSystem } from "core/Particles/thinParticleSystem";
6+
import type { NodeParticleConnectionPoint } from "core/Particles/Node/nodeParticleBlockConnectionPoint";
7+
8+
import { Color4 } from "core/Maths/math.color";
9+
import { Vector2, Vector3 } from "core/Maths/math.vector";
10+
import { NodeParticleBlockConnectionPointTypes } from "core/Particles/Node/Enums/nodeParticleBlockConnectionPointTypes";
11+
import { NodeParticleContextualSources } from "core/Particles/Node/Enums/nodeParticleContextualSources";
12+
import { NodeParticleSystemSources } from "core/Particles/Node/Enums/nodeParticleSystemSources";
1213

1314
/**
1415
* Class used to store node based geometry build state
@@ -114,6 +115,8 @@ export class NodeParticleBuildState {
114115
return this.particleContext.position;
115116
case NodeParticleContextualSources.Direction:
116117
return this.particleContext.direction;
118+
case NodeParticleContextualSources.DirectionScale:
119+
return this.particleContext._directionScale;
117120
case NodeParticleContextualSources.ScaledDirection:
118121
this.particleContext.direction.scaleToRef(this.particleContext._directionScale, this.particleContext._scaledDirection);
119122
return this.particleContext._scaledDirection;

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

Lines changed: 103 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ type ConversionContext = {
5252
// Connections for the start value of a gradient. These are stored so they can be reused for the Creation phase and the Update phase of the particle
5353
sizeGradientValue0Output: NodeParticleConnectionPoint;
5454
colorGradientValue0Output: NodeParticleConnectionPoint;
55+
// Updated scaled direction direction based on velocity and drag
56+
scaledDirection: NodeParticleConnectionPoint;
5557
};
5658

5759
type RuntimeConversionContext = Partial<ConversionContext>;
@@ -134,7 +136,7 @@ function _SystemBlockGroup(oldSystem: ParticleSystem, context: RuntimeConversion
134136
// ------------- CREATE PARTICLE FUNCTIONS -------------
135137

136138
// The creation of the different properties follows the order they are added to the CreationQueue in ThinParticleSystem:
137-
// Lifetime, Emit Power, Size, Scale/StartSize, Angle, Velocity, VelocityLimit, Color, Drag, Noise, ColorDead, Ramp, Sheet
139+
// Lifetime, Emit Power, Size, Scale/StartSize, Angle, VelocityLimit, Color, Drag, Noise, ColorDead, Ramp, Sheet
138140
function _CreateParticleBlockGroup(oldSystem: ParticleSystem, context: RuntimeConversionContext): CreateParticleBlock {
139141
// Create particle block
140142
const createParticleBlock = new CreateParticleBlock("Create Particle");
@@ -453,7 +455,12 @@ function _UpdateParticleBlockGroup(inputParticle: NodeParticleConnectionPoint, o
453455

454456
updateBlockGroupOutput = _UpdateParticleColorBlockGroup(updateBlockGroupOutput, oldSystem._colorGradients, context);
455457
updateBlockGroupOutput = _UpdateParticleAngleBlockGroup(updateBlockGroupOutput, oldSystem, context);
456-
updateBlockGroupOutput = _UpdateParticlePositionBlockGroup(updateBlockGroupOutput, oldSystem.isLocal);
458+
459+
if (oldSystem._velocityGradients && oldSystem._velocityGradients.length > 0) {
460+
context.scaledDirection = _UpdateParticleVelocityGradientBlockGroup(updateBlockGroupOutput, oldSystem._velocityGradients, context);
461+
}
462+
463+
updateBlockGroupOutput = _UpdateParticlePositionBlockGroup(updateBlockGroupOutput, oldSystem.isLocal, context);
457464

458465
if (oldSystem._sizeGradients && oldSystem._sizeGradients.length > 0) {
459466
updateBlockGroupOutput = _UpdateParticleSizeGradientBlockGroup(updateBlockGroupOutput, oldSystem._sizeGradients, context);
@@ -466,6 +473,13 @@ function _UpdateParticleBlockGroup(inputParticle: NodeParticleConnectionPoint, o
466473
return updateBlockGroupOutput;
467474
}
468475

476+
/**
477+
* Creates the group of blocks that represent the particle color update
478+
* @param inputParticle The input particle to update
479+
* @param colorGradients The color gradients (if any)
480+
* @param context The context of the current conversion
481+
* @returns The output of the group of blocks that represent the particle color update
482+
*/
469483
function _UpdateParticleColorBlockGroup(
470484
inputParticle: NodeParticleConnectionPoint,
471485
colorGradients: Nullable<Array<ColorGradient>>,
@@ -482,7 +496,7 @@ function _UpdateParticleColorBlockGroup(
482496
context.colorGradientValue0Output,
483497
]);
484498
} else {
485-
colorCalculation = _CreateBasicColorUpdate();
499+
colorCalculation = _BasicColorUpdateBlockGroup();
486500
}
487501

488502
// Create the color update block clamping alpha >= 0
@@ -493,6 +507,13 @@ function _UpdateParticleColorBlockGroup(
493507
return colorUpdateBlock.output;
494508
}
495509

510+
/**
511+
* Creates the group of blocks that represent the particle angle update
512+
* @param inputParticle The input particle to update
513+
* @param oldSystem The old particle system to convert
514+
* @param context The context of the current conversion
515+
* @returns The output of the group of blocks that represent the particle color update
516+
*/
496517
function _UpdateParticleAngleBlockGroup(inputParticle: NodeParticleConnectionPoint, oldSystem: ParticleSystem, context: RuntimeConversionContext): NodeParticleConnectionPoint {
497518
// We will try to use gradients if they exist
498519
// If not, we will try to use min/max angular speed
@@ -525,29 +546,48 @@ function _UpdateParticleAngleBlockGroup(inputParticle: NodeParticleConnectionPoi
525546
}
526547
}
527548

528-
function _UpdateParticleAngularSpeedGradientBlockGroup(angularSpeedGradients: Array<FactorGradient>, context: RuntimeConversionContext): NodeParticleConnectionPoint {
549+
/**
550+
* Creates the group of blocks that represent the particle velocity update
551+
* @param inputParticle The input particle to update
552+
* @param velocityGradients The velocity gradients (if any)
553+
* @param context The context of the current conversion
554+
* @returns The output of the group of blocks that represent the particle velocity update
555+
*/
556+
function _UpdateParticleVelocityGradientBlockGroup(
557+
inputParticle: NodeParticleConnectionPoint,
558+
velocityGradients: Array<FactorGradient>,
559+
context: RuntimeConversionContext
560+
): NodeParticleConnectionPoint {
529561
context.ageToLifeTimeRatioBlockGroupOutput = _CreateAgeToLifeTimeRatioBlockGroup(context);
530562

531563
// Generate the gradient
532-
const angularSpeedValueOutput = _CreateGradientBlockGroup(
533-
context.ageToLifeTimeRatioBlockGroupOutput,
534-
angularSpeedGradients,
535-
ParticleRandomBlockLocks.OncePerParticle,
536-
"Angular Speed"
537-
);
538-
return angularSpeedValueOutput;
539-
}
540-
541-
function _UpdateParticleAngularSpeedBlockGroup(minAngularSpeed: number, maxAngularSpeed: number): NodeParticleConnectionPoint {
542-
// Random value between for the angular speed of the particle
543-
const randomAngularSpeedBlock = new ParticleRandomBlock("Random Angular Speed");
544-
randomAngularSpeedBlock.lockMode = ParticleRandomBlockLocks.OncePerParticle;
545-
_CreateAndConnectInput("Min Angular Speed", minAngularSpeed, randomAngularSpeedBlock.min);
546-
_CreateAndConnectInput("Max Angular Speed", maxAngularSpeed, randomAngularSpeedBlock.max);
547-
return randomAngularSpeedBlock.output;
564+
const velocityValueOutput = _CreateGradientBlockGroup(context.ageToLifeTimeRatioBlockGroupOutput, velocityGradients, ParticleRandomBlockLocks.OncePerParticle, "Velocity");
565+
566+
// Update the direction scale based on the velocity
567+
const multiplyScaleByVelocity = new ParticleMathBlock("Multiply Direction Scale by Velocity");
568+
multiplyScaleByVelocity.operation = ParticleMathBlockOperations.Multiply;
569+
velocityValueOutput.connectTo(multiplyScaleByVelocity.left);
570+
_CreateAndConnectContextualSource("Direction Scale", NodeParticleContextualSources.DirectionScale, multiplyScaleByVelocity.right);
571+
572+
// Update the particle direction scale
573+
const multiplyDirection = new ParticleMathBlock("Multiply Direction");
574+
multiplyDirection.operation = ParticleMathBlockOperations.Multiply;
575+
multiplyScaleByVelocity.output.connectTo(multiplyDirection.left);
576+
_CreateAndConnectContextualSource("Direction", NodeParticleContextualSources.Direction, multiplyDirection.right);
577+
578+
// Store the new calculation of the scaled direction in the context
579+
context.scaledDirection = multiplyDirection.output;
580+
return multiplyDirection.output;
548581
}
549582

550-
function _UpdateParticlePositionBlockGroup(inputParticle: NodeParticleConnectionPoint, isLocal: boolean): NodeParticleConnectionPoint {
583+
/**
584+
* Creates the group of blocks that represent the particle position update
585+
* @param inputParticle The input particle to update
586+
* @param isLocal Whether the particle coordinate system is local or not
587+
* @param context The context of the current conversion
588+
* @returns The output of the group of blocks that represent the particle position update
589+
*/
590+
function _UpdateParticlePositionBlockGroup(inputParticle: NodeParticleConnectionPoint, isLocal: boolean, context: RuntimeConversionContext): NodeParticleConnectionPoint {
551591
// Update the particle position
552592
const updatePosition = new UpdatePositionBlock("Position Update");
553593
inputParticle.connectTo(updatePosition.particle);
@@ -559,13 +599,25 @@ function _UpdateParticlePositionBlockGroup(inputParticle: NodeParticleConnection
559599
const addPositionBlock = new ParticleMathBlock("Add Position");
560600
addPositionBlock.operation = ParticleMathBlockOperations.Add;
561601
_CreateAndConnectContextualSource("Position", NodeParticleContextualSources.Position, addPositionBlock.left);
562-
_CreateAndConnectContextualSource("Scaled Direction", NodeParticleContextualSources.ScaledDirection, addPositionBlock.right);
602+
if (context.scaledDirection === undefined) {
603+
_CreateAndConnectContextualSource("Scaled Direction", NodeParticleContextualSources.ScaledDirection, addPositionBlock.right);
604+
} else {
605+
context.scaledDirection.connectTo(addPositionBlock.right);
606+
}
607+
563608
addPositionBlock.output.connectTo(updatePosition.position);
564609
}
565610

566611
return updatePosition.output;
567612
}
568613

614+
/**
615+
* Creates the group of blocks that represent the particle size update
616+
* @param inputParticle The input particle to update
617+
* @param sizeGradients The size gradients (if any)
618+
* @param context The context of the current conversion
619+
* @returns The output of the group of blocks that represent the particle size update
620+
*/
569621
function _UpdateParticleSizeGradientBlockGroup(
570622
inputParticle: NodeParticleConnectionPoint,
571623
sizeGradients: Array<FactorGradient>,
@@ -590,6 +642,12 @@ function _UpdateParticleSizeGradientBlockGroup(
590642
return updateSizeBlock.output;
591643
}
592644

645+
/**
646+
* Creates the group of blocks that represent the particle gravity update
647+
* @param inputParticle The input particle to update
648+
* @param gravity The gravity vector to apply
649+
* @returns The output of the group of blocks that represent the particle gravity update
650+
*/
593651
function _UpdateParticleGravityBlockGroup(inputParticle: NodeParticleConnectionPoint, gravity: Vector3): NodeParticleConnectionPoint {
594652
// Create the gravity delta
595653
const gravityDeltaOutput = _CreateDeltaModifiedInput("Gravity", gravity);
@@ -608,12 +666,33 @@ function _UpdateParticleGravityBlockGroup(inputParticle: NodeParticleConnectionP
608666
return updateDirection.output;
609667
}
610668

611-
function _CreateBasicColorUpdate(): NodeParticleConnectionPoint {
669+
function _UpdateParticleAngularSpeedGradientBlockGroup(angularSpeedGradients: Array<FactorGradient>, context: RuntimeConversionContext): NodeParticleConnectionPoint {
670+
context.ageToLifeTimeRatioBlockGroupOutput = _CreateAgeToLifeTimeRatioBlockGroup(context);
671+
672+
// Generate the gradient
673+
const angularSpeedValueOutput = _CreateGradientBlockGroup(
674+
context.ageToLifeTimeRatioBlockGroupOutput,
675+
angularSpeedGradients,
676+
ParticleRandomBlockLocks.OncePerParticle,
677+
"Angular Speed"
678+
);
679+
return angularSpeedValueOutput;
680+
}
681+
682+
function _UpdateParticleAngularSpeedBlockGroup(minAngularSpeed: number, maxAngularSpeed: number): NodeParticleConnectionPoint {
683+
// Random value between for the angular speed of the particle
684+
const randomAngularSpeedBlock = new ParticleRandomBlock("Random Angular Speed");
685+
randomAngularSpeedBlock.lockMode = ParticleRandomBlockLocks.OncePerParticle;
686+
_CreateAndConnectInput("Min Angular Speed", minAngularSpeed, randomAngularSpeedBlock.min);
687+
_CreateAndConnectInput("Max Angular Speed", maxAngularSpeed, randomAngularSpeedBlock.max);
688+
return randomAngularSpeedBlock.output;
689+
}
690+
691+
function _BasicColorUpdateBlockGroup(): NodeParticleConnectionPoint {
612692
const addColorBlock = new ParticleMathBlock("Add Color");
613693
addColorBlock.operation = ParticleMathBlockOperations.Add;
614694
_CreateAndConnectContextualSource("Color", NodeParticleContextualSources.Color, addColorBlock.left);
615695
_CreateAndConnectContextualSource("Scaled Color Step", NodeParticleContextualSources.ScaledColorStep, addColorBlock.right);
616-
617696
return addColorBlock.output;
618697
}
619698

packages/tools/nodeParticleEditor/src/blockTools.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,11 @@ export class BlockTools {
179179
block.contextualValue = NodeParticleContextualSources.Direction;
180180
return block;
181181
}
182+
case "DirectionScaleBlock": {
183+
const block = new ParticleInputBlock("Direction scale");
184+
block.contextualValue = NodeParticleContextualSources.DirectionScale;
185+
return block;
186+
}
182187
case "ScaledDirectionBlock": {
183188
const block = new ParticleInputBlock("Scaled direction");
184189
block.contextualValue = NodeParticleContextualSources.ScaledDirection;

packages/tools/nodeParticleEditor/src/components/nodeList/nodeListComponent.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export class NodeListComponent extends React.Component<INodeListComponentProps,
5151
SubtractBlock: "Math block set to Subtract",
5252
PositionBlock: "Contextual block to get the position of a particle",
5353
DirectionBlock: "Contextual block to get the direction of a particle",
54+
DirectionScaleBlock: "Contextual block to get the direction scale of a particle",
5455
ScaledDirectionBlock: "Contextual block to get the scaled direction of a particle",
5556
ColorBlock: "Contextual block to get the color of a particle",
5657
InitialColorBlock: "Contextual block to get the initial color of a particle",
@@ -268,6 +269,7 @@ export class NodeListComponent extends React.Component<INodeListComponentProps,
268269
Contextual: [
269270
"PositionBlock",
270271
"DirectionBlock",
272+
"DirectionScaleBlock",
271273
"ScaledDirectionBlock",
272274
"ColorBlock",
273275
"AgeBlock",

packages/tools/nodeParticleEditor/src/graphSystem/display/inputDisplayManager.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1+
import type { Color4 } from "core/Maths/math.color";
12
import type { Vector2, Vector3 } from "core/Maths/math.vector";
2-
import { BlockTools } from "../../blockTools";
3+
import type { ParticleInputBlock } from "core/Particles/Node/Blocks/particleInputBlock";
34
import type { IDisplayManager } from "shared-ui-components/nodeGraphSystem/interfaces/displayManager";
45
import type { INodeData } from "shared-ui-components/nodeGraphSystem/interfaces/nodeData";
6+
7+
import { BlockTools } from "../../blockTools";
58
import * as styles from "./inputDisplayManager.module.scss";
6-
import type { ParticleInputBlock } from "core/Particles/Node/Blocks/particleInputBlock";
79
import { NodeParticleBlockConnectionPointTypes } from "core/Particles/Node/Enums/nodeParticleBlockConnectionPointTypes";
810
import { NodeParticleContextualSources } from "core/Particles/Node/Enums/nodeParticleContextualSources";
9-
import type { Color4 } from "core/Maths/math.color";
1011
import { NodeParticleSystemSources } from "core/Particles/Node/Enums/nodeParticleSystemSources";
1112

1213
export class InputDisplayManager implements IDisplayManager {
@@ -80,6 +81,9 @@ export class InputDisplayManager implements IDisplayManager {
8081
case NodeParticleContextualSources.Direction:
8182
value = "Direction";
8283
break;
84+
case NodeParticleContextualSources.DirectionScale:
85+
value = "Direction Scale";
86+
break;
8387
case NodeParticleContextualSources.ScaledDirection:
8488
value = "Scaled Direction";
8589
break;

0 commit comments

Comments
 (0)