using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
//using Unity.Mathematics;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.Rendering;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.Universal;
using UnityEngine.XR;
using Unity.Profiling;
#if UNITY_EDITOR
using UnityEditor;
#endif

class VolumeRenderingUtils //Importing some functions from HDRP to have simular terms   
{
    public static float MeanFreePathFromExtinction(float extinction)
    {
        return 1.0f / extinction;
    }

    public static float ExtinctionFromMeanFreePath(float meanFreePath)
    {
        return 1.0f / meanFreePath;
    }

    public static Vector3 AbsorptionFromExtinctionAndScattering(float extinction, Vector3 scattering)
    {
        return new Vector3(extinction, extinction, extinction) - scattering;
    }

    public static Vector3 ScatteringFromExtinctionAndAlbedo(float extinction, Vector3 albedo)
    {
        return extinction * albedo;
    }

    public static Vector3 AlbedoFromMeanFreePathAndScattering(float meanFreePath, Vector3 scattering)
    {
        return meanFreePath * scattering;
    }
}

//TODO: Add semi dynamic lighting which is generated in the clipmap and not previously baked out. Will need smarter clipmap gen to avoid hitching.
//Add cascading clipmaps to have higher detail up close and include father clipping without exploding memory.
//Convert this to a render feature. This should remove the need for the platform switcher too because that would be handled by the quality settings pipeline asset instead


//[RequireComponent(typeof( Camera ) )]
[ExecuteInEditMode]
public class VolumetricRendering : MonoBehaviour
{

    #region variables
    static ProfilingSampler profileUpdateFunc = new ProfilingSampler("VolumetricRendering.UpdateFunc");
    static ProfilingSampler profileUpdateClipmap = new ProfilingSampler("VolumetricRendering.UpdateClipmap");

    public float tempOffset = 0;
    Texture3D BlackTex; //Temp texture for 
    Color clearColor = new Color(0.0f, 0.0f, 0.0f, 0f);

    static VolumetricRendering lastClipmapUpdate;
    static VolumetricRendering lastBlur;
    static VolumetricRendering lastFroxelFog;
    static VolumetricRendering lastFroxelIntegrate;


    public Camera cam; //Main camera to base settings on
    private Camera activeCam;
    private UniversalAdditionalCameraData activeCamData;
    // Prevent script from trying to initialize itself twice
    bool hasInitialized;

    // Sometimes, the volumetric register gets filled after the volumetric script initializes.
    // This means that the clipmaps will be empty until the player moves far enough to trigger
    // a clipmap update. Instead, set a bool that triggers the clipmaps to try to update every
    // frame until the volumtric registry contains >0 volumes
    bool VolumetricRegisterEmpty;
    
    [HideInInspector] public bool VolumetricRegisterForceRefresh = false;


    // Debug counter to print a message every x frames
    int debugHeartBeatCount = 30;
    int debugHeartBeat = 0;

    public VolumetricData volumetricData;
    [Range(0, 1)]
    public float reprojectionAmount = 0.95f;
    //   [Tooltip("Does a final blur pass on the rendered fog")]
    //    public bool FroxelBlur = false;

    [HideInInspector]
    public enum BlurType {None, Gaussian};
    public BlurType FroxelBlur = BlurType.None;
    [Range(0, 1)]
    public float SliceDistributionUniformity = 0.5f;

    [HideInInspector] public bool enableEditorPreview = false;
    //public Texture skytex;
    //[Header("Volumetric camera settings")]
    //[Tooltip("Near Clip plane")]
    //public float near = 1;
    //[Tooltip("Far Clip plane")]
    //public float far = 40;
    //[Tooltip("Resolution")]
    //public int FroxelWidthResolution = 128;
    //[Tooltip("Resolution")]
    //public int FroxelHeightResolution = 128;
    //[Tooltip("Resolution")]
    //public int FroxelDepthResolution = 64;
    ////[Tooltip("Controls the bias of the froxel dispution. A value of 1 is linear. ")]
    ////public float FroxelDispution;

    //[Header("Prebaked clipmap settings")]
    //[Tooltip("Textile resolution per unit")]
    //public int ClipMapResolution = 128;
    //[Tooltip("Size of clipmap in units")]
    //public float ClipmapScale = 80;
    //[Tooltip("Distance (m) from previous sampling point to trigger resampling clipmap")]
    //public float ClipmapResampleThreshold = 1;


    Vector3 ClipmapTransform; //Have this follow the camera and resample when the camera moves enough 
    Vector3 ClipmapCurrentPos; //chached location of previous sample point
    
    private ComputeBuffer participatingMediaSphereBuffer;
    
    [StructLayout(LayoutKind.Sequential)]
    struct MediaSphere
    {
        public Vector3 CenterPosition;
        public float LocalExtinction;
        public float LocalFalloff;
        public float LocalRange;
    }

    private const int MediaSphereStride = (3 + 1 + 1 + 1) * sizeof(float);
    int MediaCount;

    
    //public Matrix4x4 randomatrix;

    //Required shaders
    [SerializeField, HideInInspector] ComputeShader FroxelFogCompute;
    [SerializeField, HideInInspector] ComputeShader FroxelIntegrationCompute;
    [SerializeField, HideInInspector] ComputeShader FroxelLocalFogCompute;
    [SerializeField, HideInInspector] ComputeShader ClipmapCompute;
    [SerializeField, HideInInspector] ComputeShader BlurCompute;

    //Texture buffers
    RenderTexture ClipmapBufferA;  //Sampling and combining baked maps asynchronously
    RenderTexture ClipmapBufferB;  //Sampling and combining baked maps asynchronously
    RenderTexture ClipmapBufferC;  //Sampling and combining baked maps asynchronously
    RenderTexture ClipmapBufferD;  //Sampling and combining baked maps asynchronously //TODO: get rid of this extra buffer and bool
    bool FlipClipBufferNear = true;
    bool FlipClipBufferFar = true;


    RenderTexture FroxelBufferA;   //Single froxel projection use for scattering and history reprojection
    RenderTexture FroxelBufferB;   //for history reprojection

    RenderTexture IntegrationBuffer;    //Integration and stereo reprojection
                                        //  RenderTexture IntegrationBufferB;    //Integration and stereo reprojection
    RenderTexture BlurBuffer;    //blur
    RenderTexture BlurBufferB;    //blur

    RenderTexture VolumetricResult;

    // This is a sequence of 7 equidistant numbers from 1/14 to 13/14.
    // Each of them is the centroid of the interval of length 2/14.
    // They've been rearranged in a sequence of pairs {small, large}, s.t. (small + large) = 1.
    // That way, the running average position is close to 0.5.
    // | 6 | 2 | 4 | 1 | 5 | 3 | 7 |
    // |   |   |   | o |   |   |   |
    // |   | o |   | x |   |   |   |
    // |   | x |   | x |   | o |   |
    // |   | x | o | x |   | x |   |
    // |   | x | x | x | o | x |   |
    // | o | x | x | x | x | x |   |
    // | x | x | x | x | x | x | o |
    // | x | x | x | x | x | x | x |
    float[] m_zSeq = { 7.0f / 14.0f, 3.0f / 14.0f, 11.0f / 14.0f, 5.0f / 14.0f, 9.0f / 14.0f, 1.0f / 14.0f, 13.0f / 14.0f };


    // Ref: https://en.wikipedia.org/wiki/Close-packing_of_equal_spheres
    // The returned {x, y} coordinates (and all spheres) are all within the (-0.5, 0.5)^2 range.
    // The pattern has been rotated by 15 degrees to maximize the resolution along X and Y:
    // https://www.desmos.com/calculator/kcpfvltz7c
    static void GetHexagonalClosePackedSpheres7(Vector2[] coords)
    {

        float r = 0.17054068870105443882f;
        float d = 2 * r;
        float s = r * Mathf.Sqrt(3);

        // Try to keep the weighted average as close to the center (0.5) as possible.
        //  (7)(5)    ( )( )    ( )( )    ( )( )    ( )( )    ( )(o)    ( )(x)    (o)(x)    (x)(x)
        // (2)(1)(3) ( )(o)( ) (o)(x)( ) (x)(x)(o) (x)(x)(x) (x)(x)(x) (x)(x)(x) (x)(x)(x) (x)(x)(x)
        //  (4)(6)    ( )( )    ( )( )    ( )( )    (o)( )    (x)( )    (x)(o)    (x)(x)    (x)(x)
        coords[0] = new Vector2(0, 0);
        coords[1] = new Vector2(-d, 0);
        coords[2] = new Vector2(d, 0);
        coords[3] = new Vector2(-r, -s);
        coords[4] = new Vector2(r, s);
        coords[5] = new Vector2(r, -s);
        coords[6] = new Vector2(-r, s);

        // Rotate the sampling pattern by 15 degrees.
        const float cos15 = 0.96592582628906828675f;
        const float sin15 = 0.25881904510252076235f;

        for (int i = 0; i < 7; i++)
        {
            Vector2 coord = coords[i];

            coords[i].x = coord.x * cos15 - coord.y * sin15;
            coords[i].y = coord.x * sin15 + coord.y * cos15;
        }
    }

    Vector2[] m_xySeq = new Vector2[7];

    //camera.aspect no longer returns the XR aspect ratio but rather the final viewport's. Rather worthless now.
    float CamAspectRatio;
    //camera.fieldOfView is unreliable because the physical camera toggle will return the incorrect fov.
    // float CamFieldOfView = XRSettings.vi


    //Unity implemented their own cookie method, so we'll just tie into that system instead. This is no longer needed. 
    /// Dynamic Light Projection///      
    // [SerializeField, HideInInspector] List<Light> Lights; // TODO: Make this a smart dynamic list not living here
    // public struct LightObject
    // {
    //     public Matrix4x4 LightProjectionMatrix;
    //     public Vector3 LightPosition;
    //     public Vector4 LightColor;
    //     public int LightCookie; //TODO: Add general light cookie system to render engine
    // }

    //Figure out how much data is in the struct above
    // int LightObjectStride = sizeof(float) * 4 * 4 + sizeof(float) * 3 + sizeof(float) * 4 + sizeof(int);
    // Texture2DArray LightProjectionTextures; // TODO: Make this a smart dynamic list pulling from light cookies
    // private static List<LightObject> LightObjects;
    // ComputeBuffer LightBuffer;

