//>>> _using
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SharpDX;
using SharpDX.Direct3D11;
using SharpDX.Windows;
//<<< _using

using System.Runtime.InteropServices;
using Framefield.Core;
using Framefield.Core.Rendering;
using SharpDX.DXGI;
using SharpDX.Direct3D;
using SharpDX.D3DCompiler;
using System.CodeDom.Compiler;
using Buffer = SharpDX.Direct3D11.Buffer;

namespace Framefield.Core.ID34d93986_f4dd_4cc3_b57e_129d47b8cb68
{
    public class Class_SimpleGPUParticles2 : FXSourceCodeFunction, IFXSceneSourceCode
    {
        //structured buffer element type. size must be multiple of 4
        [StructLayout(LayoutKind.Explicit, Size = 36)]
        public struct ParticleStateLayout
        {
            public ParticleStateLayout(Vector3 position, Vector3 velocity, float size, float birthTime, float textureRow)
            {
                Position = position;
                Velocity = velocity;
                Size = size;
                BirthTime = birthTime;
                TextureRow = textureRow;
            }
            [FieldOffset(0)]
            Vector3 Position;
            [FieldOffset(12)]
            Vector3 Velocity;
            [FieldOffset(24)]
            float Size;
            [FieldOffset(28)]
            float BirthTime;
            [FieldOffset(32)]
            float TextureRow;
        }

        //constant buffer element type. size must be multiple of 16
        [StructLayout(LayoutKind.Explicit, Size = 208)]
        public struct ParticleInitParametersBufferLayout
        {
            public ParticleInitParametersBufferLayout(int particlesStartIndex, int particlesEndIndex, float time, 
                                                      Vector3 emitPosition, Vector3 previousEmitPosition, Vector3 emitDirection, Vector3 previousEmitDirection,
                                                      float scatterSpeed, float scatterAngle, Vector2 textureCells, Vector2 emitSizeRange, 
                                                      int maxNumParticles, int reset)
            {
                StartIndex = particlesStartIndex;
                EndIndex = particlesEndIndex;
                Time = time;
                EmitPosition = emitPosition;
                PreviousEmitPosition = previousEmitPosition;
                EmitDirection = emitDirection;
                PreviousEmitDirection = previousEmitDirection;
                ScatterSpeed = scatterSpeed;
                ScatterAngle = scatterAngle;
                TextureCells = textureCells;
                EmitSizeRange = emitSizeRange;
                MaxNumParticles = maxNumParticles;
                Reset = reset;
            }
            [FieldOffset(0)]
            int StartIndex;
            [FieldOffset(16)]
            int EndIndex;
            [FieldOffset(32)]
            float Time;
            [FieldOffset(48)]
            Vector3 EmitPosition;
            [FieldOffset(64)]
            Vector3 PreviousEmitPosition;
            [FieldOffset(80)]
            Vector3 EmitDirection;
            [FieldOffset(96)]
            Vector3 PreviousEmitDirection;
            [FieldOffset(112)]
            float ScatterSpeed;
            [FieldOffset(128)]
            float ScatterAngle;
            [FieldOffset(144)]
            Vector2 TextureCells;
            [FieldOffset(160)]
            Vector2 EmitSizeRange;
            [FieldOffset(176)]
            int MaxNumParticles;
            [FieldOffset(192)]
            int Reset;
        }
        private Buffer _particleInitParametersBuffer;

        //constant buffer element type. size must be multiple of 16
        [StructLayout(LayoutKind.Explicit, Size = 208)]
        public struct ParticleUpdateParametersBufferLayout
        {
            public ParticleUpdateParametersBufferLayout(float deltaTime, float friction, Vector3 wind, Vector3 attractorPosition,
                                                        float attractorStrength, float attractorRadius, float attractorDecay, 
                                                        float noiseTime, float noiseAmount, float noiseScale, float noiseSeed, float noiseRadius)
            {
                DeltaTime = deltaTime;
                Friction = friction;
                Wind = wind;
                AttractorPosition = attractorPosition;
                AttractorStrength = attractorStrength;
                AttractorRadius = attractorRadius;
                AttractorDecay = attractorDecay;
                NoiseTime = noiseTime;
                NoiseAmount = noiseAmount;
                NoiseScale = noiseScale;
                NoiseSeed = noiseSeed;
                NoiseRadius = noiseRadius;
                
            }
            [FieldOffset(0)]
            float DeltaTime;
            [FieldOffset(16)]
            float Friction;
            [FieldOffset(32)]
            Vector3 Wind;
            [FieldOffset(48)]
            Vector3 AttractorPosition;
            [FieldOffset(64)]
            float AttractorStrength;
            [FieldOffset(80)]
            float AttractorRadius;
            [FieldOffset(96)]
            float AttractorDecay;            
            [FieldOffset(112)]
            float NoiseTime;
            [FieldOffset(128)]
            float NoiseAmount;
            [FieldOffset(144)]
            float NoiseScale;
            [FieldOffset(160)]
            float NoiseSeed;
            [FieldOffset(176)]
            float NoiseRadius;
        }
        private Buffer _particleUpdateParametersBuffer;

