668 lines
30 KiB
C#
668 lines
30 KiB
C#
//------------------------------------------------------------------------------------------------------------------
|
|
// Volumetric Fog & Mist 2
|
|
// Created by Kronnect
|
|
//------------------------------------------------------------------------------------------------------------------
|
|
using UnityEngine;
|
|
using System.Collections.Generic;
|
|
|
|
|
|
namespace VolumetricFogAndMist2 {
|
|
|
|
|
|
public enum MASK_TEXTURE_BRUSH_MODE {
|
|
AddFog = 0,
|
|
RemoveFog = 1,
|
|
ColorFog = 2
|
|
}
|
|
|
|
|
|
public partial class VolumetricFog : MonoBehaviour {
|
|
|
|
public bool enableFogOfWar;
|
|
public Vector3 fogOfWarCenter;
|
|
public bool fogOfWarIsLocal;
|
|
public Vector3 fogOfWarSize = new Vector3(1024, 0, 1024);
|
|
[Range(32, 2048)] public int fogOfWarTextureSize = 256;
|
|
[Range(0, 100)] public float fogOfWarRestoreDelay;
|
|
[Range(0, 25)] public float fogOfWarRestoreDuration = 2f;
|
|
[Range(0, 1)] public float fogOfWarSmoothness = 1f;
|
|
public bool fogOfWarBlur;
|
|
|
|
const int MAX_SIMULTANEOUS_TRANSITIONS = 10000;
|
|
bool canDestroyFOWTexture;
|
|
|
|
#region In-Editor fog of war painter
|
|
|
|
public bool maskEditorEnabled;
|
|
public MASK_TEXTURE_BRUSH_MODE maskBrushMode = MASK_TEXTURE_BRUSH_MODE.RemoveFog;
|
|
public Color maskBrushColor = Color.white;
|
|
[Range(1, 128)] public int maskBrushWidth = 20;
|
|
[Range(0, 1)] public float maskBrushFuzziness = 0.5f;
|
|
[Range(0, 1)] public float maskBrushOpacity = 0.15f;
|
|
|
|
#endregion
|
|
|
|
[SerializeField]
|
|
Texture2D _fogOfWarTexture;
|
|
|
|
public Vector3 anchoredFogOfWarCenter => fogOfWarIsLocal ? transform.position + fogOfWarCenter : fogOfWarCenter;
|
|
|
|
|
|
public Texture2D fogOfWarTexture {
|
|
get { return _fogOfWarTexture; }
|
|
set {
|
|
if (_fogOfWarTexture != value) {
|
|
if (value != null) {
|
|
if (value.width != value.height) {
|
|
Debug.LogError("Fog of war texture must be square.");
|
|
} else {
|
|
_fogOfWarTexture = value;
|
|
canDestroyFOWTexture = false;
|
|
ReloadFogOfWarTexture();
|
|
if (fogMat != null) {
|
|
fogMat.SetTexture(ShaderParams.FogOfWarTexture, _fogOfWarTexture);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
Color32[] fogOfWarColorBuffer;
|
|
|
|
struct FogOfWarTransition {
|
|
public bool enabled;
|
|
public int x, y;
|
|
public float startTime, startDelay;
|
|
public float duration;
|
|
public int initialAlpha;
|
|
public int targetAlpha;
|
|
}
|
|
|
|
FogOfWarTransition[] fowTransitionList;
|
|
int lastTransitionPos;
|
|
Dictionary<int, int> fowTransitionIndices;
|
|
bool requiresTextureUpload;
|
|
Material fowBlur;
|
|
RenderTexture fowBlur1, fowBlur2;
|
|
|
|
|
|
void FogOfWarInit() {
|
|
if (fowTransitionList == null || fowTransitionList.Length != MAX_SIMULTANEOUS_TRANSITIONS) {
|
|
fowTransitionList = new FogOfWarTransition[MAX_SIMULTANEOUS_TRANSITIONS];
|
|
}
|
|
if (fowTransitionIndices == null) {
|
|
fowTransitionIndices = new Dictionary<int, int>(MAX_SIMULTANEOUS_TRANSITIONS);
|
|
} else {
|
|
fowTransitionIndices.Clear();
|
|
}
|
|
lastTransitionPos = -1;
|
|
|
|
if (_fogOfWarTexture == null) {
|
|
FogOfWarUpdateTexture();
|
|
} else if (enableFogOfWar && (fogOfWarColorBuffer == null || fogOfWarColorBuffer.Length == 0)) {
|
|
ReloadFogOfWarTexture();
|
|
}
|
|
}
|
|
|
|
void FogOfWarDestroy() {
|
|
if (canDestroyFOWTexture) {
|
|
DestroyImmediate(_fogOfWarTexture);
|
|
}
|
|
if (fowBlur1 != null) {
|
|
fowBlur1.Release();
|
|
}
|
|
if (fowBlur2 != null) {
|
|
fowBlur2.Release();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reloads the current contents of the fog of war texture
|
|
/// </summary>
|
|
public void ReloadFogOfWarTexture() {
|
|
if (_fogOfWarTexture == null || profile == null) return;
|
|
fogOfWarTextureSize = _fogOfWarTexture.width;
|
|
fogOfWarColorBuffer = _fogOfWarTexture.GetPixels32();
|
|
lastTransitionPos = -1;
|
|
fowTransitionIndices.Clear();
|
|
if (!enableFogOfWar) {
|
|
enableFogOfWar = true;
|
|
UpdateMaterialProperties();
|
|
}
|
|
}
|
|
|
|
|
|
void FogOfWarUpdateTexture() {
|
|
if (!enableFogOfWar || !Application.isPlaying)
|
|
return;
|
|
int size = GetScaledSize(fogOfWarTextureSize, 1.0f);
|
|
if (_fogOfWarTexture == null || _fogOfWarTexture.width != size || _fogOfWarTexture.height != size) {
|
|
_fogOfWarTexture = new Texture2D(size, size, TextureFormat.RGBA32, false, true);
|
|
_fogOfWarTexture.hideFlags = HideFlags.DontSave;
|
|
_fogOfWarTexture.filterMode = FilterMode.Bilinear;
|
|
_fogOfWarTexture.wrapMode = TextureWrapMode.Clamp;
|
|
canDestroyFOWTexture = true;
|
|
ResetFogOfWar();
|
|
}
|
|
}
|
|
|
|
int GetScaledSize(int size, float factor) {
|
|
size = (int)(size / factor);
|
|
size /= 4;
|
|
if (size < 1)
|
|
size = 1;
|
|
return size * 4;
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// Updates fog of war transitions and uploads texture changes to GPU if required
|
|
/// </summary>
|
|
public void UpdateFogOfWar(bool forceUpload = false) {
|
|
if (!enableFogOfWar || _fogOfWarTexture == null)
|
|
return;
|
|
|
|
if (forceUpload) {
|
|
requiresTextureUpload = true;
|
|
}
|
|
|
|
int tw = _fogOfWarTexture.width;
|
|
for (int k = 0; k <= lastTransitionPos; k++) {
|
|
FogOfWarTransition fw = fowTransitionList[k];
|
|
if (!fw.enabled)
|
|
continue;
|
|
float elapsed = Time.time - fw.startTime - fw.startDelay;
|
|
if (elapsed > 0) {
|
|
float t = fw.duration <= 0 ? 1 : elapsed / fw.duration;
|
|
if (t < 0) t = 0; else if (t > 1f) t = 1f;
|
|
int alpha = (int)(fw.initialAlpha + (fw.targetAlpha - fw.initialAlpha) * t);
|
|
int colorPos = fw.y * tw + fw.x;
|
|
fogOfWarColorBuffer[colorPos].a = (byte)alpha;
|
|
requiresTextureUpload = true;
|
|
if (t >= 1f) {
|
|
fowTransitionList[k].enabled = false;
|
|
// Add refill slot if needed
|
|
if (fw.targetAlpha < 255 && fogOfWarRestoreDelay > 0) {
|
|
AddFogOfWarTransitionSlot(fw.x, fw.y, (byte)fw.targetAlpha, 255, fogOfWarRestoreDelay, fogOfWarRestoreDuration);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (requiresTextureUpload) {
|
|
requiresTextureUpload = false;
|
|
_fogOfWarTexture.SetPixels32(fogOfWarColorBuffer);
|
|
_fogOfWarTexture.Apply();
|
|
|
|
// Smooth texture
|
|
if (fogOfWarBlur) {
|
|
SetFowBlurTexture();
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
if (!Application.isPlaying) {
|
|
UnityEditor.EditorUtility.SetDirty(_fogOfWarTexture);
|
|
}
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
if (fogOfWarIsLocal) {
|
|
UpdateFogOfWarMaterialBoundsProperties();
|
|
}
|
|
}
|
|
|
|
void SetFowBlurTexture() {
|
|
if (fowBlur == null) {
|
|
fowBlur = new Material(Shader.Find("VolumetricFog2/FoWBlur"));
|
|
fowBlur.hideFlags = HideFlags.DontSave;
|
|
}
|
|
if (fowBlur == null)
|
|
return;
|
|
|
|
if (fowBlur1 == null || fowBlur1.width != _fogOfWarTexture.width || fowBlur2 == null || fowBlur2.width != _fogOfWarTexture.width) {
|
|
CreateFoWBlurRTs();
|
|
}
|
|
fowBlur1.DiscardContents();
|
|
Graphics.Blit(_fogOfWarTexture, fowBlur1, fowBlur, 0);
|
|
fowBlur2.DiscardContents();
|
|
Graphics.Blit(fowBlur1, fowBlur2, fowBlur, 1);
|
|
fogMat.SetTexture(ShaderParams.FogOfWarTexture, fowBlur2);
|
|
}
|
|
|
|
|
|
void CreateFoWBlurRTs() {
|
|
if (fowBlur1 != null) {
|
|
fowBlur1.Release();
|
|
}
|
|
if (fowBlur2 != null) {
|
|
fowBlur2.Release();
|
|
}
|
|
RenderTextureDescriptor desc = new RenderTextureDescriptor(_fogOfWarTexture.width, _fogOfWarTexture.height, RenderTextureFormat.ARGB32, 0);
|
|
fowBlur1 = new RenderTexture(desc);
|
|
fowBlur2 = new RenderTexture(desc);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Instantly changes the alpha value of the fog of war at world position. It takes into account FogOfWarCenter and FogOfWarSize.
|
|
/// Note that only x and z coordinates are used. Y (vertical) coordinate is ignored.
|
|
/// </summary>
|
|
/// <param name="worldPosition">in world space coordinates.</param>
|
|
/// <param name="radius">radius of application in world units.</param>
|
|
/// <param name="fogNewAlpha">target alpha value.</param>
|
|
public void SetFogOfWarAlpha(Vector3 worldPosition, float radius, float fogNewAlpha) {
|
|
SetFogOfWarAlpha(worldPosition, radius, fogNewAlpha, 1f);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Changes the alpha value of the fog of war at world position creating a transition from current alpha value to specified target alpha. It takes into account FogOfWarCenter and FogOfWarSize.
|
|
/// Note that only x and z coordinates are used. Y (vertical) coordinate is ignored.
|
|
/// </summary>
|
|
/// <param name="worldPosition">in world space coordinates.</param>
|
|
/// <param name="radius">radius of application in world units.</param>
|
|
/// <param name="fogNewAlpha">target alpha value.</param>
|
|
/// <param name="duration">duration of transition in seconds (0 = apply fogNewAlpha instantly).</param>
|
|
public void SetFogOfWarAlpha(Vector3 worldPosition, float radius, float fogNewAlpha, float duration) {
|
|
SetFogOfWarAlpha(worldPosition, radius, fogNewAlpha, true, duration, fogOfWarSmoothness, fogOfWarRestoreDelay, fogOfWarRestoreDuration);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Changes the alpha value of the fog of war at world position creating a transition from current alpha value to specified target alpha. It takes into account FogOfWarCenter and FogOfWarSize.
|
|
/// Note that only x and z coordinates are used. Y (vertical) coordinate is ignored.
|
|
/// </summary>
|
|
/// <param name="worldPosition">in world space coordinates.</param>
|
|
/// <param name="radius">radius of application in world units.</param>
|
|
/// <param name="fogNewAlpha">target alpha value.</param>
|
|
/// <param name="duration">duration of transition in seconds (0 = apply fogNewAlpha instantly).</param>
|
|
/// <param name="smoothness">border smoothness.</param>
|
|
public void SetFogOfWarAlpha(Vector3 worldPosition, float radius, float fogNewAlpha, float duration, float smoothness) {
|
|
SetFogOfWarAlpha(worldPosition, radius, fogNewAlpha, true, duration, smoothness, fogOfWarRestoreDelay, fogOfWarRestoreDuration);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Changes the alpha value of the fog of war at world position creating a transition from current alpha value to specified target alpha. It takes into account FogOfWarCenter and FogOfWarSize.
|
|
/// Note that only x and z coordinates are used. Y (vertical) coordinate is ignored.
|
|
/// </summary>
|
|
/// <param name="worldPosition">in world space coordinates.</param>
|
|
/// <param name="radius">radius of application in world units.</param>
|
|
/// <param name="fogNewAlpha">target alpha value.</param>
|
|
/// <param name="blendAlpha">if new alpha is combined with preexisting alpha value or replaced.</param>
|
|
/// <param name="duration">duration of transition in seconds (0 = apply fogNewAlpha instantly).</param>
|
|
/// <param name="smoothness">border smoothness.</param>
|
|
/// <param name="restoreDelay">delay before the fog alpha is restored. Pass 0 to keep change forever.</param>
|
|
/// <param name="restoreDuration">restore duration in seconds.</param>
|
|
public void SetFogOfWarAlpha(Vector3 worldPosition, float radius, float fogNewAlpha, bool blendAlpha, float duration, float smoothness, float restoreDelay, float restoreDuration) {
|
|
if (_fogOfWarTexture == null || fogOfWarColorBuffer == null || fogOfWarColorBuffer.Length == 0)
|
|
return;
|
|
|
|
Vector3 fogOfWarCenter = anchoredFogOfWarCenter;
|
|
|
|
float tx = (worldPosition.x - fogOfWarCenter.x) / fogOfWarSize.x + 0.5f;
|
|
if (tx < 0 || tx > 1f)
|
|
return;
|
|
float tz = (worldPosition.z - fogOfWarCenter.z) / fogOfWarSize.z + 0.5f;
|
|
if (tz < 0 || tz > 1f)
|
|
return;
|
|
|
|
int tw = _fogOfWarTexture.width;
|
|
int th = _fogOfWarTexture.height;
|
|
int px = (int)(tx * tw);
|
|
int pz = (int)(tz * th);
|
|
float sm = 0.0001f + smoothness;
|
|
byte newAlpha8 = (byte)(fogNewAlpha * 255);
|
|
float tr = radius / fogOfWarSize.z;
|
|
int delta = (int)(th * tr);
|
|
int deltaSqr = delta * delta;
|
|
for (int r = pz - delta; r <= pz + delta; r++) {
|
|
if (r > 0 && r < th - 1) {
|
|
for (int c = px - delta; c <= px + delta; c++) {
|
|
if (c > 0 && c < tw - 1) {
|
|
int distanceSqr = (pz - r) * (pz - r) + (px - c) * (px - c);
|
|
if (distanceSqr <= deltaSqr) {
|
|
int colorBufferPos = r * tw + c;
|
|
Color32 colorBuffer = fogOfWarColorBuffer[colorBufferPos];
|
|
if (!blendAlpha) {
|
|
colorBuffer.a = 255;
|
|
}
|
|
distanceSqr = deltaSqr - distanceSqr;
|
|
float t = (float)distanceSqr / (deltaSqr * sm);
|
|
t = 1f - t;
|
|
if (t < 0) {
|
|
t = 0;
|
|
} else if (t > 1f) {
|
|
t = 1f;
|
|
}
|
|
byte targetAlpha = (byte)(newAlpha8 + (colorBuffer.a - newAlpha8) * t);
|
|
if (targetAlpha < 255) {
|
|
if (duration > 0) {
|
|
AddFogOfWarTransitionSlot(c, r, colorBuffer.a, targetAlpha, 0, duration);
|
|
} else {
|
|
colorBuffer.a = targetAlpha;
|
|
fogOfWarColorBuffer[colorBufferPos] = colorBuffer;
|
|
requiresTextureUpload = true;
|
|
if (restoreDelay > 0) {
|
|
AddFogOfWarTransitionSlot(c, r, targetAlpha, 255, restoreDelay, restoreDuration);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Changes the alpha value of the fog of war within bounds creating a transition from current alpha value to specified target alpha. It takes into account FogOfWarCenter and FogOfWarSize.
|
|
/// Note that only x and z coordinates are used. Y (vertical) coordinate is ignored.
|
|
/// </summary>
|
|
/// <param name="bounds">in world space coordinates.</param>
|
|
/// <param name="fogNewAlpha">target alpha value.</param>
|
|
/// <param name="duration">duration of transition in seconds (0 = apply fogNewAlpha instantly).</param>
|
|
public void SetFogOfWarAlpha(Bounds bounds, float fogNewAlpha, float duration) {
|
|
SetFogOfWarAlpha(bounds, fogNewAlpha, true, duration, fogOfWarSmoothness, fogOfWarRestoreDelay, fogOfWarRestoreDuration);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Changes the alpha value of the fog of war within bounds creating a transition from current alpha value to specified target alpha. It takes into account FogOfWarCenter and FogOfWarSize.
|
|
/// Note that only x and z coordinates are used. Y (vertical) coordinate is ignored.
|
|
/// </summary>
|
|
/// <param name="bounds">in world space coordinates.</param>
|
|
/// <param name="fogNewAlpha">target alpha value.</param>
|
|
/// <param name="duration">duration of transition in seconds (0 = apply fogNewAlpha instantly).</param>
|
|
/// <param name="smoothness">border smoothness.</param>
|
|
public void SetFogOfWarAlpha(Bounds bounds, float fogNewAlpha, float duration, float smoothness) {
|
|
SetFogOfWarAlpha(bounds, fogNewAlpha, true, duration, smoothness, fogOfWarRestoreDelay, fogOfWarRestoreDuration);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Changes the alpha value of the fog of war within bounds creating a transition from current alpha value to specified target alpha. It takes into account FogOfWarCenter and FogOfWarSize.
|
|
/// Note that only x and z coordinates are used. Y (vertical) coordinate is ignored.
|
|
/// </summary>
|
|
/// <param name="bounds">in world space coordinates.</param>
|
|
/// <param name="fogNewAlpha">target alpha value.</param>
|
|
/// <param name="blendAlpha">if new alpha is combined with preexisting alpha value or replaced.</param>
|
|
/// <param name="duration">duration of transition in seconds (0 = apply fogNewAlpha instantly).</param>
|
|
/// <param name="smoothness">border smoothness.</param>
|
|
/// <param name="fuzzyness">randomization of border noise.</param>
|
|
/// <param name="restoreDelay">delay before the fog alpha is restored. Pass 0 to keep change forever.</param>
|
|
/// <param name="restoreDuration">restore duration in seconds.</param>
|
|
public void SetFogOfWarAlpha(Bounds bounds, float fogNewAlpha, bool blendAlpha, float duration, float smoothness, float restoreDelay, float restoreDuration) {
|
|
if (_fogOfWarTexture == null || fogOfWarColorBuffer == null || fogOfWarColorBuffer.Length == 0)
|
|
return;
|
|
|
|
Vector3 fogOfWarCenter = anchoredFogOfWarCenter;
|
|
|
|
Vector3 worldPosition = bounds.center;
|
|
float tx = (worldPosition.x - fogOfWarCenter.x) / fogOfWarSize.x + 0.5f;
|
|
if (tx < 0 || tx > 1f)
|
|
return;
|
|
float tz = (worldPosition.z - fogOfWarCenter.z) / fogOfWarSize.z + 0.5f;
|
|
if (tz < 0 || tz > 1f)
|
|
return;
|
|
|
|
int tw = _fogOfWarTexture.width;
|
|
int th = _fogOfWarTexture.height;
|
|
int px = (int)(tx * tw);
|
|
int pz = (int)(tz * th);
|
|
byte newAlpha8 = (byte)(fogNewAlpha * 255);
|
|
float trz = bounds.extents.z / fogOfWarSize.z;
|
|
float trx = bounds.extents.x / fogOfWarSize.x;
|
|
float aspect1 = trx > trz ? 1f : trz / trx;
|
|
float aspect2 = trx > trz ? trx / trz : 1f;
|
|
int deltaz = (int)(th * trz);
|
|
int deltazSqr = deltaz * deltaz;
|
|
int deltax = (int)(tw * trx);
|
|
int deltaxSqr = deltax * deltax;
|
|
float sm = 0.0001f + smoothness;
|
|
for (int r = pz - deltaz; r <= pz + deltaz; r++) {
|
|
if (r > 0 && r < th - 1) {
|
|
int distancezSqr = (pz - r) * (pz - r);
|
|
distancezSqr = deltazSqr - distancezSqr;
|
|
float t1 = (float)distancezSqr * aspect1 / (deltazSqr * sm);
|
|
for (int c = px - deltax; c <= px + deltax; c++) {
|
|
if (c > 0 && c < tw - 1) {
|
|
int distancexSqr = (px - c) * (px - c);
|
|
int colorBufferPos = r * tw + c;
|
|
Color32 colorBuffer = fogOfWarColorBuffer[colorBufferPos];
|
|
if (!blendAlpha) colorBuffer.a = 255;
|
|
distancexSqr = deltaxSqr - distancexSqr;
|
|
float t2 = (float)distancexSqr * aspect2 / (deltaxSqr * sm);
|
|
float t = t1 < t2 ? t1 : t2;
|
|
t = 1f - t;
|
|
if (t < 0) t = 0; else if (t > 1f) t = 1f;
|
|
byte targetAlpha = (byte)(newAlpha8 + (colorBuffer.a - newAlpha8) * t); // Mathf.Lerp(newAlpha8, colorBuffer.a, t);
|
|
if (targetAlpha < 255) {
|
|
if (duration > 0) {
|
|
AddFogOfWarTransitionSlot(c, r, colorBuffer.a, targetAlpha, 0, duration);
|
|
} else {
|
|
colorBuffer.a = targetAlpha;
|
|
fogOfWarColorBuffer[colorBufferPos] = colorBuffer;
|
|
requiresTextureUpload = true;
|
|
if (restoreDelay > 0) {
|
|
AddFogOfWarTransitionSlot(c, r, targetAlpha, 255, restoreDelay, restoreDuration);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Restores fog of war to full opacity
|
|
/// </summary>
|
|
/// <param name="worldPosition">World position.</param>
|
|
/// <param name="radius">Radius.</param>
|
|
public void ResetFogOfWarAlpha(Vector3 worldPosition, float radius) {
|
|
if (_fogOfWarTexture == null || fogOfWarColorBuffer == null || fogOfWarColorBuffer.Length == 0)
|
|
return;
|
|
|
|
Vector3 fogOfWarCenter = anchoredFogOfWarCenter;
|
|
|
|
float tx = (worldPosition.x - fogOfWarCenter.x) / fogOfWarSize.x + 0.5f;
|
|
if (tx < 0 || tx > 1f)
|
|
return;
|
|
float tz = (worldPosition.z - fogOfWarCenter.z) / fogOfWarSize.z + 0.5f;
|
|
if (tz < 0 || tz > 1f)
|
|
return;
|
|
|
|
int tw = _fogOfWarTexture.width;
|
|
int th = _fogOfWarTexture.height;
|
|
int px = (int)(tx * tw);
|
|
int pz = (int)(tz * th);
|
|
float tr = radius / fogOfWarSize.z;
|
|
int delta = (int)(th * tr);
|
|
int deltaSqr = delta * delta;
|
|
for (int r = pz - delta; r <= pz + delta; r++) {
|
|
if (r > 0 && r < th - 1) {
|
|
for (int c = px - delta; c <= px + delta; c++) {
|
|
if (c > 0 && c < tw - 1) {
|
|
int distanceSqr = (pz - r) * (pz - r) + (px - c) * (px - c);
|
|
if (distanceSqr <= deltaSqr) {
|
|
int colorBufferPos = r * tw + c;
|
|
Color32 colorBuffer = fogOfWarColorBuffer[colorBufferPos];
|
|
colorBuffer.a = 255;
|
|
fogOfWarColorBuffer[colorBufferPos] = colorBuffer;
|
|
requiresTextureUpload = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// Restores fog of war to full opacity
|
|
/// </summary>
|
|
public void ResetFogOfWarAlpha(Bounds bounds) {
|
|
ResetFogOfWarAlpha(bounds.center, bounds.extents.x, bounds.extents.z);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Restores fog of war to full opacity
|
|
/// </summary>
|
|
public void ResetFogOfWarAlpha(Vector3 position, Vector3 size) {
|
|
ResetFogOfWarAlpha(position, size.x * 0.5f, size.z * 0.5f);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Restores fog of war to full opacity
|
|
/// </summary>
|
|
/// <param name="position">Position in world space.</param>
|
|
/// <param name="extentsX">Half of the length of the rectangle in X-Axis.</param>
|
|
/// <param name="extentsZ">Half of the length of the rectangle in Z-Axis.</param>
|
|
public void ResetFogOfWarAlpha(Vector3 position, float extentsX, float extentsZ) {
|
|
if (_fogOfWarTexture == null || fogOfWarColorBuffer == null || fogOfWarColorBuffer.Length == 0)
|
|
return;
|
|
|
|
Vector3 fogOfWarCenter = anchoredFogOfWarCenter;
|
|
|
|
float tx = (position.x - fogOfWarCenter.x) / fogOfWarSize.x + 0.5f;
|
|
if (tx < 0 || tx > 1f)
|
|
return;
|
|
float tz = (position.z - fogOfWarCenter.z) / fogOfWarSize.z + 0.5f;
|
|
if (tz < 0 || tz > 1f)
|
|
return;
|
|
|
|
int tw = _fogOfWarTexture.width;
|
|
int th = _fogOfWarTexture.height;
|
|
int px = (int)(tx * tw);
|
|
int pz = (int)(tz * th);
|
|
float trz = extentsZ / fogOfWarSize.z;
|
|
float trx = extentsX / fogOfWarSize.x;
|
|
int deltaz = (int)(th * trz);
|
|
int deltax = (int)(tw * trx);
|
|
for (int r = pz - deltaz; r <= pz + deltaz; r++) {
|
|
if (r > 0 && r < th - 1) {
|
|
for (int c = px - deltax; c <= px + deltax; c++) {
|
|
if (c > 0 && c < tw - 1) {
|
|
int colorBufferPos = r * tw + c;
|
|
Color32 colorBuffer = fogOfWarColorBuffer[colorBufferPos];
|
|
colorBuffer.a = 255;
|
|
fogOfWarColorBuffer[colorBufferPos] = colorBuffer;
|
|
requiresTextureUpload = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public void ResetFogOfWar(byte alpha = 255) {
|
|
if (_fogOfWarTexture == null)
|
|
return;
|
|
int h = _fogOfWarTexture.height;
|
|
int w = _fogOfWarTexture.width;
|
|
int newLength = h * w;
|
|
if (fogOfWarColorBuffer == null || fogOfWarColorBuffer.Length != newLength) {
|
|
fogOfWarColorBuffer = new Color32[newLength];
|
|
}
|
|
Color32 opaque = new Color32(255, 255, 255, alpha);
|
|
for (int k = 0; k < newLength; k++) {
|
|
fogOfWarColorBuffer[k] = opaque;
|
|
}
|
|
_fogOfWarTexture.SetPixels32(fogOfWarColorBuffer);
|
|
_fogOfWarTexture.Apply();
|
|
lastTransitionPos = -1;
|
|
fowTransitionIndices.Clear();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or set fog of war state as a Color32 buffer. The alpha channel stores the transparency of the fog at that position (0 = no fog, 1 = opaque).
|
|
/// </summary>
|
|
public Color32[] fogOfWarTextureData {
|
|
get {
|
|
return fogOfWarColorBuffer;
|
|
}
|
|
set {
|
|
enableFogOfWar = true;
|
|
fogOfWarColorBuffer = value;
|
|
if (value == null || _fogOfWarTexture == null)
|
|
return;
|
|
if (value.Length != _fogOfWarTexture.width * _fogOfWarTexture.height)
|
|
return;
|
|
_fogOfWarTexture.SetPixels32(fogOfWarColorBuffer);
|
|
_fogOfWarTexture.Apply();
|
|
}
|
|
}
|
|
|
|
void AddFogOfWarTransitionSlot(int x, int y, byte initialAlpha, byte targetAlpha, float delay, float duration) {
|
|
|
|
// Check if this slot exists
|
|
int index;
|
|
int key = y * 64000 + x;
|
|
|
|
if (!fowTransitionIndices.TryGetValue(key, out index)) {
|
|
index = -1;
|
|
for (int k = 0; k <= lastTransitionPos; k++) {
|
|
if (!fowTransitionList[k].enabled) {
|
|
index = k;
|
|
fowTransitionIndices[key] = index;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (index >= 0) {
|
|
if (fowTransitionList[index].enabled && (fowTransitionList[index].x != x || fowTransitionList[index].y != y)) {
|
|
index = -1;
|
|
}
|
|
}
|
|
|
|
if (index < 0) {
|
|
if (lastTransitionPos >= MAX_SIMULTANEOUS_TRANSITIONS - 1)
|
|
return;
|
|
index = ++lastTransitionPos;
|
|
fowTransitionIndices[key] = index;
|
|
} else if (fowTransitionList[index].enabled) return; // ongoing transition
|
|
|
|
fowTransitionList[index].x = x;
|
|
fowTransitionList[index].y = y;
|
|
fowTransitionList[index].duration = duration;
|
|
fowTransitionList[index].startTime = Time.time;
|
|
fowTransitionList[index].startDelay = delay;
|
|
fowTransitionList[index].initialAlpha = initialAlpha;
|
|
fowTransitionList[index].targetAlpha = targetAlpha;
|
|
fowTransitionList[index].enabled = true;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Gets the current alpha value of the Fog of War at a given world position
|
|
/// </summary>
|
|
/// <returns>The fog of war alpha.</returns>
|
|
/// <param name="worldPosition">World position.</param>
|
|
public float GetFogOfWarAlpha(Vector3 worldPosition) {
|
|
if (fogOfWarColorBuffer == null || fogOfWarColorBuffer.Length == 0 || _fogOfWarTexture == null)
|
|
return 1f;
|
|
|
|
float tx = (worldPosition.x - fogOfWarCenter.x) / fogOfWarSize.x + 0.5f;
|
|
if (tx < 0 || tx > 1f)
|
|
return 1f;
|
|
float tz = (worldPosition.z - fogOfWarCenter.z) / fogOfWarSize.z + 0.5f;
|
|
if (tz < 0 || tz > 1f)
|
|
return 1f;
|
|
|
|
int tw = _fogOfWarTexture.width;
|
|
int th = _fogOfWarTexture.height;
|
|
int px = (int)(tx * tw);
|
|
int pz = (int)(tz * th);
|
|
int colorBufferPos = pz * tw + px;
|
|
if (colorBufferPos < 0 || colorBufferPos >= fogOfWarColorBuffer.Length)
|
|
return 1f;
|
|
return fogOfWarColorBuffer[colorBufferPos].a / 255f;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
} |