[NUI] Remove unsafe functions
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / ParticleSystem / ParticleEmitter.cs
1 /*
2  * Copyright(c) 2023 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 using System;
19 using System.Collections.Generic;
20 using System.Runtime.InteropServices;
21 using System.ComponentModel;
22
23 namespace Tizen.NUI.ParticleSystem
24 {
25     using Tizen.NUI.BaseComponents;
26
27     /// <summary>
28     /// Enum defining blending options when rendering the particles.
29     /// </summary>
30     [EditorBrowsable(EditorBrowsableState.Never)]
31     public enum ParticleBlendingMode
32     {
33         Additive = 0,
34         Screen = 1,
35         Default = Additive
36     }
37
38     /// <summary>
39     /// Internal class defining data types stored in the data streams
40     /// </summary>
41     internal enum StreamType
42     {
43         Float = 0,
44         FloatVector2 = 1,
45         FloatVector3 = 2,
46         FloatVector4 = 3,
47         Integer = 4,
48         IntVector2 = 5,
49         IntVector3 = 6,
50         IntVector4 = 7,
51     }
52     
53     /// <summary>
54     /// Class ParticleEmitter creates a single emitter attached to a specified
55     /// View. ParticleEmitter is responsible for spawning and updating particles.
56     ///
57     /// Emitter must contain:
58     /// ParticleSource - responsible for spawning new particles
59     /// ParticleModifier(s) - responsible for updating particles in the system
60     ///
61     /// ParticleSource and ParticleModifier callback interfaces should not be accessing
62     /// Event side (NUI) objects. Both callbacks are executed on Update thread.
63     /// </summary>
64     [EditorBrowsable(EditorBrowsableState.Never)]
65     public class ParticleEmitter : BaseHandle
66     {
67         internal ParticleEmitter(global::System.IntPtr cPtr, bool cMemoryOwn) : base(cPtr, cMemoryOwn)
68         {
69         }
70
71         /// <summary>
72         /// Create an initialized ParticleEmitter.
73         /// </summary>
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)
77         {
78             mProxy = new ParticleEmitterProxy(this);
79             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
80         }
81         
82         /// <summary>
83         /// Copy constructor.
84         /// </summary>
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)
88         {
89             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
90         }
91
92         /// <summary>
93         /// Dispose.
94         /// </summary>
95         [EditorBrowsable(EditorBrowsableState.Never)]
96         protected override void Dispose(DisposeTypes type)
97         {
98             if (disposed) return;
99             base.Dispose(type);
100         }
101
102         /// <summary>
103         /// Assignment operator.
104         /// </summary>
105         /// <param name="particleEmitter">Source object to be assigned.</param>
106         /// <returns>Reference to this.</returns>
107         internal ParticleEmitter Assign( ParticleEmitter particleEmitter)
108         {
109             ParticleEmitter ret = new ParticleEmitter(Interop.ParticleEmitter.Assign(SwigCPtr, ParticleEmitter.getCPtr(particleEmitter)), false);
110             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
111             return ret;
112         }
113         
114         /// <summary>
115         /// Raises the window to the top of the window stack.
116         /// </summary>
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()
120         {
121             // update interface
122             source.SetEmitter(this);
123             
124             // Set native source
125             Interop.ParticleEmitter.SetSource(SwigCPtr, source.SwigCPtr);
126             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
127         }
128         
129         /// <summary>
130         /// Maximum particle count
131         /// </summary>
132         [EditorBrowsable(EditorBrowsableState.Never)]
133         public uint ParticleCount
134         {
135             get
136             {
137                 var value = Interop.ParticleEmitter.GetParticleCount(SwigCPtr);
138                 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
139
140                 return value;
141             }
142             set
143             {
144                 Interop.ParticleEmitter.SetParticleCount(SwigCPtr, value);
145                 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
146             }
147         }
148
149         /// <summary>
150         /// Rate of emission per second
151         /// </summary>
152         /// <remarks>
153         /// EmissionRate defines number of particles emitted per second.
154         /// </remarks>
155         [EditorBrowsable(EditorBrowsableState.Never)]
156         public uint EmissionRate
157         {
158             get
159             {
160                 var value = Interop.ParticleEmitter.GetEmissionRate(SwigCPtr);
161                 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
162                 return value;
163             }
164             set
165             {
166                 Interop.ParticleEmitter.SetEmissionRate(SwigCPtr, value);
167                 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
168             }
169         }
170         
171         /// <summary>
172         /// Initial particle count
173         /// </summary>
174         /// <remarks>
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.
177         /// </remarks>
178         [EditorBrowsable(EditorBrowsableState.Never)]
179         public uint InitialParticleCount
180         {
181             get
182             {
183                 var value = Interop.ParticleEmitter.GetInitialParticleCount(SwigCPtr);
184                 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
185                 return value;
186             }
187             set 
188             {
189                 Interop.ParticleEmitter.SetInitialParticleCount(SwigCPtr, value);
190                 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve(); 
191             }
192         }
193         
194         /// <summary>
195         /// Limit of active particles in the system
196         /// </summary>
197         /// <remarks>
198         /// Active particles in the system can be limited without changing <see cref="ParticleCount"/>.
199         /// </remarks>
200         [EditorBrowsable(EditorBrowsableState.Never)]
201         public uint ActiveParticleLimit
202         {
203             get{
204                 var value = Interop.ParticleEmitter.GetActiveParticlesLimit(SwigCPtr);
205                 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
206                 return value;
207             }
208             set 
209             {
210                 Interop.ParticleEmitter.SetActiveParticlesLimit(SwigCPtr, value);
211                 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
212             }
213             
214         }
215
216         /// <summary>
217         /// Gets/sets blending mode for particle renderer
218         /// </summary>
219         /// <remarks>
220         /// Currently two blending modes are supported: Additive and Screen (advanced blending mode).
221         /// <see cref="ParticleBlendingMode"/>
222         /// </remarks>
223         [EditorBrowsable(EditorBrowsableState.Never)]
224         public ParticleBlendingMode RendererBlendingMode
225         {
226             get
227             {                
228                 var value = Interop.ParticleEmitter.GetBlendingMode(SwigCPtr);
229                 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
230                 return (ParticleBlendingMode)value;
231             }
232             set
233             {
234                 Interop.ParticleEmitter.SetBlendingMode(SwigCPtr, value);
235                 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
236             }
237         }
238         
239         /// <summary>
240         /// Sets texture to be used by the renderer
241         /// </summary>
242         [EditorBrowsable(EditorBrowsableState.Never)]
243         public Texture RendererTexture
244         {
245             set
246             {
247                 Interop.ParticleEmitter.SetTexture(SwigCPtr, value.SwigCPtr);
248                 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
249             }
250         }
251         
252         /// <summary>
253         /// Adds ParticleModifier to the stack
254         /// </summary>
255         /// <remarks>
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.
259         /// </remarks>
260         /// <param name="modifier">Valid modifier object</param>
261         [EditorBrowsable(EditorBrowsableState.Never)]
262         public void AddModifier<T>(ParticleModifier<T> modifier) where T : ParticleModifierInterface, new()
263         {
264             // update interface
265             modifier.SetEmitter(this);
266             
267             Interop.ParticleEmitter.AddModifier(SwigCPtr, modifier.SwigCPtr);
268             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
269         }
270
271         /// <summary>
272         /// Returns associated ParticleSource object
273         /// </summary>
274         /// <returns>Valid ParticleSource object or null</returns>
275         [EditorBrowsable(EditorBrowsableState.Never)]
276         public ParticleSource<T> GetSource<T>() where T : ParticleSourceInterface, new()
277         {
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>;
281             return ret;
282         }
283
284         /// <summary>
285         /// Returns modifier at specified index
286         /// </summary>
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)
291         {
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>;
295             return ret;
296         }
297
298         /// <summary>
299         /// Starts emission of particles.
300         /// </summary>
301         [EditorBrowsable(EditorBrowsableState.Never)]
302         public void Start()
303         {
304             Interop.ParticleEmitter.Start(SwigCPtr);
305             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
306         }
307
308         /// <summary>
309         /// Stops emission of particles.
310         /// </summary>
311         [EditorBrowsable(EditorBrowsableState.Never)]
312         public void Stop()
313         {
314             Interop.ParticleEmitter.Stop(SwigCPtr);
315             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
316         }
317         
318         /// <summary>
319         /// Adds local (not used by shader) data stream to the particle emitter
320         /// </summary>
321         /// <remarks>
322         /// Adds new stream of float type.
323         /// </remarks>
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)
328         {
329             var result = Interop.ParticleEmitter.AddLocalStream_Float(SwigCPtr, (uint)StreamType.Float, defaultValue, sizeof(float));
330             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
331             return result;
332         }
333         
334         /// <summary>
335         /// Adds local (not used by shader) data stream to the particle emitter
336         /// </summary>
337         /// <remarks>
338         /// Adds new stream of Vector2 type.
339         /// </remarks>
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)
344         {
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();
347             return result;
348         }
349         
350         /// <summary>
351         /// Adds local (not used by shader) data stream to the particle emitter
352         /// </summary>
353         /// <remarks>
354         /// Adds new stream of Vector3 type.
355         /// </remarks>
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)
360         {
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();
363             return result;
364         }
365         
366         /// <summary>
367         /// Adds local (not used by shader) data stream to the particle emitter
368         /// </summary>
369         /// <remarks>
370         /// Adds new stream of Vector4 type.
371         /// </remarks>
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)
376         {
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();
379             return result;
380         }
381
382         // Internal proxy object to be used on the update thread
383         internal ParticleEmitterProxy EmitterProxy => mProxy;
384         private ParticleEmitterProxy mProxy = null;
385     }
386     
387     /// <summary>
388     /// This class provides functionality that can be used inside the Source/Modifier callbacks.
389     /// </summary>
390     [EditorBrowsable(EditorBrowsableState.Never)]
391     public class ParticleEmitterProxy
392     {
393         internal ParticleEmitterProxy(ParticleEmitter emitter)
394         {
395             mEmitterBasePtr = emitter.SwigCPtr.Handle;
396             mEmitter = emitter;
397         }
398         
399         /// <summary>
400         /// Creates new particle
401         /// </summary>
402         /// <remarks>
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.
406         /// </remarks>
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 )
411         {
412             var result = Interop.ParticleEmitter.NewParticle(mEmitterBasePtr, lifetime);
413             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
414             if (result >= 0)
415             {
416                 // TODO: new particles should be coming form cached queue
417                 return new Particle(mEmitter.SwigCPtr, (uint)result);
418             }
419             return null;
420         }
421         
422         /// <summary>
423         /// Acquires list of Particles of specified length
424         /// </summary>
425         /// <remarks>
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.
428         ///
429         /// Internal Particle cache is used to speed up acquiring new Particle.
430         /// </remarks>
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)
436         {
437             // Populate enough particles into cache
438             while(mParticleCache.Count < count)
439             {
440                 mParticleCache.Push(new Particle(mEmitter.SwigCPtr, 0));
441             }
442             
443             List<Particle> retval = new List<Particle>();
444             for (var i = 0; i < count; ++i)
445             {
446                 var particleIndex = Marshal.ReadInt32(nativePtr, i * 4);
447                 Particle p = mParticleCache.Pop();
448                 p.Index = (uint)particleIndex;
449                 retval.Add(p);
450             }
451
452             return retval;
453         }
454
455         /// <summary>
456         /// Releases list of Particles back into the pool
457         /// </summary>
458         /// <remarks>
459         /// Acquired particles come from internal pool and must be returned so then they can
460         /// be recycled.
461         /// </remarks>
462         /// <param name="particles">List of particles to be returned</param>
463         [EditorBrowsable(EditorBrowsableState.Never)]
464         internal void ReleaseParticleList(List<Particle> particles)
465         {
466             // return particles back into the pull
467             for(var i = 0; i < particles.Count; ++i)
468             {
469                 mParticleCache.Push(particles[i]);
470             }
471             
472             // clear the list (probably not needed?)
473             particles.Clear();
474         }
475         
476         /// <summary>
477         /// Adds local particle data stream of float values
478         /// </summary>
479         /// <param name="defaultValue">Default value</param>
480         /// <returns>Index of new stream</returns>
481         [EditorBrowsable(EditorBrowsableState.Never)]
482         public uint AddLocalStreamFloat(float defaultValue)
483         {
484             return mEmitter.AddLocalStreamFloat(defaultValue);
485         }
486         
487         /// <summary>
488         /// Adds local particle data stream of Vector2 values
489         /// </summary>
490         /// <param name="defaultValue">Default value</param>
491         /// <returns>Index of new stream</returns>
492         [EditorBrowsable(EditorBrowsableState.Never)]
493         public uint AddLocalStreamVector2(Vector2 defaultValue)
494         {
495             return mEmitter.AddLocalStreamVector2(defaultValue);
496         }
497         
498         /// <summary>
499         /// Adds local particle data stream of Vector3 values
500         /// </summary>
501         /// <param name="defaultValue">Default value</param>
502         /// <returns>Index of new stream</returns>
503         [EditorBrowsable(EditorBrowsableState.Never)]
504         public uint AddLocalStreamVector3(Vector3 defaultValue)
505         {
506             return mEmitter.AddLocalStreamVector3(defaultValue);
507         }
508         
509         /// <summary>
510         /// Adds local particle data stream of Vector4 values
511         /// </summary>
512         /// <param name="defaultValue">Default value</param>
513         /// <returns>Index of new stream</returns>
514         [EditorBrowsable(EditorBrowsableState.Never)]
515         public uint AddLocalStreamVector4(Vector4 defaultValue)
516         {
517             return mEmitter.AddLocalStreamVector4(defaultValue);
518         }
519         
520         // Stack of cached particles
521         private Stack<Particle> mParticleCache = new Stack<Particle>();
522         
523         private IntPtr mEmitterBasePtr;
524         private ParticleEmitter mEmitter;
525
526     }
527
528     /// <summary>
529     /// Register binding Source/Modifier interface to the pointer of a native counterpart 
530     /// </summary>
531     /// <typeparam name="T">Class type of objects to be stored in the register</typeparam>
532     internal class ParticleInterfaceRegister<T> where T : class
533     {
534         internal void Register(IntPtr cPtr, T iface)
535         {
536             lock (mBasePtr)
537             {
538                 mBasePtr.Add(cPtr);
539                 mInterfaces.Add(iface);
540             }
541         }
542
543         internal bool Remove(IntPtr cPtr)
544         {
545             lock (mBasePtr)
546             {
547                 var result = mBasePtr.FindIndex(0, x => x == cPtr);
548                 if (result >= 0)
549                 {
550                     mBasePtr.RemoveAt(result);
551                     mInterfaces.RemoveAt(result);
552                 }
553
554                 return result >= 0;
555             }
556         }
557
558         internal bool Remove(T iface)
559         {
560             lock (mBasePtr)
561             {
562                 var result = mInterfaces.FindIndex(0, x => x.Equals(iface));
563                 if (result >= 0)
564                 {
565                     mBasePtr.RemoveAt(result);
566                     mInterfaces.RemoveAt(result);
567                 }
568
569                 return result >= 0;
570             }
571         }
572         
573         internal T Get(IntPtr cPtr)
574         {
575             var result = mBasePtr.FindIndex(0, x => x == cPtr );
576             if (result >= 0)
577             {
578                 return mInterfaces[result];
579             }
580
581             return null;
582         }
583         
584         private List<IntPtr> mBasePtr = new List<IntPtr>();
585         private List<T> mInterfaces = new List<T>();
586     }
587 }