334 lines
12 KiB
C#
334 lines
12 KiB
C#
///
|
||
/// RTUtils by Nothke
|
||
///
|
||
/// RenderTexture utilities for direct drawing meshes, texts and sprites and converting to Texture2D.
|
||
/// Requires BlitQuad shader.
|
||
///
|
||
/// ============================================================================
|
||
///
|
||
/// MIT License
|
||
///
|
||
/// Copyright(c) 2021 Ivan Notaro<72>
|
||
///
|
||
/// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
/// of this software and associated documentation files (the "Software"), to deal
|
||
/// in the Software without restriction, including without limitation the rights
|
||
/// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
/// copies of the Software, and to permit persons to whom the Software is
|
||
/// furnished to do so, subject to the following conditions:
|
||
///
|
||
/// The above copyright notice and this permission notice shall be included in all
|
||
/// copies or substantial portions of the Software.
|
||
///
|
||
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||
/// SOFTWARE.
|
||
///
|
||
/// ============================================================================
|
||
///
|
||
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
|
||
using TMPro;
|
||
|
||
//Original
|
||
//namespace Nothke.Utils
|
||
namespace BITKit
|
||
{
|
||
public static class RTUtils
|
||
{
|
||
#region Quad creation
|
||
|
||
static Mesh quad;
|
||
public static Mesh GetQuad()
|
||
{
|
||
if (quad)
|
||
return quad;
|
||
|
||
Mesh mesh = new Mesh();
|
||
|
||
float width = 1;
|
||
float height = 1;
|
||
|
||
Vector3[] vertices = new Vector3[4]
|
||
{
|
||
new Vector3(0, 0, 0),
|
||
new Vector3(width, 0, 0),
|
||
new Vector3(0, height, 0),
|
||
new Vector3(width, height, 0)
|
||
};
|
||
mesh.vertices = vertices;
|
||
|
||
int[] tris = new int[6]
|
||
{
|
||
// lower left triangle
|
||
0, 2, 1,
|
||
// upper right triangle
|
||
2, 3, 1
|
||
};
|
||
mesh.triangles = tris;
|
||
|
||
Vector2[] uv = new Vector2[4]
|
||
{
|
||
new Vector2(0, 0),
|
||
new Vector2(1, 0),
|
||
new Vector2(0, 1),
|
||
new Vector2(1, 1)
|
||
};
|
||
mesh.uv = uv;
|
||
|
||
quad = mesh;
|
||
return quad;
|
||
}
|
||
|
||
static Shader blitShader;
|
||
public static Shader GetBlitShader()
|
||
{
|
||
if (blitShader)
|
||
return blitShader;
|
||
|
||
//const string SHADER_NAME = "Sprites/Default";
|
||
const string SHADER_NAME = "Sprites/Default";
|
||
var shader = Shader.Find(SHADER_NAME);
|
||
|
||
if (!shader)
|
||
Debug.LogError(SHADER_NAME + " shader not found, did you forget to include it in the project settings?");
|
||
|
||
blitShader = shader;
|
||
return blitShader;
|
||
}
|
||
|
||
static Material blitMaterial;
|
||
|
||
public static Material GetBlitMaterial()
|
||
{
|
||
if (blitMaterial)
|
||
return blitMaterial;
|
||
|
||
|
||
return new Material(GetBlitShader());
|
||
}
|
||
|
||
#endregion
|
||
|
||
static RenderTexture prevRT;
|
||
|
||
public static void BeginOrthoRendering(this RenderTexture rt, float zBegin = -100, float zEnd = 100)
|
||
{
|
||
// Create an orthographic matrix (for 2D rendering)
|
||
Matrix4x4 projectionMatrix = Matrix4x4.Ortho(0, 1, 0, 1, zBegin, zEnd);
|
||
|
||
rt.BeginRendering(projectionMatrix);
|
||
}
|
||
|
||
public static void BeginPixelRendering(this RenderTexture rt, float zBegin = -100, float zEnd = 100)
|
||
{
|
||
Matrix4x4 projectionMatrix = Matrix4x4.Ortho(0, rt.width, 0, rt.height, zBegin, zEnd);
|
||
|
||
rt.BeginRendering(projectionMatrix);
|
||
}
|
||
|
||
public static void BeginPerspectiveRendering(
|
||
this RenderTexture rt, float fov, in Vector3 position, in Quaternion rotation,
|
||
float zNear = 0.01f, float zFar = 1000f)
|
||
{
|
||
float aspect = (float)rt.width / rt.height;
|
||
Matrix4x4 projectionMatrix = Matrix4x4.Perspective(fov, aspect, zNear, zFar);
|
||
Matrix4x4 viewMatrix = Matrix4x4.TRS(position, rotation, new Vector3(1, 1, -1));
|
||
|
||
Matrix4x4 cameraMatrix = (projectionMatrix * viewMatrix.inverse);
|
||
|
||
rt.BeginRendering(cameraMatrix);
|
||
}
|
||
|
||
public static void BeginRendering(this RenderTexture rt, Matrix4x4 projectionMatrix)
|
||
{
|
||
// This fixes flickering (by @guycalledfrank)
|
||
// (because there's some switching back and forth between cameras, I don't fully understand)
|
||
if (Camera.current != null)
|
||
projectionMatrix *= Camera.current.worldToCameraMatrix.inverse;
|
||
|
||
// Remember the current texture and make our own active
|
||
prevRT = RenderTexture.active;
|
||
RenderTexture.active = rt;
|
||
|
||
// Push the projection matrix
|
||
GL.PushMatrix();
|
||
GL.LoadProjectionMatrix(projectionMatrix);
|
||
}
|
||
|
||
public static void EndRendering(this RenderTexture rt)
|
||
{
|
||
// Pop the projection matrix to set it back to the previous one
|
||
GL.PopMatrix();
|
||
|
||
// Revert culling
|
||
GL.invertCulling = false;
|
||
|
||
// Re-set the RenderTexture to the last used one
|
||
RenderTexture.active = prevRT;
|
||
}
|
||
|
||
public static void DrawMesh(this RenderTexture rt, Mesh mesh, Material material, in Matrix4x4 objectMatrix, int pass = 0)
|
||
{
|
||
bool canRender = material.SetPass(pass);
|
||
|
||
if (canRender)
|
||
Graphics.DrawMeshNow(mesh, objectMatrix);
|
||
}
|
||
|
||
public static void DrawTMPText(this RenderTexture rt, TMP_Text text, in Vector2 position, float size)
|
||
{
|
||
float aspect = (float)rt.width / rt.height;
|
||
Vector3 scale = new Vector3(size, size * aspect, 1);
|
||
|
||
Matrix4x4 matrix = Matrix4x4.TRS(position, Quaternion.identity, scale);
|
||
|
||
rt.DrawTMPText(text, matrix);
|
||
}
|
||
|
||
public static void DrawTMPText(this RenderTexture rt, TMP_Text text, in Matrix4x4 matrix)
|
||
{
|
||
Material material = text.fontSharedMaterial;
|
||
rt.DrawMesh(text.mesh, material, matrix);
|
||
}
|
||
|
||
#region Blit once functions
|
||
|
||
/// <summary>
|
||
/// Draws a mesh to render texture.
|
||
/// Position is defined in camera view 0-1 space where 0,0 is in bottom left corner.
|
||
/// <para>
|
||
/// For non-square textures, aspect ratio will be calculated so that the 0-1 space fits in the width.
|
||
/// Meaning that, for example, wider than square texture will have larger font size per texture area.
|
||
/// </para>
|
||
/// </summary>
|
||
/// <param name="rt"></param>
|
||
/// <param name="text"></param>
|
||
/// <param name="pos"></param>
|
||
/// <param name="size"></param>
|
||
/// <param name="clear"></param>
|
||
/// <param name="clearColor"></param>
|
||
public static void BlitTMPText(this RenderTexture rt, TMP_Text text, in Vector2 pos, float size,
|
||
bool clear = true, Color clearColor = default)
|
||
{
|
||
float aspect = (float)rt.width / rt.height;
|
||
Vector3 scale = new Vector3(size, size * aspect, 1);
|
||
|
||
Matrix4x4 matrix = Matrix4x4.TRS(pos, Quaternion.identity, scale);
|
||
BlitTMPText(rt, text, matrix, clear, clearColor);
|
||
}
|
||
|
||
public static void BlitTMPText(this RenderTexture rt, TMP_Text text, Matrix4x4 objectMatrix,
|
||
bool clear = true, Color clearColor = default)
|
||
{
|
||
Material mat = text.fontSharedMaterial;
|
||
BlitMesh(rt, objectMatrix, text.mesh, mat, clear, true, clearColor);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Draws a mesh to render texture. The camera space is defined in normalized 0-1 coordinates, near and far planes are -100 and 100.
|
||
/// </summary>
|
||
/// <param name="objectMatrix">The model-matrix of the object</param>
|
||
/// <param name="invertCulling">In case the mesh renders inside-out, toggle this</param>
|
||
/// <param name="clear">Clears a texture to clearColor before drawing</param>
|
||
public static void BlitMesh(this RenderTexture rt, Matrix4x4 objectMatrix, Mesh mesh, Material material,
|
||
bool invertCulling = true, bool clear = true, Color clearColor = default)
|
||
{
|
||
// Create an orthographic matrix (for 2D rendering)
|
||
// You can otherwise use Matrix4x4.Perspective()
|
||
Matrix4x4 projectionMatrix = Matrix4x4.Ortho(0, 1, 0, 1, -100, 100);
|
||
|
||
// This fixes flickering (by @guycalledfrank)
|
||
// (because there's some switching back and forth between cameras, I don't fully understand)
|
||
if (Camera.current != null)
|
||
projectionMatrix *= Camera.current.worldToCameraMatrix.inverse;
|
||
|
||
// Remember the current texture and set our own as "active".
|
||
RenderTexture prevRT = RenderTexture.active;
|
||
RenderTexture.active = rt;
|
||
|
||
// Set material as "active". Without this, Unity editor will freeze.
|
||
bool canRender = material.SetPass(0);
|
||
|
||
// Push the projection matrix
|
||
GL.PushMatrix();
|
||
GL.LoadProjectionMatrix(projectionMatrix);
|
||
|
||
// It seems that the faces are in a wrong order, so we need to flip them
|
||
GL.invertCulling = invertCulling;
|
||
|
||
// Clear the texture
|
||
if (clear)
|
||
GL.Clear(true, true, clearColor);
|
||
|
||
// Draw the mesh!
|
||
if (canRender)
|
||
Graphics.DrawMeshNow(mesh, objectMatrix);
|
||
|
||
// Pop the projection matrix to set it back to the previous one
|
||
GL.PopMatrix();
|
||
|
||
// Revert culling
|
||
GL.invertCulling = false;
|
||
|
||
// Re-set the RenderTexture to the last used one
|
||
RenderTexture.active = prevRT;
|
||
}
|
||
|
||
#endregion
|
||
|
||
public static void DrawQuad(this RenderTexture rt, Material material, in Rect rect)
|
||
{
|
||
Matrix4x4 objectMatrix = Matrix4x4.TRS(
|
||
rect.position, Quaternion.identity, rect.size);
|
||
|
||
rt.DrawMesh(GetQuad(), material, objectMatrix);
|
||
|
||
//GL.invertCulling = true;
|
||
}
|
||
|
||
public static void DrawSprite(this RenderTexture rt, Texture texture, in Rect rect)
|
||
{
|
||
Material material = GetBlitMaterial();
|
||
material.mainTexture = texture;
|
||
|
||
DrawQuad(rt, material, rect);
|
||
}
|
||
|
||
#region Utils
|
||
|
||
public static float Aspect(this Texture rt) => (float)rt.width / rt.height;
|
||
|
||
public static Texture2D ConvertToTexture2D(this RenderTexture rt,
|
||
TextureFormat format = TextureFormat.RGB24,
|
||
FilterMode filterMode = FilterMode.Bilinear)
|
||
{
|
||
Texture2D tex = new Texture2D(rt.width, rt.height, format, false);
|
||
tex.filterMode = filterMode;
|
||
|
||
RenderTexture prevActive = RenderTexture.active;
|
||
RenderTexture.active = rt;
|
||
tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
|
||
|
||
tex.Apply();
|
||
|
||
RenderTexture.active = prevActive;
|
||
|
||
return tex;
|
||
}
|
||
|
||
public static void DrawTextureGUI(Texture texture)
|
||
{
|
||
GUI.DrawTexture(new Rect(0, 0, texture.width, texture.height), texture);
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
} |