        //>>> _inputids
        private enum InputId
        {
            InitCode = 0,
            UpdateCode = 1,
            RenderCode = 2,
            Time = 3,
            Image = 4,
            Count = 5,
            LifeTime = 6,
            EmitRate = 7,
            EmitPositionX = 8,
            EmitPositionY = 9,
            EmitPositionZ = 10,
            EmitDirectionX = 11,
            EmitDirectionY = 12,
            EmitDirectionZ = 13,
            ScatterSpeed = 14,
            ScatterAngle = 15,
            WindX = 16,
            WindY = 17,
            WindZ = 18,
            Friction = 19,
            ColorR = 20,
            ColorG = 21,
            ColorB = 22,
            ColorA = 23,
            ColorRandomR = 24,
            ColorRandomG = 25,
            ColorRandomB = 26,
            ColorRandomA = 27,
            TextureCellsX = 28,
            TextureCellsY = 29,
            EmitSizeRangeMin = 30,
            EmitSizeRangeMax = 31,
            AspectRatio = 32,
            Scale = 33,
            MotionStretch = 34,
            SizeOverLifeX = 35,
            SizeOverLifeY = 36,
            OpacityOverLifeX = 37,
            OpacityOverLifeY = 38,
            ResetTrigger = 39,
            AttractorPositionX = 40,
            AttractorPositionY = 41,
            AttractorPositionZ = 42,
            AttractorStrength = 43,
            AttractorRadius = 44,
            AttractorDecay = 45,
            NoiseTime = 46,
            NoiseAmount = 47,
            NoiseScale = 48,
            NoiseSeed = 49,
            NoiseRadius = 50
        }
        //<<< _inputids

        public override void Dispose()
        {
            Utilities.DisposeObj(ref _csInitParticles);
            Utilities.DisposeObj(ref _csUpdateParticles);
            Utilities.DisposeObj(ref _fxRenderParticles);
            Utilities.DisposeObj(ref _particleStateBuffer);
            base.Dispose();
        }

