-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Add Solid Particle System (SPS) Support to Node Particle Editor #17320
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
Soullnik
wants to merge
74
commits into
BabylonJS:master
Choose a base branch
from
Soullnik:feat/solid-particles-to-node-particle-editor
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,927
−279
Draft
Changes from all commits
Commits
Show all changes
74 commits
Select commit
Hold shift + click to select a range
4629a7d
Add Solid Particle System (SPS) blocks to Node Particle Editor
c5d8db6
Enhance Node Particle System with Solid Particle Blocks
fc2df4b
Refactor Solid Particle System initialization and cleanup
0be8117
Update NodeParticleEditor to use setToDefault method for NodeParticle…
6d72319
Enhance Solid Particle System with isStarted flag and update NodePart…
5a6e5b9
Enhance NodeParticleConnectionPoint to support multiple connections
b31248e
Refactor Solid Particle System and Node Particle Editor for improved …
3f7d61f
Refactor SPSSystemBlock to streamline particle initialization and upd…
7539783
Enhance SolidParticleSystem and NodeParticleSystemSet with improved d…
4ff96c9
Refactor NodeParticleBlock and NodeParticleConnectionPoint to simplif…
2d7e11c
Add unregisterInput method to NodeParticleBlock for dynamic input man…
396662b
Remove optional `allowMultipleConnections` property from IPortData in…
72c885b
Refactor SolidParticleSystem and related blocks for improved initiali…
014681f
Refactor SolidParticleSystem blocks to introduce SPSParticleConfigBlo…
9c25aa6
Refactor SPSCreateBlock and SPSSystemBlock to improve lifecycle manag…
6da7359
Enhance ParticleSystemSet to support SolidParticleSystem integration
e37f61f
Enhance NodeParticleBuildState and NodeParticleSystemSet for SolidPar…
dc4db46
Refactor SPSInitBlock and SPSUpdateBlock to streamline value retrieval
d116da5
Refactor NodeParticleSystemSet to streamline random position and rota…
69a803a
Remove commented-out steps in NodeParticleSystemSet for improved code…
50115b2
Add ParticlePropsSetBlock and ParticlePropsGetBlock for dynamic prope…
3289658
Enhance particle property management with String type support and dis…
8833c50
Add Solid Particle System (SPS) blocks to Node Particle Editor
3430d2f
Enhance Node Particle System with Solid Particle Blocks
be2185e
Refactor Solid Particle System initialization and cleanup
5b797bc
Update NodeParticleEditor to use setToDefault method for NodeParticle…
b63f06e
Enhance Solid Particle System with isStarted flag and update NodePart…
5ee2820
Enhance NodeParticleConnectionPoint to support multiple connections
1617792
Refactor Solid Particle System and Node Particle Editor for improved …
5dc1e1d
Refactor SPSSystemBlock to streamline particle initialization and upd…
eaeecf4
Enhance SolidParticleSystem and NodeParticleSystemSet with improved d…
4994414
Refactor NodeParticleBlock and NodeParticleConnectionPoint to simplif…
140a45e
Add unregisterInput method to NodeParticleBlock for dynamic input man…
62c7143
Remove optional `allowMultipleConnections` property from IPortData in…
9791892
Refactor SolidParticleSystem and related blocks for improved initiali…
dfe540f
Refactor SolidParticleSystem blocks to introduce SPSParticleConfigBlo…
a4207e3
Refactor SPSCreateBlock and SPSSystemBlock to improve lifecycle manag…
6ac1d15
Enhance ParticleSystemSet to support SolidParticleSystem integration
cc9bbe0
Enhance NodeParticleBuildState and NodeParticleSystemSet for SolidPar…
1eb3110
Refactor SPSInitBlock and SPSUpdateBlock to streamline value retrieval
b9a365b
Refactor NodeParticleSystemSet to streamline random position and rota…
bf82fdc
Remove commented-out steps in NodeParticleSystemSet for improved code…
2085aa6
Add ParticlePropsSetBlock and ParticlePropsGetBlock for dynamic prope…
c0c3d1d
Enhance particle property management with String type support and dis…
da65fb7
Merge branch 'feat/solid-particles-to-node-particle-editor' of https:…
e03fa4b
Remove String property from PropertyTypeForEdition enum and refactor …
499386d
Refactor particle property blocks to enhance naming consistency and t…
c3bf211
Merge remote-tracking branch 'upstream/master'
c3fa640
Merge branch 'master' into feat/solid-particles-to-node-particle-editor
28912f4
Implement SPSParticlePropsSetBlock and SPSParticlePropsGetBlock for d…
98d1dec
Add lifetime and disposeOnEnd properties to SPSSystemBlock and SolidP…
a0c36ab
Enhance particle system functionality with new mesh and material blocks
e1d549e
Refactor and enhance Solid Particle System blocks for improved functi…
87ee819
Merge branch 'master' of https://github.com/BabylonJS/Babylon.js
428a4e0
Merge branch 'master' of https://github.com/BabylonJS/Babylon.js
db23b2e
Merge branch 'master' into feat/solid-particles-to-node-particle-editor
7e2ce20
Enhance SolidParticleSystem and NodeParticleBuildState for improved c…
3654695
Refactor SPSSystemBlock to streamline properties and improve serializ…
7375fbc
Refactor SolidParticleSystem and NodeParticle architecture for improv…
52877ef
Refactor SPSParticleConfigBlock and SPSUpdateBlock for improved parti…
a3b6630
Refactor SPSMeshSourcePropertyTabComponent and SPSNodeMaterialPropert…
0318c74
Enhance SolidParticle support in NodeParticle system
1e3ebda
Implement Solid Particle System Blocks in Node Particle Editor
0e003da
Refactor Node Particle System and Mesh Source Blocks for improved typ…
330b50f
Add default mesh source and enhance particle initialization in NodePa…
716244c
Rename NodeParticleModes for clarity and consistency
d656ce3
Implement NodeMaterialSourceBlock and update references in Node Parti…
1d52dc2
Enhance serialization and block definitions in Node Particle Editor
f6ee853
Update block definition for NodeMaterialSourceBlock in Node Particle …
43ac6ac
Refactor SolidParticle exports in Node Particle Editor
5fde820
Refactor SolidParticle imports in Node Particle System and Editor
20493b5
Enhance BlockNodeData logic in Node Particle Editor
e568f3c
Refactor NodeParticleBlockConnectionPointTypes enum for consistency
5b11243
Update BlockTools to include SolidParticleConfig type
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
39 changes: 39 additions & 0 deletions
39
packages/dev/core/src/Particles/Node/Blocks/SolidParticle/ISolidParticleData.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| import type { Vector3 } from "core/Maths/math.vector"; | ||
| import type { Color4 } from "core/Maths/math.color"; | ||
| import type { Material } from "core/Materials/material"; | ||
| import type { VertexData } from "core/Meshes/mesh.vertexData"; | ||
|
|
||
| /** | ||
| * Interface for solid particle mesh source data | ||
| */ | ||
| export interface ISolidParticleMeshSourceData { | ||
| customMeshName?: string; | ||
| vertexData?: VertexData; | ||
| } | ||
|
|
||
| /** | ||
| * Interface for solid particle update block data | ||
| */ | ||
| export interface ISolidParticleUpdateData { | ||
| position?: () => Vector3; | ||
| velocity?: () => Vector3; | ||
| color?: () => Color4; | ||
| scaling?: () => Vector3; | ||
| rotation?: () => Vector3; | ||
| } | ||
|
|
||
| /** | ||
| * Interface for solid particle create block data | ||
| */ | ||
| export interface ISolidParticleInitData { | ||
| meshData: ISolidParticleMeshSourceData | null; | ||
| count: number; | ||
| material?: Material; | ||
| position?: () => Vector3; | ||
| velocity?: () => Vector3; | ||
| color?: () => Color4; | ||
| scaling?: () => Vector3; | ||
| rotation?: () => Vector3; | ||
| lifeTime?: () => number; | ||
| updateBlock?: ISolidParticleUpdateData | null; | ||
| } |
233 changes: 233 additions & 0 deletions
233
packages/dev/core/src/Particles/Node/Blocks/SolidParticle/createSolidParticleBlock.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,233 @@ | ||
| import { RegisterClass } from "../../../../Misc/typeStore"; | ||
| import { NodeParticleBlockConnectionPointTypes } from "../../Enums/nodeParticleBlockConnectionPointTypes"; | ||
| import { NodeParticleBlock } from "../../nodeParticleBlock"; | ||
| import type { NodeParticleConnectionPoint } from "../../nodeParticleBlockConnectionPoint"; | ||
| import type { NodeParticleBuildState } from "../../nodeParticleBuildState"; | ||
| import { SolidParticleSystem } from "core/Particles/solidParticleSystem"; | ||
| import type { ISolidParticleInitData } from "./ISolidParticleData"; | ||
| import { Mesh } from "core/Meshes/mesh"; | ||
| import type { SolidParticle } from "../../../solidParticle"; | ||
| import type { Observer } from "core/Misc/observable"; | ||
|
|
||
| /** | ||
| * Block used to create SolidParticleSystem and collect all Create blocks | ||
| */ | ||
| export class CreateSolidParticleBlock extends NodeParticleBlock { | ||
| private _connectionObservers = new Map<number, Observer<NodeParticleConnectionPoint>>(); | ||
| private _disconnectionObservers = new Map<number, Observer<NodeParticleConnectionPoint>>(); | ||
|
|
||
| public constructor(name: string) { | ||
| super(name); | ||
| this.registerInput(`config-${this._entryCount - 1}`, NodeParticleBlockConnectionPointTypes.SolidParticleConfig); | ||
| this.registerOutput("solidParticle", NodeParticleBlockConnectionPointTypes.SolidParticle); | ||
|
|
||
| this._manageExtendedInputs(0); | ||
| } | ||
|
|
||
| public override getClassName() { | ||
| return "CreateSolidParticleBlock"; | ||
| } | ||
|
|
||
| private _entryCount = 1; | ||
|
|
||
| private _extend() { | ||
| this._entryCount++; | ||
| this.registerInput(`config-${this._entryCount - 1}`, NodeParticleBlockConnectionPointTypes.SolidParticleConfig, true); | ||
| this._manageExtendedInputs(this._entryCount - 1); | ||
| } | ||
|
|
||
| private _shrink() { | ||
| if (this._entryCount > 1) { | ||
| this._unmanageExtendedInputs(this._entryCount - 1); | ||
| this._entryCount--; | ||
| this.unregisterInput(`config-${this._entryCount}`); | ||
| } | ||
| } | ||
|
|
||
| private _manageExtendedInputs(index: number) { | ||
| const connectionObserver = this._inputs[index].onConnectionObservable.add(() => { | ||
| if (this._entryCount - 1 > index) { | ||
| return; | ||
| } | ||
| this._extend(); | ||
| }); | ||
|
|
||
| const disconnectionObserver = this._inputs[index].onDisconnectionObservable.add(() => { | ||
| if (this._entryCount - 1 > index) { | ||
| return; | ||
| } | ||
| this._shrink(); | ||
| }); | ||
|
|
||
| // Store observers for later removal | ||
| this._connectionObservers.set(index, connectionObserver); | ||
| this._disconnectionObservers.set(index, disconnectionObserver); | ||
| } | ||
|
|
||
| private _unmanageExtendedInputs(index: number) { | ||
| const connectionObserver = this._connectionObservers.get(index); | ||
| const disconnectionObserver = this._disconnectionObservers.get(index); | ||
|
|
||
| if (connectionObserver) { | ||
| this._inputs[index].onConnectionObservable.remove(connectionObserver); | ||
| this._connectionObservers.delete(index); | ||
| } | ||
|
|
||
| if (disconnectionObserver) { | ||
| this._inputs[index].onDisconnectionObservable.remove(disconnectionObserver); | ||
| this._disconnectionObservers.delete(index); | ||
| } | ||
| } | ||
|
|
||
| public get config(): NodeParticleConnectionPoint { | ||
| return this._inputs[this._entryCount - 1]; | ||
| } | ||
|
|
||
| public get solidParticle(): NodeParticleConnectionPoint { | ||
| return this._outputs[0]; | ||
| } | ||
|
|
||
| public override _build(state: NodeParticleBuildState) { | ||
| if (!state.scene) { | ||
| throw new Error("Scene is not initialized in NodeParticleBuildState"); | ||
| } | ||
|
|
||
| const sps = new SolidParticleSystem(this.name, state.scene, { | ||
| useModelMaterial: true, | ||
| }); | ||
|
|
||
| const createBlocks = new Map<number, ISolidParticleInitData>(); | ||
| for (let i = 0; i < this._inputs.length; i++) { | ||
| const creatData = this._inputs[i].getConnectedValue(state) as ISolidParticleInitData; | ||
| if (!this._inputs[i].isConnected || !creatData || !creatData.meshData || !creatData.count) { | ||
| continue; | ||
| } | ||
|
|
||
| if (!creatData.meshData.vertexData) { | ||
| continue; | ||
| } | ||
|
|
||
| const mesh = new Mesh(`${this.name}_shape_${i}`, state.scene); | ||
| creatData.meshData.vertexData.applyToMesh(mesh, true); | ||
| if (creatData.material) { | ||
| mesh.material = creatData.material; | ||
| } | ||
|
|
||
| const shapeId = sps.addShape(mesh, creatData.count); | ||
| createBlocks.set(shapeId, creatData); | ||
| mesh.dispose(); | ||
| } | ||
|
|
||
| sps.initParticles = () => { | ||
| if (!sps) { | ||
| return; | ||
| } | ||
|
|
||
| const originalContext = state.particleContext; | ||
| const originalSystemContext = state.systemContext; | ||
|
|
||
| try { | ||
| for (let p = 0; p < sps.nbParticles; p++) { | ||
| const particle = sps.particles[p]; | ||
| const particleCreateData = createBlocks.get(particle.shapeId); | ||
| if (!particleCreateData) { | ||
| continue; | ||
| } | ||
| const { lifeTime, position, velocity, color, scaling, rotation } = particleCreateData; | ||
|
|
||
| state.particleContext = particle; | ||
| state.systemContext = sps; | ||
|
|
||
| if (lifeTime) { | ||
| particle.lifeTime = lifeTime(); | ||
| particle.age = 0; | ||
| particle.alive = true; | ||
| } | ||
|
|
||
| if (position) { | ||
| particle.position.copyFrom(position()); | ||
| } | ||
| if (velocity) { | ||
| particle.velocity.copyFrom(velocity()); | ||
| } | ||
| if (color) { | ||
| const particleColor = particle.color; | ||
| if (particleColor) { | ||
| particleColor.copyFrom(color()); | ||
| } | ||
| } | ||
| if (scaling) { | ||
| particle.scaling.copyFrom(scaling()); | ||
| } | ||
| if (rotation) { | ||
| particle.rotation.copyFrom(rotation()); | ||
| } | ||
| } | ||
| } finally { | ||
| state.particleContext = originalContext; | ||
| state.systemContext = originalSystemContext; | ||
| } | ||
| }; | ||
|
|
||
| sps.updateParticle = (particle: SolidParticle) => { | ||
| if (!sps) { | ||
| return particle; | ||
| } | ||
|
|
||
| const particleCreateData = createBlocks.get(particle.shapeId); | ||
| const updateBlock = particleCreateData?.updateBlock; | ||
| if (!updateBlock) { | ||
| return particle; | ||
| } | ||
| // Set particle context in state for PerParticle lock mode | ||
| const originalContext = state.particleContext; | ||
| const originalSystemContext = state.systemContext; | ||
|
|
||
| // Temporarily set particle context for PerParticle lock mode | ||
| state.particleContext = particle; | ||
| state.systemContext = sps; | ||
|
|
||
| try { | ||
| if (updateBlock.position) { | ||
| particle.position.copyFrom(updateBlock.position()); | ||
| } | ||
| if (updateBlock.velocity) { | ||
| particle.velocity.copyFrom(updateBlock.velocity()); | ||
| } | ||
| if (updateBlock.color) { | ||
| particle.color?.copyFrom(updateBlock.color()); | ||
| } | ||
| if (updateBlock.scaling) { | ||
| particle.scaling.copyFrom(updateBlock.scaling()); | ||
| } | ||
| if (updateBlock.rotation) { | ||
| particle.rotation.copyFrom(updateBlock.rotation()); | ||
| } | ||
| } finally { | ||
| // Restore original context | ||
| state.particleContext = originalContext; | ||
| state.systemContext = originalSystemContext; | ||
| } | ||
| return particle; | ||
| }; | ||
|
|
||
| this.solidParticle._storedValue = sps; | ||
| } | ||
|
|
||
| public override serialize(): any { | ||
| const serializationObject = super.serialize(); | ||
| serializationObject._entryCount = this._entryCount; | ||
| return serializationObject; | ||
| } | ||
|
|
||
| public override _deserialize(serializationObject: any) { | ||
| super._deserialize(serializationObject); | ||
| if (serializationObject._entryCount && serializationObject._entryCount > 1) { | ||
| for (let i = 1; i < serializationObject._entryCount; i++) { | ||
| this._extend(); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| RegisterClass("BABYLON.CreateSolidParticleBlock", CreateSolidParticleBlock); | ||
106 changes: 106 additions & 0 deletions
106
packages/dev/core/src/Particles/Node/Blocks/SolidParticle/initSolidParticleBlock.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,106 @@ | ||
| import type { Color4, Vector3 } from "../../../../Maths"; | ||
| import { RegisterClass } from "../../../../Misc/typeStore"; | ||
| import { NodeParticleBlockConnectionPointTypes } from "../../Enums/nodeParticleBlockConnectionPointTypes"; | ||
| import { NodeParticleBlock } from "../../nodeParticleBlock"; | ||
| import type { NodeParticleConnectionPoint } from "../../nodeParticleBlockConnectionPoint"; | ||
| import type { NodeParticleBuildState } from "../../nodeParticleBuildState"; | ||
| import type { ISolidParticleInitData } from "./ISolidParticleData"; | ||
|
|
||
| /** | ||
| * Block used to configure SPS particle parameters (mesh, count, material, position, velocity, color, scaling, rotation) | ||
| */ | ||
| export class InitSolidParticleBlock extends NodeParticleBlock { | ||
| public constructor(name: string) { | ||
| super(name); | ||
|
|
||
| this.registerInput("count", NodeParticleBlockConnectionPointTypes.Int, true, 1); | ||
| this.registerInput("lifeTime", NodeParticleBlockConnectionPointTypes.Float, true, Infinity); | ||
| this.registerInput("position", NodeParticleBlockConnectionPointTypes.Vector3, true); | ||
| this.registerInput("velocity", NodeParticleBlockConnectionPointTypes.Vector3, true); | ||
| this.registerInput("color", NodeParticleBlockConnectionPointTypes.Color4, true); | ||
| this.registerInput("scaling", NodeParticleBlockConnectionPointTypes.Vector3, true); | ||
| this.registerInput("rotation", NodeParticleBlockConnectionPointTypes.Vector3, true); | ||
| this.registerInput("mesh", NodeParticleBlockConnectionPointTypes.Mesh); | ||
| this.registerInput("material", NodeParticleBlockConnectionPointTypes.Material, true); | ||
|
|
||
| this.registerOutput("config", NodeParticleBlockConnectionPointTypes.SolidParticleConfig); | ||
| } | ||
|
|
||
| public override getClassName() { | ||
| return "InitSolidParticleBlock"; | ||
| } | ||
|
|
||
| public get count(): NodeParticleConnectionPoint { | ||
| return this._inputs[0]; | ||
| } | ||
|
|
||
| public get lifeTime(): NodeParticleConnectionPoint { | ||
| return this._inputs[1]; | ||
| } | ||
|
|
||
| public get position(): NodeParticleConnectionPoint { | ||
| return this._inputs[2]; | ||
| } | ||
|
|
||
| public get velocity(): NodeParticleConnectionPoint { | ||
| return this._inputs[3]; | ||
| } | ||
|
|
||
| public get color(): NodeParticleConnectionPoint { | ||
| return this._inputs[4]; | ||
| } | ||
|
|
||
| public get scaling(): NodeParticleConnectionPoint { | ||
| return this._inputs[5]; | ||
| } | ||
|
|
||
| public get rotation(): NodeParticleConnectionPoint { | ||
| return this._inputs[6]; | ||
| } | ||
|
|
||
| public get mesh(): NodeParticleConnectionPoint { | ||
| return this._inputs[7]; | ||
| } | ||
|
|
||
| public get material(): NodeParticleConnectionPoint { | ||
| return this._inputs[8]; | ||
| } | ||
|
|
||
| public get config(): NodeParticleConnectionPoint { | ||
| return this._outputs[0]; | ||
| } | ||
|
|
||
| public override _build(state: NodeParticleBuildState) { | ||
| const meshData = this.mesh.getConnectedValue(state); | ||
| const count = (this.count.getConnectedValue(state) as number) ?? 1; | ||
| const lifeTime = this.lifeTime.isConnected | ||
| ? () => { | ||
| return (this.lifeTime.getConnectedValue(state) as number) ?? Infinity; | ||
| } | ||
| : undefined; | ||
| const material = this.material.getConnectedValue(state); | ||
|
|
||
| const position = this.position.isConnected ? () => this.position.getConnectedValue(state) as Vector3 : undefined; | ||
| const velocity = this.velocity.isConnected ? () => this.velocity.getConnectedValue(state) as Vector3 : undefined; | ||
| const color = this.color.isConnected ? () => this.color.getConnectedValue(state) as Color4 : undefined; | ||
| const scaling = this.scaling.isConnected ? () => this.scaling.getConnectedValue(state) as Vector3 : undefined; | ||
| const rotation = this.rotation.isConnected ? () => this.rotation.getConnectedValue(state) as Vector3 : undefined; | ||
|
|
||
| const particleConfig: ISolidParticleInitData = { | ||
| meshData, | ||
| count, | ||
| material, | ||
| lifeTime, | ||
| position, | ||
| velocity, | ||
| color, | ||
| scaling, | ||
| rotation, | ||
| updateBlock: null, | ||
| }; | ||
|
|
||
| this.config._storedValue = particleConfig; | ||
| } | ||
| } | ||
|
|
||
| RegisterClass("BABYLON.InitSolidParticleBlock", InitSolidParticleBlock); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we name it MergeBlock maybe? The neme seems off (like we already created the SPS)