    /// END Dynamic Light Projection/// 
    /// 

    // public Texture2D BlueNoise; //Temp ref

    //AABB 

    //Stored compute shader IDs and numbers

    protected int ScatteringKernel = 0;
    protected int IntegrateKernel = 0;
    protected int BlurKernelX = 0;
    protected int BlurKernelY = 0;

    Matrix4x4 matScaleBias;
    Vector3 ThreadsToDispatch;

    //Stored shader variable name IDs

    // Constants so the VolumetricConstant script can access the names
    // The texture/buffers associated with each name will get set
    // as shader globals just before the camera associated with this
    // script renders by the render pipeline, so only that camera uses
    // the volumetrics rendered by this script. 
    public const string resultTextureName = "_VolumetricResult";
    public const string shaderCBName = "VolumetricsCB";
    public const string volumetricKWName = "_VOLUMETRICS_ENABLED";

    int ID_VolumetricResult = Shader.PropertyToID(resultTextureName);
    int ID_VolumetricsCB = Shader.PropertyToID(shaderCBName); // not actually used now since this script doesn't set the constant buffer as the global

    int ID_Result = Shader.PropertyToID("Result");
    int ID_InLightingTexture = Shader.PropertyToID("InLightingTexture");
    int ID_InTex = Shader.PropertyToID("InTex");
    int ID_LightProjectionTextureArray = Shader.PropertyToID("LightProjectionTextureArray");
    int ID_VolumetricClipmapTexture = Shader.PropertyToID("_VolumetricClipmapTexture");
    int ID_VolumetricClipmapTexture2 = Shader.PropertyToID("_VolumetricClipmapTexture2");
    int ID_PreResult = Shader.PropertyToID("PreResult");
    int ID_VolumeMap = Shader.PropertyToID("VolumeMap");
    int ID_PreviousFrameLighting = Shader.PropertyToID("PreviousFrameLighting");
    int ID_HistoryBuffer = Shader.PropertyToID("HistoryBuffer");
    int ID_LeftEyeMatrix = Shader.PropertyToID("LeftEyeMatrix");
    int ID_RightEyeMatrix = Shader.PropertyToID("RightEyeMatrix");
    int ID_ClipmapScale0 = Shader.PropertyToID("ClipmapScale");
    int ID_ClipmapScale1 = Shader.PropertyToID("_ClipmapScale");
    int ID_ClipmapScale2 = Shader.PropertyToID("_ClipmapScale2");
    int ID_ClipmapWorldPosition = Shader.PropertyToID("ClipmapWorldPosition");
    int ID_VBufferUnitDepthTexelSpacing = Shader.PropertyToID("_VBufferUnitDepthTexelSpacing");
    int ID_VolZBufferParams = Shader.PropertyToID("_VolZBufferParams");
    int ID_GlobalExtinction = Shader.PropertyToID("_GlobalExtinction");
    int ID_StaticLightMultiplier = Shader.PropertyToID("_StaticLightMultiplier");
    int ID_GlobalScattering = Shader.PropertyToID("_GlobalScattering");
    int ID_VolumeWorldSize = Shader.PropertyToID("VolumeWorldSize");
    int ID_VolumeWorldPosition = Shader.PropertyToID("VolumeWorldPosition");

    private int ID_media_sphere_buffer_length = Shader.PropertyToID("media_sphere_buffer_length");
    private int ID_media_sphere_buffer = Shader.PropertyToID("media_sphere_buffer");

    int ID_ClipMapGenKern;
    int ID_ClipMapClearKern;
    int ID_ClipMapHeightKern;

    //Froxel Ids
    int PerFrameConstBufferID = Shader.PropertyToID("PerFrameCB");

    //int CameraProjectionMatrixID = Shader.PropertyToID("CameraProjectionMatrix");
    //int TransposedCameraProjectionMatrixID = Shader.PropertyToID("TransposedCameraProjectionMatrix");
    //int inverseCameraProjectionMatrixID = Shader.PropertyToID("inverseCameraProjectionMatrix");
    int PreviousFrameMatrixID = Shader.PropertyToID("PreviousFrameMatrix");
    //int Camera2WorldID = Shader.PropertyToID("Camera2World");
    //int CameraPositionID = Shader.PropertyToID("CameraPosition");
    //Clipmap IDs
    //int CameraMotionVectorID = Shader.PropertyToID("CameraMotionVector");
    //int ClipmapTextureID = Shader.PropertyToID("_ClipmapTexture");
    //int ClipmapTextureID2 = Shader.PropertyToID("_VolumetricClipmapTexture"); //TODO: Make these two the same name
    
    int ClipmapScaleID = Shader.PropertyToID("_ClipmapScale");
    int ClipmapTransformID = Shader.PropertyToID("_ClipmapPosition");

    // int LightObjectsID = Shader.PropertyToID("LightObjects");

    //Temp Jitter stuff
    int tempjitter = 0; //TEMP jitter switcher thing 
    [Header("Extra variables"), Range(0, 1)]
    float[] jitters = new float[2] { 0.0f, 0.5f };

    //GlobalKeyword VolumetricsKW;
    //Previous view matrix data

    Matrix4x4 PreviousFrameMatrix = Matrix4x4.identity;
    Matrix4x4 LeftEyeMatrix;
    Matrix4x4 RightEyeMatrix;
    Vector3 PreviousCameraPosition;
    Vector3 previousPos;
    Quaternion previousQuat;
    Vector4 VolZBufferParams;

    float ZPlaneTexelSpacing;
    //float Extinction;
    //Color ExtinctionColor;

    //General fog settings
    // [HideInInspector]
    [Header("Base values that are overridden by Volumes")]
    public Color albedo = Color.white;
    //    public Color extinctionTint = Color.white;
    public float meanFreePath = 15.0f;
    public float StaticLightMultiplier = 1.0f;

    private ComputeBuffer ShaderConstantBuffer;
    private ComputeBuffer ComputePerFrameConstantBuffer;
    private ComputeBuffer StepAddPerFrameConstantBuffer;

    [StructLayout(LayoutKind.Sequential)]
    struct ShaderConstants
    {
        public Matrix4x4 TransposedCameraProjectionMatrix;
        public Matrix4x4 CameraProjectionMatrix;
        public Vector4 _VBufferDistanceEncodingParams;
        public Vector4 _VolumetricResultDim;
        public Vector3 _VolCameraPos;
    }
    public const int ShaderConstantsCount = 43;
    public const int ShaderConstantsSize = ShaderConstantsCount * sizeof(float);


    [StructLayout(LayoutKind.Sequential)]
    struct ScatteringPerFrameConstants
    {
        public Matrix4x4    _VBufferCoordToViewDirWS;
        public Matrix4x4    _PrevViewProjMatrix;
        public Matrix4x4    _ViewMatrix;
        public Matrix4x4    TransposedCameraProjectionMatrix;
        public Matrix4x4    CameraProjectionMatrix;
        public Vector4      _VBufferDistanceEncodingParams;
        public Vector4      _VBufferDistanceDecodingParams;
        public Vector4      SeqOffset;
        public Vector4      CameraPosition;
        public Vector4      CameraMotionVector;
    }

    private const int ScatterPerFrameCount = 100;


    [StructLayout(LayoutKind.Sequential)]
    struct StepAddPerFrameConstants
    {
        public Vector4 _VBufferDistanceDecodingParams;
        public Vector3 SeqOffset;
    }

    private const int StepAddPerFrameCount = 7;

    private static float[] VolStructToArray<T>(T rawData, int count, int size) where T : struct
    {
        var pinnedRawData = GCHandle.Alloc(rawData, GCHandleType.Pinned);
        try
        {
            var pinnedRawDataPtr = pinnedRawData.AddrOfPinnedObject();
            float[] data = new float[size];
            Marshal.Copy(pinnedRawDataPtr, data, 0, count);
            return data;
        }
        finally
        {
            pinnedRawData.Free();
        }
    }


    #endregion

    private void Awake()
    {
#if UNITY_EDITOR

        if (Application.isPlaying || activeCam == null)
        {
            activeCam = cam;
        }
        else
        {
            //Debug.Log("Volumetric Editor On Awake");
            activeCam = SceneView.lastActiveSceneView.camera;
        }
#else
        activeCam = cam;
        activeCamData = cam.GetComponent<UniversalAdditionalCameraData>();
        if (activeCam.usePhysicalProperties == true) Debug.LogError("Physical camera is not properlly supportted by Unity and WILL mess up XR calulations like voulmetrics and LoDs");
        //  cam = GetComponent<Camera>();
#endif
    }



    void Start() {
//#if !UNITY_EDITOR
        Intialize();
//#endif
    }   

    // bool createdLightProjectionTexture = false;
    // void CheckCookieList()
    // {
    //     if (LightProjectionTextures != null) return;
    //     LightProjectionTextures = new Texture2DArray(1, 1, 1, TextureFormat.RGBA32, false);
    //     LightProjectionTextures.hideFlags = HideFlags.DontSave;
    //     LightProjectionTextures.name = activeCam.name + " Volumetric Light Cookies";
    //     createdLightProjectionTexture = true;
    //     //Debug.Log("Made blank cookie sheet");
    // }

    //void dedbugRTC()
    //{
    //    RenderTexture.active = (RenderTexture)skytex;
    //    GL.Clear(true, true, Color.yellow);
    //    RenderTexture.active = null;

    //}
    //void SetSkyTexture(Texture cubemap)
    //{
    //  //  cam.RenderToCubemap((Cubemap)cubemap);
    // //   dedbugRTC();
    //    Shader.SetGlobalTexture("_SkyTexture", cubemap);
    //}

    bool VerifyVolumetricRegisters()
    {
        //Add realtime light check here too
        if (VolumetricRegisters.volumetricAreas.Count > 0) //brute force check
                                                           //  if (VolumetricRegisters.volumetricAreas.Count > 0)
        {
            Debug.Log(VolumetricRegisters.volumetricAreas.Count + " Volumes ready to render");
            return true;
        }
        Debug.Log("No Volumetric volumes in " + SceneManager.GetActiveScene().name + ". Disabling froxel rendering.");
        this.enabled = false;
        return false;
    }

