private void OnValidate() { if (!enabled || !gameObject.activeSelf) { return; } if (m_Profile != null) { bool hasIndexDimensionChangedOnProfileSwitch = m_PrevProfile == null || (m_PrevProfile != null && m_PrevProfile.indexDimensions != m_Profile.indexDimensions); if (hasIndexDimensionChangedOnProfileSwitch) { var refVol = ProbeReferenceVolume.instance; refVol.AddPendingIndexDimensionChange(indexDimensions); } m_PrevProfile = m_Profile; QueueAssetLoading(); } if (volumeAsset != m_PrevAsset && m_PrevAsset != null) { ProbeReferenceVolume.instance.AddPendingAssetRemoval(m_PrevAsset); } m_PrevAsset = volumeAsset; }
internal void RemovePendingAsset(ProbeVolumeAsset asset) { var key = asset.GetSerializedFullPath(); if (m_ActiveAssets.ContainsKey(key)) { m_ActiveAssets.Remove(key); } // Remove bricks and empty cells foreach (var cell in asset.cells) { if (cells.ContainsKey(cell.index)) { cells.Remove(cell.index); } } // Unload brick data if (m_AssetPathToBricks.ContainsKey(key)) { var regIds = m_AssetPathToBricks[key]; foreach (var regId in regIds) { ReleaseBricks(regId); } m_AssetPathToBricks.Remove(key); } }
internal void AddPendingAssetLoading(ProbeVolumeAsset asset) { var key = asset.GetSerializedFullPath(); if (m_PendingAssetsToBeLoaded.ContainsKey(key)) { m_PendingAssetsToBeLoaded.Remove(key); } m_PendingAssetsToBeLoaded.Add(asset.GetSerializedFullPath(), asset); m_NeedLoadAsset = true; // Compute the max index dimension from all the loaded assets + assets we need to load Vector3Int indexDimension = Vector3Int.zero; foreach (var a in m_PendingAssetsToBeLoaded.Values) { indexDimension = Vector3Int.Max(indexDimension, a.maxCellIndex); } foreach (var a in m_ActiveAssets.Values) { indexDimension = Vector3Int.Max(indexDimension, a.maxCellIndex); } m_PendingIndexDimChange = indexDimension; m_NeedsIndexDimChange = true; }
internal void AddPendingAssetRemoval(ProbeVolumeAsset asset) { var key = asset.GetSerializedFullPath(); if (m_PendingAssetsToBeUnloaded.ContainsKey(key)) { m_PendingAssetsToBeUnloaded.Remove(key); } m_PendingAssetsToBeUnloaded.Add(asset.GetSerializedFullPath(), asset); }
internal void AddPendingAssetLoading(ProbeVolumeAsset asset) { var key = asset.GetSerializedFullPath(); if (m_PendingAssetsToBeLoaded.ContainsKey(key)) { m_PendingAssetsToBeLoaded.Remove(key); } m_PendingAssetsToBeLoaded.Add(asset.GetSerializedFullPath(), asset); m_NeedLoadAsset = true; }
internal void QueueAssetRemoval() { if (volumeAsset == null) { return; } #if UNITY_EDITOR m_PrevAsset = null; #endif ProbeReferenceVolume.instance.AddPendingAssetRemoval(volumeAsset); }
public static ProbeVolumeAsset CreateAsset(ProbeVolumePerSceneData data) { ProbeVolumeAsset asset = CreateInstance <ProbeVolumeAsset>(); if (data.asset != null) { asset.m_AssetFullPath = UnityEditor.AssetDatabase.GetAssetPath(data.asset); } if (string.IsNullOrEmpty(asset.m_AssetFullPath)) { asset.m_AssetFullPath = GetPath(data.gameObject.scene); } UnityEditor.AssetDatabase.CreateAsset(asset, asset.m_AssetFullPath); return(asset); }
private void OnValidate() { if (!enabled || !gameObject.activeSelf) { return; } if (m_Profile != null) { m_PrevProfile = m_Profile; QueueAssetLoading(); } if (volumeAsset != m_PrevAsset && m_PrevAsset != null) { ProbeReferenceVolume.instance.AddPendingAssetRemoval(m_PrevAsset); } m_PrevAsset = volumeAsset; }
private void LoadAsset(ProbeVolumeAsset asset) { var path = asset.GetSerializedFullPath(); m_AssetPathToBricks[path] = new List <RegId>(); foreach (var cell in asset.cells) { // Push data to HDRP bool compressed = false; var dataLocation = ProbeBrickPool.CreateDataLocation(cell.sh.Length, compressed, ProbeVolumeSHBands.SphericalHarmonicsL2); ProbeBrickPool.FillDataLocation(ref dataLocation, cell.sh, ProbeVolumeSHBands.SphericalHarmonicsL2); // TODO register ID of brick list List <ProbeBrickIndex.Brick> brickList = new List <ProbeBrickIndex.Brick>(); brickList.AddRange(cell.bricks); var regId = AddBricks(brickList, dataLocation); cells[cell.index] = cell; m_AssetPathToBricks[path].Add(regId); } }
private static void OnAdditionalProbesBakeCompleted() { UnityEditor.Experimental.Lightmapping.additionalBakedProbesCompleted -= OnAdditionalProbesBakeCompleted; var numCells = bakingCells.Count; // Fetch results of all cells for (int c = 0; c < numCells; ++c) { var cell = bakingCells[c].cell; if (cell.probePositions == null) { continue; } int numProbes = cell.probePositions.Length; Debug.Assert(numProbes > 0); int numUniqueProbes = bakingCells[c].numUniqueProbes; var sh = new NativeArray <SphericalHarmonicsL2>(numUniqueProbes, Allocator.Temp, NativeArrayOptions.UninitializedMemory); var validity = new NativeArray <float>(numUniqueProbes, Allocator.Temp, NativeArrayOptions.UninitializedMemory); var bakedProbeOctahedralDepth = new NativeArray <float>(numUniqueProbes * 64, Allocator.Temp, NativeArrayOptions.UninitializedMemory); UnityEditor.Experimental.Lightmapping.GetAdditionalBakedProbes(cell.index, sh, validity, bakedProbeOctahedralDepth); cell.sh = new SphericalHarmonicsL2[numProbes]; cell.validity = new float[numProbes]; for (int i = 0; i < numProbes; ++i) { int j = bakingCells[c].probeIndices[i]; SphericalHarmonicsL2 shv = sh[j]; // Compress the range of all coefficients but the DC component to [0..1] // Upper bounds taken from http://ppsloan.org/publications/Sig20_Advances.pptx // Divide each coefficient by DC*f to get to [-1,1] where f is from slide 33 for (int rgb = 0; rgb < 3; ++rgb) { var l0 = sh[j][rgb, 0]; if (l0 == 0.0f) { continue; } // TODO: We're working on irradiance instead of radiance coefficients // Add safety margin 2 to avoid out-of-bounds values float l1scale = 1.7320508f; // 3/(2*sqrt(3)) * 2 float l2scale = 3.5777088f; // 4/sqrt(5) * 2 // L_1^m shv[rgb, 1] = sh[j][rgb, 1] / (l0 * l1scale * 2.0f) + 0.5f; shv[rgb, 2] = sh[j][rgb, 2] / (l0 * l1scale * 2.0f) + 0.5f; shv[rgb, 3] = sh[j][rgb, 3] / (l0 * l1scale * 2.0f) + 0.5f; // L_2^-2 shv[rgb, 4] = sh[j][rgb, 4] / (l0 * l2scale * 2.0f) + 0.5f; shv[rgb, 5] = sh[j][rgb, 5] / (l0 * l2scale * 2.0f) + 0.5f; shv[rgb, 6] = sh[j][rgb, 6] / (l0 * l2scale * 2.0f) + 0.5f; shv[rgb, 7] = sh[j][rgb, 7] / (l0 * l2scale * 2.0f) + 0.5f; shv[rgb, 8] = sh[j][rgb, 8] / (l0 * l2scale * 2.0f) + 0.5f; // Assert coefficient range for (int coeff = 1; coeff < 9; ++coeff) { Debug.Assert(shv[rgb, coeff] >= 0.0f && shv[rgb, coeff] <= 1.0f); } } SphericalHarmonicsL2Utils.SetL0(ref cell.sh[i], new Vector3(shv[0, 0], shv[1, 0], shv[2, 0])); SphericalHarmonicsL2Utils.SetL1R(ref cell.sh[i], new Vector3(shv[0, 3], shv[0, 1], shv[0, 2])); SphericalHarmonicsL2Utils.SetL1G(ref cell.sh[i], new Vector3(shv[1, 3], shv[1, 1], shv[1, 2])); SphericalHarmonicsL2Utils.SetL1B(ref cell.sh[i], new Vector3(shv[2, 3], shv[2, 1], shv[2, 2])); cell.validity[i] = validity[j]; } for (int i = 0; i < numProbes; ++i) { int j = bakingCells[c].probeIndices[i]; SphericalHarmonicsL2Utils.SetCoefficient(ref cell.sh[i], 4, new Vector3(sh[j][0, 4], sh[j][1, 4], sh[j][2, 4])); SphericalHarmonicsL2Utils.SetCoefficient(ref cell.sh[i], 5, new Vector3(sh[j][0, 5], sh[j][1, 5], sh[j][2, 5])); SphericalHarmonicsL2Utils.SetCoefficient(ref cell.sh[i], 6, new Vector3(sh[j][0, 6], sh[j][1, 6], sh[j][2, 6])); SphericalHarmonicsL2Utils.SetCoefficient(ref cell.sh[i], 7, new Vector3(sh[j][0, 7], sh[j][1, 7], sh[j][2, 7])); SphericalHarmonicsL2Utils.SetCoefficient(ref cell.sh[i], 8, new Vector3(sh[j][0, 8], sh[j][1, 8], sh[j][2, 8])); } // Reset index UnityEditor.Experimental.Lightmapping.SetAdditionalBakedProbes(cell.index, null); DilateInvalidProbes(cell.probePositions, cell.bricks, cell.sh, cell.validity, bakingReferenceVolumeAuthoring.GetDilationSettings()); ProbeReferenceVolume.instance.cells[cell.index] = cell; } // Map from each scene to an existing reference volume var scene2RefVol = new Dictionary <Scene, ProbeReferenceVolumeAuthoring>(); foreach (var refVol in GameObject.FindObjectsOfType <ProbeReferenceVolumeAuthoring>()) { if (refVol.enabled) { scene2RefVol[refVol.gameObject.scene] = refVol; } } // Map from each reference volume to its asset var refVol2Asset = new Dictionary <ProbeReferenceVolumeAuthoring, ProbeVolumeAsset>(); foreach (var refVol in scene2RefVol.Values) { refVol2Asset[refVol] = ProbeVolumeAsset.CreateAsset(refVol.gameObject.scene); } // Put cells into the respective assets foreach (var cell in ProbeReferenceVolume.instance.cells.Values) { foreach (var scene in cellIndex2SceneReferences[cell.index]) { // This scene has a reference volume authoring component in it? ProbeReferenceVolumeAuthoring refVol = null; if (scene2RefVol.TryGetValue(scene, out refVol)) { var asset = refVol2Asset[refVol]; asset.cells.Add(cell); } } } // Connect the assets to their components foreach (var pair in refVol2Asset) { var refVol = pair.Key; var asset = pair.Value; refVol.volumeAsset = asset; if (UnityEditor.Lightmapping.giWorkflowMode != UnityEditor.Lightmapping.GIWorkflowMode.Iterative) { UnityEditor.EditorUtility.SetDirty(refVol); UnityEditor.EditorUtility.SetDirty(refVol.volumeAsset); } } UnityEditor.AssetDatabase.SaveAssets(); UnityEditor.AssetDatabase.Refresh(); foreach (var refVol in refVol2Asset.Keys) { if (refVol.enabled && refVol.gameObject.activeSelf) { refVol.QueueAssetLoading(); } } }
private static void OnAdditionalProbesBakeCompleted() { UnityEditor.Experimental.Lightmapping.additionalBakedProbesCompleted -= OnAdditionalProbesBakeCompleted; var numCells = bakingCells.Count; // Fetch results of all cells for (int c = 0; c < numCells; ++c) { var cell = bakingCells[c].cell; if (cell.probePositions == null) { continue; } int numProbes = cell.probePositions.Length; Debug.Assert(numProbes > 0); int numUniqueProbes = bakingCells[c].numUniqueProbes; var sh = new NativeArray <SphericalHarmonicsL2>(numUniqueProbes, Allocator.Temp, NativeArrayOptions.UninitializedMemory); var validity = new NativeArray <float>(numUniqueProbes, Allocator.Temp, NativeArrayOptions.UninitializedMemory); var bakedProbeOctahedralDepth = new NativeArray <float>(numUniqueProbes * 64, Allocator.Temp, NativeArrayOptions.UninitializedMemory); UnityEditor.Experimental.Lightmapping.GetAdditionalBakedProbes(cell.index, sh, validity, bakedProbeOctahedralDepth); cell.sh = new SphericalHarmonicsL1[numProbes]; cell.validity = new float[numProbes]; for (int i = 0; i < numProbes; ++i) { Vector4[] channels = new Vector4[3]; int j = bakingCells[c].probeIndices[i]; // compare to SphericalHarmonicsL2::GetShaderConstantsFromNormalizedSH channels[0] = new Vector4(sh[j][0, 3], sh[j][0, 1], sh[j][0, 2], sh[j][0, 0]); channels[1] = new Vector4(sh[j][1, 3], sh[j][1, 1], sh[j][1, 2], sh[j][1, 0]); channels[2] = new Vector4(sh[j][2, 3], sh[j][2, 1], sh[j][2, 2], sh[j][2, 0]); // It can be shown that |L1_i| <= |2*L0| // Precomputed Global Illumination in Frostbite by Yuriy O'Donnell. // https://media.contentapi.ea.com/content/dam/eacom/frostbite/files/gdc2018-precomputedgiobalilluminationinfrostbite.pdf // // So divide by L0 brings us to [-2, 2], // divide by 4 brings us to [-0.5, 0.5], // and plus by 0.5 brings us to [0, 1]. for (int channel = 0; channel < 3; ++channel) { var l0 = channels[channel][3]; if (l0 != 0.0f) { for (int axis = 0; axis < 3; ++axis) { channels[channel][axis] = channels[channel][axis] / (l0 * 4.0f) + 0.5f; Debug.Assert(channels[channel][axis] >= 0.0f && channels[channel][axis] <= 1.0f); } } } SphericalHarmonicsL1 sh1 = new SphericalHarmonicsL1(); sh1.shAr = channels[0]; sh1.shAg = channels[1]; sh1.shAb = channels[2]; cell.sh[i] = sh1; cell.validity[i] = validity[j]; } // Reset index UnityEditor.Experimental.Lightmapping.SetAdditionalBakedProbes(cell.index, null); DilateInvalidProbes(cell.probePositions, cell.bricks, cell.sh, cell.validity, bakingReferenceVolumeAuthoring.GetDilationSettings()); ProbeReferenceVolume.instance.cells[cell.index] = cell; } // Map from each scene to an existing reference volume var scene2RefVol = new Dictionary <Scene, ProbeReferenceVolumeAuthoring>(); foreach (var refVol in GameObject.FindObjectsOfType <ProbeReferenceVolumeAuthoring>()) { if (refVol.enabled) { scene2RefVol[refVol.gameObject.scene] = refVol; } } // Map from each reference volume to its asset var refVol2Asset = new Dictionary <ProbeReferenceVolumeAuthoring, ProbeVolumeAsset>(); foreach (var refVol in scene2RefVol.Values) { refVol2Asset[refVol] = ProbeVolumeAsset.CreateAsset(refVol.gameObject.scene); } // Put cells into the respective assets foreach (var cell in ProbeReferenceVolume.instance.cells.Values) { foreach (var scene in cellIndex2SceneReferences[cell.index]) { // This scene has a reference volume authoring component in it? ProbeReferenceVolumeAuthoring refVol = null; if (scene2RefVol.TryGetValue(scene, out refVol)) { var asset = refVol2Asset[refVol]; asset.cells.Add(cell); } } } // Connect the assets to their components foreach (var pair in refVol2Asset) { var refVol = pair.Key; var asset = pair.Value; refVol.volumeAsset = asset; if (UnityEditor.Lightmapping.giWorkflowMode != UnityEditor.Lightmapping.GIWorkflowMode.Iterative) { UnityEditor.EditorUtility.SetDirty(refVol); UnityEditor.EditorUtility.SetDirty(refVol.volumeAsset); } } UnityEditor.AssetDatabase.SaveAssets(); UnityEditor.AssetDatabase.Refresh(); foreach (var refVol in refVol2Asset.Keys) { if (refVol.enabled && refVol.gameObject.activeSelf) { refVol.QueueAssetLoading(); } } }
internal bool CompatibleWith(ProbeVolumeAsset otherAsset) { return((maxSubdivision == otherAsset.maxSubdivision) && (minBrickSize == otherAsset.minBrickSize) && (cellSizeInBricks == otherAsset.cellSizeInBricks) && (chunkSizeInBricks == otherAsset.chunkSizeInBricks)); }