2 * Copyright(c) 2023 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 using System.Collections.Generic;
20 using System.Runtime.InteropServices;
21 using System.ComponentModel;
23 namespace Tizen.NUI.ParticleSystem
25 using Tizen.NUI.BaseComponents;
28 /// Enum defining blending options when rendering the particles.
30 [EditorBrowsable(EditorBrowsableState.Never)]
31 public enum ParticleBlendingMode
39 /// Internal class defining data types stored in the data streams
41 internal enum StreamType
54 /// Class ParticleEmitter creates a single emitter attached to a specified
55 /// View. ParticleEmitter is responsible for spawning and updating particles.
57 /// Emitter must contain:
58 /// ParticleSource - responsible for spawning new particles
59 /// ParticleModifier(s) - responsible for updating particles in the system
61 /// ParticleSource and ParticleModifier callback interfaces should not be accessing
62 /// Event side (NUI) objects. Both callbacks are executed on Update thread.
64 [EditorBrowsable(EditorBrowsableState.Never)]
65 public class ParticleEmitter : BaseHandle
67 internal ParticleEmitter(global::System.IntPtr cPtr, bool cMemoryOwn) : base(cPtr, cMemoryOwn)
72 /// Create an initialized ParticleEmitter.
74 /// <param name="view">View to attach the particle emitter.</param>
75 [EditorBrowsable(EditorBrowsableState.Never)]
76 public ParticleEmitter(View view) : this(Interop.ParticleEmitter.New(view.SwigCPtr), true)
78 mProxy = new ParticleEmitterProxy(this);
79 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
85 /// <param name="particleEmitter">Source object to copy.</param>
86 [EditorBrowsable(EditorBrowsableState.Never)]
87 public ParticleEmitter( ParticleEmitter particleEmitter) : this(Interop. ParticleEmitter.New( ParticleEmitter.getCPtr(particleEmitter)), true)
89 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
95 [EditorBrowsable(EditorBrowsableState.Never)]
96 protected override void Dispose(DisposeTypes type)
103 /// Assignment operator.
105 /// <param name="particleEmitter">Source object to be assigned.</param>
106 /// <returns>Reference to this.</returns>
107 internal ParticleEmitter Assign( ParticleEmitter particleEmitter)
109 ParticleEmitter ret = new ParticleEmitter(Interop.ParticleEmitter.Assign(SwigCPtr, ParticleEmitter.getCPtr(particleEmitter)), false);
110 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
115 /// Raises the window to the top of the window stack.
117 /// <param name="particleEmitter">Source object to copy.</param>
118 [EditorBrowsable(EditorBrowsableState.Never)]
119 public void SetSource<T>(ParticleSource<T> source) where T : ParticleSourceInterface, new()
122 source.SetEmitter(this);
125 Interop.ParticleEmitter.SetSource(SwigCPtr, source.SwigCPtr);
126 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
130 /// Maximum particle count
132 [EditorBrowsable(EditorBrowsableState.Never)]
133 public uint ParticleCount
137 var value = Interop.ParticleEmitter.GetParticleCount(SwigCPtr);
138 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
144 Interop.ParticleEmitter.SetParticleCount(SwigCPtr, value);
145 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
150 /// Rate of emission per second
153 /// EmissionRate defines number of particles emitted per second.
155 [EditorBrowsable(EditorBrowsableState.Never)]
156 public uint EmissionRate
160 var value = Interop.ParticleEmitter.GetEmissionRate(SwigCPtr);
161 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
166 Interop.ParticleEmitter.SetEmissionRate(SwigCPtr, value);
167 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
172 /// Initial particle count
175 /// Initial number of particles to be emitted immediately after emitter starts. It allows
176 /// initial burst emission. By default it's set to 0.
178 [EditorBrowsable(EditorBrowsableState.Never)]
179 public uint InitialParticleCount
183 var value = Interop.ParticleEmitter.GetInitialParticleCount(SwigCPtr);
184 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
189 Interop.ParticleEmitter.SetInitialParticleCount(SwigCPtr, value);
190 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
195 /// Limit of active particles in the system
198 /// Active particles in the system can be limited without changing <see cref="ParticleCount"/>.
200 [EditorBrowsable(EditorBrowsableState.Never)]
201 public uint ActiveParticleLimit
204 var value = Interop.ParticleEmitter.GetActiveParticlesLimit(SwigCPtr);
205 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
210 Interop.ParticleEmitter.SetActiveParticlesLimit(SwigCPtr, value);
211 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
217 /// Gets/sets blending mode for particle renderer
220 /// Currently two blending modes are supported: Additive and Screen (advanced blending mode).
221 /// <see cref="ParticleBlendingMode"/>
223 [EditorBrowsable(EditorBrowsableState.Never)]
224 public ParticleBlendingMode RendererBlendingMode
228 var value = Interop.ParticleEmitter.GetBlendingMode(SwigCPtr);
229 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
230 return (ParticleBlendingMode)value;
234 Interop.ParticleEmitter.SetBlendingMode(SwigCPtr, value);
235 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
240 /// Sets texture to be used by the renderer
242 [EditorBrowsable(EditorBrowsableState.Never)]
243 public Texture RendererTexture
247 Interop.ParticleEmitter.SetTexture(SwigCPtr, value.SwigCPtr);
248 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
253 /// Adds ParticleModifier to the stack
256 /// ParticleEmitter implements a stack of modifiers which are responsible for
257 /// updating particles in the system. The stack is processed such as result of
258 /// previous modifier is an input for next modifier.
260 /// <param name="modifier">Valid modifier object</param>
261 [EditorBrowsable(EditorBrowsableState.Never)]
262 public void AddModifier<T>(ParticleModifier<T> modifier) where T : ParticleModifierInterface, new()
265 modifier.SetEmitter(this);
267 Interop.ParticleEmitter.AddModifier(SwigCPtr, modifier.SwigCPtr);
268 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
272 /// Returns associated ParticleSource object
274 /// <returns>Valid ParticleSource object or null</returns>
275 [EditorBrowsable(EditorBrowsableState.Never)]
276 public ParticleSource<T> GetSource<T>() where T : ParticleSourceInterface, new()
278 IntPtr cPtr = Interop.ParticleEmitter.GetSource(SwigCPtr);
279 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
280 ParticleSource<T> ret = (cPtr == IntPtr.Zero) ? null : Registry.GetManagedBaseHandleFromNativePtr(cPtr) as ParticleSource<T>;
285 /// Returns modifier at specified index
287 /// <param name="index">Index within modifier stack</param>
288 /// <returns>Valid ParticleModifier object or null</returns>
289 [EditorBrowsable(EditorBrowsableState.Never)]
290 public ParticleModifier<ParticleModifierInterface> GetModifierAt(uint index)
292 IntPtr cPtr = Interop.ParticleEmitter.GetModifierAt(SwigCPtr, index);
293 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
294 ParticleModifier<ParticleModifierInterface> ret = (cPtr == IntPtr.Zero) ? null : Registry.GetManagedBaseHandleFromNativePtr(cPtr) as ParticleModifier<ParticleModifierInterface>;
299 /// Starts emission of particles.
301 [EditorBrowsable(EditorBrowsableState.Never)]
304 Interop.ParticleEmitter.Start(SwigCPtr);
305 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
309 /// Stops emission of particles.
311 [EditorBrowsable(EditorBrowsableState.Never)]
314 Interop.ParticleEmitter.Stop(SwigCPtr);
315 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
319 /// Adds local (not used by shader) data stream to the particle emitter
322 /// Adds new stream of float type.
324 /// <param name="defaultValue">Default value to fill the stream with</param>
325 /// <returns>Index of newly created data stream</returns>
326 [EditorBrowsable(EditorBrowsableState.Never)]
327 public uint AddLocalStreamFloat(float defaultValue)
329 var result = Interop.ParticleEmitter.AddLocalStream_Float(SwigCPtr, (uint)StreamType.Float, defaultValue, sizeof(float));
330 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
335 /// Adds local (not used by shader) data stream to the particle emitter
338 /// Adds new stream of Vector2 type.
340 /// <param name="defaultValue">Default value to fill the stream with</param>
341 /// <returns>Index of newly created data stream</returns>
342 [EditorBrowsable(EditorBrowsableState.Never)]
343 public uint AddLocalStreamVector2(Vector2 defaultValue)
345 var result = Interop.ParticleEmitter.AddLocalStream_Vector2(SwigCPtr, (uint)StreamType.FloatVector2, defaultValue.SwigCPtr.Handle, sizeof(float)*2);
346 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
351 /// Adds local (not used by shader) data stream to the particle emitter
354 /// Adds new stream of Vector3 type.
356 /// <param name="defaultValue">Default value to fill the stream with</param>
357 /// <returns>Index of newly created data stream</returns>
358 [EditorBrowsable(EditorBrowsableState.Never)]
359 public uint AddLocalStreamVector3(Vector3 defaultValue)
361 var result = Interop.ParticleEmitter.AddLocalStream_Vector3(SwigCPtr, (uint)StreamType.FloatVector3, defaultValue.SwigCPtr.Handle, sizeof(float)*3);
362 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
367 /// Adds local (not used by shader) data stream to the particle emitter
370 /// Adds new stream of Vector4 type.
372 /// <param name="defaultValue">Default value to fill the stream with</param>
373 /// <returns>Index of newly created data stream</returns>
374 [EditorBrowsable(EditorBrowsableState.Never)]
375 public uint AddLocalStreamVector4(Vector4 defaultValue)
377 var result = Interop.ParticleEmitter.AddLocalStream_Vector4(SwigCPtr, (uint)StreamType.FloatVector4, defaultValue.SwigCPtr.Handle, sizeof(float)*4);
378 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
382 // Internal proxy object to be used on the update thread
383 internal ParticleEmitterProxy EmitterProxy => mProxy;
384 private ParticleEmitterProxy mProxy = null;
388 /// This class provides functionality that can be used inside the Source/Modifier callbacks.
390 [EditorBrowsable(EditorBrowsableState.Never)]
391 public class ParticleEmitterProxy
393 internal ParticleEmitterProxy(ParticleEmitter emitter)
395 mEmitterBasePtr = emitter.SwigCPtr.Handle;
400 /// Creates new particle
403 /// Function may fail and return null if current number of particles exceeds limits of emitter.
404 /// Particle is valid only inside the callback and must not be stored and used anywhere else. Otherwise
405 /// the behaviour is undefined.
407 /// <param name="lifetime">Lifetime of the particle in seconds</param>
408 /// <returns>New Particle object or null</returns>
409 [EditorBrowsable(EditorBrowsableState.Never)]
410 public Particle NewParticle( float lifetime )
412 var result = Interop.ParticleEmitter.NewParticle(mEmitterBasePtr, lifetime);
413 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
416 // TODO: new particles should be coming form cached queue
417 return new Particle(mEmitter.SwigCPtr, (uint)result);
423 /// Acquires list of Particles of specified length
426 /// The function should be use internally only. Native side passes list of indices of particles (int[]).
427 /// Before calling the callback the indices must be marshalled and converted into the Particle objects.
429 /// Internal Particle cache is used to speed up acquiring new Particle.
431 /// <param name="nativePtr">Native pointer to the list of indices (int32)</param>
432 /// <param name="count">Number of elements</param>
433 /// <returns>List of Particle objects</returns>
434 [EditorBrowsable(EditorBrowsableState.Never)]
435 internal List<Particle> AcquireParticleList(IntPtr nativePtr, uint count)
437 // Populate enough particles into cache
438 while(mParticleCache.Count < count)
440 mParticleCache.Push(new Particle(mEmitter.SwigCPtr, 0));
443 List<Particle> retval = new List<Particle>();
444 for (var i = 0; i < count; ++i)
446 var particleIndex = Marshal.ReadInt32(nativePtr, i * 4);
447 Particle p = mParticleCache.Pop();
448 p.Index = (uint)particleIndex;
456 /// Releases list of Particles back into the pool
459 /// Acquired particles come from internal pool and must be returned so then they can
462 /// <param name="particles">List of particles to be returned</param>
463 [EditorBrowsable(EditorBrowsableState.Never)]
464 internal void ReleaseParticleList(List<Particle> particles)
466 // return particles back into the pull
467 for(var i = 0; i < particles.Count; ++i)
469 mParticleCache.Push(particles[i]);
472 // clear the list (probably not needed?)
477 /// Adds local particle data stream of float values
479 /// <param name="defaultValue">Default value</param>
480 /// <returns>Index of new stream</returns>
481 [EditorBrowsable(EditorBrowsableState.Never)]
482 public uint AddLocalStreamFloat(float defaultValue)
484 return mEmitter.AddLocalStreamFloat(defaultValue);
488 /// Adds local particle data stream of Vector2 values
490 /// <param name="defaultValue">Default value</param>
491 /// <returns>Index of new stream</returns>
492 [EditorBrowsable(EditorBrowsableState.Never)]
493 public uint AddLocalStreamVector2(Vector2 defaultValue)
495 return mEmitter.AddLocalStreamVector2(defaultValue);
499 /// Adds local particle data stream of Vector3 values
501 /// <param name="defaultValue">Default value</param>
502 /// <returns>Index of new stream</returns>
503 [EditorBrowsable(EditorBrowsableState.Never)]
504 public uint AddLocalStreamVector3(Vector3 defaultValue)
506 return mEmitter.AddLocalStreamVector3(defaultValue);
510 /// Adds local particle data stream of Vector4 values
512 /// <param name="defaultValue">Default value</param>
513 /// <returns>Index of new stream</returns>
514 [EditorBrowsable(EditorBrowsableState.Never)]
515 public uint AddLocalStreamVector4(Vector4 defaultValue)
517 return mEmitter.AddLocalStreamVector4(defaultValue);
520 // Stack of cached particles
521 private Stack<Particle> mParticleCache = new Stack<Particle>();
523 private IntPtr mEmitterBasePtr;
524 private ParticleEmitter mEmitter;
529 /// Register binding Source/Modifier interface to the pointer of a native counterpart
531 /// <typeparam name="T">Class type of objects to be stored in the register</typeparam>
532 internal class ParticleInterfaceRegister<T> where T : class
534 internal void Register(IntPtr cPtr, T iface)
539 mInterfaces.Add(iface);
543 internal bool Remove(IntPtr cPtr)
547 var result = mBasePtr.FindIndex(0, x => x == cPtr);
550 mBasePtr.RemoveAt(result);
551 mInterfaces.RemoveAt(result);
558 internal bool Remove(T iface)
562 var result = mInterfaces.FindIndex(0, x => x.Equals(iface));
565 mBasePtr.RemoveAt(result);
566 mInterfaces.RemoveAt(result);
573 internal T Get(IntPtr cPtr)
575 var result = mBasePtr.FindIndex(0, x => x == cPtr );
578 return mInterfaces[result];
584 private List<IntPtr> mBasePtr = new List<IntPtr>();
585 private List<T> mInterfaces = new List<T>();