    void CheckOverrideVolumes() //TODO: Is there a better way to do this?
    {
        //UniversalRenderPipeline.UpdateVolumeFramework(activeCam, activeCamData);

        var stack = VolumeManager.instance.stack;

        var Volumetrics = stack.GetComponent<Volumetrics>();
        if (Volumetrics != null)
            Volumetrics.PushFogShaderParameters();
    }

    void IntializeBlur(RenderTextureDescriptor rtdiscrpt)
    {
        BlurBuffer = new RenderTexture(rtdiscrpt);
        BlurBuffer.name = activeCam.name + "_BlurBuffer";
        BlurBuffer.graphicsFormat = GraphicsFormat.R16G16B16A16_SFloat;
        BlurBuffer.enableRandomWrite = true;
        BlurBuffer.Create();
        Clear3DTexture(BlurBuffer);


        BlurBufferB = new RenderTexture(rtdiscrpt);
        BlurBuffer.name = activeCam.name + "_BlurBufferB";
        BlurBufferB.graphicsFormat = GraphicsFormat.R16G16B16A16_SFloat;
        BlurBufferB.enableRandomWrite = true;
        BlurBufferB.Create();
        Clear3DTexture(BlurBufferB);

        BlurKernelX = BlurCompute.FindKernel("VolBlurX");
        BlurKernelY = BlurCompute.FindKernel("VolBlurY");
    }

    void Intialize()
    {
        if (hasInitialized)
        {
            return;
        }
        if (cam == null)
        {
            Debug.LogWarning("Volumetric Rendering Script with no camera assigned, disabling");
            this.enabled = false;
            return;
        }

#if UNITY_EDITOR
        AssemblyReloadEvents.beforeAssemblyReload += CleanupOnReload;
#endif
        activeCam = cam;
        activeCamData = activeCam?.GetComponent<UniversalAdditionalCameraData>();
#if UNITY_EDITOR
        if (!Application.isPlaying && !enableEditorPreview)
        {
            //Debug.Log("Intialize disabled volumetrics");
            disable();
            return;
        }
#endif
        if (activeCamData == null)
        {
            activeCam = null;
            Debug.LogWarning("Volumetric Rendering: Assigned camera is missing a Universal Additional Camera Data component, disabling");
            this.enabled = false;
            return;
        }


        //Debug.Log("Volumetric Renderer Initialized");
        //DebugPrintTextureIDs();
        ShaderConstantBuffer = new ComputeBuffer(1, ShaderConstantsSize, ComputeBufferType.Constant);
        ComputePerFrameConstantBuffer = new ComputeBuffer(1, ScatterPerFrameCount * sizeof(float), ComputeBufferType.Constant);
        StepAddPerFrameConstantBuffer = new ComputeBuffer(1, StepAddPerFrameCount * sizeof(float), ComputeBufferType.Constant);
        int mediaCount = VolumetricRegisters.VolumetricMediaEntities.Count;
        MediaCount = Math.Max(mediaCount, 1);
        participatingMediaSphereBuffer = new ComputeBuffer(MediaCount, MediaSphereStride, ComputeBufferType.Structured);


        //activeCameraState = activeCam.isActiveAndEnabled;
        CheckOverrideVolumes();
     //   if (VerifyVolumetricRegisters() == false) return; //Check registers to see if there's anything to render. If not, then disable system. TODO: Remove this 
      //  CheckCookieList();


        //   SetSkyTexture( skytex);

        //Making prescaled matrix 
        matScaleBias = Matrix4x4.identity;
        matScaleBias.m00 = -0.5f;
        matScaleBias.m11 = -0.5f;
        matScaleBias.m22 = 0.5f;
        matScaleBias.m03 = 0.5f;
        matScaleBias.m13 = 0.5f;
        matScaleBias.m23 = 0.5f;

        //Create 3D Render Texture 1
        RenderTextureDescriptor rtdiscrpt = new RenderTextureDescriptor();
        rtdiscrpt.enableRandomWrite = true;
        rtdiscrpt.dimension = TextureDimension.Tex3D;
        rtdiscrpt.width = volumetricData.FroxelWidthResolution;
        rtdiscrpt.height = volumetricData.FroxelHeightResolution;
        rtdiscrpt.volumeDepth = volumetricData.FroxelDepthResolution;
        rtdiscrpt.graphicsFormat = GraphicsFormat.R16G16B16A16_SFloat;
        rtdiscrpt.msaaSamples = 1;

        FroxelBufferA = new RenderTexture(rtdiscrpt);
        FroxelBufferA.name = activeCam.name + "_FroxelBufferA";

        FroxelBufferA.Create();

        //Ugh... extra android buffer mess. Can I use a custom RT double buffer instead?
        FroxelBufferB = new RenderTexture(rtdiscrpt);
        FroxelBufferB.name = activeCam.name + "_FroxelBufferB";
        FroxelBufferB.Create();

        rtdiscrpt.width = volumetricData.FroxelWidthResolution * 2; // Make double wide texture for stereo use. Make smarter for non VR use case?
        IntegrationBuffer = new RenderTexture(rtdiscrpt);
        IntegrationBuffer.name = activeCam.name + "_IntegrationBuffer";
        //  IntegrationBuffer.format = RenderTextureFormat.ARGB32;
        IntegrationBuffer.graphicsFormat = GraphicsFormat.R16G16B16A16_SFloat;
        IntegrationBuffer.filterMode = FilterMode.Trilinear;
        IntegrationBuffer.enableRandomWrite = true;
        IntegrationBuffer.Create();

        //IntegrationBufferB = new RenderTexture(rtdiscrpt);
        //IntegrationBufferB.format = RenderTextureFormat.ARGB32;
        //IntegrationBufferB.enableRandomWrite = true;
        //IntegrationBufferB.Create();

        //Extinction = VolumeRenderingUtils.ExtinctionFromMeanFreePath(meanFreePath);
        //ExtinctionColor = albedo * Extinction;

        if (FroxelBlur == BlurType.Gaussian) IntializeBlur(rtdiscrpt);

        // LightObjects = new List<LightObject>();

        ScatteringKernel = FroxelFogCompute.FindKernel("Scatter");

        ZPlaneTexelSpacing = ComputZPlaneTexelSpacing(1, activeCam.fieldOfView, volumetricData.FroxelHeightResolution);


        //UpdateClipmap(Clipmap.Far);
       // FroxelFogCompute.SetTexture(ScatteringKernel, ClipmapTextureID, ClipmapBufferA);
       // temp light cookie array. TODO: Make dynamic. Add to lighting engine too.
                                                                                                              //     FroxelFogCompute.SetTexture(FogFroxelKernel, "BlueNoise", BlueNoise); // temp light cookie array. TODO: Make dynamic. Add to lighting engine too.

        ///Second compute pass setup

        IntegrateKernel = FroxelIntegrationCompute.FindKernel("StepAdd");
        

        //Make view projection matricies

        Matrix4x4 CenterProjectionMatrix = matScaleBias * Matrix4x4.Perspective(activeCam.fieldOfView, CamAspectRatio, volumetricData.near, volumetricData.far);
        Matrix4x4 LeftProjectionMatrix = matScaleBias * Matrix4x4.Perspective(activeCam.fieldOfView, CamAspectRatio, volumetricData.near, volumetricData.far) * Matrix4x4.Translate(new Vector3(activeCam.stereoSeparation * 0.5f, 0, 0)); //temp ipd scaler. Combine factors when confirmed
        Matrix4x4 RightProjectionMatrix = matScaleBias * Matrix4x4.Perspective(activeCam.fieldOfView, CamAspectRatio, volumetricData.near, volumetricData.far) * Matrix4x4.Translate(new Vector3(-activeCam.stereoSeparation * 0.5f, 0, 0));


        Matrix4x4 CenterProjectionMatrixInverse = CenterProjectionMatrix.inverse;
        LeftEyeMatrix = LeftProjectionMatrix * CenterProjectionMatrixInverse;
        RightEyeMatrix = RightProjectionMatrix * CenterProjectionMatrixInverse;
       


        //Global Variable setup

        if (FroxelBlur == BlurType.Gaussian)
        {
            //Shader.SetGlobalTexture(ID_VolumetricResult, BlurBufferB);
            VolumetricResult = BlurBufferB;
        }
        else
        {
            //FroxelFogCompute.SetTexture(ScatteringKernel, ID_VolumetricResult, IntegrationBuffer);
            //Shader.SetGlobalTexture(ID_VolumetricResult, IntegrationBuffer);
            VolumetricResult = IntegrationBuffer;
        }

        ThreadsToDispatch = new Vector3(
             Mathf.Max(Mathf.CeilToInt(volumetricData.FroxelWidthResolution / 4.0f), 1.0f),
              Mathf.Max(Mathf.CeilToInt(volumetricData.FroxelHeightResolution / 4.0f), 1.0f),
              Mathf.Max(Mathf.CeilToInt(volumetricData.FroxelDepthResolution / 4.0f), 1.0f)
            );

        //    ComputZPlaneTexelSpacing(1.0f, vFoV, parameters.resolution.y);

        // Unused as far as I can tell, declared in the VolumetricCore but not actually used
        //Shader.SetGlobalVector("_VolumePlaneSettings", new Vector4(volumetricData.near, volumetricData.far, volumetricData.far - volumetricData.near, volumetricData.near * volumetricData.far));

        VolZBufferParams = new Vector4();
        VolZBufferParams.x = 1.0f - volumetricData.far / volumetricData.near;
        VolZBufferParams.y = volumetricData.far / volumetricData.near;
        VolZBufferParams.z = VolZBufferParams.x / volumetricData.far;
        VolZBufferParams.w = VolZBufferParams.y / volumetricData.far;


        ID_ClipMapGenKern = ClipmapCompute.FindKernel("ClipMapGen");
        ID_ClipMapClearKern = ClipmapCompute.FindKernel("ClipMapClear");
        ID_ClipMapHeightKern = ClipmapCompute.FindKernel("ClipMapHeight");

        //Debug.Log("Dispatching " + ThreadsToDispatch);

        SkyManager.CheckSky();

        Clear3DTexture(FroxelBufferA);
        Clear3DTexture(FroxelBufferB);
        Clear3DTexture(IntegrationBuffer);

        SetVariables();
        SetupClipmap();
        UpdateClipmaps();
        SetFroxelFogUniforms(true);
        SetFroxelIntegrationUniforms(true);
        SetBlurUniforms(true);

        hasInitialized = true;
        VolumetricRegisters.RegisterVolumetricRenderer(this);
        //RenderPipelineManager.beginCameraRendering += UpdatePreRender;
    }