        bool _firstEval = true;
        public override OperatorPartContext Eval(OperatorPartContext context, List<OperatorPart> inputs, int outputIdx)
        {
            if (_firstEval)
            {
                for (int i = 0; i < NumCodes(); ++i)
                    Compile(i);
                _firstEval = false;
                Changed = true;
            }

            //>>> _params
            var InitCode = inputs[(int)InputId.InitCode].Eval(context).Text;
            var UpdateCode = inputs[(int)InputId.UpdateCode].Eval(context).Text;
            var RenderCode = inputs[(int)InputId.RenderCode].Eval(context).Text;
            var Time = inputs[(int)InputId.Time].Eval(context).Value;
            var Image = inputs[(int)InputId.Image].Eval(context).Image; // Needs to be checked for null!
            var Count = inputs[(int)InputId.Count].Eval(context).Value;
            var LifeTime = inputs[(int)InputId.LifeTime].Eval(context).Value;
            var EmitRate = inputs[(int)InputId.EmitRate].Eval(context).Value;
            var EmitPositionX = inputs[(int)InputId.EmitPositionX].Eval(context).Value;
            var EmitPositionY = inputs[(int)InputId.EmitPositionY].Eval(context).Value;
            var EmitPositionZ = inputs[(int)InputId.EmitPositionZ].Eval(context).Value;
            var EmitPosition = new Vector3(EmitPositionX, EmitPositionY, EmitPositionZ);
            var EmitDirectionX = inputs[(int)InputId.EmitDirectionX].Eval(context).Value;
            var EmitDirectionY = inputs[(int)InputId.EmitDirectionY].Eval(context).Value;
            var EmitDirectionZ = inputs[(int)InputId.EmitDirectionZ].Eval(context).Value;
            var EmitDirection = new Vector3(EmitDirectionX, EmitDirectionY, EmitDirectionZ);
            var ScatterSpeed = inputs[(int)InputId.ScatterSpeed].Eval(context).Value;
            var ScatterAngle = inputs[(int)InputId.ScatterAngle].Eval(context).Value;
            var WindX = inputs[(int)InputId.WindX].Eval(context).Value;
            var WindY = inputs[(int)InputId.WindY].Eval(context).Value;
            var WindZ = inputs[(int)InputId.WindZ].Eval(context).Value;
            var Wind = new Vector3(WindX, WindY, WindZ);
            var Friction = inputs[(int)InputId.Friction].Eval(context).Value;
            var ColorR = inputs[(int)InputId.ColorR].Eval(context).Value;
            var ColorG = inputs[(int)InputId.ColorG].Eval(context).Value;
            var ColorB = inputs[(int)InputId.ColorB].Eval(context).Value;
            var ColorA = inputs[(int)InputId.ColorA].Eval(context).Value;
            var Color = new Color4(ColorR, ColorG, ColorB, ColorA);
            var ColorRandomR = inputs[(int)InputId.ColorRandomR].Eval(context).Value;
            var ColorRandomG = inputs[(int)InputId.ColorRandomG].Eval(context).Value;
            var ColorRandomB = inputs[(int)InputId.ColorRandomB].Eval(context).Value;
            var ColorRandomA = inputs[(int)InputId.ColorRandomA].Eval(context).Value;
            var ColorRandom = new Color4(ColorRandomR, ColorRandomG, ColorRandomB, ColorRandomA);
            var TextureCellsX = inputs[(int)InputId.TextureCellsX].Eval(context).Value;
            var TextureCellsY = inputs[(int)InputId.TextureCellsY].Eval(context).Value;
            var TextureCells = new Vector2(TextureCellsX, TextureCellsY);
            var EmitSizeRangeMin = inputs[(int)InputId.EmitSizeRangeMin].Eval(context).Value;
            var EmitSizeRangeMax = inputs[(int)InputId.EmitSizeRangeMax].Eval(context).Value;
            var EmitSizeRange = new Vector2(EmitSizeRangeMin, EmitSizeRangeMax);
            var AspectRatio = inputs[(int)InputId.AspectRatio].Eval(context).Value;
            var Scale = inputs[(int)InputId.Scale].Eval(context).Value;
            var MotionStretch = inputs[(int)InputId.MotionStretch].Eval(context).Value;
            var SizeOverLifeX = inputs[(int)InputId.SizeOverLifeX].Eval(context).Value;
            var SizeOverLifeY = inputs[(int)InputId.SizeOverLifeY].Eval(context).Value;
            var SizeOverLife = new Vector2(SizeOverLifeX, SizeOverLifeY);
            var OpacityOverLifeX = inputs[(int)InputId.OpacityOverLifeX].Eval(context).Value;
            var OpacityOverLifeY = inputs[(int)InputId.OpacityOverLifeY].Eval(context).Value;
            var OpacityOverLife = new Vector2(OpacityOverLifeX, OpacityOverLifeY);
            var ResetTrigger = inputs[(int)InputId.ResetTrigger].Eval(context).Value;
            var AttractorPositionX = inputs[(int)InputId.AttractorPositionX].Eval(context).Value;
            var AttractorPositionY = inputs[(int)InputId.AttractorPositionY].Eval(context).Value;
            var AttractorPositionZ = inputs[(int)InputId.AttractorPositionZ].Eval(context).Value;
            var AttractorPosition = new Vector3(AttractorPositionX, AttractorPositionY, AttractorPositionZ);
            var AttractorStrength = inputs[(int)InputId.AttractorStrength].Eval(context).Value;
            var AttractorRadius = inputs[(int)InputId.AttractorRadius].Eval(context).Value;
            var AttractorDecay = inputs[(int)InputId.AttractorDecay].Eval(context).Value;
            var NoiseTime = inputs[(int)InputId.NoiseTime].Eval(context).Value;
            var NoiseAmount = inputs[(int)InputId.NoiseAmount].Eval(context).Value;
            var NoiseScale = inputs[(int)InputId.NoiseScale].Eval(context).Value;
            var NoiseSeed = inputs[(int)InputId.NoiseSeed].Eval(context).Value;
            var NoiseRadius = inputs[(int)InputId.NoiseRadius].Eval(context).Value;
            //<<< _params

            float deltaTime = Time - _time;
            _time = Time;

            InitializeBuffer(context, (int)Count);

            var uavDesc = new UnorderedAccessViewDescription
                              {
                                  Format = Format.Unknown, 
                                  Dimension = UnorderedAccessViewDimension.Buffer,
                                  Buffer = new UnorderedAccessViewDescription.BufferResource
                                               {
                                                   FirstElement = 0,
                                                   ElementCount = _maxNumParticles,
                                                   Flags = UnorderedAccessViewBufferFlags.None
                                               }
                              };

            var deviceContext = context.D3DDevice.ImmediateContext;

            //particle initializing
            int particleEmitCountForFrame = Math.Max(0, (int)(deltaTime*EmitRate*60));
            int particleEndIndex = (_currentParticleIndex + particleEmitCountForFrame) % _maxNumParticles;

            SetupParticleInitParametersBuffer(context, _currentParticleIndex, particleEndIndex, (float)_time,
                                              EmitPosition, _previousEmitPosition, EmitDirection, _previousEmitDirection,
                                              ScatterSpeed, ScatterAngle, TextureCells, EmitSizeRange, _maxNumParticles,
                                              (int)ResetTrigger);
            
            _currentParticleIndex = particleEndIndex;

            using (var particleStateUAV = new UnorderedAccessView(context.D3DDevice, _particleStateBuffer, uavDesc))
            {
                deviceContext.ComputeShader.Set(_csInitParticles);
                deviceContext.ComputeShader.SetUnorderedAccessView(0, particleStateUAV);
                deviceContext.ComputeShader.SetConstantBuffer(0, _particleInitParametersBuffer);

                deviceContext.Dispatch(_maxNumParticles/512, 1, 1);

                deviceContext.ComputeShader.SetUnorderedAccessView(0, null);
                deviceContext.ComputeShader.SetConstantBuffer(0, null);
            }
            _previousEmitPosition = EmitPosition;
            _previousEmitDirection = EmitDirection;

            //particle updating
            SetupParticleUpdateParametersBuffer(context, Time, Friction, Wind, AttractorPosition, AttractorStrength, 
                                                AttractorRadius, AttractorDecay, 
                                                NoiseTime, NoiseAmount, NoiseScale, NoiseSeed, NoiseRadius);

            using (var particleStateUAV = new UnorderedAccessView(context.D3DDevice, _particleStateBuffer, uavDesc))
            {
                deviceContext.ComputeShader.Set(_csUpdateParticles);
                deviceContext.ComputeShader.SetUnorderedAccessView(0, particleStateUAV);
                deviceContext.ComputeShader.SetConstantBuffer(0, _particleUpdateParametersBuffer);

                deviceContext.Dispatch(_maxNumParticles/512, 1, 1);

                deviceContext.ComputeShader.SetUnorderedAccessView(0, null);
                deviceContext.ComputeShader.SetConstantBuffer(0, null);
            }

            //particle rendering
            using (var particleStateSRV = new ShaderResourceView(context.D3DDevice, _particleStateBuffer))
            using (var imageSRV = new ShaderResourceView(context.D3DDevice, Image))
            {
                try
                {
                    deviceContext.ClearState();

                    _fxRenderParticles.GetVariableByName("ParticleStates").AsShaderResource().SetResource(particleStateSRV);
                    _fxRenderParticles.GetVariableByName("Image").AsShaderResource().SetResource(imageSRV);
                    _fxRenderParticles.GetVariableByName("Time").AsScalar().Set((float)_time);
                    // SetScalar("Time",_time); cynic: this doesn't work, as this method uses _effect, but _effect is not set for this op!
                    _fxRenderParticles.GetVariableByName("LifeTime").AsScalar().Set((float)LifeTime);
                    _fxRenderParticles.GetVariableByName("TextureCells").AsVector().Set(TextureCells);
                    _fxRenderParticles.GetVariableByName("Color").AsVector().Set(Color);
                    _fxRenderParticles.GetVariableByName("Scale").AsScalar().Set(Scale);
                    _fxRenderParticles.GetVariableByName("AspectRatio").AsScalar().Set(AspectRatio);
                    _fxRenderParticles.GetVariableByName("MotionStretch").AsScalar().Set(MotionStretch);
                    _fxRenderParticles.GetVariableByName("SizeOverLife").AsVector().Set(SizeOverLife);
                    _fxRenderParticles.GetVariableByName("OpacityOverLife").AsVector().Set(OpacityOverLife);
                    _fxRenderParticles.GetVariableByName("NoiseTime").AsScalar().Set(NoiseTime);
                    //_fxRenderParticles.GetVariableByName("NoiseAmount").AsScalar().Set(NoiseAmount);
                    //_fxRenderParticles.GetVariableByName("NoiseScale").AsScalar().Set(NoiseScale);
                    //_fxRenderParticles.GetVariableByName("NoiseSeed").AsScalar().Set(NoiseSeed);
                    //_fxRenderParticles.GetVariableByName("NoiseRadius").AsScalar().Set(NoiseRadius);

                    var previousEffect = context.Effect;
                    context.Effect = _fxRenderParticles;
                    context.Renderer.SetupEffect(context);

                    if (context.DepthStencilView != null)
                        deviceContext.OutputMerger.SetTargets(context.DepthStencilView, context.RenderTargetView);
                    else
                        deviceContext.OutputMerger.SetTargets(context.RenderTargetView);

                    if (context.BlendState != null) {
                        deviceContext.OutputMerger.BlendState = context.BlendState;
                        deviceContext.OutputMerger.BlendFactor = context.BlendFactor;
                    }

                    if (context.DepthStencilState != null) {
                        deviceContext.OutputMerger.DepthStencilState = context.DepthStencilState;
                    }

                    if (context.RasterizerState != null) {
                        deviceContext.Rasterizer.State = context.RasterizerState;
                    }

                    deviceContext.Rasterizer.SetViewports(new [] { context.Viewport });
                    deviceContext.InputAssembler.InputLayout = context.InputLayout;
                    deviceContext.InputAssembler.PrimitiveTopology = PrimitiveTopology.PointList;
                    deviceContext.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(null, 0, 0));

                    var technique = context.Effect.GetTechniqueByIndex(0);
                    for (int i = 0; i < technique.Description.PassCount; ++i) {
                        technique.GetPassByIndex(i).Apply(deviceContext);
                        deviceContext.Draw(_maxNumParticles, 0);
                    }

                    // remove target views that they are no longer bound as output and can be used also as input
                    DepthStencilView dsv = null;
                    RenderTargetView rtv = null;
                    deviceContext.OutputMerger.SetTargets(dsv, rtv);
                    deviceContext.VertexShader.SetShaderResource(0, null);
                    deviceContext.PixelShader.SetShaderResource(0, null);
                    context.Effect = previousEffect;
                }
                catch (Exception exception)
                {
                    Logger.Error(this,"render error: {0}", exception.Message);
                }
            }

