1613 lines
64 KiB
C#
1613 lines
64 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Unity.Mathematics;
|
|
using UnityEngine;
|
|
using UnityEditor;
|
|
using UnityEngine.SceneManagement;
|
|
using UnityEngine.Experimental.Rendering;
|
|
using UnityEngine.Rendering.Universal;
|
|
using System.IO;
|
|
using Unity.Collections;
|
|
using SLZ.SLZEditorTools;
|
|
using UnityEngine.Rendering;
|
|
using System.Diagnostics;
|
|
using UnityEditor.SceneManagement;
|
|
#if UNITY_EDITOR_WIN
|
|
using Microsoft.Win32;
|
|
#endif
|
|
using Debug = UnityEngine.Debug;
|
|
|
|
public class VolumetricBaking : EditorWindow
|
|
{
|
|
// Add menu item named "My Window" to the Window menu
|
|
[MenuItem("Stress Level Zero/Volumetric Baking")]
|
|
public static void ShowWindow()
|
|
{
|
|
//Show existing window instance. If one doesn't exist, make one.
|
|
GetWindow(typeof(VolumetricBaking));
|
|
SetGPUTimeout();
|
|
// BuildComboList();
|
|
// BuildSelectionGrid();
|
|
}
|
|
|
|
static void SetGPUTimeout()
|
|
{
|
|
#if UNITY_EDITOR_WIN
|
|
const string key = "HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Control\\GraphicsDrivers";
|
|
const string value = "TdrDelay";
|
|
object currentValueBoxed = Registry.GetValue(key, value, null);
|
|
int currentValue = currentValueBoxed != null ? (int)currentValueBoxed : 2;
|
|
//EditorPrefs.SetBool("VolBakeDontShowGPUTimeoutWarning", false);
|
|
if (!EditorPrefs.GetBool("VolBakeDontShowGPUTimeoutWarning", false) && currentValue < 30)
|
|
{
|
|
int allowKey = EditorUtility.DisplayDialogComplex("Increase GPU timeout",
|
|
"This tool needs to set a Windows registry key to increase the time the GPU is allowed to take " +
|
|
"processing graphics jobs in a single frame. Otherwise Windows will consider the GPU to be stalled out and will kill " +
|
|
"Unity mid-bake.\n\n Key: " + key + "\\" + value + "\n\nIncrease the timeout from the default (2 seconds) to 30s?\n",
|
|
"Proceed",
|
|
"Cancel",
|
|
"Cancel - Don't Show Again"
|
|
|
|
);
|
|
|
|
bool proceed = allowKey == 0;
|
|
//if (allowKey == 1)
|
|
//{
|
|
// proceed = !EditorUtility.DisplayDialog("Don't increase timeout", "Are you sure? If the timeout period isn't increased, unity is almost guaranteed to crash when baking volumetrics." +
|
|
// " Increasing the value of this key will not cause any issues, it only means that Windows will wait 30 seconds before killing applications that have actually experienced a GPU crash.",
|
|
// "Don't increase timeout",
|
|
// "Increase timeout");
|
|
//
|
|
//}
|
|
|
|
if (allowKey == 2)
|
|
{
|
|
int choice2 = EditorUtility.DisplayDialogComplex("Don't increase timeout", "Are you sure? If the timeout period isn't increased, unity is almost guaranteed to crash when baking volumetrics." +
|
|
"\n\nIncreasing the value of this key should not cause any issues, it only means that Windows will wait 30 seconds before killing applications that have actually experienced a GPU crash. " +
|
|
"\n\nThis will never ask you again! Only do this if you know what you're doing!",
|
|
"Don't increase timeout and never ask again",
|
|
"Don't increase timeout",
|
|
"Increase timeout");
|
|
|
|
proceed = choice2 == 2;
|
|
if (choice2 == 0) {
|
|
EditorPrefs.SetBool("VolBakeDontShowGPUTimeoutWarning", true);
|
|
}
|
|
}
|
|
|
|
|
|
if (proceed)
|
|
{
|
|
// Registry.SetValue(key, value, 30, RegistryValueKind.DWord);
|
|
System.Diagnostics.Process process = new System.Diagnostics.Process();
|
|
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
|
|
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;
|
|
startInfo.FileName = "cmd.exe";
|
|
startInfo.Arguments = string.Format("/C reg add {0} /v {1} /t REG_DWORD /d 30 /f", key, value);
|
|
//Debug.Log(startInfo.Arguments);
|
|
startInfo.Verb = "runas";
|
|
process.StartInfo = startInfo;
|
|
process.Start();
|
|
process.WaitForExit();
|
|
if (process.ExitCode != 0)
|
|
{
|
|
Debug.LogError("Setting registry key failed");
|
|
}
|
|
//Debug.Log("Volumetric Baking: Set GPU Timeout Registry Key to 30 seconds (" + key + "\\" + value + ")");
|
|
}
|
|
|
|
}
|
|
Debug.Log("Volumetric Baking: GPU Timeout Registry Key value is " + currentValue + " seconds (" + key + "\\" + value + ")");
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
//TODO: Save to scene asset file
|
|
//Public variables
|
|
[Range(1, 2048)]
|
|
public int AreaLightSamples = 256;
|
|
[Range(4, 128), Tooltip("Size of the render buckets.")]
|
|
public int BucketSize = 32;
|
|
public bool DXRAcceletration = true;
|
|
public bool SkyboxContribution = false;
|
|
public Cubemap CustomEnvorment;
|
|
public int EnvLightSamples = 2048;
|
|
public int RayChunkSize = 2048;
|
|
public float VolExposure = 0.05f;
|
|
|
|
bool checkedD3D12 = false;
|
|
bool isD3D12 = false;
|
|
|
|
//interal
|
|
bool saveWarning = false;
|
|
bool Running = false;
|
|
private void OnGUI()
|
|
{
|
|
DisplayProgress();
|
|
EditorGUILayout.LabelField("System Settings", EditorStyles.boldLabel);
|
|
GUI.enabled = !DXRAcceletration;
|
|
BucketSize = EditorGUILayout.IntSlider("Bucket Size", BucketSize, 4, 256);
|
|
GUI.enabled = true;
|
|
VolExposure = EditorGUILayout.Slider("Debug Exposure", RefreshExposure(VolExposure), 0, 2);
|
|
DXRAcceletration = EditorGUILayout.Toggle("DXR Acceletration", DXRAcceletration);
|
|
RayChunkSize = EditorGUILayout.IntSlider("DXR Ray Chunk Size", RayChunkSize, 256, 8192);
|
|
|
|
EditorGUILayout.Space();
|
|
EditorGUILayout.LabelField("Light Settings", EditorStyles.boldLabel);
|
|
AreaLightSamples = EditorGUILayout.IntSlider("Area Samples", AreaLightSamples, 1, 1024);
|
|
|
|
EditorGUILayout.Space();
|
|
GUI.enabled = DXRAcceletration;
|
|
EditorGUILayout.LabelField("Enviorment Settings", EditorStyles.boldLabel);
|
|
SkyboxContribution = EditorGUILayout.Toggle("Skybox Contribution", SkyboxContribution);
|
|
EditorGUILayout.BeginHorizontal();
|
|
EditorGUILayout.LabelField("Custom Skybox", EditorStyles.label);
|
|
CustomEnvorment = (Cubemap)EditorGUILayout.ObjectField(CustomEnvorment, typeof(Cubemap), true);
|
|
EditorGUILayout.EndHorizontal();
|
|
EnvLightSamples = EditorGUILayout.IntSlider("Environmental Samples", EnvLightSamples, 1, 8192 ); ;
|
|
GUI.enabled = true;
|
|
// EditorGUILayout.IntField(AreaLightSamples, "Area light samples" );
|
|
|
|
|
|
if (GUILayout.Button("Bake Volumetrics"))
|
|
{
|
|
ClearWarning();
|
|
if (VerifySettings() == false) return; //Check settings and return if something is wrong
|
|
if (DXRAcceletration)
|
|
{
|
|
BakeDXR();
|
|
}
|
|
else
|
|
{
|
|
RebuildMeshObjectBuffers();
|
|
BakeLights();
|
|
ReleaseBuffers();
|
|
}
|
|
|
|
};
|
|
|
|
EditorGUILayout.LabelField(BakingStatus);
|
|
WarningGUI();
|
|
if (saveWarning) if (GUILayout.Button("Save scene(s)")) SaveScenes();
|
|
|
|
if (!checkedD3D12)
|
|
{
|
|
checkedD3D12 = true;
|
|
isD3D12 = SystemInfo.graphicsDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Direct3D12;
|
|
}
|
|
if (!isD3D12)
|
|
{
|
|
if (GUILayout.Button("Restart Editor in DX12"))
|
|
{
|
|
if (EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo())
|
|
{
|
|
Process unity = new Process();
|
|
string projectPath = Path.GetDirectoryName(Application.dataPath);
|
|
unity.StartInfo.FileName = EditorApplication.applicationPath;
|
|
unity.StartInfo.Arguments = "-projectPath \"" + projectPath + "\" -force-d3d12";
|
|
unity.Start();
|
|
EditorApplication.Exit(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
// EditorGUILayout.HelpBox("Some warning text", MessageType.Warning); //Todo: add warning box to window
|
|
}
|
|
|
|
float RefreshExposure(float Exposure)
|
|
{
|
|
Shader.SetGlobalFloat("_VolExposure", Exposure);
|
|
return Exposure;
|
|
}
|
|
|
|
struct WarningStatus
|
|
{
|
|
public bool Display;
|
|
public string Text;
|
|
}
|
|
|
|
WarningStatus warningStatus;
|
|
|
|
void WarningGUI()
|
|
{
|
|
if (warningStatus.Display == false) return;
|
|
EditorGUILayout.HelpBox(warningStatus.Text, MessageType.Warning);
|
|
}
|
|
|
|
struct Progress{
|
|
public string title;
|
|
public string info;
|
|
public float percent;
|
|
}
|
|
Progress progress;
|
|
double ProgressTimeStart;
|
|
void DisplayProgress()
|
|
{
|
|
if (Running)
|
|
{
|
|
EditorUtility.DisplayProgressBar(progress.title + EditorApplication.timeSinceStartup, progress.info, progress.percent);
|
|
}
|
|
}
|
|
|
|
private void UpdateProgress(string title, string info, float percent)
|
|
{
|
|
progress.title = title;
|
|
progress.info = info;
|
|
progress.percent = percent;
|
|
|
|
DisplayProgress();
|
|
|
|
}
|
|
string BakingStatus;
|
|
|
|
void UpdateStatus(string Status)
|
|
{
|
|
BakingStatus = Status;
|
|
Debug.Log(Status);
|
|
Repaint();
|
|
}
|
|
|
|
void UpdateWarning(string text)
|
|
{
|
|
warningStatus.Display = true;
|
|
warningStatus.Text = text;
|
|
Debug.LogWarning(warningStatus.Text);
|
|
}
|
|
|
|
void ClearWarning()
|
|
{
|
|
warningStatus.Display = false;
|
|
warningStatus.Text = null;
|
|
saveWarning = false;
|
|
}
|
|
|
|
bool VerifySettings()
|
|
{
|
|
if (DXRAcceletration && SystemInfo.graphicsDeviceType != UnityEngine.Rendering.GraphicsDeviceType.Direct3D12)
|
|
{
|
|
UpdateWarning("DirectX 12 not in use. DXR Acceletration requires it.");
|
|
return false;
|
|
}
|
|
|
|
|
|
if (VolumetricRegisters.volumetricAreas.Count < 1) //Checking volumes
|
|
{
|
|
UpdateWarning("No volumetric areas in the scene. Nothing to bake.");
|
|
return false;
|
|
}
|
|
|
|
if (AreScenesDirty() == true) //Checking if scenes are dirty then ask to save
|
|
{
|
|
bool notCancelled = UnityEditor.SceneManagement.EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo();
|
|
if (notCancelled)
|
|
{
|
|
if (AreScenesDirty()) //checking AGAIN because only sets to false when cancelled
|
|
{
|
|
UpdateWarning("Save your scene before baking."); //This is not techincally required, but it helps save progress incase of crash
|
|
saveWarning = true;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
return false; //cancelled
|
|
}
|
|
}
|
|
|
|
if (SceneManager.sceneCount > 1) //Checking scene count
|
|
{
|
|
UpdateWarning("More than one scene open. Saving to active scene.");
|
|
}
|
|
|
|
return true; //everything checks out
|
|
}
|
|
bool AreScenesDirty()
|
|
{
|
|
for (int i = 0; i < SceneManager.sceneCount; i++)
|
|
{
|
|
if (SceneManager.GetSceneAt(i).isDirty) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void SaveScene()
|
|
{
|
|
Scene scene = SceneManager.GetActiveScene();
|
|
UnityEditor.SceneManagement.EditorSceneManager.SaveScene(scene);
|
|
|
|
ClearWarning();
|
|
}
|
|
|
|
void SaveScenes()
|
|
{
|
|
|
|
for (int i = 0; i < SceneManager.sceneCount; i++)
|
|
{
|
|
Scene currentS = SceneManager.GetSceneAt(i);
|
|
if (currentS.isDirty)
|
|
{
|
|
UnityEditor.SceneManagement.EditorSceneManager.SaveScene(currentS);
|
|
Debug.Log("Saved " + currentS.name);
|
|
}
|
|
}
|
|
AssetDatabase.SaveAssets();
|
|
ClearWarning();
|
|
}
|
|
|
|
|
|
// public int tex1Res = 64;
|
|
// ComputeShader BakingShader; // Baking shader
|
|
// ComputeShader slicer; //Slicer shader for saving
|
|
//public Light[] BakeLights;
|
|
|
|
// public Vector3 DirectionalLight = new Vector3(0,1,0);
|
|
|
|
public Vector3 Size = Vector3.one;
|
|
|
|
public float AmbientMedia = 0;
|
|
|
|
public GameObject[] Spheres;
|
|
|
|
//Data Lists
|
|
public struct LightStruct
|
|
{
|
|
public Color[] color;
|
|
public float[] extinction;
|
|
public Vector3[] Position;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Triangle Object
|
|
/// </summary>
|
|
|
|
struct MeshObject
|
|
{
|
|
public Matrix4x4 localToWorldMatrix;
|
|
public int indices_offset;
|
|
public int indices_count;
|
|
}
|
|
|
|
private static List<MeshObject> _meshObjects = new List<MeshObject>();
|
|
private static List<Vector3> _vertices = new List<Vector3>();
|
|
private static List<int> _indices = new List<int>();
|
|
private ComputeBuffer _meshObjectBuffer;
|
|
private ComputeBuffer _vertexBuffer;
|
|
private ComputeBuffer _indexBuffer;
|
|
|
|
struct Debuger
|
|
{
|
|
public int DebugCcounter;
|
|
}
|
|
/////////////// Unused
|
|
public void BakeVolumetrics()
|
|
{
|
|
System.DateTime startTime = System.DateTime.Now;
|
|
|
|
UpdateStatus("Baking " + SceneManager.GetActiveScene().name);
|
|
|
|
//Make RT
|
|
ComputeShader BakingShader = AssetDatabase.LoadAssetAtPath<ComputeShader>("Assets/VolumetricFog/Shaders/VolumetricBaking.compute");
|
|
|
|
//float displayProgress = 0;
|
|
|
|
//EditorUtility.DisplayProgressBar("Baking Volumetrics: ", "Shows a progress bar for the given seconds", displayProgress);
|
|
|
|
|
|
for (int j = 0; j < VolumetricRegisters.volumetricAreas.Count; j++)
|
|
{
|
|
// EditorUtility.DisplayProgressBar("Baking Volumetrics... ", VolumetricRegisters.volumetricAreas[j].name, (float)j / (float)VolumetricRegisters.volumetricAreas.Count );
|
|
|
|
Vector3Int Texels = VolumetricRegisters.volumetricAreas[j].NormalizedTexelDensity;
|
|
|
|
RenderTextureDescriptor rtdiscrpt = new RenderTextureDescriptor();
|
|
rtdiscrpt.enableRandomWrite = true;
|
|
rtdiscrpt.dimension = UnityEngine.Rendering.TextureDimension.Tex3D;
|
|
rtdiscrpt.width = Texels.x;
|
|
rtdiscrpt.height = Texels.y;
|
|
rtdiscrpt.volumeDepth = Texels.z;
|
|
rtdiscrpt.graphicsFormat = UnityEngine.Experimental.Rendering.GraphicsFormat.R16G16B16A16_SFloat; //R32G32B32A32_SFloat is excessive, the compressed formats won't befefit and even lightmaps are saved as 16 bit EXRs
|
|
rtdiscrpt.msaaSamples = 1;
|
|
|
|
RenderTexture RT3d = new RenderTexture(rtdiscrpt);
|
|
RT3d.Create();
|
|
|
|
// Color[] colorstring = new Color[RT3d.width * RT3d.height * RT3d.depth];
|
|
//for (int c = 0; c < colorstring.Length; c++) colorstring[c] = Color.cyan;
|
|
|
|
//Setup light data
|
|
|
|
Light[] PointLights = GatherBakedLights(LightType.Point);
|
|
Light[] DirectionalLights = GatherBakedLights(LightType.Directional);
|
|
|
|
Vector4[] lightColors = new Vector4[PointLights.Length];
|
|
Vector4[] lightPos = new Vector4[PointLights.Length];
|
|
|
|
for (int i = 0; i < PointLights.Length; i++)
|
|
{
|
|
lightColors[i] = PointLights[i].color * PointLights[i].intensity;
|
|
lightPos[i] = PointLights[i].transform.position;
|
|
}
|
|
|
|
//participatingMediaEntities = FindObjectsOfType<ParticipatingMediaEntity>();
|
|
|
|
Vector4[] mediaPos = new Vector4[VolumetricRegisters.VolumetricMediaEntities.Count];
|
|
Vector4[] mediaAbs = new Vector4[VolumetricRegisters.VolumetricMediaEntities.Count];
|
|
|
|
for (int i = 0; i < mediaPos.Length; i++)
|
|
{
|
|
mediaPos[i] = VolumetricRegisters.VolumetricMediaEntities[i].transform.position;
|
|
// mediaAbs[i] = new Vector4(VolumetricRegisters.VolumetricMediaEntities[i].Absorption, 0, 0, 0); //Only Absorption for now
|
|
}
|
|
|
|
|
|
//Setup and dispatch Baking compute shader
|
|
|
|
// Debug.Log("Baking " + PointLights.Length + " lights");
|
|
// Debug.Log("Baking " + mediaPos.Length + " Media");
|
|
|
|
//Setting shader variables
|
|
|
|
int shaderKernel = BakingShader.FindKernel("VolumetricAreaBake");
|
|
BakingShader.SetTexture(shaderKernel, "AccumulatedLights", RT3d);
|
|
BakingShader.SetVectorArray("LightColor", lightColors);
|
|
BakingShader.SetVectorArray("LightPosition", lightPos);
|
|
BakingShader.SetInt("LightCount", lightColors.Length);
|
|
|
|
BakingShader.SetVectorArray("MediaSphere", mediaPos);
|
|
BakingShader.SetVectorArray("MediaSphereAbsorption", mediaAbs);
|
|
BakingShader.SetFloat("AmbientMedia", AmbientMedia);
|
|
BakingShader.SetInt("MediaSphereCount", mediaPos.Length);
|
|
|
|
BakingShader.SetVector("Size", VolumetricRegisters.volumetricAreas[j].NormalizedScale);
|
|
BakingShader.SetVector("Position", VolumetricRegisters.volumetricAreas[j].Corner);
|
|
//Only select the first directional light. Should only have one in a scene, but will add support for more later for artists
|
|
BakingShader.SetVector("DirectionalLightDirection", DirectionalLights[0].transform.rotation.eulerAngles);
|
|
BakingShader.SetVector("DirectionalLightColor", DirectionalLights[0].color * DirectionalLights[0].intensity);
|
|
//Temp Shadow spheres
|
|
|
|
//Vector4[] SpherePos = new Vector4[Spheres.Length];
|
|
Vector4[] SpherePos = new Vector4[0];
|
|
|
|
//for (int i = 0; i < SpherePos.Length; i++)
|
|
//{
|
|
// SpherePos[i] = new Vector4(
|
|
// Spheres[i].transform.position.x,
|
|
// Spheres[i].transform.position.y,
|
|
// Spheres[i].transform.position.z,
|
|
// Spheres[i].transform.lossyScale.z * 0.5f);
|
|
//}
|
|
|
|
// SetComputeBuffer("_Spheres", _sphereBuffer);
|
|
SetComputeBuffer("_MeshObjects", BakingShader, shaderKernel, _meshObjectBuffer);
|
|
SetComputeBuffer("_Vertices", BakingShader, shaderKernel, _vertexBuffer);
|
|
SetComputeBuffer("_Indices", BakingShader, shaderKernel, _indexBuffer);
|
|
|
|
BakingShader.SetVectorArray("OpaqueSphere", SpherePos);
|
|
BakingShader.SetInt("SphereCount", SpherePos.Length);
|
|
|
|
Vector3 ThreadsToDispatch = new Vector3(
|
|
Mathf.CeilToInt((float)Texels.x / 4.0f),
|
|
Mathf.CeilToInt((float)Texels.y / 4.0f),
|
|
Mathf.CeilToInt((float)Texels.z / 4.0f)
|
|
);
|
|
|
|
///GPU Baking
|
|
///
|
|
///
|
|
|
|
///Sending data to the GPU
|
|
//Debuger[] debuger = new Debuger[1];
|
|
//debuger[0].DebugCcounter = 0;
|
|
|
|
//int DebugCountStride = sizeof(int); //Size of debug strut
|
|
//int DebugID = Shader.PropertyToID("DebugBuffer");
|
|
|
|
//ComputeBuffer BakeBuffer = new ComputeBuffer(1, DebugCountStride);
|
|
//BakeBuffer.SetData(debuger);
|
|
//BakingShader.SetBuffer(shaderKernel, DebugID, BakeBuffer);
|
|
///
|
|
|
|
// Graphics.CreateGraphicsFence ?
|
|
|
|
|
|
//DISPATCHING
|
|
|
|
BakingShader.Dispatch(shaderKernel, (int)ThreadsToDispatch.x, (int)ThreadsToDispatch.y, (int)ThreadsToDispatch.z);
|
|
|
|
|
|
// while
|
|
|
|
//Sending data back to the CPU to check if work is done before writing to disk
|
|
// BakeBuffer.GetData(debuger);
|
|
// Debug.Log("Shot " + debuger[0].DebugCcounter + " rays");
|
|
//BakeBuffer.Release(); //Avoiding memory leak
|
|
///
|
|
|
|
//Define path and save 3d texture
|
|
string path = CheckDirectoryAndReturnPath() + j;
|
|
//RT3d.SaveToTexture3D(path);
|
|
RenderTexture[] mipChain = Get3DMips(RT3d);
|
|
|
|
Texture3D ReadBackTex = ReadRT2Tex3D(mipChain);
|
|
for (int i = 0; i < mipChain.Length; i++)
|
|
{
|
|
mipChain[i].Release();
|
|
DestroyImmediate(mipChain[i]);
|
|
}
|
|
Vol3d.WriteTex3DToVol3D(ReadBackTex, path + Vol3d.fileExtension);
|
|
AssetDatabase.ImportAsset(path + Vol3d.fileExtension);
|
|
|
|
Texture3D volTex = (Texture3D)AssetDatabase.LoadAssetAtPath(path + Vol3d.fileExtension, typeof(Texture3D));
|
|
VolumetricRegisters.volumetricAreas[j].bakedTexture = volTex;
|
|
EditorUtility.UnloadUnusedAssetsImmediate();
|
|
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);
|
|
//MaterialPropertyBlock propertyBlock = new MaterialPropertyBlock();
|
|
//propertyBlock.SetTexture("_3dTexture", VolumetricRegisters.volumetricAreas[j].bakedTexture);
|
|
// VolumetricRegisters.volumetricAreas[j].DebugCube.SetPropertyBlock(propertyBlock);
|
|
//Repaint();
|
|
|
|
}
|
|
|
|
EditorUtility.ClearProgressBar();
|
|
|
|
System.DateTime endTime = System.DateTime.Now;
|
|
|
|
UpdateStatus(" Volumetric bake took " + (endTime.Minute - startTime.Minute) + " Minutes and " +
|
|
(endTime.Second - startTime.Second) + " Seconds. Baked "+ VolumetricRegisters.volumetricAreas.Count + " areas."
|
|
);
|
|
|
|
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(SceneManager.GetActiveScene());
|
|
}
|
|
|
|
static int prop_OutputDim = Shader.PropertyToID("_OutputDim");
|
|
static int prop_Input = Shader.PropertyToID("_Input");
|
|
static int prop_Output = Shader.PropertyToID("_Output");
|
|
const string Mip3DTextureGUID = "a7b6f45f3454c3345a78c989cedd7229";
|
|
static ComputeShader s_mip3DCompute;
|
|
static ComputeShader Mip3DCompute
|
|
{
|
|
get {
|
|
if (s_mip3DCompute == null)
|
|
{
|
|
string mip3dComputePath = AssetDatabase.GUIDToAssetPath(Mip3DTextureGUID);
|
|
if (string.IsNullOrEmpty(mip3dComputePath))
|
|
{
|
|
throw new FileNotFoundException("Volumetric Baking: Failed to load Mip3DTexture compute shader by hard-coded GUID (" + Mip3DTextureGUID + "). Either the shader is missing or its meta file got regenerated");
|
|
}
|
|
ComputeShader t_mip3DCompute = AssetDatabase.LoadAssetAtPath<ComputeShader>(mip3dComputePath);
|
|
if (t_mip3DCompute == null)
|
|
{
|
|
throw new FileNotFoundException("Volumetric Baking: Failed to load Mip3DTexture compute shader by hard-coded GUID (" + Mip3DTextureGUID + "). No compute shader was found at the path of the GUID, meaning another asset has the same GUID!");
|
|
}
|
|
s_mip3DCompute = t_mip3DCompute;
|
|
}
|
|
return s_mip3DCompute;
|
|
}
|
|
}
|
|
|
|
RenderTexture[] Get3DMips(RenderTexture mip0)
|
|
{
|
|
int numMips = (int)math.floor(math.log2(math.max(mip0.width, math.max(mip0.height, mip0.volumeDepth)))) + 1;
|
|
RenderTexture[] mips = new RenderTexture[numMips];
|
|
|
|
RenderTextureDescriptor rtDesc = mip0.descriptor;
|
|
|
|
ComputeShader mip3DCompute = Mip3DCompute;
|
|
|
|
int kernelMip = mip3DCompute.FindKernel("CalculateMip");
|
|
|
|
mips[0] = mip0;
|
|
|
|
for (int level = 1; level < numMips; level++)
|
|
{
|
|
|
|
|
|
rtDesc.width = math.max(rtDesc.width / 2, 1);
|
|
rtDesc.height = math.max(rtDesc.height / 2, 1);
|
|
rtDesc.volumeDepth = math.max(rtDesc.volumeDepth / 2, 1);
|
|
mips[level] = new RenderTexture(rtDesc);
|
|
mips[level].Create();
|
|
|
|
mip3DCompute.SetTexture(kernelMip, prop_Input, mips[level-1]);
|
|
mip3DCompute.SetTexture(kernelMip, prop_Output, mips[level]);
|
|
mip3DCompute.SetInts(prop_OutputDim, new int[] { rtDesc.width, rtDesc.height, rtDesc.volumeDepth, 0 });
|
|
|
|
mip3DCompute.Dispatch(kernelMip, (rtDesc.width + 3) / 4, (rtDesc.height + 3) / 4, (rtDesc.volumeDepth + 3) / 4);
|
|
}
|
|
return mips;
|
|
}
|
|
Texture3D ReadRT2Tex3D(RenderTexture[] rtAndMips)
|
|
{
|
|
|
|
GraphicsFormat gfmt = rtAndMips[0].graphicsFormat;
|
|
TextureFormat tfmt = GraphicsFormatUtility.GetTextureFormat(gfmt);
|
|
int width = rtAndMips[0].width;
|
|
int height = rtAndMips[0].height;
|
|
int depth = rtAndMips[0].volumeDepth;
|
|
|
|
Texture3D output = new Texture3D(width, height, depth, gfmt, rtAndMips.Length > 1 ? TextureCreationFlags.MipChain : TextureCreationFlags.None);
|
|
|
|
AsyncGPUReadbackRequest[] request = new AsyncGPUReadbackRequest[rtAndMips.Length];
|
|
for (int i = 0; i < rtAndMips.Length; i++)
|
|
{
|
|
request[i] = AsyncGPUReadback.Request(rtAndMips[i], 0);
|
|
}
|
|
|
|
for (int mip = 0; mip < output.mipmapCount; mip++)
|
|
{
|
|
int currPtr = 0;
|
|
|
|
NativeArray<byte> outputRaw = output.GetPixelData<byte>(mip);
|
|
request[mip].WaitForCompletion();
|
|
int layers = request[mip].layerCount;
|
|
for (int slice = 0; slice < layers; slice++)
|
|
{
|
|
|
|
NativeArray<byte> readBackRaw = request[mip].GetData<byte>(slice);
|
|
NativeArray<byte>.Copy(readBackRaw, 0, outputRaw, currPtr, readBackRaw.Length);
|
|
currPtr += readBackRaw.Length;
|
|
|
|
readBackRaw.Dispose();
|
|
}
|
|
}
|
|
//Texture2D temp = new Texture2D(width, height, gfmt, TextureCreationFlags.None);
|
|
//Rect sliceRect = new Rect(0, 0, width, height);
|
|
//int sliceSize = width * height;
|
|
//RenderTexture oldActive = RenderTexture.active;
|
|
//RenderTexture.active = rt;
|
|
//int outputRawPtr = 0;
|
|
//for (int depthSlice = 0; depthSlice < depth; depthSlice++)
|
|
//{
|
|
// Graphics.SetRenderTarget(rt, 0, CubemapFace.Unknown, depthSlice);
|
|
// temp.ReadPixels(sliceRect,0,0);
|
|
// temp.Apply(false);
|
|
// NativeArray<byte> tempNative = temp.GetPixelData<byte>(0);
|
|
// NativeArray<byte>.Copy(tempNative, 0, outputRaw, outputRawPtr, tempNative.Length);
|
|
// outputRawPtr += tempNative.Length;
|
|
//}
|
|
//Graphics.SetRenderTarget(oldActive, 0, CubemapFace.Unknown, 0);
|
|
//RenderTexture.active = oldActive;
|
|
|
|
return output;
|
|
}
|
|
|
|
static int prop_PrevMipDimOffset = Shader.PropertyToID("_PrevMipDimOffset");
|
|
static int prop_MipDimOffset = Shader.PropertyToID("_MipDimOffset");
|
|
static int prop_Buffer = Shader.PropertyToID("_Buffer");
|
|
ComputeBuffer Get3DMipsBuffer(RenderTexture mip0)
|
|
{
|
|
int numMips = (int)math.floor(math.log2(math.max(mip0.width, math.max(mip0.height, mip0.volumeDepth)))) + 1;
|
|
RenderTextureDescriptor rtDesc = mip0.descriptor;
|
|
int3 textureDim = new int3(rtDesc.width, rtDesc.height, rtDesc.volumeDepth);
|
|
int bufferCount = textureDim.x * textureDim.y * textureDim.z;
|
|
for (int i = 0; i < numMips; i++)
|
|
{
|
|
textureDim = math.max(textureDim / 2, 1);
|
|
bufferCount += textureDim.x * textureDim.y * textureDim.z;
|
|
}
|
|
|
|
ComputeBuffer mips = new ComputeBuffer(bufferCount, 4 * sizeof(ushort), ComputeBufferType.Structured);
|
|
|
|
ComputeShader mip3DCompute = Mip3DCompute;
|
|
|
|
int initKernel = mip3DCompute.FindKernel("CopyTexToBuffer");
|
|
int mipKernel = mip3DCompute.FindKernel("CalculateMipBuffer");
|
|
int3 mipDim = new int3(rtDesc.width, rtDesc.height, rtDesc.volumeDepth);
|
|
|
|
mip3DCompute.SetInts(prop_MipDimOffset, new int[] { mipDim.x, mipDim.y, mipDim.z, 0 });
|
|
mip3DCompute.SetBuffer(initKernel, prop_Buffer, mips);
|
|
mip3DCompute.SetTexture(initKernel, prop_Input, mip0);
|
|
mip3DCompute.Dispatch(initKernel, (mipDim.x + 3) / 4, (mipDim.y + 3) / 4, (mipDim.z + 3) / 4);
|
|
|
|
int3 prevMipDim = mipDim;
|
|
int mipPtr = 0;
|
|
int prevMipPtr = 0;
|
|
mip3DCompute.SetBuffer(mipKernel, prop_Buffer, mips);
|
|
for (int level = 1; level < numMips; level++)
|
|
{
|
|
prevMipDim = mipDim;
|
|
prevMipPtr = mipPtr;
|
|
mipDim = math.max(mipDim / 2, new int3(1,1,1));
|
|
mipPtr += prevMipDim.x * prevMipDim.y * prevMipDim.z;
|
|
mip3DCompute.SetInts(prop_PrevMipDimOffset, new int[] { prevMipDim.x, prevMipDim.y, prevMipDim.z, prevMipPtr });
|
|
mip3DCompute.SetInts(prop_MipDimOffset, new int[] { mipDim.x, mipDim.y, mipDim.z, mipPtr });
|
|
mip3DCompute.Dispatch(mipKernel, (mipDim.x + 3) / 4, (mipDim.y + 3) / 4, (mipDim.z + 3) / 4);
|
|
}
|
|
return mips;
|
|
}
|
|
|
|
Texture3D ReadBufferToTex3D(ComputeBuffer rtAndMips, int width, int height, int depth)
|
|
{
|
|
GraphicsFormat gfmt = GraphicsFormat.R16G16B16A16_SFloat;
|
|
Texture3D output = new Texture3D(width, height, depth, gfmt, TextureCreationFlags.MipChain);
|
|
int stride = rtAndMips.stride / sizeof(ushort);
|
|
ushort[] bufferReadBack = new ushort[rtAndMips.count * stride];
|
|
rtAndMips.GetData(bufferReadBack);
|
|
int ptr = 0;
|
|
for (int mip = 0; mip < output.mipmapCount; mip++)
|
|
{
|
|
|
|
NativeArray<ushort> outputRaw = output.GetPixelData<ushort>(mip);
|
|
int copyCount = width * height * depth * stride;
|
|
NativeArray<ushort>.Copy(bufferReadBack, ptr, outputRaw, 0, width * height * depth * stride);
|
|
//if (mip == output.mipmapCount - 1)
|
|
//{
|
|
// half4 lastColor = new half4(
|
|
// new half() { value = bufferReadBack[ptr - 4] },
|
|
// new half() { value = bufferReadBack[ptr - 3] },
|
|
// new half() { value = bufferReadBack[ptr - 2] },
|
|
// new half() { value = bufferReadBack[ptr - 1] });
|
|
// Debug.Log("Last Color: " + lastColor.ToString());
|
|
//}
|
|
ptr += copyCount;
|
|
width = math.max(1, width / 2);
|
|
height = math.max(1, height / 2);
|
|
depth = math.max(1, depth / 2);
|
|
}
|
|
return output;
|
|
}
|
|
|
|
//////
|
|
|
|
ComputeShader BakingShader;
|
|
|
|
|
|
void BakeLights()
|
|
{
|
|
System.DateTime startTime = System.DateTime.Now;
|
|
BakingShader = AssetDatabase.LoadAssetAtPath<ComputeShader>("Packages/com.unity.render-pipelines.universal/Shaders/Volumetrics/VolumetricBaking.compute");
|
|
UpdateStatus("Baking " + SceneManager.GetActiveScene().name);
|
|
ComputeShader BlitShader = AssetDatabase.LoadAssetAtPath<ComputeShader>("Packages/com.unity.render-pipelines.universal/Shaders/Volumetrics/3dBlit.compute");
|
|
int BlitBucketKernal = BlitShader.FindKernel("BlitBucket");
|
|
|
|
Running = true;
|
|
|
|
for (int j = 0; j < VolumetricRegisters.volumetricAreas.Count; j++)
|
|
{
|
|
// EditorUtility.DisplayProgressBar("Baking Volumetrics... ", VolumetricRegisters.volumetricAreas[j].name, (float)j / (float)VolumetricRegisters.volumetricAreas.Count);
|
|
|
|
Vector3Int Texels = VolumetricRegisters.volumetricAreas[j].NormalizedTexelDensity;
|
|
RenderTextureDescriptor rtdiscrpt = new RenderTextureDescriptor();
|
|
rtdiscrpt.enableRandomWrite = true;
|
|
rtdiscrpt.dimension = UnityEngine.Rendering.TextureDimension.Tex3D;
|
|
rtdiscrpt.width = Texels.x;
|
|
rtdiscrpt.height = Texels.y;
|
|
rtdiscrpt.volumeDepth = Texels.z;
|
|
rtdiscrpt.graphicsFormat = UnityEngine.Experimental.Rendering.GraphicsFormat.R16G16B16A16_SFloat;
|
|
rtdiscrpt.msaaSamples = 1;
|
|
|
|
//Target buffer
|
|
RenderTexture RT3d = new RenderTexture(rtdiscrpt);
|
|
RT3d.Create();
|
|
|
|
|
|
//Bucket
|
|
rtdiscrpt.width = BucketSize;
|
|
rtdiscrpt.height = BucketSize;
|
|
rtdiscrpt.volumeDepth = BucketSize;
|
|
|
|
RenderTexture bucketBuffer = new RenderTexture(rtdiscrpt);
|
|
bucketBuffer.Create();
|
|
|
|
Light[] PointLights = GatherBakedLights(LightType.Point);
|
|
|
|
//Figuring out the buckets per dimension
|
|
|
|
int bx = Mathf.CeilToInt((float)Texels.x / BucketSize);
|
|
int by = Mathf.CeilToInt((float)Texels.y / BucketSize);
|
|
int bz = Mathf.CeilToInt((float)Texels.z / BucketSize);
|
|
|
|
//Total number of buckets
|
|
int BucketCount = bx * by * bz;
|
|
|
|
Debug.Log(BucketCount + " buckets");
|
|
|
|
BlitShader.SetTexture(BlitBucketKernal, "BucketBuffer", bucketBuffer);
|
|
BlitShader.SetTexture(BlitBucketKernal, "Result", RT3d);
|
|
|
|
|
|
//Bucket Loop
|
|
for (int b = 0; b < BucketCount; b++)
|
|
{
|
|
|
|
Vector3 ThreadsToDispatch = new Vector3(
|
|
Mathf.CeilToInt((float)Texels.x / 4.0f),
|
|
Mathf.CeilToInt((float)Texels.y / 4.0f),
|
|
Mathf.CeilToInt((float)Texels.z / 4.0f) );
|
|
|
|
Vector3 BucketThreads = new Vector3(
|
|
Mathf.CeilToInt((float)BucketSize / 4.0f),
|
|
Mathf.CeilToInt((float)BucketSize / 4.0f),
|
|
Mathf.CeilToInt((float)BucketSize / 4.0f));
|
|
|
|
|
|
//Generate cell offset
|
|
|
|
int x = b % bx;
|
|
int y = (b / bx) % by;
|
|
int z = b / (by * bx);
|
|
|
|
Vector3Int CellOffset = new Vector3Int(x, y, z);
|
|
Vector3Int TextileOffset = CellOffset * BucketSize;
|
|
|
|
// Debug.Log(TextileOffset + ", size:" + BucketSize);
|
|
|
|
/// BlitShader.set
|
|
///
|
|
//Clear buffer
|
|
BakingShader.SetTexture(BakingShader.FindKernel("ClearBuffer"), "AccumulatedLights", bucketBuffer);
|
|
BakingShader.Dispatch(BakingShader.FindKernel("ClearBuffer"), (int)BucketThreads.x, (int)BucketThreads.y, (int)BucketThreads.z);
|
|
|
|
//Light Loop
|
|
for (int i = 0; i < PointLights.Length; i++)
|
|
{
|
|
UpdateProgress("Baking " + (j + 1) + "/" + VolumetricRegisters.volumetricAreas.Count + " "
|
|
+ VolumetricRegisters.volumetricAreas[j].name + " "
|
|
+ (System.DateTime.Now.Minute - startTime.Minute) + ":" + (System.DateTime.Now.Second - startTime.Second) //TODO: Format correctly
|
|
, PointLights[i].name, (float)i / PointLights.Length);
|
|
|
|
//TODO: Do some checking to only render lights affecting the area. AABB?
|
|
|
|
//Render to bucket buffer
|
|
DispatchLight(PointLights[i], bucketBuffer, new Vector3Int(BucketSize, BucketSize, BucketSize), j, TextileOffset);
|
|
}
|
|
|
|
//Blit from bucket to larger buffer
|
|
BlitShader.SetTexture(BlitBucketKernal, "BucketBuffer", bucketBuffer); //bucket buffer
|
|
BlitShader.SetTexture(BlitBucketKernal, "Result", RT3d); //target buffer
|
|
BlitShader.SetVector( "BucketOffset", (Vector3)TextileOffset) ;
|
|
BlitShader.SetInt( "BucketSize", BucketSize) ;
|
|
BlitShader.Dispatch(BlitBucketKernal, (int)ThreadsToDispatch.x, (int)ThreadsToDispatch.y, (int)ThreadsToDispatch.z);
|
|
|
|
// RT3d
|
|
|
|
}
|
|
//do
|
|
//{
|
|
|
|
//} while (i < 0);
|
|
//bool dothething = true;
|
|
//while (dothething)
|
|
//{
|
|
// UpdateProgress("Baking " + (j + 1) + "/" + VolumetricRegisters.volumetricAreas.Count + " "
|
|
// + VolumetricRegisters.volumetricAreas[j].name + " "
|
|
// + (System.DateTime.Now.Minute - startTime.Minute) + ":" + (System.DateTime.Now.Second - startTime.Second) //TODO: Format correctly
|
|
// , PointLights[i].name, (float)i / PointLights.Length);
|
|
|
|
// //TODO: Do some checking to only render lights affecting the area. AABB?
|
|
// DispatchLight(PointLights[i], RT3d, Texels, j);
|
|
|
|
// Debug.Log("");
|
|
|
|
// return;
|
|
//}
|
|
|
|
//Define path and save 3d texture
|
|
string path = CheckDirectoryAndReturnPath() + j;
|
|
RenderTexture[] mipChain = Get3DMips(RT3d);
|
|
|
|
Texture3D ReadBackTex = ReadRT2Tex3D(mipChain);
|
|
for (int i = 0; i < mipChain.Length; i++)
|
|
{
|
|
mipChain[i].Release();
|
|
DestroyImmediate(mipChain[i]);
|
|
}
|
|
|
|
Vol3d.WriteTex3DToVol3D(ReadBackTex, path + Vol3d.fileExtension);
|
|
|
|
bucketBuffer.Release();
|
|
DestroyImmediate(bucketBuffer);
|
|
|
|
AssetDatabase.ImportAsset(path + Vol3d.fileExtension);
|
|
VolumetricRegisters.volumetricAreas[j].bakedTexture = (Texture3D)AssetDatabase.LoadAssetAtPath(path + Vol3d.fileExtension, typeof(Texture3D));
|
|
EditorUtility.UnloadUnusedAssetsImmediate();
|
|
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);
|
|
// Debug.Log(VolumetricRegisters.volumetricAreas[j].gameObject.scene.name + VolumetricRegisters.volumetricAreas[j].name);
|
|
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(VolumetricRegisters.volumetricAreas[j].gameObject.scene);
|
|
|
|
}
|
|
|
|
Running = false;
|
|
EditorUtility.ClearProgressBar();
|
|
|
|
System.DateTime endTime = System.DateTime.Now;
|
|
|
|
UpdateStatus(" Volumetric bake took " + (endTime.Minute - startTime.Minute) + " Minutes and " +
|
|
(endTime.Second - startTime.Second) + " Seconds. Baked " + VolumetricRegisters.volumetricAreas.Count + " areas."
|
|
);
|
|
|
|
// UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(SceneManager.GetActiveScene());
|
|
|
|
}
|
|
|
|
string CheckDirectoryAndReturnPath()
|
|
{
|
|
//Define path
|
|
string path = SceneManager.GetActiveScene().path;
|
|
path = path.Replace(".unity", "");
|
|
if (!Directory.Exists(path)) //Check if path exists
|
|
{
|
|
Directory.CreateDirectory(path); //if it doesn't, create it
|
|
Debug.Log("Made Directory " + path);
|
|
AssetDatabase.Refresh();
|
|
}
|
|
return path + "/" + "Volumemap-";
|
|
}
|
|
|
|
RenderTexture initializeVolume(int i)
|
|
{
|
|
|
|
Vector3Int Texels = VolumetricRegisters.volumetricAreas[i].NormalizedTexelDensity;
|
|
RenderTextureDescriptor rtdiscrpt = new RenderTextureDescriptor();
|
|
rtdiscrpt.enableRandomWrite = true;
|
|
rtdiscrpt.dimension = UnityEngine.Rendering.TextureDimension.Tex3D;
|
|
rtdiscrpt.width = Texels.x;
|
|
rtdiscrpt.height = Texels.y;
|
|
rtdiscrpt.volumeDepth = Texels.z;
|
|
rtdiscrpt.graphicsFormat = UnityEngine.Experimental.Rendering.GraphicsFormat.R16G16B16A16_SFloat;
|
|
rtdiscrpt.msaaSamples = 1;
|
|
|
|
//Target buffer
|
|
RenderTexture RT3d = new RenderTexture(rtdiscrpt);
|
|
RT3d.Create();
|
|
|
|
return RT3d;
|
|
}
|
|
Texture MakeEnvironmentalCubemap()
|
|
{
|
|
//Black Background
|
|
if (SkyboxContribution != true){
|
|
|
|
RenderTexture cubetex = new RenderTexture(32, 32, 1, RenderTextureFormat.DefaultHDR);
|
|
cubetex.enableRandomWrite = true;
|
|
cubetex.dimension = UnityEngine.Rendering.TextureDimension.Cube;
|
|
cubetex.Create();
|
|
|
|
Camera renderCam = new GameObject().AddComponent<Camera>();
|
|
renderCam.cullingMask = 0;
|
|
renderCam.backgroundColor = Color.black;
|
|
renderCam.clearFlags = CameraClearFlags.Color;
|
|
renderCam.RenderToCubemap(cubetex);
|
|
DestroyImmediate(renderCam.gameObject);
|
|
return cubetex;
|
|
}
|
|
|
|
//Generate Skybox
|
|
if (CustomEnvorment == null)
|
|
{
|
|
RenderTexture cubetex = new RenderTexture(256, 256, 1, RenderTextureFormat.DefaultHDR);
|
|
cubetex.enableRandomWrite = true;
|
|
cubetex.dimension = UnityEngine.Rendering.TextureDimension.Cube;
|
|
cubetex.Create();
|
|
|
|
Camera renderCam = new GameObject().AddComponent<Camera>();
|
|
renderCam.cullingMask = 0;
|
|
renderCam.backgroundColor = Color.black;
|
|
renderCam.clearFlags = CameraClearFlags.Skybox;
|
|
renderCam.RenderToCubemap(cubetex);
|
|
DestroyImmediate(renderCam.gameObject);
|
|
return cubetex;
|
|
}
|
|
//Provided skybox
|
|
else
|
|
{
|
|
return CustomEnvorment;
|
|
}
|
|
}
|
|
|
|
void RefreshDebuggers()
|
|
{
|
|
for (int i = 0; i < VolumetricRegisters.volumetricAreas.Count; i++)
|
|
{
|
|
if (VolumetricRegisters.volumetricAreas[i].DEBUG) VolumetricRegisters.volumetricAreas[i].RefreshDebugMesh();
|
|
}
|
|
}
|
|
|
|
Color ColorExtraction(Light light)
|
|
{
|
|
Color colorModulation = light.color.linear;
|
|
if (light.useColorTemperature) colorModulation *= Mathf.CorrelatedColorTemperatureToRGB(light.colorTemperature);
|
|
colorModulation *= light.intensity;
|
|
colorModulation *= light.gameObject.GetComponent<UniversalAdditionalLightData>().volumetricDimmer;
|
|
return colorModulation;
|
|
}
|
|
|
|
void BakeDXR()
|
|
{
|
|
|
|
System.DateTime startTime = System.DateTime.Now;
|
|
UpdateStatus("Baking " + SceneManager.GetActiveScene().name);
|
|
//ComputeShader BlitShader = AssetDatabase.LoadAssetAtPath<ComputeShader>("Packages/com.unity.render-pipelines.universal/Shaders/Volumetrics/3dBlit.compute");
|
|
|
|
RayTracingShader rtshader = AssetDatabase.LoadAssetAtPath<RayTracingShader>("Packages/com.unity.render-pipelines.universal/Shaders/Volumetrics/DXR-Volumebaker.raytrace");
|
|
rtshader.SetShaderPass("BakedRaytrace");
|
|
|
|
RayTracingAccelerationStructure accelerationStructure = new RayTracingAccelerationStructure(); ;
|
|
Renderer[] renderers = GatherStaticRenderers();
|
|
for (int i = 0; i < renderers.Length; i++) accelerationStructure.AddInstance(renderers[i]);
|
|
accelerationStructure.Build();
|
|
rtshader.SetAccelerationStructure("g_SceneAccelStruct", accelerationStructure);
|
|
|
|
Running = true;
|
|
|
|
Vector3Int threads = new Vector3Int();
|
|
|
|
|
|
List<Light> PointLights, ConeLights, DirectionalLights, AreaLights;
|
|
|
|
Light[] Lights = GatherBakedLights();
|
|
|
|
PointLights = new List<Light>();
|
|
ConeLights = new List<Light>();
|
|
DirectionalLights = new List<Light>();
|
|
AreaLights = new List<Light>();
|
|
|
|
for (int i = 0; i < Lights.Length; i++)
|
|
{
|
|
|
|
switch (Lights[i].type)
|
|
{
|
|
case LightType.Point:
|
|
PointLights.Add(Lights[i]);
|
|
break;
|
|
|
|
case LightType.Spot:
|
|
ConeLights.Add(Lights[i]);
|
|
break;
|
|
|
|
case LightType.Directional:
|
|
DirectionalLights.Add(Lights[i]);
|
|
break;
|
|
|
|
case LightType.Area:
|
|
AreaLights.Add(Lights[i]);
|
|
break;
|
|
|
|
case LightType.Disc:
|
|
AreaLights.Add(Lights[i]); //Stacking area and disc
|
|
break;
|
|
default:
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
Debug.Log(PointLights.Count + "Point Lights, " + ConeLights.Count + " Cone Lights, " + DirectionalLights.Count + " Dir Lights, " + AreaLights.Count + " area lights.");
|
|
|
|
//Set up buffers with data stride. Keeping a min count of 1 to keep buffer valid. Get's skipped in shader.
|
|
ComputeBuffer pointBuffer = new ComputeBuffer(Mathf.Max(PointLights.Count, 1), (3 + 4) * 4);
|
|
ComputeBuffer coneBuffer = new ComputeBuffer(Mathf.Max(ConeLights.Count, 1), (3 + 4 + 3 + 2) * 4);
|
|
ComputeBuffer dirBuffer = new ComputeBuffer(Mathf.Max(DirectionalLights.Count, 1), (3 + 4) * 4);
|
|
ComputeBuffer areaBuffer = new ComputeBuffer(Mathf.Max(AreaLights.Count, 1), (4 * 4 + 4 * 4 + 3 + 4 + 3) * 4);
|
|
|
|
PointLightData[] PointLDatas = new PointLightData[PointLights.Count];
|
|
ConeLightData[] ConeLDatas = new ConeLightData[ConeLights.Count];
|
|
DirLightData[] DirLDatas = new DirLightData[DirectionalLights.Count];
|
|
AreaLightData[] AreaLDatas = new AreaLightData[AreaLights.Count];
|
|
|
|
for (int i = 0; i < PointLights.Count; i++)
|
|
{
|
|
PointLDatas[i].PointLightsPos = PointLights[i].transform.position;
|
|
PointLDatas[i].PointLightsColors = ColorExtraction(PointLights[i]);
|
|
}
|
|
|
|
for (int i = 0; i < ConeLights.Count; i++)
|
|
{
|
|
ConeLDatas[i].ConeLightsWS = ConeLights[i].transform.position;
|
|
ConeLDatas[i].ConeLightsColors = ColorExtraction(ConeLights[i]);
|
|
ConeLDatas[i].ConeLightsDir = ConeLights[i].transform.forward;
|
|
|
|
float flPhiDot = Mathf.Clamp01(Mathf.Cos(ConeLights[i].spotAngle * 0.5f * Mathf.Deg2Rad)); // outer cone
|
|
float flThetaDot = Mathf.Clamp01(Mathf.Cos(ConeLights[i].innerSpotAngle * 0.5f * Mathf.Deg2Rad)); // inner cone
|
|
|
|
ConeLDatas[i].ConeLightsPram = new Vector4(flPhiDot, 1.0f / Mathf.Max(0.01f, flThetaDot - flPhiDot), 0, 0);
|
|
}
|
|
|
|
for (int i = 0; i < DirectionalLights.Count; i++)
|
|
{
|
|
DirLDatas[i].DirLightsDir = DirectionalLights[i].transform.forward;
|
|
DirLDatas[i].DirLightsColors = ColorExtraction(DirectionalLights[i]);
|
|
}
|
|
|
|
for (int i = 0; i < AreaLights.Count; i++)
|
|
{
|
|
AreaLDatas[i].AreaLightsPos = AreaLights[i].transform.position;
|
|
AreaLDatas[i].AreaLightsMatrix = Matrix4x4.TRS(AreaLights[i].transform.position, AreaLights[i].transform.rotation, Vector3.one);
|
|
AreaLDatas[i].AreaLightsMatrixInv = AreaLDatas[i].AreaLightsMatrix.inverse;
|
|
AreaLDatas[i].AreaLightsColors = ColorExtraction(AreaLights[i]);
|
|
AreaLDatas[i].AreaLightsSize = new Vector3(AreaLights[i].areaSize.x, AreaLights[i].areaSize.y, AreaLights[i].type == LightType.Disc ? 1 : 0); //Packing for area or disc logic
|
|
}
|
|
|
|
pointBuffer.SetData(PointLDatas);
|
|
coneBuffer.SetData(ConeLDatas);
|
|
dirBuffer.SetData(DirLDatas);
|
|
areaBuffer.SetData(AreaLDatas);
|
|
|
|
Texture skyTex = MakeEnvironmentalCubemap();
|
|
CommandBuffer cmd = CommandBufferPool.Get();
|
|
|
|
cmd.SetRayTracingIntParam(rtshader, "PointLightCount", PointLDatas.Length); //Add a stack overflow loop or computebuffer
|
|
cmd.SetRayTracingBufferParam(rtshader, "PLD", pointBuffer);
|
|
|
|
//Cone
|
|
cmd.SetRayTracingIntParam(rtshader, "ConeLightCount", ConeLDatas.Length); //Add a stack overflow loop or computebuffer
|
|
cmd.SetRayTracingBufferParam(rtshader, "CLD", coneBuffer);
|
|
|
|
//Directional
|
|
cmd.SetRayTracingIntParam(rtshader, "DirLightCount", DirLDatas.Length); //Add a stack overflow loop or computebuffer
|
|
cmd.SetRayTracingBufferParam(rtshader, "DLD", dirBuffer);
|
|
|
|
//Area
|
|
cmd.SetRayTracingIntParam(rtshader, "AreaLightCount", AreaLDatas.Length); //Add a stack overflow loop or computebuffer
|
|
cmd.SetRayTracingIntParam(rtshader, "AreaLightSamples", System.Convert.ToInt32(AreaLightSamples));
|
|
cmd.SetRayTracingBufferParam(rtshader, "ALD", areaBuffer);
|
|
|
|
//Env
|
|
rtshader.SetTexture("_SkyTexture", skyTex);
|
|
cmd.SetRayTracingIntParam(rtshader, "EnvLightSamples", System.Convert.ToInt32(EnvLightSamples));
|
|
|
|
int id_startIdx = Shader.PropertyToID("StartRayIdx");
|
|
cmd.SetRayTracingIntParam(rtshader, "PerDispatchRayCount", RayChunkSize);
|
|
|
|
|
|
for (int j = 0; j < VolumetricRegisters.volumetricAreas.Count; j++)
|
|
{
|
|
Vector3Int resolution = VolumetricRegisters.volumetricAreas[j].NormalizedTexelDensity;
|
|
threads = resolution;
|
|
Vector3 boxSize = VolumetricRegisters.volumetricAreas[j].BoxScale;
|
|
float maxVoxelSize = math.max(boxSize.x / (float)resolution.x, math.max(boxSize.y / (float)resolution.y, boxSize.z / (float)resolution.z));
|
|
|
|
RenderTexture RT3d = initializeVolume(j);
|
|
cmd.SetRayTracingTextureParam(rtshader, "g_Output", RT3d);
|
|
|
|
cmd.SetRayTracingVectorParam(rtshader, "Size", VolumetricRegisters.volumetricAreas[j].NormalizedScale);
|
|
cmd.SetRayTracingVectorParam(rtshader, "WPosition", VolumetricRegisters.volumetricAreas[j].Corner);
|
|
cmd.SetRayTracingFloatParam(rtshader, "_Seed", (UnityEngine.Random.Range(0.0f, 64.0f)));
|
|
cmd.SetRayTracingFloatParam(rtshader, "HalfVoxelSize", maxVoxelSize * 0.5f);
|
|
|
|
//Point
|
|
|
|
//Dispatching
|
|
int maxRays = PointLDatas.Length + ConeLDatas.Length + DirLDatas.Length + EnvLightSamples + (AreaLDatas.Length * AreaLightSamples);
|
|
Debug.Log("Max Rays: " + maxRays);
|
|
|
|
for (int i = 0; i < maxRays; i += RayChunkSize)
|
|
{
|
|
cmd.SetRayTracingIntParam(rtshader, id_startIdx, i);
|
|
cmd.DispatchRays(rtshader, "MainRayGenShader", (uint)threads.x, (uint)threads.y, (uint)threads.z);
|
|
// Execute the command buffer once per volume, if we add all the volumes to the buffer then execute the gpu will time out!
|
|
Graphics.ExecuteCommandBuffer(cmd);
|
|
cmd.Clear();
|
|
}
|
|
|
|
|
|
///
|
|
///
|
|
///
|
|
|
|
string path = CheckDirectoryAndReturnPath() + j;
|
|
ComputeBuffer mipChain = Get3DMipsBuffer(RT3d);
|
|
|
|
Texture3D ReadBackTex = ReadBufferToTex3D(mipChain, RT3d.width, RT3d.height, RT3d.volumeDepth);
|
|
mipChain.Release();
|
|
Debug.Log("Used Buffer Readback");
|
|
//for (int i = 0; i < mipChain.Length; i++)
|
|
//{
|
|
// mipChain[i].Release();
|
|
// DestroyImmediate(mipChain[i]);
|
|
//}
|
|
Vol3d.WriteTex3DToVol3D(ReadBackTex, path + Vol3d.fileExtension);
|
|
AssetDatabase.ImportAsset(path + Vol3d.fileExtension);
|
|
VolumetricRegisters.volumetricAreas[j].bakedTexture = (Texture3D)AssetDatabase.LoadAssetAtPath(path + Vol3d.fileExtension, typeof(Texture3D));
|
|
|
|
// Debug.Log(VolumetricRegisters.volumetricAreas[j].gameObject.scene.name + VolumetricRegisters.volumetricAreas[j].name);
|
|
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(VolumetricRegisters.volumetricAreas[j].gameObject.scene);
|
|
|
|
}
|
|
pointBuffer.Release();
|
|
coneBuffer.Release();
|
|
dirBuffer.Release();
|
|
areaBuffer.Release();
|
|
Running = false;
|
|
EditorUtility.ClearProgressBar();
|
|
if (skyTex != null && skyTex.GetType() == typeof(RenderTexture))
|
|
{
|
|
RenderTexture rtSky = skyTex as RenderTexture;
|
|
rtSky.DiscardContents(true, true);
|
|
rtSky.Release();
|
|
DestroyImmediate(rtSky);
|
|
}
|
|
accelerationStructure.Dispose();
|
|
|
|
System.DateTime endTime = System.DateTime.Now;
|
|
|
|
UpdateStatus(" Volumetric bake took " + (endTime.Minute - startTime.Minute) + " Minutes and " +
|
|
(endTime.Second - startTime.Second) + " Seconds. Baked " + VolumetricRegisters.volumetricAreas.Count + " areas."
|
|
);
|
|
|
|
RefreshDebuggers();
|
|
EditorUtility.UnloadUnusedAssetsImmediate();
|
|
GC.Collect();
|
|
}
|
|
|
|
|
|
void DispatchLight(Light light, RenderTexture RT3d, Vector3Int Texels, int AreaID, Vector3Int TextileOffset ) //Used to render each light indavidually. TODO Generalize into pure pathtracing
|
|
{
|
|
|
|
//Setup light data
|
|
|
|
Vector4 lightColor = light.color * light.intensity;
|
|
Vector4 lightPos = light.transform.position;
|
|
int shaderKernel;
|
|
|
|
//Setup and dispatch Baking compute shader
|
|
switch (light.type)
|
|
{
|
|
case LightType.Point :
|
|
|
|
shaderKernel = BakingShader.FindKernel("PointLight");
|
|
break;
|
|
case LightType.Spot :
|
|
shaderKernel = BakingShader.FindKernel("SpotLight");
|
|
|
|
float flPhiDot = Mathf.Clamp01(Mathf.Cos(light.spotAngle * 0.5f * Mathf.Deg2Rad)); // outer cone
|
|
float flThetaDot = Mathf.Clamp01(Mathf.Cos(light.innerSpotAngle * 0.5f * Mathf.Deg2Rad)); // inner cone
|
|
|
|
|
|
BakingShader.SetFloat("SpotPram", flPhiDot);
|
|
BakingShader.SetFloat("InnerSpotPram", 1.0f / Mathf.Max(0.01f, flThetaDot - flPhiDot));
|
|
|
|
break;
|
|
case LightType.Directional:
|
|
shaderKernel = BakingShader.FindKernel("DirectionalLight");
|
|
break;
|
|
case LightType.Rectangle:
|
|
// Debug.Log("baked " + light.name);
|
|
shaderKernel = BakingShader.FindKernel("RectangleLight");
|
|
BakingShader.SetVector("AreaSize", light.areaSize);
|
|
BakingShader.SetFloat("AreaLightSamples", AreaLightSamples);
|
|
BakingShader.SetMatrix("AreaMatrix", Matrix4x4.Rotate(light.transform.rotation));
|
|
|
|
BakingShader.SetFloat("_Seed", UnityEngine.Random.value);
|
|
|
|
break;
|
|
case LightType.Disc:
|
|
shaderKernel = BakingShader.FindKernel("DiscLight");
|
|
BakingShader.SetVector("AreaSize", light.areaSize); //Only the first float is used for radius
|
|
BakingShader.SetFloat("AreaLightSamples", AreaLightSamples);
|
|
BakingShader.SetMatrix("AreaMatrix", Matrix4x4.Rotate(light.transform.rotation));
|
|
|
|
BakingShader.SetFloat("_Seed", UnityEngine.Random.value);
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
|
|
BakingShader.SetTexture(shaderKernel, "AccumulatedLights", RT3d);
|
|
|
|
BakingShader.SetVector("LightColor", lightColor);
|
|
BakingShader.SetVector("LightPosition", lightPos);
|
|
|
|
BakingShader.SetVector("LightDirection", light.transform.rotation * Vector3.forward);
|
|
// BakingShader.SetVector("Size", VolumetricRegisters.volumetricAreas[AreaID].NormalizedScale);
|
|
|
|
Vector3 BucketScaler = (Vector3)VolumetricRegisters.volumetricAreas[AreaID].NormalizedTexelDensity / (float)BucketSize ;
|
|
// BucketScaler = Vector3.one * 1.5f;
|
|
|
|
//Debug.Log("Bucket Scaler" + BucketScaler+
|
|
// "NormalizedTexelDensity" + (VolumetricRegisters.volumetricAreas[AreaID].NormalizedTexelDensity)+
|
|
// "BucketSize" + BucketSize );
|
|
|
|
//Offset each cell in world space based on the textiles
|
|
Vector3 D = VolumetricRegisters.volumetricAreas[AreaID].NormalizedScale;
|
|
Vector3 T = (Vector3)VolumetricRegisters.volumetricAreas[AreaID].NormalizedTexelDensity;
|
|
Vector3 PositionOffset = new Vector3( (D.x / T.x) * TextileOffset.x, (D.y / T.y) * TextileOffset.y, (D.z / T.z) * TextileOffset.z);
|
|
|
|
BakingShader.SetVector("Size", new Vector3(VolumetricRegisters.volumetricAreas[AreaID].NormalizedScale.x / BucketScaler.x,
|
|
VolumetricRegisters.volumetricAreas[AreaID].NormalizedScale.y / BucketScaler.y,
|
|
VolumetricRegisters.volumetricAreas[AreaID].NormalizedScale.z / BucketScaler.z) );
|
|
// BakingShader.SetVector("Size", (Vector3)(Vector3Int.one * BucketSize) );
|
|
BakingShader.SetVector("Position", VolumetricRegisters.volumetricAreas[AreaID].Corner + PositionOffset);
|
|
|
|
|
|
///Temp Shadow spheres
|
|
Vector4[] SpherePos = new Vector4[0];
|
|
SetComputeBuffer("_MeshObjects", BakingShader, shaderKernel, _meshObjectBuffer);
|
|
SetComputeBuffer("_Vertices", BakingShader, shaderKernel, _vertexBuffer);
|
|
SetComputeBuffer("_Indices", BakingShader, shaderKernel, _indexBuffer);
|
|
|
|
BakingShader.SetVectorArray("OpaqueSphere", SpherePos);
|
|
BakingShader.SetInt("SphereCount", SpherePos.Length);
|
|
///
|
|
|
|
|
|
Vector3 ThreadsToDispatch = new Vector3(
|
|
Mathf.CeilToInt(Texels.x / 4.0f),
|
|
Mathf.CeilToInt(Texels.y / 4.0f),
|
|
Mathf.CeilToInt(Texels.z / 4.0f)
|
|
);
|
|
|
|
//int DebugCountStride = sizeof(int); //Size of debug strut
|
|
//int DebugID = Shader.PropertyToID("DebugBuffer");
|
|
|
|
//Debuger[] debuger = new Debuger[1];
|
|
//debuger[0].DebugCcounter = 0;
|
|
|
|
//ComputeBuffer BakeBuffer = new ComputeBuffer(1, DebugCountStride);
|
|
//BakingShader.SetBuffer(shaderKernel, DebugID, BakeBuffer);
|
|
|
|
BakingShader.Dispatch(shaderKernel, (int)ThreadsToDispatch.x, (int)ThreadsToDispatch.y, (int)ThreadsToDispatch.z);
|
|
|
|
//while (debuger[0].DebugCcounter == 0)
|
|
//{
|
|
// //BakeBuffer.GetData(debuger);
|
|
// // Debug.Log("Counter " + debuger[0].DebugCcounter);
|
|
//}
|
|
|
|
//BakeBuffer.Release(); //Avoiding memory leak
|
|
// return RT3d;
|
|
}
|
|
|
|
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");
|
|
}
|
|
}
|
|
|
|
|
|
Light[] GatherBakedLights(LightType lightType)
|
|
{
|
|
Light[] lights = FindObjectsOfType<Light>(); //TODO: Make it smarter to find only baked lights affecting zone.
|
|
List<Light> FilteredLights = new List<Light>();
|
|
|
|
for (int i = 0; i < lights.Length; i++)
|
|
{
|
|
//TODO: Handle mixed lights differently in the future
|
|
if (lights[i].lightmapBakeType == LightmapBakeType.Baked || lights[i].lightmapBakeType == LightmapBakeType.Mixed)
|
|
{
|
|
if (lights[i].enabled) FilteredLights.Add(lights[i]);
|
|
}
|
|
}
|
|
|
|
return FilteredLights.ToArray();
|
|
}
|
|
|
|
|
|
Light[] GatherBakedLights()
|
|
{
|
|
Light[] lights = FindObjectsOfType<Light>(); //TODO: Make it smarter to find only baked lights affecting zone.
|
|
List<Light> FilteredLights = new List<Light>();
|
|
|
|
for (int i = 0; i < lights.Length; i++)
|
|
{
|
|
//TODO: Handle mixed lights differently in the future
|
|
if (lights[i].lightmapBakeType == LightmapBakeType.Baked || lights[i].lightmapBakeType == LightmapBakeType.Mixed)
|
|
{
|
|
if (lights[i].enabled) FilteredLights.Add(lights[i]);
|
|
}
|
|
}
|
|
|
|
return FilteredLights.ToArray();
|
|
}
|
|
|
|
GameObject[] GatherStaticObjects() {
|
|
|
|
List<GameObject> StatcGameobject = new List<GameObject>();
|
|
Renderer[] AllRenderers = FindObjectsOfType<Renderer>();
|
|
|
|
StaticEditorFlags staticFlag = StaticEditorFlags.ContributeGI;
|
|
|
|
//Loop through GO's and see if the correct static flag is enabled. If it is, then add it to the list;
|
|
for (int i = 0; i < AllRenderers.Length; i++){
|
|
if (GameObjectUtility.AreStaticEditorFlagsSet(AllRenderers[i].gameObject, staticFlag) && AllRenderers[i].shadowCastingMode != UnityEngine.Rendering.ShadowCastingMode.Off) {
|
|
StatcGameobject.Add(AllRenderers[i].gameObject);
|
|
}
|
|
}
|
|
|
|
return StatcGameobject.ToArray();
|
|
}
|
|
Renderer[] GatherStaticRenderers()
|
|
{
|
|
List<Renderer> StatcRenderer = new List<Renderer>();
|
|
Renderer[] AllRenderers = FindObjectsOfType<Renderer>();
|
|
StaticEditorFlags staticFlag = StaticEditorFlags.ContributeGI;
|
|
|
|
//Loop through GO's and see if the correct static flag is enabled. If it is, then add it to the list;
|
|
for (int i = 0; i < AllRenderers.Length; i++)
|
|
{
|
|
if (GameObjectUtility.AreStaticEditorFlagsSet(AllRenderers[i].gameObject, staticFlag) && AllRenderers[i].shadowCastingMode != UnityEngine.Rendering.ShadowCastingMode.Off)
|
|
{
|
|
StatcRenderer.Add(AllRenderers[i]);
|
|
}
|
|
}
|
|
return StatcRenderer.ToArray();
|
|
}
|
|
|
|
|
|
|
|
public void RebuildMeshObjectBuffers()
|
|
{
|
|
//if (!VolumetricRegisters._meshObjectsNeedRebuilding)
|
|
//{
|
|
// return;
|
|
//}
|
|
|
|
// VolumetricBakingRegisters._meshObjectsNeedRebuilding = false;
|
|
|
|
// Clear all lists
|
|
_meshObjects.Clear();
|
|
_vertices.Clear();
|
|
_indices.Clear();
|
|
|
|
GameObject[] staticGOs = GatherStaticObjects();
|
|
|
|
// Loop over all objects and gather their data
|
|
for (int i=0; i< staticGOs.Length; i++)
|
|
{
|
|
Mesh mesh = staticGOs[i].GetComponent<MeshFilter>().sharedMesh;
|
|
|
|
if (mesh == null) return;
|
|
// Add vertex data
|
|
int firstVertex = _vertices.Count;
|
|
|
|
// if (_vertices.Count == 0) return;
|
|
|
|
_vertices.AddRange(mesh.vertices);
|
|
// Add index data - if the vertex buffer wasn't empty before, the
|
|
// indices need to be offset
|
|
//
|
|
// Debug.Log(_indices.Count + " index");
|
|
int firstIndex = _indices.Count;
|
|
var indices = mesh.GetIndices(0); //Extend to support submeshes
|
|
// _indices.AddRange(indices.Select(index => index + firstVertex))
|
|
_indices.AddRange( indices.Select(index => index + firstVertex) );
|
|
|
|
// Add the object itself
|
|
_meshObjects.Add(new MeshObject()
|
|
{
|
|
localToWorldMatrix = staticGOs[i].transform.localToWorldMatrix,
|
|
indices_offset = firstIndex,
|
|
indices_count = indices.Length
|
|
});
|
|
}
|
|
|
|
|
|
//TODO: Covert terrain data into an ingestable format!!
|
|
//for (int i = 0; i < staticGOs.Length; i++)
|
|
//{
|
|
// Mesh mesh = staticGOs[i].GetComponent<MeshFilter>().sharedMesh;
|
|
|
|
// if (mesh == null) return;
|
|
// // Add vertex data
|
|
// int firstVertex = _vertices.Count;
|
|
|
|
// // if (_vertices.Count == 0) return;
|
|
|
|
// _vertices.AddRange(mesh.vertices);
|
|
// // Add index data - if the vertex buffer wasn't empty before, the
|
|
// // indices need to be offset
|
|
// //
|
|
// // Debug.Log(_indices.Count + " index");
|
|
// int firstIndex = _indices.Count;
|
|
// var indices = mesh.GetIndices(0); //Extend to support submeshes
|
|
// // _indices.AddRange(indices.Select(index => index + firstVertex))
|
|
// _indices.AddRange(indices.Select(index => index + firstVertex));
|
|
|
|
// // Add the object itself
|
|
// _meshObjects.Add(new MeshObject()
|
|
// {
|
|
// localToWorldMatrix = staticGOs[i].transform.localToWorldMatrix,
|
|
// indices_offset = firstIndex,
|
|
// indices_count = indices.Length
|
|
// });
|
|
//}
|
|
|
|
// Debug.Log(_meshObjects.Count);
|
|
|
|
|
|
CreateComputeBuffer(ref _meshObjectBuffer, _meshObjects, 72);
|
|
CreateComputeBuffer(ref _vertexBuffer, _vertices, 12);
|
|
CreateComputeBuffer(ref _indexBuffer, _indices, 4);
|
|
}
|
|
|
|
void ReleaseBuffers()
|
|
{
|
|
_meshObjectBuffer.Release();
|
|
_meshObjectBuffer = null;
|
|
_vertexBuffer.Release();
|
|
_vertexBuffer = null;
|
|
_indexBuffer.Release();
|
|
_indexBuffer = null;
|
|
}
|
|
|
|
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 != 0)
|
|
{
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
struct PointLightData
|
|
{
|
|
//Point
|
|
public Vector3 PointLightsPos;
|
|
public Vector4 PointLightsColors;
|
|
}
|
|
|
|
struct ConeLightData
|
|
{
|
|
public Vector3 ConeLightsWS;
|
|
public Vector4 ConeLightsColors;
|
|
public Vector3 ConeLightsDir;
|
|
public Vector2 ConeLightsPram;
|
|
}
|
|
|
|
struct DirLightData
|
|
{
|
|
public Vector3 DirLightsDir;
|
|
public Vector4 DirLightsColors;
|
|
}
|
|
struct AreaLightData
|
|
{
|
|
public Matrix4x4 AreaLightsMatrix;
|
|
public Matrix4x4 AreaLightsMatrixInv;
|
|
public Vector3 AreaLightsPos;
|
|
public Vector4 AreaLightsColors;
|
|
public Vector3 AreaLightsSize;
|
|
}
|
|
|
|
|
|
//public static void RepaintInspector(System.Type t)
|
|
//{
|
|
// Editor[] ed = (Editor[])Resources.FindObjectsOfTypeAll<Editor>();
|
|
// for (int i = 0; i < ed.Length; i++)
|
|
// {
|
|
// if (ed[i].GetType() == t)
|
|
// {
|
|
// ed[i].Repaint();
|
|
// return;
|
|
// }
|
|
// }
|
|
//}
|
|
|
|
}
|