    void SetFroxelFogUniforms(bool forceUpdate = false)
    {
        if (lastFroxelFog != this || forceUpdate)
        {
            FroxelFogCompute.SetFloat(ID_VBufferUnitDepthTexelSpacing, ZPlaneTexelSpacing);
            FroxelFogCompute.SetFloat(ID_ClipmapScale1, volumetricData.ClipmapScale);
            FroxelFogCompute.SetFloat(ID_ClipmapScale2, volumetricData.ClipmapScale2);
            //FroxelFogCompute.SetFloat(ID_GlobalExtinction, Extinction);
            //FroxelFogCompute.SetFloat(ID_StaticLightMultiplier, StaticLightMultiplier);
            FroxelFogCompute.SetTexture(ScatteringKernel, ID_Result, FroxelBufferA);
            // CheckCookieList();
            // FroxelFogCompute.SetTexture(ScatteringKernel, ID_LightProjectionTextureArray, LightProjectionTextures);
            FroxelFogCompute.SetConstantBuffer(PerFrameConstBufferID, StepAddPerFrameConstantBuffer, 0, StepAddPerFrameCount * sizeof(float));
            lastFroxelFog = this;
        }
        if (lastClipmapUpdate != this || forceUpdate)
        {
            FroxelFogCompute.SetFloat(ClipmapScaleID, volumetricData.ClipmapScale);
            FroxelFogCompute.SetVector(ClipmapTransformID, ClipmapTransform);
            if (FlipClipBufferNear)
            {
                FroxelFogCompute.SetTexture(ScatteringKernel, ID_VolumetricClipmapTexture, ClipmapBufferB);
            }
            else
            {
                FroxelFogCompute.SetTexture(ScatteringKernel, ID_VolumetricClipmapTexture, ClipmapBufferA);
            }
            if (FlipClipBufferFar)
            {
                FroxelFogCompute.SetTexture(ScatteringKernel, ID_VolumetricClipmapTexture2, ClipmapBufferC);
            }
            else
            {
                FroxelFogCompute.SetTexture(ScatteringKernel, ID_VolumetricClipmapTexture2, ClipmapBufferD);
            }
        }
    }

    void SetFroxelIntegrationUniforms(bool forceUpdate = false)
    {
        if (lastFroxelIntegrate != this || forceUpdate)
        {
            FroxelIntegrationCompute.SetMatrix(ID_LeftEyeMatrix, LeftEyeMatrix);
            FroxelIntegrationCompute.SetMatrix(ID_RightEyeMatrix, RightEyeMatrix);
            FroxelIntegrationCompute.SetVector(ID_VolZBufferParams, VolZBufferParams);
            //FroxelIntegrationCompute.SetVector(ID_GlobalScattering, ExtinctionColor);
            FroxelIntegrationCompute.SetTexture(IntegrateKernel, ID_Result, IntegrationBuffer);
            FroxelIntegrationCompute.SetTexture(IntegrateKernel, ID_InLightingTexture, FroxelBufferA);
            FroxelIntegrationCompute.SetConstantBuffer(PerFrameConstBufferID, StepAddPerFrameConstantBuffer, 0, StepAddPerFrameCount * sizeof(float));
            lastFroxelIntegrate = this;
        }
    }

    void SetBlurUniforms(bool forceUpdate = false)
    {
        if (FroxelBlur == BlurType.Gaussian && (lastBlur != this || forceUpdate))
        {
            BlurCompute.SetTexture(BlurKernelX, ID_InTex, IntegrationBuffer);
            BlurCompute.SetTexture(BlurKernelX, ID_Result, BlurBuffer);
            BlurCompute.SetTexture(BlurKernelY, ID_InTex, BlurBuffer);
            BlurCompute.SetTexture(BlurKernelY, ID_Result, BlurBufferB);
            lastBlur = this;
        }
    }

    public void ClearAllBuffers()
    {
        ClearClipmap(ClipmapBufferA);
        ClearClipmap(ClipmapBufferB);
        ClearClipmap(ClipmapBufferC);
        ClearClipmap(ClipmapBufferD);

        Clear3DTexture(FroxelBufferA);
        Clear3DTexture(FroxelBufferB);
        Clear3DTexture(IntegrationBuffer);
    }

    // void UpdateLights()
    // {
    //     LightObjects.Clear(); //clear and rebuild for now. TODO: Make a smarter constructor
    //     if (LightBuffer != null) LightBuffer.Release();
    //
    //     for (int i = 0; i < Lights.Count; i++)
    //     {
    //         LightObject lightObject = new LightObject();
    //         lightObject.LightPosition = Lights[i].transform.position;
    //         lightObject.LightColor = new Color(
    //             Lights[i].color.r * Lights[i].intensity,
    //             Lights[i].color.g * Lights[i].intensity,
    //             Lights[i].color.b * Lights[i].intensity,
    //             Lights[i].color.a);
    //         lightObject.LightProjectionMatrix = matScaleBias
    //             * Matrix4x4.Perspective(Lights[i].spotAngle, 1, 0.1f, Lights[i].range)
    //             * Matrix4x4.Rotate(Lights[i].transform.rotation).inverse;
    //
    //         LightObjects.Add(lightObject);
    //     }
    //     LightBuffer = new ComputeBuffer(LightObjects.Count, LightObjectStride);
    //     LightBuffer.SetData(LightObjects);
    //     FroxelFogCompute.SetBuffer(ScatteringKernel, LightObjectsID, LightBuffer); 
    // }
    
    
#region Clipmap funtions
    void SetupClipmap()
    {

        RenderTextureDescriptor ClipRTdiscrpt = new RenderTextureDescriptor();
        ClipRTdiscrpt.enableRandomWrite = true;
        ClipRTdiscrpt.dimension = UnityEngine.Rendering.TextureDimension.Tex3D;
        ClipRTdiscrpt.width = volumetricData.ClipMapResolution;
        ClipRTdiscrpt.height = volumetricData.ClipMapResolution;    
        ClipRTdiscrpt.volumeDepth = volumetricData.ClipMapResolution;
        ClipRTdiscrpt.graphicsFormat = UnityEngine.Experimental.Rendering.GraphicsFormat.R16G16B16A16_SFloat;
        ClipRTdiscrpt.msaaSamples = 1;
        
        ClipmapBufferA = new RenderTexture(ClipRTdiscrpt);
        ClipmapBufferA.name = activeCam.name + "_ClipmapBufferA";
        ClipmapBufferA.Create();
        ClipmapBufferB = new RenderTexture(ClipRTdiscrpt);
        ClipmapBufferB.name = activeCam.name + "_ClipmapBufferB";
        ClipmapBufferB.Create();        
        ClipmapBufferC = new RenderTexture(ClipRTdiscrpt);        
        ClipmapBufferC.name = activeCam.name + "_ClipmapBufferC";
        ClipmapBufferC.Create();        
        ClipmapBufferD = new RenderTexture(ClipRTdiscrpt); 
        ClipmapBufferD.name = activeCam.name + "_ClipmapBufferD";
        ClipmapBufferD.Create();

        ////TODO: Loop through and remove one of the buffers

        ClearClipmap(ClipmapBufferA);
        ClearClipmap(ClipmapBufferB);
        ClearClipmap(ClipmapBufferC);
        ClearClipmap(ClipmapBufferD);

    }

    void ClearClipmap(RenderTexture buffer)
    {
        int clipMapDispatchNum = Mathf.Max(volumetricData.ClipMapResolution / 4, 1);

        ClipmapCompute.SetTexture(ID_ClipMapClearKern, ID_Result, buffer);
        ClipmapCompute.Dispatch(ID_ClipMapClearKern, clipMapDispatchNum, clipMapDispatchNum, clipMapDispatchNum);
    }

    bool ClipFar = false;
    void CheckClipmap() //Check distance from previous sample and recalulate if over threshold. TODO: make it resample chunks
    {

        if (Vector3.Distance(ClipmapCurrentPos, activeCam.transform.position) > volumetricData.ClipmapResampleThreshold || VolumetricRegisterEmpty || VolumetricRegisterForceRefresh)
        {
            //TODO: seperate the frames where this is rendered
            UpdateClipmaps();
            //if (ClipFar == false) UpdateClipmap(Clipmap.Near);
            //else {
            //    UpdateClipmap(Clipmap.Far);
            //    ClipFar = false;
            //    };
        }
    }

    public void UpdateClipmaps()
    {
        //Debug.Log("Clipmap Update: " + activeCam.transform.position);
        if (VolumetricRegisters.volumetricAreas.Count == 0)
        {
            VolumetricRegisterEmpty = true;
            return;
        }
        else if (VolumetricRegisterEmpty)
        {
            VolumetricRegisterEmpty = false;
        }
        UpdateClipmap(Clipmap.Near);
        UpdateClipmap(Clipmap.Far);
        if (VolumetricRegisterForceRefresh) VolumetricRegisterForceRefresh = false;
    }

    public enum Clipmap { Near,Far};