            Changed = false;
            return context;
        }

        void InitializeBuffer(OperatorPartContext context, int newMaxNumParticles)
        {
            newMaxNumParticles = Math.Max(512, newMaxNumParticles); //at least we need to have 512 particles

            if (_particleStateBuffer == null || _maxNumParticles != newMaxNumParticles)
            {
                using (var data = new DataStream(Marshal.SizeOf(typeof(ParticleStateLayout))*newMaxNumParticles, true, true))
                {
                    for (int i = 0; i < newMaxNumParticles; ++i)
                        data.Write(new ParticleStateLayout(new Vector3(0), new Vector3(0), 0.0f, -1.0f, 0.0f));
                    data.Position = 0;

                    var bufferDesc = new BufferDescription
                                         {
                                             Usage = ResourceUsage.Default,
                                             SizeInBytes = Marshal.SizeOf(typeof(ParticleStateLayout))*newMaxNumParticles,
                                             StructureByteStride = Marshal.SizeOf(typeof(ParticleStateLayout)),
                                             BindFlags = BindFlags.ShaderResource | BindFlags.UnorderedAccess,
                                             OptionFlags = ResourceOptionFlags.BufferStructured
                                         };
                    _particleStateBuffer = new Buffer(context.D3DDevice, data, bufferDesc);
                }
                _maxNumParticles = newMaxNumParticles;
            }
        }

