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 public enum ParticleBlendingMode
38 /// Internal class defining data types stored in the data streams
40 internal enum StreamType
53 /// Class ParticleEmitter creates a single emitter attached to a specified
54 /// View. ParticleEmitter is responsible for spawning and updating particles.
56 /// Emitter must contain:
57 /// ParticleSource - responsible for spawning new particles
58 /// ParticleModifier(s) - responsible for updating particles in the system
60 /// ParticleSource and ParticleModifier callback interfaces should not be accessing
61 /// Event side (NUI) objects. Both callbacks are executed on Update thread.
63 public class ParticleEmitter : BaseHandle
65 internal ParticleEmitter(global::System.IntPtr cPtr, bool cMemoryOwn) : base(cPtr, cMemoryOwn)
70 /// Create an initialized ParticleEmitter.
72 /// <param name="view">View to attach the particle emitter.</param>
73 [EditorBrowsable(EditorBrowsableState.Never)]
74 public ParticleEmitter(View view) : this(Interop.ParticleEmitter.New(view.SwigCPtr), true)
76 mProxy = new ParticleEmitterProxy(this);
77 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
83 /// <param name="particleEmitter">Source object to copy.</param>
84 [EditorBrowsable(EditorBrowsableState.Never)]
85 public ParticleEmitter( ParticleEmitter particleEmitter) : this(Interop. ParticleEmitter.New( ParticleEmitter.getCPtr(particleEmitter)), true)
87 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
91 /// Assignment operator.
93 /// <param name="particleEmitter">Source object to be assigned.</param>
94 /// <returns>Reference to this.</returns>
95 internal ParticleEmitter Assign( ParticleEmitter particleEmitter)
97 ParticleEmitter ret = new ParticleEmitter(Interop.ParticleEmitter.Assign(SwigCPtr, ParticleEmitter.getCPtr(particleEmitter)), false);
98 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
103 /// Raises the window to the top of the window stack.
105 /// <param name="particleEmitter">Source object to copy.</param>
106 [EditorBrowsable(EditorBrowsableState.Never)]
107 public void SetSource<T>(ParticleSource<T> source) where T : ParticleSourceInterface, new()
110 source.SetEmitter(this);
113 Interop.ParticleEmitter.SetSource(SwigCPtr, source.SwigCPtr);
114 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
118 /// Maximum particle count
120 [EditorBrowsable(EditorBrowsableState.Never)]
121 public uint ParticleCount
125 var value = Interop.ParticleEmitter.GetParticleCount(SwigCPtr);
126 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
132 Interop.ParticleEmitter.SetParticleCount(SwigCPtr, value);
133 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
138 /// Rate of emission per second
141 /// EmissionRate defines number of particles emitted per second.
143 [EditorBrowsable(EditorBrowsableState.Never)]
144 public uint EmissionRate
148 var value = Interop.ParticleEmitter.GetEmissionRate(SwigCPtr);
149 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
154 Interop.ParticleEmitter.SetEmissionRate(SwigCPtr, value);
155 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
160 /// Initial particle count
163 /// Initial number of particles to be emitted immediately after emitter starts. It allows
164 /// initial burst emission. By default it's set to 0.
166 [EditorBrowsable(EditorBrowsableState.Never)]
167 public uint InitialParticleCount
171 var value = Interop.ParticleEmitter.GetInitialParticleCount(SwigCPtr);
172 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
177 Interop.ParticleEmitter.SetInitialParticleCount(SwigCPtr, value);
178 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
183 /// Limit of active particles in the system
186 /// Active particles in the system can be limited without changing <see cref="ParticleCount"/>.
188 [EditorBrowsable(EditorBrowsableState.Never)]
189 public uint ActiveParticleLimit
192 var value = Interop.ParticleEmitter.GetActiveParticlesLimit(SwigCPtr);
193 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
198 Interop.ParticleEmitter.SetActiveParticlesLimit(SwigCPtr, value);
199 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
205 /// Gets/sets blending mode for particle renderer
208 /// Currently two blending modes are supported: Additive and Screen (advanced blending mode).
209 /// <see cref="ParticleBlendingMode"/>
211 [EditorBrowsable(EditorBrowsableState.Never)]
212 public ParticleBlendingMode RendererBlendingMode
216 var value = Interop.ParticleEmitter.GetBlendingMode(SwigCPtr);
217 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
218 return (ParticleBlendingMode)value;
222 Interop.ParticleEmitter.SetBlendingMode(SwigCPtr, value);
223 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
228 /// Gets/sets texture to be used by the renderer
230 [EditorBrowsable(EditorBrowsableState.Never)]
231 public Texture RendererTexture
235 Interop.ParticleEmitter.SetTexture(SwigCPtr, value.SwigCPtr);
236 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
241 /// Adds ParticleModifier to the stack
244 /// ParticleEmitter implements a stack of modifiers which are responsible for
245 /// updating particles in the system. The stack is processed such as result of
246 /// previous modifier is an input for next modifier.
248 /// <param name="modifier">Valid modifier object</param>
249 [EditorBrowsable(EditorBrowsableState.Never)]
250 public void AddModifier<T>(ParticleModifier<T> modifier) where T : ParticleModifierInterface, new()
253 modifier.SetEmitter(this);
255 Interop.ParticleEmitter.AddModifier(SwigCPtr, modifier.SwigCPtr);
256 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
260 /// Returns associated ParticleSource object
262 /// <returns>Valid ParticleSource object or null</returns>
263 [EditorBrowsable(EditorBrowsableState.Never)]
264 public ParticleSource<T> GetSource<T>() where T : ParticleSourceInterface, new()
266 IntPtr cPtr = Interop.ParticleEmitter.GetSource(SwigCPtr);
267 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
268 ParticleSource<T> ret = (cPtr == IntPtr.Zero) ? null : Registry.GetManagedBaseHandleFromNativePtr(cPtr) as ParticleSource<T>;
273 /// Returns modifier at specified index
275 /// <param name="index">Index within modifier stack</param>
276 /// <returns>Valid ParticleModifier object or null</returns>
277 [EditorBrowsable(EditorBrowsableState.Never)]
278 public ParticleModifier<ParticleModifierInterface> GetModifierAt(uint index)
280 IntPtr cPtr = Interop.ParticleEmitter.GetModifierAt(SwigCPtr, index);
281 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
282 ParticleModifier<ParticleModifierInterface> ret = (cPtr == IntPtr.Zero) ? null : Registry.GetManagedBaseHandleFromNativePtr(cPtr) as ParticleModifier<ParticleModifierInterface>;
287 /// Starts emission of particles.
289 [EditorBrowsable(EditorBrowsableState.Never)]
292 Interop.ParticleEmitter.Start(SwigCPtr);
293 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
297 /// Stops emission of particles.
299 [EditorBrowsable(EditorBrowsableState.Never)]
302 Interop.ParticleEmitter.Stop(SwigCPtr);
303 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
307 /// Adds local (not used by shader) data stream to the particle emitter
310 /// Adds new stream of float type.
312 /// <param name="defaultValue">Default value to fill the stream with</param>
313 /// <returns>Index of newly created data stream</returns>
314 [EditorBrowsable(EditorBrowsableState.Never)]
315 public unsafe uint AddLocalStreamFloat(float defaultValue)
317 var result = Interop.ParticleEmitter.AddLocalStream(SwigCPtr, (uint)StreamType.Float, &defaultValue, sizeof(float));
318 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
323 /// Adds local (not used by shader) data stream to the particle emitter
326 /// Adds new stream of Vector2 type.
328 /// <param name="defaultValue">Default value to fill the stream with</param>
329 /// <returns>Index of newly created data stream</returns>
330 [EditorBrowsable(EditorBrowsableState.Never)]
331 public unsafe uint AddLocalStreamVector2(Vector2 defaultValue)
333 var result = Interop.ParticleEmitter.AddLocalStream(SwigCPtr, (uint)StreamType.FloatVector2, (void*)defaultValue.SwigCPtr.Handle, sizeof(float)*2);
334 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
339 /// Adds local (not used by shader) data stream to the particle emitter
342 /// Adds new stream of Vector3 type.
344 /// <param name="defaultValue">Default value to fill the stream with</param>
345 /// <returns>Index of newly created data stream</returns>
346 [EditorBrowsable(EditorBrowsableState.Never)]
347 public unsafe uint AddLocalStreamVector3(Vector3 defaultValue)
349 var result = Interop.ParticleEmitter.AddLocalStream(SwigCPtr, (uint)StreamType.FloatVector3, (void*)defaultValue.SwigCPtr.Handle, sizeof(float)*3);
350 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
355 /// Adds local (not used by shader) data stream to the particle emitter
358 /// Adds new stream of Vector4 type.
360 /// <param name="defaultValue">Default value to fill the stream with</param>
361 /// <returns>Index of newly created data stream</returns>
362 [EditorBrowsable(EditorBrowsableState.Never)]
363 public unsafe uint AddLocalStreamVector4(Vector4 defaultValue)
365 var result = Interop.ParticleEmitter.AddLocalStream(SwigCPtr, (uint)StreamType.FloatVector4, (void*)defaultValue.SwigCPtr.Handle, sizeof(float)*4);
366 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
370 // Internal proxy object to be used on the update thread
371 internal ParticleEmitterProxy EmitterProxy => mProxy;
372 private ParticleEmitterProxy mProxy = null;
376 /// This class provides functionality that can be used inside the Source/Modifier callbacks.
378 [EditorBrowsable(EditorBrowsableState.Never)]
379 public class ParticleEmitterProxy
381 internal ParticleEmitterProxy(ParticleEmitter emitter)
383 mEmitterBasePtr = emitter.SwigCPtr.Handle;
388 /// Creates new particle
391 /// Function may fail and return null if current number of particles exceeds limits of emitter.
392 /// Particle is valid only inside the callback and must not be stored and used anywhere else. Otherwise
393 /// the behaviour is undefined.
395 /// <param name="lifetime">Lifetime of the particle in seconds</param>
396 /// <returns>New Particle object or null</returns>
397 [EditorBrowsable(EditorBrowsableState.Never)]
398 public Particle NewParticle( float lifetime )
400 var result = Interop.ParticleEmitter.NewParticle(mEmitterBasePtr, lifetime);
401 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
404 // TODO: new particles should be coming form cached queue
405 return new Particle(mEmitter.SwigCPtr, (uint)result);
411 /// Acquires list of Particles of specified length
414 /// The function should be use internally only. Native side passes list of indices of particles (int[]).
415 /// Before calling the callback the indices must be marshalled and converted into the Particle objects.
417 /// Internal Particle cache is used to speed up acquiring new Particle.
419 /// <param name="nativePtr">Native pointer to the list of indices (int32)</param>
420 /// <param name="count">Number of elements</param>
421 /// <returns>List of Particle objects</returns>
422 [EditorBrowsable(EditorBrowsableState.Never)]
423 internal List<Particle> AcquireParticleList(IntPtr nativePtr, uint count)
425 // Populate enough particles into cache
426 while(mParticleCache.Count < count)
428 mParticleCache.Push(new Particle(mEmitter.SwigCPtr, 0));
431 List<Particle> retval = new List<Particle>();
432 for (var i = 0; i < count; ++i)
434 var particleIndex = Marshal.ReadInt32(nativePtr, i * 4);
435 Particle p = mParticleCache.Pop();
436 p.Index = (uint)particleIndex;
444 /// Releases list of Particles back into the pool
447 /// Acquired particles come from internal pool and must be returned so then they can
450 /// <param name="particles">List of particles to be returned</param>
451 [EditorBrowsable(EditorBrowsableState.Never)]
452 internal void ReleaseParticleList(List<Particle> particles)
454 // return particles back into the pull
455 for(var i = 0; i < particles.Count; ++i)
457 mParticleCache.Push(particles[i]);
460 // clear the list (probably not needed?)
465 /// Adds local particle data stream of float values
467 /// <param name="defaultValue">Default value</param>
468 /// <returns>Index of new stream</returns>
469 [EditorBrowsable(EditorBrowsableState.Never)]
470 public uint AddLocalStreamFloat(float defaultValue)
472 return mEmitter.AddLocalStreamFloat(defaultValue);
476 /// Adds local particle data stream of Vector2 values
478 /// <param name="defaultValue">Default value</param>
479 /// <returns>Index of new stream</returns>
480 [EditorBrowsable(EditorBrowsableState.Never)]
481 public uint AddLocalStreamVector2(Vector2 defaultValue)
483 return mEmitter.AddLocalStreamVector2(defaultValue);
487 /// Adds local particle data stream of Vector3 values
489 /// <param name="defaultValue">Default value</param>
490 /// <returns>Index of new stream</returns>
491 [EditorBrowsable(EditorBrowsableState.Never)]
492 public uint AddLocalStreamVector3(Vector3 defaultValue)
494 return mEmitter.AddLocalStreamVector3(defaultValue);
498 /// Adds local particle data stream of Vector4 values
500 /// <param name="defaultValue">Default value</param>
501 /// <returns>Index of new stream</returns>
502 [EditorBrowsable(EditorBrowsableState.Never)]
503 public uint AddLocalStreamVector4(Vector4 defaultValue)
505 return mEmitter.AddLocalStreamVector4(defaultValue);
508 // Stack of cached particles
509 private Stack<Particle> mParticleCache = new Stack<Particle>();
511 private IntPtr mEmitterBasePtr;
512 private ParticleEmitter mEmitter;
517 /// Register binding Source/Modifier interface to the pointer of a native counterpart
519 /// <typeparam name="T">Class type of objects to be stored in the register</typeparam>
520 internal class ParticleInterfaceRegister<T> where T : class
522 internal void Register(IntPtr cPtr, T iface)
527 mInterfaces.Add(iface);
531 internal bool Remove(IntPtr cPtr)
535 var result = mBasePtr.FindIndex(0, x => x == cPtr);
538 mBasePtr.RemoveAt(result);
539 mInterfaces.RemoveAt(result);
546 internal bool Remove(T iface)
550 var result = mInterfaces.FindIndex(0, x => x.Equals(iface));
553 mBasePtr.RemoveAt(result);
554 mInterfaces.RemoveAt(result);
561 internal T Get(IntPtr cPtr)
563 var result = mBasePtr.FindIndex(0, x => x == cPtr );
566 return mInterfaces[result];
572 private List<IntPtr> mBasePtr = new List<IntPtr>();
573 private List<T> mInterfaces = new List<T>();