    public void UpdateClipmap(Clipmap clipmap)
    {
        ClipmapTransform = activeCam.transform.position;

        float farscale = volumetricData.ClipmapScale2;

        RenderTexture BufferA;
        RenderTexture BufferB;
        //TODO: bake out variables at start to avoid extra math per clip gen

        //ClipmapCompute.SetFloat(ID_GlobalExtinction, Extinction);

        if (clipmap == Clipmap.Near)
        {
            BufferA = ClipmapBufferB;
            BufferB = ClipmapBufferA;
            ClipmapCompute.SetFloat(ID_ClipmapScale0, volumetricData.ClipmapScale);
            ClipmapCompute.SetVector(ID_ClipmapWorldPosition, ClipmapTransform - (0.5f * volumetricData.ClipmapScale * Vector3.one));
        }
        else
        {
            BufferA = ClipmapBufferC;
            BufferB = ClipmapBufferD;

            ClipmapCompute.SetFloat(ID_ClipmapScale0, volumetricData.ClipmapScale2);
            ClipmapCompute.SetVector(ID_ClipmapWorldPosition, ClipmapTransform - (0.5f * volumetricData.ClipmapScale2 * Vector3.one));

        }

        //Clipmap variables
        //ClipmapCompute.SetVector("ClipmapWorldPosition", ClipmapTransform - (0.5f * volumetricData.ClipmapScale * Vector3.one));
    //    ClipmapCompute.SetFloat("ClipmapScale", volumetricData.ClipmapScale);

        bool FlipClipBuffer = false;
        //Clear previous capture
        int clipMapDispatchNum = Mathf.Max(volumetricData.ClipMapResolution / 4, 1);
     //   ClipmapCompute.SetVector("clearColor", RenderSettings.ambientProbe.Evaluate);


        ClipmapCompute.SetTexture(ID_ClipMapClearKern, ID_Result, BufferA);
        //Debug.Log("Dispatching 0");
        ClipmapCompute.Dispatch(ID_ClipMapClearKern, clipMapDispatchNum, clipMapDispatchNum, clipMapDispatchNum);
        ClipmapCompute.SetTexture(ID_ClipMapClearKern, ID_Result, BufferB);
        //Debug.Log("Dispatching 1");
        ClipmapCompute.Dispatch(ID_ClipMapClearKern, clipMapDispatchNum, clipMapDispatchNum, clipMapDispatchNum);

        //ClipmapCompute.SetFloat("VolumeDensity", 0); //

        //Loop through bake texture volumes and put into clipmap //TODO: Add pass for static unbaked elements
        //Debug.Log("VolumetricRegisters.volumetricAreas.Count: " + VolumetricRegisters.volumetricAreas.Count);
        for (int i = 0; i < VolumetricRegisters.volumetricAreas.Count; i++)
        {
            FlipClipBuffer = !FlipClipBuffer;

            if (FlipClipBuffer)
            {
                ClipmapCompute.SetTexture(ID_ClipMapGenKern, ID_PreResult, BufferB);
                ClipmapCompute.SetTexture(ID_ClipMapGenKern, ID_Result, BufferA);
            }
            else
            {
                ClipmapCompute.SetTexture(ID_ClipMapGenKern, ID_PreResult, BufferA);
                ClipmapCompute.SetTexture(ID_ClipMapGenKern, ID_Result, BufferB);
            }

            //Volumetric variables
            ClipmapCompute.SetTexture(ID_ClipMapGenKern, ID_VolumeMap, VolumetricRegisters.volumetricAreas[i].bakedTexture);
            ClipmapCompute.SetVector(ID_VolumeWorldSize, VolumetricRegisters.volumetricAreas[i].NormalizedScale);
            ClipmapCompute.SetVector(ID_VolumeWorldPosition, VolumetricRegisters.volumetricAreas[i].Corner);
            //Debug.Log("Dispatching 2");
            ClipmapCompute.Dispatch(ID_ClipMapGenKern, clipMapDispatchNum, clipMapDispatchNum, clipMapDispatchNum);
        }
        
        //Height Densitiy

        //FlipClipBuffer = !FlipClipBuffer;

        //if (FlipClipBuffer)
        //{
        //    ClipmapCompute.SetTexture(HeightClipmapKernal, "PreResult", BufferB);
        //    ClipmapCompute.SetTexture(HeightClipmapKernal, "Result", BufferA);
        //}
        //else
        //{
        //    ClipmapCompute.SetTexture(HeightClipmapKernal, "PreResult", BufferA);
        //    ClipmapCompute.SetTexture(HeightClipmapKernal, "Result", BufferB);
        //}

        ////Volumetric variables
        ////ClipmapCompute.SetTexture(HeightClipmapKernal, "VolumeMap", VolumetricRegisters.volumetricAreas[i].bakedTexture);
        ////ClipmapCompute.SetVector("VolumeWorldSize", VolumetricRegisters.volumetricAreas[i].NormalizedScale);
        ////ClipmapCompute.SetVector("VolumeWorldPosition", VolumetricRegisters.volumetricAreas[i].Corner);

        //ClipmapCompute.Dispatch(HeightClipmapKernal, clipMapDispatchNum, clipMapDispatchNum, clipMapDispatchNum);

        //End Height Densitiy

        if (FlipClipBuffer)
        {
            SetClipmap(BufferA, volumetricData.ClipmapScale, ClipmapTransform, clipmap);
        }
        else
        {
            SetClipmap(BufferB, volumetricData.ClipmapScale, ClipmapTransform, clipmap);
        }
        
        switch (clipmap)
        {
            case Clipmap.Near:
                FlipClipBufferNear = FlipClipBuffer;
                break;
            case Clipmap.Far:
                FlipClipBufferFar = FlipClipBuffer;
                break;
            default:
                break;
        }

        ClipmapCurrentPos = ClipmapTransform; //Set History
        lastClipmapUpdate = this;
    }



    void SetClipmap(RenderTexture ClipmapTexture, float ClipmapScale, Vector3 ClipmapTransform, Clipmap clipmap)
    {
        FroxelFogCompute.SetFloat(ClipmapScaleID, ClipmapScale);
        FroxelFogCompute.SetVector(ClipmapTransformID, ClipmapTransform);
        
        if (clipmap == Clipmap.Far)
        {
            FroxelFogCompute.SetTexture(ScatteringKernel, ID_VolumetricClipmapTexture2, ClipmapTexture);
         //   Debug.Log("Added clipmap far :" + ClipmapTexture.name);
        }
        else
        {        //TODO COMBINE THESE
            FroxelFogCompute.SetTexture(ScatteringKernel, ID_VolumetricClipmapTexture, ClipmapTexture); //Set clipmap for
            //FroxelFogCompute.SetTexture(ScatteringKernel, ClipmapTextureID, ClipmapTexture); //Set clipmap for
        }
    }
#endregion

    bool FlopIntegralBuffer = false;
    void FlopIntegralBuffers(){

        FlopIntegralBuffer = !FlopIntegralBuffer;

        if (FlopIntegralBuffer)
        {
            FroxelFogCompute.SetTexture(ScatteringKernel, ID_PreviousFrameLighting, FroxelBufferA);
            FroxelFogCompute.SetTexture(ScatteringKernel, ID_Result, FroxelBufferB);
            FroxelIntegrationCompute.SetTexture(IntegrateKernel, ID_InLightingTexture, FroxelBufferB);
        }
        else
        {
            FroxelFogCompute.SetTexture(ScatteringKernel, ID_PreviousFrameLighting, FroxelBufferB);
            FroxelFogCompute.SetTexture(ScatteringKernel, ID_Result, FroxelBufferA);
            FroxelIntegrationCompute.SetTexture(IntegrateKernel, ID_InLightingTexture, FroxelBufferA);
        }

        FroxelIntegrationCompute.SetTexture(IntegrateKernel, ID_HistoryBuffer, IntegrationBuffer);
        FroxelIntegrationCompute.SetTexture(IntegrateKernel, ID_Result, IntegrationBuffer);
    }

    Matrix4x4 PrevViewProjMatrix = Matrix4x4.identity;



    public void SetVariables()
    {
        //Global multiplier for static lights
        //ScatteringFromExtinctionAndAlbedo
        //THESE ARE GLOBAL VARIABLES. THEY NEED TO STAY GLOBAL

        // The volumetrics script should be in charge of setting these,
        // if there's no volume component then all volumetric
        // scripts will use the last camera to be enabled's values
        // Not ideal, but for now it should be fine

        if (!Volumetrics.hasSetGlobals) // Added check so volumetric rendering scripts don't overwrite the volumetrics scripts values
        {
            float extinction = VolumeRenderingUtils.ExtinctionFromMeanFreePath(meanFreePath);
            Shader.SetGlobalFloat(ID_GlobalExtinction, extinction); //ExtinctionFromMeanFreePath
            Shader.SetGlobalFloat(ID_StaticLightMultiplier, StaticLightMultiplier); //Global multiplier for static lights
        }
    }

    float GetAspectRatio()
    {
        if (activeCam.stereoTargetEye == StereoTargetEyeMask.None) return activeCam.aspect;
        return XRSettings.eyeTextureHeight == 0 ? activeCam.aspect : (float)XRSettings.eyeTextureHeight / (float)XRSettings.eyeTextureWidth;
    }

//    void Update()
//    {
//#if UNITY_EDITOR
//        if (Application.isPlaying)
//        {
//            UpdateFunc();
//        }
//#else
//        UpdateFunc();
//#endif
//    }

    void UpdatePreRender(ScriptableRenderContext ctxt, Camera cam1)
    {
        if (activeCam == cam1) UpdateFunc();
    }

