Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Vector3 } from "core/Maths/math.vector";
import { Color4 } from "core/Maths/math.color";
import type { Mesh } from "core/Meshes/mesh";
import type { Material } from "core/Materials/material";

/**
* Interface for SPS update block data
*/
export interface ISPSUpdateData {
position?: () => Vector3;
velocity?: () => Vector3;
color?: () => Color4;
scaling?: () => Vector3;
rotation?: () => Vector3;
}

/**
* Interface for SPS create block data
*/
export interface ISPSParticleConfigData {
mesh: Mesh;
count: number;
material?: Material;
initBlock?: ISPSUpdateData;
updateBlock?: ISPSUpdateData;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
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 { ISPSParticleConfigData } from "./ISPSData";
import { SolidParticle } from "../../../solidParticle";
import { Observer } from "../../../../Misc";

/**
* Block used to create SolidParticleSystem and collect all Create blocks
*/
export class SPSCreateBlock extends NodeParticleBlock {
private _connectionObservers = new Map<number, Observer<NodeParticleConnectionPoint>>();
private _disconnectionObservers = new Map<number, Observer<NodeParticleConnectionPoint>>();

public constructor(name: string) {
super(name);
this.registerInput(`particleConfig-${this._entryCount - 1}`, NodeParticleBlockConnectionPointTypes.SolidParticle);
this.registerOutput("solidParticleSystem", NodeParticleBlockConnectionPointTypes.SolidParticleSystem);

this._manageExtendedInputs(0);
}

public override getClassName() {
return "SPSCreateBlock";
}

private _entryCount = 1;

private _extend() {
this._entryCount++;
this.registerInput(`particleConfig-${this._entryCount - 1}`, NodeParticleBlockConnectionPointTypes.SolidParticle, true);
this._manageExtendedInputs(this._entryCount - 1);
}

private _shrink() {
if (this._entryCount > 1) {
this._unmanageExtendedInputs(this._entryCount - 1);
this._entryCount--;
this.unregisterInput(`particleConfig-${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 particleConfig(): NodeParticleConnectionPoint {
return this._inputs[this._entryCount - 1];
}

public get solidParticleSystem(): 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, ISPSParticleConfigData>();
for (let i = 0; i < this._inputs.length; i++) {
const creatData = this._inputs[i].getConnectedValue(state) as ISPSParticleConfigData;
if (this._inputs[i].isConnected && creatData) {
if (creatData.mesh && creatData.count) {
const shapeId = sps.addShape(creatData.mesh, creatData.count);
createBlocks.set(shapeId, creatData);
creatData.mesh.isVisible = false;
}
}
}

sps.initParticles = () => {
if (!sps) {
return;
}
for (let p = 0; p < sps.nbParticles; p++) {
const particle = sps.particles[p];
const particleCreateData = createBlocks.get(particle.shapeId);
const initBlock = particleCreateData?.initBlock;
if (!initBlock) {
continue;
}
if (initBlock.position) {
particle.position.copyFrom(initBlock.position());
}
if (initBlock.velocity) {
particle.velocity.copyFrom(initBlock.velocity());
}
if (initBlock.color) {
particle.color?.copyFrom(initBlock.color());
}
if (initBlock.scaling) {
particle.scaling.copyFrom(initBlock.scaling());
}
if (initBlock.rotation) {
particle.rotation.copyFrom(initBlock.rotation());
}
}
};

sps.updateParticle = (particle: SolidParticle) => {
if (!sps) {
return particle;
}
const particleCreateData = createBlocks.get(particle.shapeId);
const updateBlock = particleCreateData?.updateBlock;
if (!updateBlock) {
return particle;
}
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());
}
return particle;
};

this.solidParticleSystem._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.SPSCreateBlock", SPSCreateBlock);
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
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 { ISPSUpdateData } from "./ISPSData";

/**
* Block used to generate initialization function for SPS particles
*/
export class SPSInitBlock extends NodeParticleBlock {
public constructor(name: string) {
super(name);

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.registerOutput("initData", NodeParticleBlockConnectionPointTypes.System);
}

public override getClassName() {
return "SPSInitBlock";
}

public get initData(): NodeParticleConnectionPoint {
return this._outputs[0];
}

public get position(): NodeParticleConnectionPoint {
return this._inputs[0];
}

public get velocity(): NodeParticleConnectionPoint {
return this._inputs[1];
}

public get color(): NodeParticleConnectionPoint {
return this._inputs[2];
}

public get scaling(): NodeParticleConnectionPoint {
return this._inputs[3];
}

public get rotation(): NodeParticleConnectionPoint {
return this._inputs[4];
}

public override _build(state: NodeParticleBuildState) {
const initData = {} as ISPSUpdateData;
if (this.position.isConnected) {
initData.position = () => {
return this.getPosition(state);
};
}
if (this.velocity.isConnected) {
initData.velocity = () => {
return this.getVelocity(state);
};
}
if (this.color.isConnected) {
initData.color = () => {
return this.getColor(state);
};
}
if (this.scaling.isConnected) {
initData.scaling = () => {
return this.getScaling(state);
};
}
if (this.rotation.isConnected) {
initData.rotation = () => {
return this.getRotation(state);
};
}

this.initData._storedValue = initData;
}

private getPosition(state: NodeParticleBuildState) {
if (this.position._storedFunction) {
return this.position._storedFunction(state);
}
return this.position.getConnectedValue(state);
}

private getVelocity(state: NodeParticleBuildState) {
if (this.velocity._storedFunction) {
return this.velocity._storedFunction(state);
}
return this.velocity.getConnectedValue(state);
}

private getColor(state: NodeParticleBuildState) {
if (this.color._storedFunction) {
return this.color._storedFunction(state);
}
return this.color.getConnectedValue(state);
}

private getScaling(state: NodeParticleBuildState) {
if (this.scaling._storedFunction) {
return this.scaling._storedFunction(state);
}
return this.scaling.getConnectedValue(state);
}

private getRotation(state: NodeParticleBuildState) {
if (this.rotation._storedFunction) {
return this.rotation._storedFunction(state);
}
return this.rotation.getConnectedValue(state);
}

public override serialize(): any {
const serializationObject = super.serialize();
return serializationObject;
}

public override _deserialize(serializationObject: any) {
super._deserialize(serializationObject);
}
}

RegisterClass("BABYLON.SPSInitBlock", SPSInitBlock);
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* Mesh shape types for SPS
*/
export enum SPSMeshShapeType {
Box = 0,
Sphere = 1,
Cylinder = 2,
Plane = 3,
Custom = 4,
}
Loading