Skip to content

Commit fda5c8c

Browse files
[NPE] Particles noise (#17500)
- Adds a new UpdateNoiseBlock to support migrating noise from old system to NPE. - Minor changes required to handle a ProceduralTexture. - Resets particle randomNoiseCoordinates1 and 2. PG to test: #R1JWLA#151 <img width="1095" height="995" alt="image" src="https://github.com/user-attachments/assets/35f002b2-4fa1-473f-b597-ad129834ca0e" />
1 parent e1364d0 commit fda5c8c

File tree

11 files changed

+262
-52
lines changed

11 files changed

+262
-52
lines changed

packages/dev/core/src/Particles/Node/Blocks/Update/updateFlowMapBlock.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1+
import type { Nullable } from "core/types";
12
import type { ThinParticleSystem } from "core/Particles/thinParticleSystem";
2-
import { RegisterClass } from "../../../../Misc/typeStore";
3-
import { NodeParticleBlockConnectionPointTypes } from "../../Enums/nodeParticleBlockConnectionPointTypes";
4-
import { NodeParticleBlock } from "../../nodeParticleBlock";
53
import type { NodeParticleConnectionPoint } from "../../nodeParticleBlockConnectionPoint";
64
import type { NodeParticleBuildState } from "../../nodeParticleBuildState";
75
import type { Particle } from "core/Particles/particle";
6+
import type { INodeParticleTextureData, ParticleTextureSourceBlock } from "../particleSourceTextureBlock";
7+
8+
import { RegisterClass } from "../../../../Misc/typeStore";
9+
import { NodeParticleBlockConnectionPointTypes } from "../../Enums/nodeParticleBlockConnectionPointTypes";
10+
import { NodeParticleBlock } from "../../nodeParticleBlock";
811
import { _ConnectAtTheEnd } from "core/Particles/Queue/executionQueue";
912
import { FlowMap } from "core/Particles/flowMap";
1013
import { editableInPropertyPage, PropertyTypeForEdition } from "core/Decorators/nodeDecorator";
11-
import type { ParticleTextureSourceBlock } from "../particleSourceTextureBlock";
1214

1315
/**
1416
* Block used to update particle position based on a flow map
@@ -72,7 +74,7 @@ export class UpdateFlowMapBlock extends NodeParticleBlock {
7274
let flowMap: FlowMap;
7375

7476
// eslint-disable-next-line github/no-then
75-
void flowMapTexture.extractTextureContentAsync().then((textureContent) => {
77+
void flowMapTexture.extractTextureContentAsync().then((textureContent: Nullable<INodeParticleTextureData>) => {
7678
if (!textureContent) {
7779
return;
7880
}
@@ -104,6 +106,10 @@ export class UpdateFlowMapBlock extends NodeParticleBlock {
104106
this.output._storedValue = system;
105107
}
106108

109+
/**
110+
* Serializes the block into a json object
111+
* @returns The serialized object
112+
*/
107113
public override serialize(): any {
108114
const serializationObject = super.serialize();
109115

@@ -112,6 +118,10 @@ export class UpdateFlowMapBlock extends NodeParticleBlock {
112118
return serializationObject;
113119
}
114120

121+
/**
122+
* Deserializes the block from a json object
123+
* @param serializationObject The object to deserialize from
124+
*/
115125
public override _deserialize(serializationObject: any) {
116126
super._deserialize(serializationObject);
117127

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
import type { Nullable } from "core/types";
2+
import type { Particle } from "core/Particles/particle";
3+
import type { ThinParticleSystem } from "core/Particles/thinParticleSystem";
4+
import type { NodeParticleConnectionPoint } from "core/Particles/Node/nodeParticleBlockConnectionPoint";
5+
import type { NodeParticleBuildState } from "core/Particles/Node/nodeParticleBuildState";
6+
import type { INodeParticleTextureData, ParticleTextureSourceBlock } from "core/Particles/Node/Blocks/particleSourceTextureBlock";
7+
8+
import { TmpVectors, Vector3 } from "core/Maths/math.vector";
9+
import { RegisterClass } from "core/Misc/typeStore";
10+
import { NodeParticleBlock } from "core/Particles/Node/nodeParticleBlock";
11+
import { NodeParticleBlockConnectionPointTypes } from "core/Particles/Node/Enums/nodeParticleBlockConnectionPointTypes";
12+
import { _ConnectAtTheEnd } from "core/Particles/Queue/executionQueue";
13+
14+
/**
15+
* Block used to update particle position based on a noise texture
16+
*/
17+
export class UpdateNoiseBlock extends NodeParticleBlock {
18+
/**
19+
* Create a new UpdateNoiseBlock
20+
* @param name defines the block name
21+
*/
22+
public constructor(name: string) {
23+
super(name);
24+
25+
this.registerInput("particle", NodeParticleBlockConnectionPointTypes.Particle);
26+
this.registerInput("noiseTexture", NodeParticleBlockConnectionPointTypes.Texture);
27+
this.registerInput("strength", NodeParticleBlockConnectionPointTypes.Vector3, true, new Vector3(100, 100, 100));
28+
this.registerOutput("output", NodeParticleBlockConnectionPointTypes.Particle);
29+
}
30+
31+
/**
32+
* Gets the particle component
33+
*/
34+
public get particle(): NodeParticleConnectionPoint {
35+
return this._inputs[0];
36+
}
37+
38+
/**
39+
* Gets the noiseTexture input component
40+
*/
41+
public get noiseTexture(): NodeParticleConnectionPoint {
42+
return this._inputs[1];
43+
}
44+
45+
/**
46+
* Gets the strength input component
47+
*/
48+
public get strength(): NodeParticleConnectionPoint {
49+
return this._inputs[2];
50+
}
51+
52+
/**
53+
* Gets the output component
54+
*/
55+
public get output(): NodeParticleConnectionPoint {
56+
return this._outputs[0];
57+
}
58+
59+
/**
60+
* Gets the current class name
61+
* @returns the class name
62+
*/
63+
public override getClassName() {
64+
return "UpdateNoiseBlock";
65+
}
66+
67+
/**
68+
* Builds the block
69+
* @param state defines the current build state
70+
*/
71+
public override _build(state: NodeParticleBuildState) {
72+
const system = this.particle.getConnectedValue(state) as ThinParticleSystem;
73+
74+
const strength = this.strength.getConnectedValue(state) as Vector3;
75+
if (!strength) {
76+
return;
77+
}
78+
79+
const noiseTexture = this.noiseTexture.connectedPoint?.ownerBlock as ParticleTextureSourceBlock;
80+
if (!noiseTexture) {
81+
return;
82+
}
83+
84+
const processNoiseAsync = async (particle: Particle) => {
85+
// eslint-disable-next-line github/no-then
86+
const textureContent: Nullable<INodeParticleTextureData> = await noiseTexture.extractTextureContentAsync();
87+
if (!textureContent) {
88+
return;
89+
}
90+
91+
if (!particle._randomNoiseCoordinates1) {
92+
particle._randomNoiseCoordinates1 = new Vector3(Math.random(), Math.random(), Math.random());
93+
}
94+
95+
if (!particle._randomNoiseCoordinates2) {
96+
particle._randomNoiseCoordinates2 = new Vector3(Math.random(), Math.random(), Math.random());
97+
}
98+
99+
const fetchedColorR = system._fetchR(
100+
particle._randomNoiseCoordinates1.x,
101+
particle._randomNoiseCoordinates1.y,
102+
textureContent.width,
103+
textureContent.height,
104+
textureContent.data
105+
);
106+
const fetchedColorG = system._fetchR(
107+
particle._randomNoiseCoordinates1.z,
108+
particle._randomNoiseCoordinates2.x,
109+
textureContent.width,
110+
textureContent.height,
111+
textureContent.data
112+
);
113+
const fetchedColorB = system._fetchR(
114+
particle._randomNoiseCoordinates2.y,
115+
particle._randomNoiseCoordinates2.z,
116+
textureContent.width,
117+
textureContent.height,
118+
textureContent.data
119+
);
120+
121+
const force = TmpVectors.Vector3[0];
122+
const scaledForce = TmpVectors.Vector3[1];
123+
124+
force.copyFromFloats((2 * fetchedColorR - 1) * strength.x, (2 * fetchedColorG - 1) * strength.y, (2 * fetchedColorB - 1) * strength.z);
125+
126+
force.scaleToRef(system._tempScaledUpdateSpeed, scaledForce);
127+
particle.direction.addInPlace(scaledForce);
128+
};
129+
130+
const noiseProcessing = {
131+
process: processNoiseAsync,
132+
previousItem: null,
133+
nextItem: null,
134+
};
135+
136+
if (system._updateQueueStart) {
137+
_ConnectAtTheEnd(noiseProcessing, system._updateQueueStart);
138+
} else {
139+
system._updateQueueStart = noiseProcessing;
140+
}
141+
142+
this.output._storedValue = system;
143+
}
144+
}
145+
146+
RegisterClass("BABYLON.UpdateNoiseBlock", UpdateNoiseBlock);

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export * from "./Update/basicSpriteUpdateBlock";
1717
export * from "./Update/basicColorUpdateBlock";
1818
export * from "./Update/updateSpriteCellIndexBlock";
1919
export * from "./Update/updateFlowMapBlock";
20+
export * from "./Update/updateNoiseBlock";
2021
export * from "./Update/updateAttractorBlock";
2122
export * from "./Update/alignAngleBlock";
2223
export * from "./Emitters/index";

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

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ import type { NodeParticleBuildState } from "../nodeParticleBuildState";
77
import type { Nullable } from "core/types";
88
import { TextureTools } from "core/Misc/textureTools";
99
import type { BaseTexture } from "../../../Materials/Textures/baseTexture";
10+
import type { ProceduralTexture } from "../../../Materials";
11+
12+
/**
13+
* Interface used to define texture data
14+
*/
15+
export interface INodeParticleTextureData {
16+
width: number;
17+
height: number;
18+
data: Uint8ClampedArray;
19+
}
1020

1121
/**
1222
* Block used to provide a texture for particles in a particle system
@@ -15,11 +25,7 @@ export class ParticleTextureSourceBlock extends NodeParticleBlock {
1525
private _url: string = "";
1626
private _textureDataUrl: string = "";
1727
private _sourceTexture: Nullable<BaseTexture> = null;
18-
private _cachedData: Nullable<{
19-
width: number;
20-
height: number;
21-
data: Uint8ClampedArray;
22-
}> = null;
28+
private _cachedData: Nullable<INodeParticleTextureData> = null;
2329

2430
/**
2531
* Indicates if the texture data should be serialized as a base64 string.
@@ -136,19 +142,36 @@ export class ParticleTextureSourceBlock extends NodeParticleBlock {
136142
return;
137143
}
138144
const size = texture.getSize();
139-
TextureTools.GetTextureDataAsync(texture, size.width, size.height)
140-
// eslint-disable-next-line github/no-then
141-
.then((data) => {
142-
this._cachedData = {
143-
width: size.width,
144-
height: size.height,
145-
data: new Uint8ClampedArray(data),
146-
};
147-
texture.dispose();
148-
resolve(this._cachedData);
149-
})
150-
// eslint-disable-next-line github/no-then
151-
.catch(reject);
145+
if (texture.getContent) {
146+
const proceduralTexture = texture as ProceduralTexture;
147+
proceduralTexture
148+
.getContent()
149+
// eslint-disable-next-line github/no-then
150+
?.then((data) => {
151+
this._cachedData = {
152+
width: size.width,
153+
height: size.height,
154+
data: data as Uint8ClampedArray,
155+
};
156+
resolve(this._cachedData);
157+
})
158+
// eslint-disable-next-line github/no-then
159+
.catch(reject);
160+
} else {
161+
TextureTools.GetTextureDataAsync(texture, size.width, size.height)
162+
// eslint-disable-next-line github/no-then
163+
.then((data) => {
164+
this._cachedData = {
165+
width: size.width,
166+
height: size.height,
167+
data: new Uint8ClampedArray(data),
168+
};
169+
texture.dispose();
170+
resolve(this._cachedData);
171+
})
172+
// eslint-disable-next-line github/no-then
173+
.catch(reject);
174+
}
152175
});
153176
}
154177

0 commit comments

Comments
 (0)