@@ -2,7 +2,7 @@ import type { Nullable } from "core/types";
22import type { Color4 } from "core/Maths/math.color" ;
33import type { Texture } from "core/Materials/Textures/texture" ;
44import type { Mesh } from "core/Meshes/mesh" ;
5- import type { ColorGradient } from "core/Misc" ;
5+ import type { ColorGradient , FactorGradient } from "core/Misc" ;
66import type { ParticleSystem } from "core/Particles/particleSystem" ;
77import type { IParticleSystem } from "core/Particles/IParticleSystem" ;
88import type { BoxParticleEmitter } from "core/Particles/EmitterTypes/boxParticleEmitter" ;
@@ -42,6 +42,14 @@ import { UpdateColorBlock } from "./Blocks/Update/updateColorBlock";
4242import { UpdateDirectionBlock } from "./Blocks/Update/updateDirectionBlock" ;
4343import { UpdatePositionBlock } from "./Blocks/Update/updatePositionBlock" ;
4444
45+ /** Represents blocks or groups of blocks that can be used in multiple places in the graph, so they are stored in this context to be reused */
46+ type ConversionContext = {
47+ targetStopDurationBlockOutput : NodeParticleConnectionPoint ;
48+ timeToStopTimeRatioBlockGroupOutput : NodeParticleConnectionPoint ;
49+ } ;
50+
51+ type RuntimeConversionContext = Partial < ConversionContext > ;
52+
4553/**
4654 * Converts a ParticleSystem to a NodeParticleSystemSet.
4755 * @param name The name of the node particle system set.
@@ -59,16 +67,16 @@ export async function ConvertToNodeParticleSystemSetAsync(name: string, particle
5967 const promises : Promise < void > [ ] = [ ] ;
6068
6169 for ( const particleSystem of particleSystemsList ) {
62- promises . push ( _ExtractDatafromParticleSystemAsync ( nodeParticleSystemSet , particleSystem ) ) ;
70+ promises . push ( _ExtractDatafromParticleSystemAsync ( nodeParticleSystemSet , particleSystem , { } ) ) ;
6371 }
6472
6573 await Promise . all ( promises ) ;
6674 return nodeParticleSystemSet ;
6775}
6876
69- async function _ExtractDatafromParticleSystemAsync ( newSet : NodeParticleSystemSet , oldSystem : ParticleSystem ) : Promise < void > {
77+ async function _ExtractDatafromParticleSystemAsync ( newSet : NodeParticleSystemSet , oldSystem : ParticleSystem , context : RuntimeConversionContext ) : Promise < void > {
7078 // CreateParticle block
71- const createParticleBlock = _CreateCreateParticleBlock ( oldSystem ) ;
79+ const createParticleBlock = _CreateCreateParticleBlockGroup ( oldSystem , context ) ;
7280
7381 // Emitter Shape block
7482 const shapeBlock = _CreateEmitterShapeBlock ( oldSystem ) ;
@@ -82,27 +90,26 @@ async function _ExtractDatafromParticleSystemAsync(newSet: NodeParticleSystemSet
8290 positionUpdatedParticle . connectTo ( colorUpdateBlock . particle ) ;
8391
8492 // System block
85- const newSystem = _CreateSystemBlock ( oldSystem ) ;
93+ const newSystem = _CreateSystemBlock ( oldSystem , context ) ;
8694 colorUpdateBlock . output . connectTo ( newSystem . particle ) ;
8795
8896 // Register
8997 newSet . systemBlocks . push ( newSystem ) ;
9098}
9199
92- function _CreateSystemBlock ( oldSystem : ParticleSystem ) : SystemBlock {
100+ function _CreateSystemBlock ( oldSystem : ParticleSystem , context : RuntimeConversionContext ) : SystemBlock {
93101 const newSystem = new SystemBlock ( oldSystem . name ) ;
94102
95103 _CreateAndConnectInput ( "Translation pivot" , oldSystem . translationPivot , newSystem . translationPivot ) ;
96104 _CreateAndConnectInput ( "Texture mask" , oldSystem . textureMask , newSystem . textureMask ) ;
105+ const targetStopDurationOutput = _CreateTargetStopDurationInputBlock ( oldSystem , context ) ;
106+ targetStopDurationOutput . connectTo ( newSystem . targetStopDuration ) ;
97107
98108 newSystem . emitRate = oldSystem . emitRate ;
99109 newSystem . manualEmitCount = oldSystem . manualEmitCount ;
100-
101110 newSystem . blendMode = oldSystem . blendMode ;
102111 newSystem . capacity = oldSystem . getCapacity ( ) ;
103- newSystem . targetStopDuration = oldSystem . targetStopDuration ;
104112 newSystem . startDelay = oldSystem . startDelay ;
105- newSystem . targetStopDuration = oldSystem . targetStopDuration ;
106113 newSystem . updateSpeed = oldSystem . updateSpeed ;
107114 newSystem . preWarmCycles = oldSystem . preWarmCycles ;
108115 newSystem . preWarmStepOffset = oldSystem . preWarmStepOffset ;
@@ -123,10 +130,15 @@ function _CreateSystemBlock(oldSystem: ParticleSystem): SystemBlock {
123130 return newSystem ;
124131}
125132
126- function _CreateCreateParticleBlock ( oldSystem : ParticleSystem ) : CreateParticleBlock {
133+ // Create Particle Block Group functions
134+
135+ function _CreateCreateParticleBlockGroup ( oldSystem : ParticleSystem , context : RuntimeConversionContext ) : CreateParticleBlock {
127136 // Create particle
128137 const createParticleBlock = new CreateParticleBlock ( "Create Particle" ) ;
129138
139+ // Lifetime
140+ _CreateParticleLifetimeBlockGroup ( oldSystem , context ) . connectTo ( createParticleBlock . lifeTime ) ;
141+
130142 // Size
131143 const randomSizeBlock = new ParticleRandomBlock ( "Random size" ) ;
132144 _CreateAndConnectInput ( "Min size" , oldSystem . minSize , randomSizeBlock . min ) ;
@@ -156,15 +168,28 @@ function _CreateCreateParticleBlock(oldSystem: ParticleSystem): CreateParticleBl
156168 _CreateAndConnectInput ( "Max Rotation" , oldSystem . maxInitialRotation , randomRotationBlock . max ) ;
157169 randomRotationBlock . output . connectTo ( createParticleBlock . angle ) ;
158170
159- // Lifetime
160- const randomLifetimeBlock = new ParticleRandomBlock ( "Random Lifetime" ) ;
161- _CreateAndConnectInput ( "Min Lifetime" , oldSystem . minLifeTime , randomLifetimeBlock . min ) ;
162- _CreateAndConnectInput ( "Max Lifetime" , oldSystem . maxLifeTime , randomLifetimeBlock . max ) ;
163- randomLifetimeBlock . output . connectTo ( createParticleBlock . lifeTime ) ;
164-
165171 return createParticleBlock ;
166172}
167173
174+ /**
175+ * Creates the group of blocks that represent the particle lifetime
176+ * @param oldSystem The old particle system to migrate
177+ * @param context The system migration context
178+ * @returns The output of the group of blocks that represent the particle lifetime
179+ */
180+ function _CreateParticleLifetimeBlockGroup ( oldSystem : ParticleSystem , context : RuntimeConversionContext ) : NodeParticleConnectionPoint {
181+ if ( oldSystem . targetStopDuration && oldSystem . _lifeTimeGradients && oldSystem . _lifeTimeGradients . length > 0 ) {
182+ context . timeToStopTimeRatioBlockGroupOutput = _CreateTimeToStopTimeRatioBlockGroup ( oldSystem , context ) ;
183+ const gradientBlockGroupOutput = _CreateGradientBlockGroup ( context . timeToStopTimeRatioBlockGroupOutput , oldSystem . _lifeTimeGradients ) ;
184+ return gradientBlockGroupOutput ;
185+ } else {
186+ const randomLifetimeBlock = new ParticleRandomBlock ( "Random Lifetime" ) ;
187+ _CreateAndConnectInput ( "Min Lifetime" , oldSystem . minLifeTime , randomLifetimeBlock . min ) ;
188+ _CreateAndConnectInput ( "Max Lifetime" , oldSystem . maxLifeTime , randomLifetimeBlock . max ) ;
189+ return randomLifetimeBlock . output ;
190+ }
191+ }
192+
168193function _CreateEmitterShapeBlock ( oldSystem : IParticleSystem ) : IShapeBlock {
169194 const emitter = oldSystem . particleEmitterType ;
170195 if ( ! emitter ) {
@@ -536,3 +561,110 @@ function _CreateAndConnectSystemSource(systemBlockName: string, systemSource: No
536561 input . systemSource = systemSource ;
537562 input . output . connectTo ( targetToConnectTo ) ;
538563}
564+
565+ /**
566+ * Creates the target stop duration input block, as it can be shared in multiple places
567+ * This block is stored in the context so the same block is shared in the graph
568+ * @param oldSystem The old particle system to migrate
569+ * @param context The system migration context
570+ * @returns
571+ */
572+ function _CreateTargetStopDurationInputBlock ( oldSystem : ParticleSystem , context : RuntimeConversionContext ) : NodeParticleConnectionPoint {
573+ // If we have already created the target stop duration input block, return it
574+ if ( context . targetStopDurationBlockOutput ) {
575+ return context . targetStopDurationBlockOutput ;
576+ }
577+
578+ // Create the target stop duration input block if not already created
579+ const targetStopDurationInputBlock = new ParticleInputBlock ( "Target Stop Duration" ) ;
580+ targetStopDurationInputBlock . value = oldSystem . targetStopDuration ;
581+
582+ // Save the output in our context to avoid regenerating it again
583+ context . targetStopDurationBlockOutput = targetStopDurationInputBlock . output ;
584+ return context . targetStopDurationBlockOutput ;
585+ }
586+
587+ /**
588+ * Create a group of blocks that calculates the ratio between the actual frame and the target stop duration, clamped between 0 and 1.
589+ * This is used to simulate the behavior of the old particle system where several particle gradient values are affected by the target stop duration.
590+ * This block group is stored in the context so the same group is shared in the graph
591+ * @param oldSystem The old particle system to migrate
592+ * @param context The system migration context
593+ * @returns The ratio block output connection point
594+ */
595+ function _CreateTimeToStopTimeRatioBlockGroup ( oldSystem : ParticleSystem , context : RuntimeConversionContext ) : NodeParticleConnectionPoint {
596+ // If we have already generated this group, return it
597+ if ( context . timeToStopTimeRatioBlockGroupOutput ) {
598+ return context . timeToStopTimeRatioBlockGroupOutput ;
599+ }
600+
601+ context . targetStopDurationBlockOutput = _CreateTargetStopDurationInputBlock ( oldSystem , context ) ;
602+
603+ // Find the ratio between the actual frame and the target stop duration
604+ const ratio = new ParticleMathBlock ( "Frame/Stop Ratio" ) ;
605+ ratio . operation = ParticleMathBlockOperations . Divide ;
606+ _CreateAndConnectSystemSource ( "Actual Frame" , NodeParticleSystemSources . Time , ratio . left ) ;
607+ context . targetStopDurationBlockOutput . connectTo ( ratio . right ) ;
608+
609+ // Make sure values is >=0
610+ const clampMin = new ParticleMathBlock ( "Clamp Min 0" ) ;
611+ clampMin . operation = ParticleMathBlockOperations . Max ;
612+ _CreateAndConnectInput ( "Zero" , 0 , clampMin . left ) ;
613+ ratio . output . connectTo ( clampMin . right ) ;
614+
615+ // Make sure values is <=1
616+ const clampMax = new ParticleMathBlock ( "Clamp Max 1" ) ;
617+ clampMax . operation = ParticleMathBlockOperations . Min ;
618+ _CreateAndConnectInput ( "One" , 1 , clampMax . left ) ;
619+ clampMin . output . connectTo ( clampMax . right ) ;
620+
621+ // Save the group output in our context to avoid regenerating it again
622+ context . timeToStopTimeRatioBlockGroupOutput = clampMax . output ;
623+ return context . timeToStopTimeRatioBlockGroupOutput ;
624+ }
625+
626+ /**
627+ * Creates the blocks that represent a gradient
628+ * @param gradientSelector The value that determines which gradient to use
629+ * @param gradientValues The list of gradient values
630+ * @returns The output connection point of the gradient block
631+ */
632+ function _CreateGradientBlockGroup ( gradientSelector : NodeParticleConnectionPoint , gradientValues : Array < FactorGradient > ) : NodeParticleConnectionPoint {
633+ // Create the gradient block and connect the value that controls the gradient selection
634+ const gradientBlock = new ParticleGradientBlock ( "Gradient Block" ) ;
635+ gradientSelector . connectTo ( gradientBlock . gradient ) ;
636+
637+ // Create the gradient values
638+ for ( let i = 0 ; i < gradientValues . length ; i ++ ) {
639+ const gradientValueBlockGroupOutput = _CreateGradientValueBlockGroup ( gradientValues [ i ] , i ) ;
640+ gradientValueBlockGroupOutput . connectTo ( gradientBlock . inputs [ i + 1 ] ) ;
641+ }
642+
643+ return gradientBlock . output ;
644+ }
645+
646+ /**
647+ * Creates the blocks that represent a gradient value
648+ * This can be either a single value or a random between two values
649+ * @param gradientStep The gradient step data
650+ * @param index The index of the gradient step
651+ * @returns The output connection point of the gradient value block
652+ */
653+ function _CreateGradientValueBlockGroup ( gradientStep : FactorGradient , index : number ) : NodeParticleConnectionPoint {
654+ const gradientValueBlock = new ParticleGradientValueBlock ( "Gradient Value " + index ) ;
655+ gradientValueBlock . reference = gradientStep . gradient ;
656+
657+ if ( gradientStep . factor2 !== undefined ) {
658+ // Create a random between value1 and value2
659+ const randomBlock = new ParticleRandomBlock ( "Random Gradient Value " + index ) ;
660+ randomBlock . lockMode = ParticleRandomBlockLocks . PerParticle ;
661+ _CreateAndConnectInput ( "Value 1" , gradientStep . factor1 , randomBlock . min ) ;
662+ _CreateAndConnectInput ( "Value 2" , gradientStep . factor2 , randomBlock . max ) ;
663+ randomBlock . output . connectTo ( gradientValueBlock . value ) ;
664+ } else {
665+ // Single value
666+ _CreateAndConnectInput ( "Value" , gradientStep . factor1 , gradientValueBlock . value ) ;
667+ }
668+
669+ return gradientValueBlock . output ;
670+ }
0 commit comments