//#define PB_RENDER_PICKER_TEXTURE
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using UObject = UnityEngine.Object;
namespace UnityEngine.ProBuilder
{
///
/// A collection of settings defining how mesh element picking behaves.
///
public struct PickerOptions
{
///
/// Should depth testing be performed when hit testing elements?
/// Enable to select only visible elements, disable to select all elements regardless of visibility.
///
public bool depthTest { get; set; }
///
/// Require elements to be completely encompassed by the rect selection (Complete) or only touched (Partial).
///
///
/// Does not apply to vertex picking.
///
public RectSelectMode rectSelectMode { get; set; }
static readonly PickerOptions k_Default = new PickerOptions()
{
depthTest = true,
rectSelectMode = RectSelectMode.Partial,
};
///
/// A set of options with default values.
///
public static PickerOptions Default
{
get { return k_Default; }
}
public override bool Equals(object obj)
{
if (!(obj is PickerOptions))
return false;
return Equals((PickerOptions)obj);
}
public bool Equals(PickerOptions other)
{
return depthTest == other.depthTest && rectSelectMode == other.rectSelectMode;
}
public override int GetHashCode()
{
unchecked
{
return (depthTest.GetHashCode() * 397) ^ (int)rectSelectMode;
}
}
public static bool operator==(PickerOptions a, PickerOptions b)
{
return a.Equals(b);
}
public static bool operator!=(PickerOptions a, PickerOptions b)
{
return !a.Equals(b);
}
}
///
/// Functions for picking elements in a view by rendering a picker texture and testing pixels.
///
static class SelectionPickerRenderer
{
const string k_FacePickerOcclusionTintUniform = "_Tint";
static readonly Color k_Blackf = new Color(0f, 0f, 0f, 1f);
static readonly Color k_Whitef = new Color(1f, 1f, 1f, 1f);
const uint k_PickerHashNone = 0x00;
const uint k_PickerHashMin = 0x1;
const uint k_PickerHashMax = 0x00FFFFFF;
const uint k_MinEdgePixelsForValidSelection = 1;
static bool s_Initialized = false;
static RenderTextureFormat renderTextureFormat
{
get
{
if (s_Initialized)
return s_RenderTextureFormat;
s_Initialized = true;
for (int i = 0; i < s_PreferredFormats.Length; i++)
{
if (SystemInfo.SupportsRenderTextureFormat(s_PreferredFormats[i]))
{
s_RenderTextureFormat = s_PreferredFormats[i];
break;
}
}
return s_RenderTextureFormat;
}
}
static TextureFormat textureFormat { get { return TextureFormat.ARGB32; } }
static RenderTextureFormat s_RenderTextureFormat = RenderTextureFormat.Default;
static RenderTextureFormat[] s_PreferredFormats = new RenderTextureFormat[]
{
#if UNITY_5_6
RenderTextureFormat.ARGBFloat,
RenderTextureFormat.ARGB32,
#else
RenderTextureFormat.ARGB32,
RenderTextureFormat.ARGBFloat,
#endif
};
///
/// Given a camera and selection rect (in screen space) return a Dictionary containing the number of faces touched by the rect.
///
///
///
///
///
///
///
public static Dictionary> PickFacesInRect(
Camera camera,
Rect pickerRect,
IList selection,
int renderTextureWidth = -1,
int renderTextureHeight = -1)
{
Dictionary> map;
Texture2D tex = RenderSelectionPickerTexture(camera, selection, out map, renderTextureWidth, renderTextureHeight);
Color32[] pix = tex.GetPixels32();
int ox = System.Math.Max(0, Mathf.FloorToInt(pickerRect.x));
int oy = System.Math.Max(0, Mathf.FloorToInt((tex.height - pickerRect.y) - pickerRect.height));
int imageWidth = tex.width;
int imageHeight = tex.height;
int width = Mathf.FloorToInt(pickerRect.width);
int height = Mathf.FloorToInt(pickerRect.height);
UObject.DestroyImmediate(tex);
Dictionary> selected = new Dictionary>();
SimpleTuple hit;
HashSet faces = null;
HashSet used = new HashSet();
#if PB_RENDER_PICKER_TEXTURE
List rectImg = new List();
#endif
for (int y = oy; y < System.Math.Min(oy + height, imageHeight); y++)
{
for (int x = ox; x < System.Math.Min(ox + width, imageWidth); x++)
{
#if PB_RENDER_PICKER_TEXTURE
rectImg.Add(pix[y * imageWidth + x]);
#endif
uint v = SelectionPickerRenderer.DecodeRGBA(pix[y * imageWidth + x]);
if (used.Add(v) && map.TryGetValue(v, out hit))
{
if (selected.TryGetValue(hit.item1, out faces))
faces.Add(hit.item2);
else
selected.Add(hit.item1, new HashSet() { hit.item2 });
}
}
}
#if PB_RENDER_PICKER_TEXTURE
if (width > 0 && height > 0)
{
// Debug.Log("used: \n" + used.Select(x => string.Format("{0} ({1})", x, EncodeRGBA(x))).ToString("\n"));
Texture2D img = new Texture2D(width, height);
img.SetPixels(rectImg.ToArray());
img.Apply();
byte[] bytes = img.EncodeToPNG();
System.IO.File.WriteAllBytes("Assets/rect.png", bytes);
#if UNITY_EDITOR
UnityEditor.AssetDatabase.Refresh();
#endif
UObject.DestroyImmediate(img);
}
#endif
return selected;
}
///
/// Select vertex indexes contained within a rect.
///
///
///
///
///
///
///
/// A dictionary of pb_Object selected vertex indexes.
public static Dictionary> PickVerticesInRect(
Camera camera,
Rect pickerRect,
IList selection,
bool doDepthTest,
int renderTextureWidth = -1,
int renderTextureHeight = -1)
{
Dictionary> map;
Dictionary> selected = new Dictionary>();
#if PB_RENDER_PICKER_TEXTURE
List rectImg = new List();
#endif
Texture2D tex = RenderSelectionPickerTexture(camera, selection, doDepthTest, out map, renderTextureWidth, renderTextureHeight);
Color32[] pix = tex.GetPixels32();
int ox = System.Math.Max(0, Mathf.FloorToInt(pickerRect.x));
int oy = System.Math.Max(0, Mathf.FloorToInt((tex.height - pickerRect.y) - pickerRect.height));
int imageWidth = tex.width;
int imageHeight = tex.height;
int width = Mathf.FloorToInt(pickerRect.width);
int height = Mathf.FloorToInt(pickerRect.height);
UObject.DestroyImmediate(tex);
SimpleTuple hit;
HashSet indexes = null;
HashSet used = new HashSet();
for (int y = oy; y < System.Math.Min(oy + height, imageHeight); y++)
{
for (int x = ox; x < System.Math.Min(ox + width, imageWidth); x++)
{
uint v = DecodeRGBA(pix[y * imageWidth + x]);
#if PB_RENDER_PICKER_TEXTURE
rectImg.Add(pix[y * imageWidth + x]);
#endif
if (used.Add(v) && map.TryGetValue(v, out hit))
{
if (selected.TryGetValue(hit.item1, out indexes))
indexes.Add(hit.item2);
else
selected.Add(hit.item1, new HashSet() { hit.item2 });
}
}
}
var coincidentSelection = new Dictionary>();
// workaround for picking vertices that share a position but are not shared
foreach (var meshSelection in selected)
{
var positions = meshSelection.Key.positionsInternal;
var sharedVertices = meshSelection.Key.sharedVerticesInternal;
var positionHash = new HashSet(meshSelection.Value.Select(x => VectorHash.GetHashCode(positions[sharedVertices[x][0]])));
var collected = new HashSet();
for (int i = 0, c = sharedVertices.Length; i < c; i++)
{
var hash = VectorHash.GetHashCode(positions[sharedVertices[i][0]]);
if (positionHash.Contains(hash))
collected.Add(i);
}
coincidentSelection.Add(meshSelection.Key, collected);
}
selected = coincidentSelection;
#if PB_RENDER_PICKER_TEXTURE
if (width > 0 && height > 0)
{
Texture2D img = new Texture2D(width, height);
img.SetPixels(rectImg.ToArray());
img.Apply();
System.IO.File.WriteAllBytes("Assets/rect_" + s_RenderTextureFormat.ToString() + ".png", img.EncodeToPNG());
#if UNITY_EDITOR
UnityEditor.AssetDatabase.Refresh();
#endif
UObject.DestroyImmediate(img);
}
#endif
return selected;
}
///
/// Select edges touching a rect.
///
///
///
///
///
///
///
/// A dictionary of pb_Object and selected edges.
public static Dictionary> PickEdgesInRect(
Camera camera,
Rect pickerRect,
IList selection,
bool doDepthTest,
int renderTextureWidth = -1,
int renderTextureHeight = -1)
{
var selected = new Dictionary>();
#if PB_RENDER_PICKER_TEXTURE
List rectImg = new List();
#endif
Dictionary> map;
Texture2D tex = RenderSelectionPickerTexture(camera, selection, doDepthTest, out map, renderTextureWidth, renderTextureHeight);
Color32[] pix = tex.GetPixels32();
#if PB_RENDER_PICKER_TEXTURE
System.IO.File.WriteAllBytes("Assets/edge_scene.png", tex.EncodeToPNG());
#endif
int ox = System.Math.Max(0, Mathf.FloorToInt(pickerRect.x));
int oy = System.Math.Max(0, Mathf.FloorToInt((tex.height - pickerRect.y) - pickerRect.height));
int imageWidth = tex.width;
int imageHeight = tex.height;
int width = Mathf.FloorToInt(pickerRect.width);
int height = Mathf.FloorToInt(pickerRect.height);
UObject.DestroyImmediate(tex);
var pixelCount = new Dictionary();
for (int y = oy; y < System.Math.Min(oy + height, imageHeight); y++)
{
for (int x = ox; x < System.Math.Min(ox + width, imageWidth); x++)
{
#if PB_RENDER_PICKER_TEXTURE
rectImg.Add(pix[y * imageWidth + x]);
#endif
uint v = DecodeRGBA(pix[y * imageWidth + x]);
if (v == k_PickerHashNone || v == k_PickerHashMax)
continue;
if (!pixelCount.ContainsKey(v))
pixelCount.Add(v, 1);
else
pixelCount[v] = pixelCount[v] + 1;
}
}
foreach (var kvp in pixelCount)
{
SimpleTuple hit;
if (kvp.Value > k_MinEdgePixelsForValidSelection && map.TryGetValue(kvp.Key, out hit))
{
HashSet edges = null;
if (selected.TryGetValue(hit.item1, out edges))
edges.Add(hit.item2);
else
selected.Add(hit.item1, new HashSet() {hit.item2});
}
}
#if PB_RENDER_PICKER_TEXTURE
if (width > 0 && height > 0)
{
Texture2D img = new Texture2D(width, height);
img.SetPixels(rectImg.ToArray());
img.Apply();
System.IO.File.WriteAllBytes("Assets/edge_rect_" + s_RenderTextureFormat.ToString() + ".png", img.EncodeToPNG());
#if UNITY_EDITOR
UnityEditor.AssetDatabase.Refresh();
#endif
UObject.DestroyImmediate(img);
}
#endif
return selected;
}
///
/// Render the pb_Object selection with the special selection picker shader and return a texture and color -> {object, face} dictionary.
///
///
///
///
///
///
///
static Texture2D RenderSelectionPickerTexture(
Camera camera,
IList selection,
out Dictionary> map,
int width = -1,
int height = -1)
{
var pickerObjects = GenerateFacePickingObjects(selection, out map);
BuiltinMaterials.facePickerMaterial.SetColor(k_FacePickerOcclusionTintUniform, k_Whitef);
Texture2D tex = RenderWithReplacementShader(camera, BuiltinMaterials.selectionPickerShader, "ProBuilderPicker", width, height);
foreach (GameObject go in pickerObjects)
{
UObject.DestroyImmediate(go.GetComponent().sharedMesh);
UObject.DestroyImmediate(go);
}
return tex;
}
///
/// Render the pb_Object selection with the special selection picker shader and return a texture and color -> {object, sharedIndex} dictionary.
///
///
///
///
///
///
///
///
static Texture2D RenderSelectionPickerTexture(
Camera camera,
IList selection,
bool doDepthTest,
out Dictionary> map,
int width = -1,
int height = -1)
{
GameObject[] depthObjects, pickerObjects;
GenerateVertexPickingObjects(selection, doDepthTest, out map, out depthObjects, out pickerObjects);
BuiltinMaterials.facePickerMaterial.SetColor(k_FacePickerOcclusionTintUniform, k_Blackf);
Texture2D tex = RenderWithReplacementShader(camera, BuiltinMaterials.selectionPickerShader, "ProBuilderPicker", width, height);
for (int i = 0, c = pickerObjects.Length; i < c; i++)
{
UObject.DestroyImmediate(pickerObjects[i].GetComponent().sharedMesh);
UObject.DestroyImmediate(pickerObjects[i]);
}
if (doDepthTest)
{
for (int i = 0, c = depthObjects.Length; i < c; i++)
{
UObject.DestroyImmediate(depthObjects[i]);
}
}
return tex;
}
///
/// Render the pb_Object selection with the special selection picker shader and return a texture and color -> {object, edge} dictionary.
///
///
///
///
///
///
///
///
static Texture2D RenderSelectionPickerTexture(
Camera camera,
IList selection,
bool doDepthTest,
out Dictionary> map,
int width = -1,
int height = -1)
{
GameObject[] depthObjects, pickerObjects;
GenerateEdgePickingObjects(selection, doDepthTest, out map, out depthObjects, out pickerObjects);
BuiltinMaterials.facePickerMaterial.SetColor(k_FacePickerOcclusionTintUniform, k_Blackf);
Texture2D tex = RenderWithReplacementShader(camera, BuiltinMaterials.selectionPickerShader, "ProBuilderPicker", width, height);
for (int i = 0, c = pickerObjects.Length; i < c; i++)
{
UObject.DestroyImmediate(pickerObjects[i].GetComponent().sharedMesh);
UObject.DestroyImmediate(pickerObjects[i]);
}
if (doDepthTest)
{
for (int i = 0, c = depthObjects.Length; i < c; i++)
{
UObject.DestroyImmediate(depthObjects[i]);
}
}
return tex;
}
static GameObject[] GenerateFacePickingObjects(
IList selection,
out Dictionary> map)
{
int selectionCount = selection.Count;
GameObject[] pickerObjects = new GameObject[selectionCount];
map = new Dictionary>();
uint index = 0;
for (int i = 0; i < selectionCount; i++)
{
var pb = selection[i];
GameObject go = InternalUtility.EmptyGameObjectWithTransform(pb.transform);
go.name = pb.name + " (Face Depth Test)";
Mesh m = new Mesh();
m.vertices = pb.positionsInternal;
m.triangles = pb.facesInternal.SelectMany(x => x.indexesInternal).ToArray();
Color32[] colors = new Color32[m.vertexCount];
foreach (Face f in pb.facesInternal)
{
Color32 color = EncodeRGBA(index++);
map.Add(DecodeRGBA(color), new SimpleTuple(pb, f));
for (int n = 0; n < f.distinctIndexesInternal.Length; n++)
colors[f.distinctIndexesInternal[n]] = color;
}
m.colors32 = colors;
go.AddComponent().sharedMesh = m;
go.AddComponent().sharedMaterial = BuiltinMaterials.facePickerMaterial;
pickerObjects[i] = go;
}
return pickerObjects;
}
static void GenerateVertexPickingObjects(
IList selection,
bool doDepthTest,
out Dictionary> map,
out GameObject[] depthObjects,
out GameObject[] pickerObjects)
{
map = new Dictionary>();
// don't start at 0 because that means one vertex would be black, matching
// the color used to cull hidden vertices.
uint index = 0x02;
int selectionCount = selection.Count;
pickerObjects = new GameObject[selectionCount];
for (int i = 0; i < selectionCount; i++)
{
// build vertex billboards
var pb = selection[i];
GameObject go = InternalUtility.EmptyGameObjectWithTransform(pb.transform);
go.name = pb.name + " (Vertex Billboards)";
go.AddComponent().sharedMesh = BuildVertexMesh(pb, map, ref index);
go.AddComponent().sharedMaterial = BuiltinMaterials.vertexPickerMaterial;
pickerObjects[i] = go;
}
if (doDepthTest)
{
depthObjects = new GameObject[selectionCount];
// copy the select gameobject just for z-write
for (int i = 0; i < selectionCount; i++)
{
var pb = selection[i];
GameObject go = InternalUtility.EmptyGameObjectWithTransform(pb.transform);
go.name = pb.name + " (Depth Mask)";
go.AddComponent().sharedMesh = pb.mesh;
go.AddComponent().sharedMaterial = BuiltinMaterials.facePickerMaterial;
depthObjects[i] = go;
}
}
else
{
depthObjects = null;
}
}
static void GenerateEdgePickingObjects(
IList selection,
bool doDepthTest,
out Dictionary> map,
out GameObject[] depthObjects,
out GameObject[] pickerObjects)
{
map = new Dictionary>();
uint index = 0x2;
int selectionCount = selection.Count;
pickerObjects = new GameObject[selectionCount];
for (int i = 0; i < selectionCount; i++)
{
// build edge billboards
var pb = selection[i];
GameObject go = InternalUtility.EmptyGameObjectWithTransform(pb.transform);
go.name = pb.name + " (Edge Billboards)";
go.AddComponent().sharedMesh = BuildEdgeMesh(pb, map, ref index);
go.AddComponent().sharedMaterial = BuiltinMaterials.edgePickerMaterial;
pickerObjects[i] = go;
}
if (doDepthTest)
{
depthObjects = new GameObject[selectionCount];
for (int i = 0; i < selectionCount; i++)
{
var pb = selection[i];
// copy the select gameobject just for z-write
GameObject go = InternalUtility.EmptyGameObjectWithTransform(pb.transform);
go.name = pb.name + " (Depth Mask)";
go.AddComponent().sharedMesh = pb.mesh;
go.AddComponent().sharedMaterial = BuiltinMaterials.facePickerMaterial;
depthObjects[i] = go;
}
}
else
{
depthObjects = null;
}
}
static Mesh BuildVertexMesh(ProBuilderMesh pb, Dictionary> map, ref uint index)
{
int length = System.Math.Min(pb.sharedVerticesInternal.Length, ushort.MaxValue / 4 - 1);
Vector3[] t_billboards = new Vector3[length * 4];
Vector2[] t_uvs = new Vector2[length * 4];
Vector2[] t_uv2 = new Vector2[length * 4];
Color[] t_col = new Color[length * 4];
int[] t_tris = new int[length * 6];
int n = 0;
int t = 0;
Vector3 up = Vector3.up;
Vector3 right = Vector3.right;
for (int i = 0; i < length; i++)
{
Vector3 v = pb.positionsInternal[pb.sharedVerticesInternal[i][0]];
t_billboards[t + 0] = v;
t_billboards[t + 1] = v;
t_billboards[t + 2] = v;
t_billboards[t + 3] = v;
t_uvs[t + 0] = Vector3.zero;
t_uvs[t + 1] = Vector3.right;
t_uvs[t + 2] = Vector3.up;
t_uvs[t + 3] = Vector3.one;
t_uv2[t + 0] = -up - right;
t_uv2[t + 1] = -up + right;
t_uv2[t + 2] = up - right;
t_uv2[t + 3] = up + right;
t_tris[n + 0] = t + 0;
t_tris[n + 1] = t + 1;
t_tris[n + 2] = t + 2;
t_tris[n + 3] = t + 1;
t_tris[n + 4] = t + 3;
t_tris[n + 5] = t + 2;
Color32 color = EncodeRGBA(index);
map.Add(index++, new SimpleTuple(pb, i));
t_col[t + 0] = color;
t_col[t + 1] = color;
t_col[t + 2] = color;
t_col[t + 3] = color;
t += 4;
n += 6;
}
Mesh mesh = new Mesh();
mesh.name = "Vertex Billboard";
mesh.vertices = t_billboards;
mesh.uv = t_uvs;
mesh.uv2 = t_uv2;
mesh.colors = t_col;
mesh.triangles = t_tris;
return mesh;
}
static Mesh BuildEdgeMesh(ProBuilderMesh pb, Dictionary> map, ref uint index)
{
int edgeCount = 0;
int faceCount = pb.faceCount;
for (int i = 0; i < faceCount; i++)
edgeCount += pb.facesInternal[i].edgesInternal.Length;
int elementCount = System.Math.Min(edgeCount, ushort.MaxValue / 2 - 1);
Vector3[] positions = new Vector3[elementCount * 2];
Color32[] color = new Color32[elementCount * 2];
int[] tris = new int[elementCount * 2];
int edgeIndex = 0;
for (int i = 0; i < faceCount && edgeIndex < elementCount; i++)
{
for (int n = 0; n < pb.facesInternal[i].edgesInternal.Length && edgeIndex < elementCount; n++)
{
var edge = pb.facesInternal[i].edgesInternal[n];
Vector3 a = pb.positionsInternal[edge.a];
Vector3 b = pb.positionsInternal[edge.b];
int positionIndex = edgeIndex * 2;
positions[positionIndex + 0] = a;
positions[positionIndex + 1] = b;
Color32 c = EncodeRGBA(index);
map.Add(index++, new SimpleTuple(pb, edge));
color[positionIndex + 0] = c;
color[positionIndex + 1] = c;
tris[positionIndex + 0] = positionIndex + 0;
tris[positionIndex + 1] = positionIndex + 1;
edgeIndex++;
}
}
Mesh mesh = new Mesh();
mesh.name = "Edge Billboard";
mesh.vertices = positions;
mesh.colors32 = color;
mesh.subMeshCount = 1;
mesh.SetIndices(tris, MeshTopology.Lines, 0);
return mesh;
}
///
/// Decode Color32.RGB values to a 32 bit unsigned int, using the RGB as the little bytes. Discards the hi byte (alpha)
///
///
///
public static uint DecodeRGBA(Color32 color)
{
uint r = (uint)color.r;
uint g = (uint)color.g;
uint b = (uint)color.b;
if (System.BitConverter.IsLittleEndian)
return r << 16 | g << 8 | b;
else
return r << 24 | g << 16 | b << 8;
}
///
/// Encode the low 24 bits of a UInt32 to RGB of Color32, using 255 for A.
///
///
///
public static Color32 EncodeRGBA(uint hash)
{
// skip using BitConverter.GetBytes since this is super simple
// bit math, and allocating arrays for each conversion is expensive
if (System.BitConverter.IsLittleEndian)
return new Color32(
(byte)(hash >> 16 & 0xFF),
(byte)(hash >> 8 & 0xFF),
(byte)(hash & 0xFF),
(byte)(255));
else
return new Color32(
(byte)(hash >> 24 & 0xFF),
(byte)(hash >> 16 & 0xFF),
(byte)(hash >> 8 & 0xFF),
(byte)(255));
}
///
/// Render the camera with a replacement shader and return the resulting image.
/// RenderTexture is always initialized with no gamma conversion (RenderTextureReadWrite.Linear)
///
///
///
///
///
///
///
static Texture2D RenderWithReplacementShader(
Camera camera,
Shader shader,
string tag,
int width = -1,
int height = -1)
{
bool autoSize = width < 0 || height < 0;
int _width = autoSize ? (int)camera.pixelRect.width : width;
int _height = autoSize ? (int)camera.pixelRect.height : height;
GameObject go = new GameObject();
Camera renderCam = go.AddComponent();
renderCam.CopyFrom(camera);
renderCam.renderingPath = RenderingPath.Forward;
renderCam.enabled = false;
renderCam.clearFlags = CameraClearFlags.SolidColor;
renderCam.backgroundColor = Color.white;
#if UNITY_5_6_OR_NEWER
renderCam.allowHDR = false;
renderCam.allowMSAA = false;
renderCam.forceIntoRenderTexture = true;
#endif
#if UNITY_2017_1_OR_NEWER
RenderTextureDescriptor descriptor = new RenderTextureDescriptor() {
width = _width,
height = _height,
colorFormat = renderTextureFormat,
autoGenerateMips = false,
depthBufferBits = 16,
dimension = UnityEngine.Rendering.TextureDimension.Tex2D,
enableRandomWrite = false,
memoryless = RenderTextureMemoryless.None,
sRGB = false,
useMipMap = false,
volumeDepth = 1,
msaaSamples = 1
};
RenderTexture rt = RenderTexture.GetTemporary(descriptor);
#else
RenderTexture rt = RenderTexture.GetTemporary(
_width,
_height,
16,
renderTextureFormat,
RenderTextureReadWrite.Linear,
1);
#endif
RenderTexture prev = RenderTexture.active;
renderCam.targetTexture = rt;
RenderTexture.active = rt;
#if PB_DEBUG
/* Debug.Log(string.Format("antiAliasing {0}\nautoGenerateMips {1}\ncolorBuffer {2}\ndepth {3}\ndepthBuffer {4}\ndimension {5}\nenableRandomWrite {6}\nformat {7}\nheight {8}\nmemorylessMode {9}\nsRGB {10}\nuseMipMap {11}\nvolumeDepth {12}\nwidth {13}",
RenderTexture.active.antiAliasing,
RenderTexture.active.autoGenerateMips,
RenderTexture.active.colorBuffer,
RenderTexture.active.depth,
RenderTexture.active.depthBuffer,
RenderTexture.active.dimension,
RenderTexture.active.enableRandomWrite,
RenderTexture.active.format,
RenderTexture.active.height,
RenderTexture.active.memorylessMode,
RenderTexture.active.sRGB,
RenderTexture.active.useMipMap,
RenderTexture.active.volumeDepth,
RenderTexture.active.width));
*/
#endif
renderCam.RenderWithShader(shader, tag);
Texture2D img = new Texture2D(_width, _height, textureFormat, false, false);
img.ReadPixels(new Rect(0, 0, _width, _height), 0, 0);
img.Apply();
RenderTexture.active = prev;
RenderTexture.ReleaseTemporary(rt);
UObject.DestroyImmediate(go);
return img;
}
}
}