        public override int NumCodes()
        {
            return 3;
        }

        public override CompilerErrorCollection Compile(int codeIdx)
        {
            var errors = new CompilerErrorCollection();
            try
            {
                switch (codeIdx)
                {
                    case 0:
                    {
                        Utilities.DisposeObj(ref _csInitParticles);
                        var t = GetCode(codeIdx);
                        using (var bytecode = ShaderBytecode.Compile(GetCode(codeIdx), "CSInitParticles", "cs_5_0", ShaderFlags.Debug))
                            _csInitParticles = new ComputeShader(D3DDevice.Device, bytecode);
                        break;
                    }
                    case 1:
                    {
                        Utilities.DisposeObj(ref _csUpdateParticles);
                        using (var bytecode = ShaderBytecode.Compile(GetCode(codeIdx), "CSUpdateParticles", "cs_5_0", ShaderFlags.Debug))
                            _csUpdateParticles = new ComputeShader(D3DDevice.Device, bytecode);
                        break;
                    }
                    case 2:
                    {
                        Utilities.DisposeObj(ref _fxRenderParticles);
                        using (var bytecode = ShaderBytecode.Compile(GetCode(codeIdx), "fx_5_0", ShaderFlags.Debug, EffectFlags.None, null, null))
                            _fxRenderParticles = new Effect(D3DDevice.Device, bytecode);
                        break;
                    }
                }
            }
            catch (SharpDX.CompilationException ex)
            {
                errors = ErrorsFromString(ex.Message);
                Logger.Error(this,"Fx compile error: {0}", ex.Message);
            }
            return errors;
        }