    void UpdateFunc()
    {
        using (new ProfilingScope(null, profileUpdateFunc))
        {
            if (!hasInitialized)
            {
                //Debug.LogWarning("Volumetric Rendering: Volumetrics trying to render without initializing");
                return;
            }
            if (activeCam == null)
            {
                Debug.LogError("Volumetric Rendering: Active camera destroyed or de-assigned, disabling");
                this.enabled = false;
                return;
            }
#if UNITY_EDITOR
            if ((Application.isPlaying && !activeCam.isActiveAndEnabled && !enableEditorPreview))
#else
        if (!activeCam.isActiveAndEnabled)
#endif
            {
                return;
            }




            CheckOverrideVolumes();
            //camera.aspect no longer returns the correct value & this workaround only works when XR is fully intialized otherwise it returns 0 and divs by 0; >W<
            //bleh
            CamAspectRatio = GetAspectRatio();

            Matrix4x4 projectionMatrix = Matrix4x4.Perspective(activeCam.fieldOfView, CamAspectRatio, activeCam.nearClipPlane, volumetricData.far) * Matrix4x4.Rotate(activeCam.transform.rotation).inverse;
            projectionMatrix = matScaleBias * projectionMatrix;

            //Previous frame's matrix//!!!!!!!!!
            FroxelFogCompute.SetMatrix(PreviousFrameMatrixID, PreviousFrameMatrix);///
            //   FroxelFogCompute.SetMatrix(PreviousFrameMatrixID, PreviousFrameMatrix );///
            //            var controller = hdCamera.volumeStack.GetComponent<Fog>(); //TODO: Link with controller
            //     UpdateLights();

            CheckClipmap(); // UpdateClipmap();

            SetFroxelFogUniforms();
            SetFroxelIntegrationUniforms();
            SetBlurUniforms();

            FlopIntegralBuffers();
            //  Matrix4x4 lightMatrix = matScaleBias * Matrix4x4.Perspective(LightPosition.spotAngle, 1, 0.1f, LightPosition.range) * Matrix4x4.Rotate(LightPosition.transform.rotation).inverse;
            VBufferParameters vbuff = new VBufferParameters(
                                            new Vector3Int(volumetricData.FroxelWidthResolution, volumetricData.FroxelWidthResolution, volumetricData.FroxelDepthResolution),
                                            volumetricData.far,
                                            activeCam.nearClipPlane,
                                            activeCam.farClipPlane,
                                            activeCam.fieldOfView,
                                            SliceDistributionUniformity);

            //     Vector2Int sharedBufferSize = new Vector2Int(volumetricData.FroxelWidthResolution, volumetricData.FroxelHeightResolution); //Taking scaler functuion from HDRP for reprojection
            //     Shader.SetGlobalVector("_VBufferSharedUvScaleAndLimit", vbuff.ComputeUvScaleAndLimit(sharedBufferSize) ); //Just assuming same scale

            Vector4 vres = new Vector4(volumetricData.FroxelWidthResolution, volumetricData.FroxelHeightResolution, 1.0f / volumetricData.FroxelWidthResolution, 1.0f / volumetricData.FroxelHeightResolution);
            //Vector4  vres = new Vector4(cam.pixelWidth, cam.pixelHeight, 1.0f / cam.pixelWidth, cam.pixelHeight);

            Matrix4x4 PixelCoordToViewDirWS = ComputePixelCoordToWorldSpaceViewDirectionMatrix(activeCam, vres);

            GetHexagonalClosePackedSpheres7(m_xySeq);
            int sampleIndex = Time.renderedFrameCount % 7;
            Vector3 seqOffset = new Vector3(m_xySeq[sampleIndex].x, m_xySeq[sampleIndex].y, m_zSeq[sampleIndex]);

            Span<ShaderConstants> shaderConsts = stackalloc ShaderConstants[1];
            shaderConsts[0].TransposedCameraProjectionMatrix = projectionMatrix.transpose;
            shaderConsts[0].CameraProjectionMatrix = projectionMatrix;
            shaderConsts[0]._VBufferDistanceEncodingParams = vbuff.depthEncodingParams;
            shaderConsts[0]._VolumetricResultDim = new Vector3(FroxelBlur != BlurType.Gaussian ? volumetricData.FroxelWidthResolution * 2 : volumetricData.FroxelWidthResolution,
                volumetricData.FroxelHeightResolution, volumetricData.FroxelDepthResolution);
            shaderConsts[0]._VolCameraPos = activeCam.transform.position;
            if (ShaderConstantBuffer == null)
            {
                ShaderConstantBuffer = new ComputeBuffer(ShaderConstantsCount, sizeof(float), ComputeBufferType.Constant);
                //Shader.SetGlobalConstantBuffer(ID_VolumetricsCB, ShaderConstantBuffer, 0, ShaderConstantsSize);
                //Debug.Log("Created New Compute Buffer");
            }
            ShaderConstantBuffer.SetData<ShaderConstants>(shaderConsts);


            Span<StepAddPerFrameConstants> stepAddConst = stackalloc StepAddPerFrameConstants[1];
            stepAddConst[0] = new StepAddPerFrameConstants();
            stepAddConst[0]._VBufferDistanceDecodingParams = vbuff.depthDecodingParams;
            stepAddConst[0].SeqOffset = seqOffset;
            if (StepAddPerFrameConstantBuffer == null)
            {
                StepAddPerFrameConstantBuffer = new ComputeBuffer(1, StepAddPerFrameCount * sizeof(float), ComputeBufferType.Constant);
                //Shader.SetGlobalConstantBuffer(PerFrameConstBufferID, StepAddPerFrameConstantBuffer, 0, StepAddPerFrameCount * sizeof(float));
                //Debug.Log("Created New Compute Buffer");
            }
            StepAddPerFrameConstantBuffer.SetData<StepAddPerFrameConstants>(stepAddConst);

            Span<ScatteringPerFrameConstants> VolScatteringCB = stackalloc ScatteringPerFrameConstants[1];
            VolScatteringCB[0] = new ScatteringPerFrameConstants()
            {
                _VBufferCoordToViewDirWS = PixelCoordToViewDirWS,
                _PrevViewProjMatrix = PrevViewProjMatrix,
                _ViewMatrix = activeCam.worldToCameraMatrix,
                TransposedCameraProjectionMatrix = projectionMatrix.transpose,
                CameraProjectionMatrix = projectionMatrix,
                _VBufferDistanceEncodingParams = vbuff.depthEncodingParams,
                _VBufferDistanceDecodingParams = vbuff.depthDecodingParams,
                SeqOffset = seqOffset,
                CameraPosition = activeCam.transform.position,
                CameraMotionVector = activeCam.transform.position - PreviousCameraPosition
            };
            //float[] VolScatteringCBArray = VolStructToArray(VolScatteringCB, PerFrameConstantsCount, PerFrameConstantsSize);
            //Debug.Log(VolScatteringCB._VBufferCoordToViewDirWS);
            //Debug.Log(VolScatteringCBArray[4] + " " + VolScatteringCBArray[5] + " " + VolScatteringCBArray[6] + " " + VolScatteringCBArray[7]);
            if (ComputePerFrameConstantBuffer == null)
            {
                ComputePerFrameConstantBuffer = new ComputeBuffer(1, ScatterPerFrameCount * sizeof(float), ComputeBufferType.Constant);
                //Debug.Log("Created New Compute Buffer");
            }
            ComputePerFrameConstantBuffer.SetData<ScatteringPerFrameConstants>(VolScatteringCB);
            FroxelFogCompute.SetConstantBuffer(PerFrameConstBufferID, ComputePerFrameConstantBuffer, 0, ScatterPerFrameCount * sizeof(float));

            /*
            int mediaCount = VolumetricRegisters.VolumetricMediaEntities.Count;
            int maxCount = Math.Max(mediaCount, 1);

            if (object.ReferenceEquals(participatingMediaSphereBuffer, null) || participatingMediaSphereBuffer == null)
            {
                participatingMediaSphereBuffer = new ComputeBuffer(maxCount, MediaSphereStride, ComputeBufferType.Structured);
                //Debug.Log("Created New Compute Buffer");
            }
            else if (maxCount > MediaCount)
            {
                participatingMediaSphereBuffer.Release();
                participatingMediaSphereBuffer = new ComputeBuffer(maxCount, MediaSphereStride, ComputeBufferType.Structured);
                MediaCount = maxCount;
            }

            
            MediaSphere[] mediadata = new MediaSphere[maxCount];

            if (mediaCount < 1)
            {
                //mediadata[0].CenterPosition = Vector3.zero;
                //mediadata[0].LocalExtinction = 0;
                //mediadata[0].LocalRange = 0; 
                //mediadata[0].LocalFalloff = 0;
            }
            else
            {
                for (int i = 0; i < mediadata.Length; i++)
                {
                    //TODO: generalize the strut between the classes so we don't have to recast it here 
                    mediadata[i].CenterPosition = VolumetricRegisters.VolumetricMediaEntities[i].gameObject.transform.position;
                    mediadata[i].LocalExtinction = VolumetricRegisters.VolumetricMediaEntities[i].LocalExtinction();
                    mediadata[i].LocalRange = VolumetricRegisters.VolumetricMediaEntities[i].Scale.magnitude; // temp mag
                    mediadata[i].LocalFalloff = VolumetricRegisters.VolumetricMediaEntities[i].falloffDistance;
                }
            }

            if (participatingMediaSphereBuffer != null)
            {
                participatingMediaSphereBuffer.SetData(mediadata);
                FroxelFogCompute.SetBuffer(ScatteringKernel, ID_media_sphere_buffer, participatingMediaSphereBuffer);
                FroxelFogCompute.SetFloat(ID_media_sphere_buffer_length, mediaCount);
            }
            

            /*
            if (VolumetricConstantBuffer != null && projectionMatrix != null && activeCam != null && vbuff.depthEncodingParams != null)
            {
                VolumetricConstants vConst = new VolumetricConstants();
                vConst.CameraProjectionMatrix = projectionMatrix.transpose;
                vConst.TransposedCameraProjectionMatrix = projectionMatrix;
                vConst._VBufferDistanceEncodingParams = vbuff.depthEncodingParams;
                vConst._VolCameraPos = activeCam.transform.position;
                float[] vConstArray = VolStructToArray(vConst);
                VolumetricConstantBuffer.SetData(vConstArray);
                Shader.SetGlobalConstantBuffer("VolumetricCB", VolumetricConstantBuffer, 0, VolCBCount * sizeof(float));
            }
            */
            PreviousFrameMatrix = projectionMatrix;
            PreviousCameraPosition = activeCam.transform.position;
            ////MATRIX
            ///
            ///camera.projectionMatrix is ALSO broken and returns the final viewport's projection rather than the center XR projection.
            ///cam.GetStereoProjectionMatrix returns the skewed XR projection matrix per eye. Just doing our own calulation
            var gpuProj = GL.GetGPUProjectionMatrix(Matrix4x4.Perspective(activeCam.fieldOfView, CamAspectRatio, activeCam.nearClipPlane, 100000f), true);
            PrevViewProjMatrix = gpuProj * activeCam.worldToCameraMatrix;
            //Debug.Log("Dispatching 3");
            FroxelFogCompute.Dispatch(ScatteringKernel, (int)ThreadsToDispatch.x, (int)ThreadsToDispatch.y, (int)ThreadsToDispatch.z);
            //    FroxelStackingCompute.DispatchIndirect
            //CONVERT TO DISPATCH INDIRECT to avoid CPU callback?
            //Debug.Log("Dispatching 4");
            FroxelIntegrationCompute.Dispatch(IntegrateKernel, (int)ThreadsToDispatch.x * 2, (int)ThreadsToDispatch.y, (int)ThreadsToDispatch.z); //x2 for stereo

            if (FroxelBlur == BlurType.Gaussian)
            {
                BlurCompute.Dispatch(BlurKernelX, (int)ThreadsToDispatch.x * 2, (int)ThreadsToDispatch.y, (int)ThreadsToDispatch.z); // Final blur
                BlurCompute.Dispatch(BlurKernelY, (int)ThreadsToDispatch.x * 2, (int)ThreadsToDispatch.y, (int)ThreadsToDispatch.z); // Final blur
            }
            /* Give the shader constant buffer and volumetric render texture to the
             * additional camera data so that on render the camera can set them as
             * globals
             */
            SetCameraData();
        }
    }


