Files
2024-11-20 15:21:28 +01:00

236 lines
11 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Gaia
{
public class ImageProcessing
{
//static RenderTexture outputTexture;
/// <summary>
/// Applies a stack of masks in an array on a render texture to get a combined final mask.
/// </summary>
/// <param name="inputTexture">The input render texture</param>
/// <param name="maskStack">An array of ImageMasks to apply on the render texture.</param>
/// <returns>A modified render texture containing the results of the filter application.</returns>
public static RenderTexture ApplyMaskStack(RenderTexture inputTexture, RenderTexture outputTexture, ImageMask[] maskStack, ImageMaskInfluence influence)
{
//early out if no valid masks to begin with
if (maskStack.Where(x=>x.m_active==true && x.m_influence == influence).Count() <=0)
{
return inputTexture;
}
//RenderTexture outputTexture = null;
for (int i = 0; i < maskStack.Length; i++)
{
if (maskStack[i].m_active && maskStack[i].m_influence == influence)
{
////release old texture first before assigning new
//if (outputTexture != null)
//{
// RenderTexture.ReleaseTemporary(outputTexture);
// outputTexture = null;
//}
outputTexture = maskStack[i].Apply(inputTexture, outputTexture);
//Write the result texture back to the input so we can use the result
//of this iteration as input for the next, and so on.
Graphics.Blit(outputTexture, inputTexture);
}
}
if (inputTexture != null)
{
RenderTexture.ReleaseTemporary(inputTexture);
inputTexture = null;
}
return outputTexture;
}
///// <summary>
///// Outputs a Render texture as a .png file
///// </summary>
///// <param name="path">The path to write to, including filename ending in .exr</param>
///// <param name="sourceRenderTexture">The render texture to export</param>
//public static void WriteRenderTexturePNG(string path, RenderTexture sourceRenderTexture, TextureFormat textureFormat = TextureFormat.RGBA32)
//{
// RenderTexture origTex = RenderTexture.active;
// RenderTexture.active = sourceRenderTexture;
// Texture2D exportTexture = new Texture2D(RenderTexture.active.width, RenderTexture.active.height, textureFormat, false);
// exportTexture.ReadPixels(new Rect(0, 0, RenderTexture.active.width, RenderTexture.active.height), 0, 0);
// exportTexture.Apply();
// byte[] exrBytes = ImageConversion.EncodeToPNG(exportTexture);
// PWCommon3.Utils.WriteAllBytes(path, exrBytes);
// RenderTexture.active = origTex;
//}
/// <summary>
/// Outputs a Render texture to different file formats (default: .exr file)
/// </summary>
/// <param name="path">The path to write to, without the extension. The correct extension will be added according to the chosen imageFileType.</param>
/// <param name="sourceRenderTexture">The render texture to export</param>
/// <param name="imageFileType">Image File Type: EXR, PNG, TGA or JPG</param>
/// <param name="textureFormat">Texture color format for the temporary Texture 2D to read the render texture into for exporting.</param>
/// <returns>The full path to the exported file.</returns>
public static string WriteRenderTexture(string path, RenderTexture sourceRenderTexture, GaiaConstants.ImageFileType imageFileType = GaiaConstants.ImageFileType.Exr, TextureFormat textureFormat = TextureFormat.RGBAFloat, int jpgQuality=75)
{
RenderTexture origTex = RenderTexture.active;
RenderTexture.active = sourceRenderTexture;
Texture2D exportTexture = new Texture2D(RenderTexture.active.width, RenderTexture.active.height, textureFormat, false);
exportTexture.ReadPixels(new Rect(0, 0, RenderTexture.active.width, RenderTexture.active.height), 0, 0);
exportTexture.Apply();
byte[] fileBytes = new byte[0];
string extension = ".file";
switch(imageFileType)
{
case GaiaConstants.ImageFileType.Exr:
fileBytes = ImageConversion.EncodeToEXR(exportTexture, Texture2D.EXRFlags.CompressZIP);
extension = ".exr";
break;
case GaiaConstants.ImageFileType.Png:
fileBytes = ImageConversion.EncodeToPNG(exportTexture);
extension = ".png";
break;
case GaiaConstants.ImageFileType.Tga:
fileBytes = ImageConversion.EncodeToTGA(exportTexture);
extension = ".tga";
break;
case GaiaConstants.ImageFileType.Jpg:
fileBytes = ImageConversion.EncodeToJPG(exportTexture,jpgQuality);
extension = ".jpg";
break;
}
path += extension;
PWCommon3.Utils.WriteAllBytes(path, fileBytes);
RenderTexture.active = origTex;
return path;
}
/// <summary>
/// Outputs a Texture2D as a .png file for debug purposes
/// </summary>
/// <param name="path">The path to write to, including filename ending in .png</param>
/// <param name="sourceTexture">The texture to export</param>
public static void WriteTexture2D(string path, Texture2D sourceTexture)
{
byte[] exrBytes = ImageConversion.EncodeToPNG(sourceTexture);
if (exrBytes != null)
{
PWCommon3.Utils.WriteAllBytes(path, exrBytes);
}
}
public static void BakeMaskStack(ImageMask[] maskStack, Terrain terrain, Transform transform, float range, int resolution, string path)
{
//Simulate an operation to allow the image masks to acces the current terrain data
GaiaMultiTerrainOperation operation = new GaiaMultiTerrainOperation(terrain, transform, range);
operation.GetHeightmap();
operation.GetNormalmap();
operation.CollectTerrainBakedMasks();
float maxCurrentTerrainHeight = 0f;
float minCurrentTerrainHeight = 0f;
float seaLevel;
GaiaSessionManager gsm = GaiaSessionManager.GetSessionManager();
gsm.GetWorldMinMax(ref minCurrentTerrainHeight, ref maxCurrentTerrainHeight);
seaLevel = gsm.GetSeaLevel();
if (maskStack.Length > 0)
{
//We start from a white texture, so we need the first mask action in the stack to always be "Multiply", otherwise there will be no result.
maskStack[0].m_blendMode = ImageMaskBlendMode.Multiply;
//Iterate through all image masks and set up the required data that masks might need to function properly
foreach (ImageMask mask in maskStack)
{
//mask.m_heightmapContext = heightmapContext;
//mask.m_normalmapContext = normalmapContext;
//mask.m_collisionContext = collisionContext;
mask.m_multiTerrainOperation = operation;
mask.m_seaLevel = seaLevel;
mask.m_maxWorldHeight = maxCurrentTerrainHeight;
mask.m_minWorldHeight = minCurrentTerrainHeight;
}
}
RenderTexture inputTexture = RenderTexture.GetTemporary(resolution, resolution, 0,RenderTextureFormat.ARGBFloat);
RenderTexture currentRT = RenderTexture.active;
RenderTexture.active = inputTexture;
GL.Clear(true, true, Color.white);
RenderTexture.active = currentRT;
RenderTexture localOutputTexture = RenderTexture.GetTemporary(inputTexture.descriptor);
RenderTexture globalOutputTexture = RenderTexture.GetTemporary(inputTexture.descriptor);
localOutputTexture = ImageProcessing.ApplyMaskStack(inputTexture, localOutputTexture, maskStack, ImageMaskInfluence.Local);
globalOutputTexture = ImageProcessing.ApplyMaskStack(localOutputTexture, globalOutputTexture, maskStack, ImageMaskInfluence.Global);
WriteRenderTexture(path, globalOutputTexture);
RenderTexture.ReleaseTemporary(inputTexture);
RenderTexture.ReleaseTemporary(localOutputTexture);
RenderTexture.ReleaseTemporary(globalOutputTexture);
inputTexture = null;
localOutputTexture = null;
globalOutputTexture = null;
operation.CloseOperation();
}
public static Texture2D CreateMaskCurveTexture(ref Texture2D inputCurveTexture)
{
if (inputCurveTexture == null)
{
TextureFormat format = TextureFormat.RGB24;
if (SystemInfo.SupportsTextureFormat(TextureFormat.RFloat))
format = TextureFormat.RFloat;
else if (SystemInfo.SupportsTextureFormat(TextureFormat.RHalf))
format = TextureFormat.RHalf;
inputCurveTexture = new Texture2D(256, 1, format, false, true)
{
name = "Height Mask curve texture",
wrapMode = TextureWrapMode.Clamp,
filterMode = FilterMode.Bilinear,
anisoLevel = 0,
hideFlags = HideFlags.DontSave
};
}
return inputCurveTexture;
}
/// <summary>
/// Bakes an animation curve into a 1 pixel high black and white texture that represents the curve contents. This texture is intended for use as shader input
/// </summary>
/// <param name="animationCurve">The curve to bake.</param>
/// <param name="bakedTexture">The Texture2D to bake into, needs to be 1 pixel high, 256 pixels wide.</param>
public static void BakeCurveTexture(AnimationCurve animationCurve, Texture2D bakedTexture)
{
if (animationCurve != null && animationCurve.length > 0)
{
float range = animationCurve[animationCurve.length - 1].time;
for (float i = 0f; i <= 1f; i += 1f / 255f)
{
float c = animationCurve.Evaluate(i * range);
bakedTexture.SetPixel(Mathf.FloorToInt(i * 255f), 0, new Color(c, c, c));
}
bakedTexture.Apply();
}
}
}
}