        void SetupParticleInitParametersBuffer(OperatorPartContext context, int particleIndex, int numParticlesToAdd, float time,
                                               Vector3 emitPosition, Vector3 previousEmitPosition, Vector3 emitDirection, Vector3 previousEmitDirection,
                                               float scatterSpeed, float scatterAngle, Vector2 textureCells, Vector2 emitSizeRange, int maxNumParticles,
                                               int reset)
        {
            var param = new ParticleInitParametersBufferLayout(particleIndex, numParticlesToAdd, time, emitPosition, previousEmitPosition, 
                                                               emitDirection, previousEmitDirection, scatterSpeed, scatterAngle, textureCells,
                                                               emitSizeRange, maxNumParticles, reset);
            using (var data = new DataStream(Marshal.SizeOf(typeof(ParticleInitParametersBufferLayout)), true, true))
            {
                data.Write(param);
                data.Position = 0;

                if (_particleInitParametersBuffer == null)
                {
                    _currentParticleIndex = 0;
                    var bufferDesc = new BufferDescription { Usage = ResourceUsage.Default, SizeInBytes = Marshal.SizeOf(typeof(ParticleInitParametersBufferLayout)), BindFlags = BindFlags.ConstantBuffer };
                    _particleInitParametersBuffer = new Buffer(context.D3DDevice, data, bufferDesc);
                }
                else
                {
                    context.D3DDevice.ImmediateContext.UpdateSubresource(new DataBox(data.DataPointer, 0, 0), _particleInitParametersBuffer);
                }
            }
        }

        void SetupParticleUpdateParametersBuffer(OperatorPartContext context, float deltaTime, float friction, Vector3 wind, Vector3 attractorPosition,
                                                 float attractorStrength, float attractorRadius, float attractorDecay, 
                                                 float noiseTime, float noiseAmount, float noiseScale, float noiseSeed, float noiseRadius)
        {
            var param = new ParticleUpdateParametersBufferLayout(deltaTime, friction, wind, 
            attractorPosition, attractorStrength, attractorRadius, attractorDecay, 
            noiseTime, noiseAmount, noiseScale, noiseSeed, noiseRadius);
            using (var data = new DataStream(Marshal.SizeOf(typeof(ParticleUpdateParametersBufferLayout)), true, true))
            {
                data.Write(param);
                data.Position = 0;

                if (_particleUpdateParametersBuffer == null)
                {
                    var bufferDesc = new BufferDescription { Usage = ResourceUsage.Default, SizeInBytes = Marshal.SizeOf(typeof(ParticleUpdateParametersBufferLayout)), BindFlags = BindFlags.ConstantBuffer };
                    _particleUpdateParametersBuffer = new Buffer(context.D3DDevice, data, bufferDesc);
                }
                else
                {
                    context.D3DDevice.ImmediateContext.UpdateSubresource(new DataBox(data.DataPointer, 0, 0), _particleUpdateParametersBuffer);
                }
            }
        }

        ComputeShader _csInitParticles;
        ComputeShader _csUpdateParticles;
        Effect _fxRenderParticles;
        Buffer _particleStateBuffer;
        int _maxNumParticles;
        int _currentParticleIndex;
        Vector3 _previousEmitPosition;
        Vector3 _previousEmitDirection;

        float _time;
    }
}