    //Coping the parms from HDRP to get the log encoded depth.
    struct VBufferParameters 
    {
        public Vector3Int viewportSize;
        public Vector4 depthEncodingParams;
        public Vector4 depthDecodingParams;

        public VBufferParameters(Vector3Int viewportResolution, float depthExtent, float camNear, float camFar, float camVFoV, float sliceDistributionUniformity)
        {
            viewportSize = viewportResolution;

            // The V-Buffer is sphere-capped, while the camera frustum is not.
            // We always start from the near plane of the camera.

            float aspectRatio = viewportResolution.x / (float)viewportResolution.y;
            float farPlaneHeight = 2.0f * Mathf.Tan(0.5f * camVFoV) * camFar;
            float farPlaneWidth = farPlaneHeight * aspectRatio;
            float farPlaneMaxDim = Mathf.Max(farPlaneWidth, farPlaneHeight);
            float farPlaneDist = Mathf.Sqrt(camFar * camFar + 0.25f * farPlaneMaxDim * farPlaneMaxDim);

            float nearDist = camNear;
            float farDist = Mathf.Min(nearDist + depthExtent, farPlaneDist);

            float c = 2 - 2 * sliceDistributionUniformity; // remap [0, 1] -> [2, 0]
            c = Mathf.Max(c, 0.001f);                // Avoid NaNs

            depthEncodingParams = ComputeLogarithmicDepthEncodingParams(nearDist, farDist, c);
            depthDecodingParams = ComputeLogarithmicDepthDecodingParams(nearDist, farDist, c);
        }

        internal Vector4 ComputeUvScaleAndLimit(Vector2Int bufferSize)
        {
            // The slice count is fixed for now.
            return ComputeUvScaleAndLimitFun(new Vector2Int(viewportSize.x, viewportSize.y), bufferSize);
        }

        internal float ComputeLastSliceDistance(int sliceCount)
        {
            float d = 1.0f - 0.5f / sliceCount;
            float ln2 = 0.69314718f;

            // DecodeLogarithmicDepthGeneralized(1 - 0.5 / sliceCount)
            return depthDecodingParams.x * Mathf.Exp(ln2 * d * depthDecodingParams.y) + depthDecodingParams.z;
        }

        // See EncodeLogarithmicDepthGeneralized().
        static Vector4 ComputeLogarithmicDepthEncodingParams(float nearPlane, float farPlane, float c)
        {
            Vector4 depthParams = new Vector4();

            float n = nearPlane;
            float f = farPlane;

            depthParams.y = 1.0f / Mathf.Log(c * (f - n) + 1, 2);
            depthParams.x = Mathf.Log(c, 2) * depthParams.y;
            depthParams.z = n - 1.0f / c; // Same
            depthParams.w = 0.0f;

            return depthParams;
        }

        // See DecodeLogarithmicDepthGeneralized().
        static Vector4 ComputeLogarithmicDepthDecodingParams(float nearPlane, float farPlane, float c)
        {
            Vector4 depthParams = new Vector4();

            float n = nearPlane;
            float f = farPlane;

            depthParams.x = 1.0f / c;
            depthParams.y = Mathf.Log(c * (f - n) + 1, 2);
            depthParams.z = n - 1.0f / c; // Same
            depthParams.w = 0.0f;

            return depthParams;
        }
    }

    internal static float ComputZPlaneTexelSpacing(float planeDepth, float verticalFoV, float resolutionY)
    {
        float tanHalfVertFoV = Mathf.Tan(0.5f * verticalFoV);
        return tanHalfVertFoV * (2.0f / resolutionY) * planeDepth;
    }

    internal static Vector4 ComputeUvScaleAndLimitFun(Vector2Int viewportResolution, Vector2Int bufferSize)
    {
        Vector2 rcpBufferSize = new Vector2(1.0f / bufferSize.x, 1.0f / bufferSize.y);

        // vp_scale = vp_dim / tex_dim.
        Vector2 uvScale = new Vector2(viewportResolution.x * rcpBufferSize.x,
                                      viewportResolution.y * rcpBufferSize.y);

        // clamp to (vp_dim - 0.5) / tex_dim.
        Vector2 uvLimit = new Vector2((viewportResolution.x - 0.5f) * rcpBufferSize.x,
                                      (viewportResolution.y - 0.5f) * rcpBufferSize.y);

        return new Vector4(uvScale.x, uvScale.y, uvLimit.x, uvLimit.y);
    }

    public void disable()
    {
        //Debug.Log("Volumetric Rendering: Disable Called");
        hasInitialized = false;
#if UNITY_EDITOR
            RenderPipelineManager.beginCameraRendering -= UpdatePreRender;
            AssemblyReloadEvents.beforeAssemblyReload -= CleanupOnReload;
#else
            RenderPipelineManager.beginCameraRendering -= UpdatePreRender;
#endif
        ReleaseAssets();
        CleanupCameraData();
        VolumetricRegisters.UnregisterVolumetricRenderer(this);
    }

    public void enable()
    {
        StartSceneViewRendering();
        Intialize();
    }

    public void StartSceneViewRendering()
    {
//#if UNITY_EDITOR
//        if (enableEditorPreview && !Application.isPlaying) RenderPipelineManager.beginCameraRendering += UpdatePreRender;
//#else
        RenderPipelineManager.beginCameraRendering += UpdatePreRender;
//#endif
    }

    public void UpdateStateAfterReload()
    {
        if (enableEditorPreview && this.isActiveAndEnabled)
        {
            enable();
        }
        else
        {
            disable();
        }
    }

    public void CleanupOnReload()
    {
        disable();
    }

    private void OnEnable()
    {
        
#if UNITY_EDITOR
        if (!Application.isPlaying)
        {
            // Every time scripts get re-compiled, everything gets reset without calling OnDisable or OnDestroy, and the keyword gets left on 
            enableEditorPreview = false;
            AssemblyReloadEvents.afterAssemblyReload += UpdateStateAfterReload;
        }
        else
        {
            enable();
        }
#else
        enable();
#endif
    }
    private void OnDisable() //Disable this if we decide to just pause rendering instead of removing. 
    {
        disable();
#if UNITY_EDITOR
        if (!Application.isPlaying)
        {
            AssemblyReloadEvents.afterAssemblyReload -= UpdateStateAfterReload;
        }
#endif
    }
    private void OnDestroy()
    {
        disable();
#if UNITY_EDITOR
        if (!Application.isPlaying)
        {
            AssemblyReloadEvents.afterAssemblyReload -= UpdateStateAfterReload;
        }
#endif

    }

    private void DestroyAllTextureAssets()
    {
        ClipmapBufferA.Clear();
        ClipmapBufferB.Clear();
        ClipmapBufferC.Clear();
        ClipmapBufferD.Clear();

        FroxelBufferA.Clear();
        FroxelBufferB.Clear();
        IntegrationBuffer.Clear();

        BlurBuffer.Clear();
        BlurBufferB.Clear();
        
       // if (createdLightProjectionTexture && LightProjectionTextures != null) { CoreUtils.Destroy(LightProjectionTextures); }
    }



    Matrix4x4 ComputePixelCoordToWorldSpaceViewDirectionMatrix(Camera cam, Vector4 resolution)
    {
        //   var proj = cam.projectionMatrix; //  GL.GetGPUProjectionMatrix(cameraProj, true); //Use this if we run into platform issues
        //bandaid fix. There's an issue with the far clip plane in the matrix projection. 
        var proj = Matrix4x4.Perspective(cam.fieldOfView, CamAspectRatio, cam.nearClipPlane, 100000f); 
        var view = cam.worldToCameraMatrix ;

        var invViewProjMatrix = (proj * view).inverse;

        var transform = Matrix4x4.Scale(new Vector3(-1.0f, -1.0f, -1.0f)) * invViewProjMatrix; // (gpuProj * gpuView).inverse
     //   transform = transform * Matrix4x4.Scale(new Vector3(1.0f, -1.0f, 1.0f));
        transform = transform * Matrix4x4.Translate(new Vector3(-1.0f, -1.0f, 0.0f));
        transform = transform * Matrix4x4.Scale(new Vector3(2.0f * resolution.z, 2.0f * resolution.w, 1.0f)) ;

        return transform.transpose;
    }


    void SetComputeVariables()
    {


    }
    
    void SetComputeBuffer(string name, ComputeShader shader, int kernel, ComputeBuffer buffer)
    {
        //   Debug.Log("Setting buffer");
        if (buffer != null)
        {
            shader.SetBuffer(kernel, name, buffer);

            //     Debug.Log(name + " set");
        }
    }
    
    private static void CreateComputeBuffer<T>(ref ComputeBuffer buffer, List<T> data, int stride)
        where T : struct
    {
        //Debug.Log("Making computebuffer ");
        //buffer = new ComputeBuffer(data.Count, stride);

        // Do we already have a compute buffer?
        if (buffer != null && data != null && stride != null)
        {
            // If no data or buffer doesn't match the given criteria, release it
            if (data.Count == 0 || buffer.count != data.Count || buffer.stride != stride)
            {
                //    Debug.Log("Buffer count = " + buffer.count);

                buffer.Release();
                buffer = null;
            }
        }

        if (data.Count != 0)
        {
            // If the buffer has been released or wasn't there to
            // begin with, create it
            if (buffer == null)
            {
                buffer = new ComputeBuffer(data.Count, stride);

                //        Debug.Log("Buffer count = " + buffer.count);

            }

            // Set data on the buffer
            buffer.SetData(data);
        }
    }

