/*
* Copyright(c) 2023 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.ComponentModel;
namespace Tizen.NUI.ParticleSystem
{
using Tizen.NUI.BaseComponents;
///
/// Enum defining blending options when rendering the particles.
///
[EditorBrowsable(EditorBrowsableState.Never)]
public enum ParticleBlendingMode
{
Additive = 0,
Screen = 1,
Default = Additive
}
///
/// Class ParticleEmitter creates a single emitter attached to a specified
/// View. ParticleEmitter is responsible for spawning and updating particles.
///
/// Emitter must contain:
/// ParticleSource - responsible for spawning new particles
/// ParticleModifier(s) - responsible for updating particles in the system
///
/// ParticleSource and ParticleModifier callback interfaces should not be accessing
/// Event side (NUI) objects. Both callbacks are executed on Update thread.
///
[EditorBrowsable(EditorBrowsableState.Never)]
public class ParticleEmitter : BaseHandle
{
internal ParticleEmitter(global::System.IntPtr cPtr, bool cMemoryOwn) : base(cPtr, cMemoryOwn)
{
}
///
/// Create an initialized ParticleEmitter.
///
/// View to attach the particle emitter.
[EditorBrowsable(EditorBrowsableState.Never)]
public ParticleEmitter(View view) : this(Interop.ParticleEmitter.New(view.SwigCPtr), true)
{
mProxy = new ParticleEmitterProxy(this);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
///
/// Copy constructor.
///
/// Source object to copy.
[EditorBrowsable(EditorBrowsableState.Never)]
public ParticleEmitter( ParticleEmitter particleEmitter) : this(Interop. ParticleEmitter.New( ParticleEmitter.getCPtr(particleEmitter)), true)
{
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
///
/// Dispose.
///
[EditorBrowsable(EditorBrowsableState.Never)]
protected override void Dispose(DisposeTypes type)
{
if (disposed) return;
base.Dispose(type);
}
///
/// Assignment operator.
///
/// Source object to be assigned.
/// Reference to this.
internal ParticleEmitter Assign( ParticleEmitter particleEmitter)
{
ParticleEmitter ret = new ParticleEmitter(Interop.ParticleEmitter.Assign(SwigCPtr, ParticleEmitter.getCPtr(particleEmitter)), false);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
return ret;
}
///
/// Raises the window to the top of the window stack.
///
/// Source object to copy.
[EditorBrowsable(EditorBrowsableState.Never)]
public void SetSource(ParticleSource source) where T : ParticleSourceInterface, new()
{
// update interface
source.SetEmitter(this);
// Set native source
Interop.ParticleEmitter.SetSource(SwigCPtr, source.SwigCPtr);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
///
/// Maximum particle count
///
[EditorBrowsable(EditorBrowsableState.Never)]
public uint ParticleCount
{
get
{
var value = Interop.ParticleEmitter.GetParticleCount(SwigCPtr);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
return value;
}
set
{
Interop.ParticleEmitter.SetParticleCount(SwigCPtr, value);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
}
///
/// Rate of emission per second
///
///
/// EmissionRate defines number of particles emitted per second.
///
[EditorBrowsable(EditorBrowsableState.Never)]
public uint EmissionRate
{
get
{
var value = Interop.ParticleEmitter.GetEmissionRate(SwigCPtr);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
return value;
}
set
{
Interop.ParticleEmitter.SetEmissionRate(SwigCPtr, value);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
}
///
/// Initial particle count
///
///
/// Initial number of particles to be emitted immediately after emitter starts. It allows
/// initial burst emission. By default it's set to 0.
///
[EditorBrowsable(EditorBrowsableState.Never)]
public uint InitialParticleCount
{
get
{
var value = Interop.ParticleEmitter.GetInitialParticleCount(SwigCPtr);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
return value;
}
set
{
Interop.ParticleEmitter.SetInitialParticleCount(SwigCPtr, value);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
}
///
/// Limit of active particles in the system
///
///
/// Active particles in the system can be limited without changing .
///
[EditorBrowsable(EditorBrowsableState.Never)]
public uint ActiveParticleLimit
{
get{
var value = Interop.ParticleEmitter.GetActiveParticlesLimit(SwigCPtr);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
return value;
}
set
{
Interop.ParticleEmitter.SetActiveParticlesLimit(SwigCPtr, value);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
}
///
/// Gets/sets blending mode for particle renderer
///
///
/// Currently two blending modes are supported: Additive and Screen (advanced blending mode).
///
///
[EditorBrowsable(EditorBrowsableState.Never)]
public ParticleBlendingMode RendererBlendingMode
{
get
{
var value = Interop.ParticleEmitter.GetBlendingMode(SwigCPtr);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
return (ParticleBlendingMode)value;
}
set
{
Interop.ParticleEmitter.SetBlendingMode(SwigCPtr, value);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
}
///
/// Sets texture to be used by the renderer
///
[EditorBrowsable(EditorBrowsableState.Never)]
public Texture RendererTexture
{
set
{
Interop.ParticleEmitter.SetTexture(SwigCPtr, value.SwigCPtr);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
}
///
/// Adds ParticleModifier to the stack
///
///
/// ParticleEmitter implements a stack of modifiers which are responsible for
/// updating particles in the system. The stack is processed such as result of
/// previous modifier is an input for next modifier.
///
/// Valid modifier object
[EditorBrowsable(EditorBrowsableState.Never)]
public void AddModifier(ParticleModifier modifier) where T : ParticleModifierInterface, new()
{
// update interface
modifier.SetEmitter(this);
Interop.ParticleEmitter.AddModifier(SwigCPtr, modifier.SwigCPtr);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
///
/// Returns associated ParticleSource object
///
/// Valid ParticleSource object or null
[EditorBrowsable(EditorBrowsableState.Never)]
public ParticleSource GetSource() where T : ParticleSourceInterface, new()
{
IntPtr cPtr = Interop.ParticleEmitter.GetSource(SwigCPtr);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
ParticleSource ret = (cPtr == IntPtr.Zero) ? null : Registry.GetManagedBaseHandleFromNativePtr(cPtr) as ParticleSource;
return ret;
}
///
/// Returns modifier at specified index
///
/// Index within modifier stack
/// Valid ParticleModifier object or null
[EditorBrowsable(EditorBrowsableState.Never)]
public ParticleModifier GetModifierAt(uint index)
{
IntPtr cPtr = Interop.ParticleEmitter.GetModifierAt(SwigCPtr, index);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
ParticleModifier ret = (cPtr == IntPtr.Zero) ? null : Registry.GetManagedBaseHandleFromNativePtr(cPtr) as ParticleModifier;
return ret;
}
///
/// Starts emission of particles.
///
[EditorBrowsable(EditorBrowsableState.Never)]
public void Start()
{
Interop.ParticleEmitter.Start(SwigCPtr);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
///
/// Stops emission of particles.
///
[EditorBrowsable(EditorBrowsableState.Never)]
public void Stop()
{
Interop.ParticleEmitter.Stop(SwigCPtr);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
///
/// Adds local (not used by shader) data stream to the particle emitter
///
///
/// Adds new stream of float type.
///
/// Default value to fill the stream with
/// Index of newly created data stream
[EditorBrowsable(EditorBrowsableState.Never)]
public uint AddLocalStreamFloat(float defaultValue)
{
var result = Interop.ParticleEmitter.AddLocalStreamFloat(SwigCPtr, defaultValue);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
return result;
}
///
/// Adds local (not used by shader) data stream to the particle emitter
///
///
/// Adds new stream of Vector2 type.
///
/// Default value to fill the stream with
/// Index of newly created data stream
[EditorBrowsable(EditorBrowsableState.Never)]
public uint AddLocalStreamVector2(Vector2 defaultValue)
{
var result = Interop.ParticleEmitter.AddLocalStreamVector2(SwigCPtr, defaultValue.SwigCPtr.Handle);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
return result;
}
///
/// Adds local (not used by shader) data stream to the particle emitter
///
///
/// Adds new stream of Vector3 type.
///
/// Default value to fill the stream with
/// Index of newly created data stream
[EditorBrowsable(EditorBrowsableState.Never)]
public uint AddLocalStreamVector3(Vector3 defaultValue)
{
var result = Interop.ParticleEmitter.AddLocalStreamVector3(SwigCPtr, defaultValue.SwigCPtr.Handle);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
return result;
}
///
/// Adds local (not used by shader) data stream to the particle emitter
///
///
/// Adds new stream of Vector4 type.
///
/// Default value to fill the stream with
/// Index of newly created data stream
[EditorBrowsable(EditorBrowsableState.Never)]
public uint AddLocalStreamVector4(Vector4 defaultValue)
{
var result = Interop.ParticleEmitter.AddLocalStreamVector4(SwigCPtr, defaultValue.SwigCPtr.Handle);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
return result;
}
// Internal proxy object to be used on the update thread
internal ParticleEmitterProxy EmitterProxy => mProxy;
private ParticleEmitterProxy mProxy = null;
}
///
/// This class provides functionality that can be used inside the Source/Modifier callbacks.
///
[EditorBrowsable(EditorBrowsableState.Never)]
public class ParticleEmitterProxy
{
internal ParticleEmitterProxy(ParticleEmitter emitter)
{
mEmitterBasePtr = emitter.SwigCPtr.Handle;
mEmitter = emitter;
}
///
/// Creates new particle
///
///
/// Function may fail and return null if current number of particles exceeds limits of emitter.
/// Particle is valid only inside the callback and must not be stored and used anywhere else. Otherwise
/// the behaviour is undefined.
///
/// Lifetime of the particle in seconds
/// New Particle object or null
[EditorBrowsable(EditorBrowsableState.Never)]
public Particle NewParticle( float lifetime )
{
var result = Interop.ParticleEmitter.NewParticle(mEmitterBasePtr, lifetime);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
if (result >= 0)
{
// TODO: new particles should be coming form cached queue
return new Particle(mEmitter.SwigCPtr, (uint)result);
}
return null;
}
///
/// Acquires list of Particles of specified length
///
///
/// The function should be use internally only. Native side passes list of indices of particles (int[]).
/// Before calling the callback the indices must be marshalled and converted into the Particle objects.
///
/// Internal Particle cache is used to speed up acquiring new Particle.
///
/// Native pointer to the list of indices (int32)
/// Number of elements
/// List of Particle objects
[EditorBrowsable(EditorBrowsableState.Never)]
internal List AcquireParticleList(IntPtr nativePtr, uint count)
{
// Populate enough particles into cache
while(mParticleCache.Count < count)
{
mParticleCache.Push(new Particle(mEmitter.SwigCPtr, 0));
}
List retval = new List();
for (var i = 0; i < count; ++i)
{
var particleIndex = Marshal.ReadInt32(nativePtr, i * 4);
Particle p = mParticleCache.Pop();
p.Index = (uint)particleIndex;
retval.Add(p);
}
return retval;
}
///
/// Releases list of Particles back into the pool
///
///
/// Acquired particles come from internal pool and must be returned so then they can
/// be recycled.
///
/// List of particles to be returned
[EditorBrowsable(EditorBrowsableState.Never)]
internal void ReleaseParticleList(List particles)
{
// return particles back into the pull
for(var i = 0; i < particles.Count; ++i)
{
mParticleCache.Push(particles[i]);
}
// clear the list (probably not needed?)
particles.Clear();
}
///
/// Adds local particle data stream of float values
///
/// Default value
/// Index of new stream
[EditorBrowsable(EditorBrowsableState.Never)]
public uint AddLocalStreamFloat(float defaultValue)
{
return mEmitter.AddLocalStreamFloat(defaultValue);
}
///
/// Adds local particle data stream of Vector2 values
///
/// Default value
/// Index of new stream
[EditorBrowsable(EditorBrowsableState.Never)]
public uint AddLocalStreamVector2(Vector2 defaultValue)
{
return mEmitter.AddLocalStreamVector2(defaultValue);
}
///
/// Adds local particle data stream of Vector3 values
///
/// Default value
/// Index of new stream
[EditorBrowsable(EditorBrowsableState.Never)]
public uint AddLocalStreamVector3(Vector3 defaultValue)
{
return mEmitter.AddLocalStreamVector3(defaultValue);
}
///
/// Adds local particle data stream of Vector4 values
///
/// Default value
/// Index of new stream
[EditorBrowsable(EditorBrowsableState.Never)]
public uint AddLocalStreamVector4(Vector4 defaultValue)
{
return mEmitter.AddLocalStreamVector4(defaultValue);
}
// Stack of cached particles
private Stack mParticleCache = new Stack();
private IntPtr mEmitterBasePtr;
private ParticleEmitter mEmitter;
}
///
/// Register binding Source/Modifier interface to the pointer of a native counterpart
///
/// Class type of objects to be stored in the register
internal class ParticleInterfaceRegister where T : class
{
internal void Register(IntPtr cPtr, T iface)
{
lock (mBasePtr)
{
mBasePtr.Add(cPtr);
mInterfaces.Add(iface);
}
}
internal bool Remove(IntPtr cPtr)
{
lock (mBasePtr)
{
var result = mBasePtr.FindIndex(0, x => x == cPtr);
if (result >= 0)
{
mBasePtr.RemoveAt(result);
mInterfaces.RemoveAt(result);
}
return result >= 0;
}
}
internal bool Remove(T iface)
{
lock (mBasePtr)
{
var result = mInterfaces.FindIndex(0, x => x.Equals(iface));
if (result >= 0)
{
mBasePtr.RemoveAt(result);
mInterfaces.RemoveAt(result);
}
return result >= 0;
}
}
internal T Get(IntPtr cPtr)
{
var result = mBasePtr.FindIndex(0, x => x == cPtr );
if (result >= 0)
{
return mInterfaces[result];
}
return null;
}
private List mBasePtr = new List();
private List mInterfaces = new List();
}
}