    /// <summary>
    /// Editor
    /// </summary>
    /// 
    private void OnDrawGizmosSelected()
    {
        if (cam == null || volumetricData == null) return;

        Gizmos.color = Color.black;
;
        Gizmos.matrix = Matrix4x4.TRS(cam.transform.position, cam.transform.rotation, Vector3.one);
        Gizmos.DrawFrustum(Vector3.zero, cam.fieldOfView, volumetricData.near, volumetricData.far, CamAspectRatio);

        Gizmos.color = Color.cyan;
        Gizmos.matrix = Matrix4x4.TRS(ClipmapCurrentPos, Quaternion.identity, Vector3.one * volumetricData.ClipmapScale);
        Gizmos.DrawWireCube(Vector3.zero, Vector3.one);

        Gizmos.color = Color.blue;
        Gizmos.matrix = Matrix4x4.TRS(ClipmapCurrentPos, Quaternion.identity, Vector3.one * volumetricData.ClipmapScale2);
        Gizmos.DrawWireCube(Vector3.zero, Vector3.one);


        //Gizmos.color = Color.red;
        //Gizmos.matrix = cam.GetStereoProjectionMatrix(Camera.StereoscopicEye.Left);
        //Gizmos.DrawWireCube(Vector3.zero, Vector3.one);        

        //Gizmos.color = Color.yellow;
        //Gizmos.matrix = cam.GetStereoProjectionMatrix(Camera.StereoscopicEye.Right);
        //Gizmos.DrawWireCube(Vector3.zero, Vector3.one);

        //Gizmos.color = Color.green;
        //Gizmos.matrix = Matrix4x4.Perspective(cam.fieldOfView, CamAspectRatio, cam.nearClipPlane, 100000f);
        //Gizmos.DrawWireCube(Vector3.zero, Vector3.one);        
        
        //Gizmos.color = Color.magenta;
        //Gizmos.matrix = Matrix4x4.Perspective(cam.fieldOfView, CamAspectRatio, volumetricData.near, volumetricData.far) * Matrix4x4.Translate(new Vector3(cam.stereoSeparation * 0.5f, 0, 0));
        //Gizmos.DrawWireCube(Vector3.zero, Vector3.one);


    }

    void ReleaseAssets()
    {
        DestroyAllTextureAssets();

        if (ComputePerFrameConstantBuffer != null)
        {
            ComputePerFrameConstantBuffer.Release();
            ComputePerFrameConstantBuffer = null;
        }
        if (StepAddPerFrameConstantBuffer != null)
        { 
            StepAddPerFrameConstantBuffer.Release();
            StepAddPerFrameConstantBuffer = null;
        }
        if (ShaderConstantBuffer != null)
        {
            ShaderConstantBuffer.Release();
            ShaderConstantBuffer = null;
        }
        if (participatingMediaSphereBuffer != null)
        {
            participatingMediaSphereBuffer.Release();
        }
    }

    void CleanupCameraData()
    {
        if (activeCamData != null)
        {
            activeCamData.m_EnableVolumetrics = false;
            activeCamData.m_VolumetricClipMap = null;
            activeCamData.m_VolumetricShaderGlobals = null;
        }
    }

    void SetCameraData()
    {
        if (activeCamData != null && VolumetricResult != null && ShaderConstantBuffer != null)
        {
            activeCamData.m_EnableVolumetrics = true;
            activeCamData.m_VolumetricClipMap = VolumetricResult;
            activeCamData.m_VolumetricShaderGlobals = ShaderConstantBuffer;
        }
        else
        {
            Debug.LogWarning("Volumetric Rendering: Null extra camera data, volumetric result, or constant buffer!");
            activeCamData.m_EnableVolumetrics = false;
        }
    }

#if UNITY_EDITOR

    void assignVaris()
    {
        
        //cam = GetComponentInChildren<Camera>();
        //Get shaders and seri
        if (FroxelFogCompute == null)
            FroxelFogCompute = AssetDatabase.LoadAssetAtPath<ComputeShader>("Packages/com.unity.render-pipelines.universal/Shaders/Volumetrics/VolumetricScattering.compute");
        if (FroxelFogCompute == null)
            FroxelFogCompute = AssetDatabase.LoadAssetAtPath<ComputeShader>("Packages/com.unity.render-pipelines.universal/Shaders/Volumetrics/VolumetricScattering.compute");
        if (FroxelIntegrationCompute == null)
            FroxelIntegrationCompute = AssetDatabase.LoadAssetAtPath<ComputeShader>("Packages/com.unity.render-pipelines.universal/Shaders/Volumetrics/StepAdd.compute");
        if (ClipmapCompute == null)
            ClipmapCompute = AssetDatabase.LoadAssetAtPath<ComputeShader>("Packages/com.unity.render-pipelines.universal/Shaders/Volumetrics/ClipMapGenerator.compute");
        if (BlurCompute == null)
            BlurCompute = AssetDatabase.LoadAssetAtPath<ComputeShader>("Packages/com.unity.render-pipelines.universal/Shaders/Volumetrics/VolumetricBlur.compute");
    }



    private void Reset()
    {
        cam = GetComponentInChildren<Camera>();
        assignVaris();
    }


    private void OnValidate()
    {

        //Black Texture in editor to not get in the way. Isolated h ere because shaders should skip volumetric tex in precompute otherwise. 
        // TODO: Add proper scene preview feature
        if (BlackTex == null) BlackTex = CoreUtils.blackVolumeTexture; //(Texture3D)MakeBlack3DTex();
        
        //        UnityEditor.SceneManagement.EditorSceneManager.sceneUnloaded += UnloadKeyword; //adding function when scene is unloaded 
        assignVaris();
        //if (cam == null) cam = GetComponent<Camera>();
        //if (volumetricData.near < cam.nearClipPlane || volumetricData.far > cam.farClipPlane)
        //{
        //    //Auto clamp to inside of the camera's clip planes
        //    volumetricData.near = Mathf.Max(volumetricData.near, cam.nearClipPlane);
        //    volumetricData.far = Mathf.Min(volumetricData.far, cam.farClipPlane);
        //}
        
        //Shader.EnableKeyword(VolumetricsKW); //enabling here so the editor knows that it exists
    }
#endif

    //Using core blackVolumeTexture instead
    //Texture MakeBlack3DTex()
    //{
    //    Debug.Log("Made blank texture");

    //    int size = 1;

    //    Texture3D BlackTex = new Texture3D(1, 1, 1, TextureFormat.ARGB32, false);
    //    var cols = new Color[size * size * size];
    //    float mul = 1.0f / (size - 1);
    //    int idx = 0;
    //    Color c = Color.white;
    //    for (int z = 0; z < size; ++z)
    //    {
    //        for (int y = 0; y < size; ++y)
    //        {
    //            for (int x = 0; x < size; ++x, ++idx)
    //            {
    //                c.r = 0;
    //                c.g = 0;
    //                c.b = 0;
    //                c.a = 1;
    //                cols[idx] = c;
    //            }
    //        }
    //    }

    //    BlackTex.SetPixels(cols);
    //    BlackTex.Apply();
    //    // SetClipmap(BlackTex, 50, Vector3.zero);

    //    Shader.SetGlobalTexture(ID_VolumetricResult, BlackTex);

    //    //    Shader.SetGlobalTexture("_VolumetricClipmapTexture", BlackTex); //Set clipmap for
    //    return BlackTex;
    //}


    //public void UnloadKeyword<Scene>(Scene scene)
    //{
    //    Shader.DisableKeyword("_VOLUMETRICS_ENABLED");

    //    print("The scene was unloaded!");
    //}

    void Clear3DTexture(RenderTexture buffer)
    {
        ClipmapCompute.SetTexture(ID_ClipMapClearKern, ID_Result, buffer);
        ClipmapCompute.Dispatch(ID_ClipMapClearKern, Mathf.Max(buffer.width / 4, 1), Mathf.Max(buffer.height / 4, 1), Mathf.Max(buffer.volumeDepth / 4, 1));

        //RenderTexture activeRT = RenderTexture.active;
        //RenderTexture.active = rt;
        //GL.sRGBWrite = rt.sRGB;
        //if (rt.dimension == TextureDimension.Tex3D)
        //{
        //    CoreUtils.SetRenderTarget(
        //        command,
        //        rt,
        //        ClearFlag.Color, Color.clear,
        //        0, CubemapFace.Unknown, -1
        //    );

        //}
        //else if (rt.dimension == TextureDimension.Cube)
        //{    
        //    Graphics.SetRenderTarget(rt, 0, CubemapFace.PositiveX, 0);
        //    GL.Clear(false, true, color);
        //    Graphics.SetRenderTarget(rt, 0, CubemapFace.PositiveY, 0);
        //    GL.Clear(false, true, color);
        //    Graphics.SetRenderTarget(rt, 0, CubemapFace.PositiveZ, 0);
        //    GL.Clear(false, true, color);
        //    Graphics.SetRenderTarget(rt, 0, CubemapFace.NegativeX, 0);
        //    GL.Clear(false, true, color);
        //    Graphics.SetRenderTarget(rt, 0, CubemapFace.NegativeY, 0);
        //    GL.Clear(false, true, color);
        //    Graphics.SetRenderTarget(rt, 0, CubemapFace.NegativeZ, 0);
        //    GL.Clear(false, true, color);
        //}
        //        CoreUtils.SetRenderTarget(
        //           rt,
        //           BuiltinRenderTextureType.CameraTarget
        //);
        //        RenderTexture.active = activeRT;
    }

    void RefreshOnSceneChange(Scene oldS, Scene newS)
    {
        if (hasInitialized && oldS != null)
        {
            Debug.Log("Volumetric Rendering: Refreshing after scene swap");
            this.disable();
            this.enable();
        }
    } 
}