mirror of
https://github.com/drewcassidy/KSP-Conformal-Decals.git
synced 2024-09-01 18:23:54 +00:00
Play around more with text rendering
This commit is contained in:
parent
2c8773ce61
commit
0a77ef57b7
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,6 +2,7 @@
|
|||||||
Assets/*
|
Assets/*
|
||||||
!Assets/Shaders/
|
!Assets/Shaders/
|
||||||
!Assets/Textures/
|
!Assets/Textures/
|
||||||
|
!Assets/Scripts/
|
||||||
KSP/
|
KSP/
|
||||||
Library/
|
Library/
|
||||||
Logs/
|
Logs/
|
||||||
|
90
Assets/Scripts/TextRenderTest.cs
Normal file
90
Assets/Scripts/TextRenderTest.cs
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using TMPro;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Rendering;
|
||||||
|
|
||||||
|
public class TextRenderTest : MonoBehaviour {
|
||||||
|
//[InspectorButton("go")] public bool button;
|
||||||
|
|
||||||
|
public Camera _camera;
|
||||||
|
|
||||||
|
public GameObject _cameraObject;
|
||||||
|
|
||||||
|
public TextMeshPro _tmp;
|
||||||
|
|
||||||
|
public Material _blitMaterial;
|
||||||
|
|
||||||
|
public Material _targetMaterial;
|
||||||
|
|
||||||
|
public RenderTexture renderTex;
|
||||||
|
private float pixelDensity = 36;
|
||||||
|
private int MaxTextureSize = 4096;
|
||||||
|
|
||||||
|
public const TextureFormat TextTextureFormat = TextureFormat.RG16;
|
||||||
|
public const RenderTextureFormat TextRenderTextureFormat = RenderTextureFormat.R8;
|
||||||
|
|
||||||
|
// Start is called before the first frame update
|
||||||
|
void Start() {
|
||||||
|
Debug.Log("starting...");
|
||||||
|
|
||||||
|
StartCoroutine(OnRender());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update is called once per frame
|
||||||
|
void Update() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private void go() {
|
||||||
|
Debug.Log("starting...");
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerator OnRender() {
|
||||||
|
Debug.Log("starting...2");
|
||||||
|
|
||||||
|
// calculate camera and texture size
|
||||||
|
_tmp.ForceMeshUpdate();
|
||||||
|
var mesh = _tmp.mesh;
|
||||||
|
mesh.RecalculateBounds();
|
||||||
|
var bounds = mesh.bounds;
|
||||||
|
Debug.Log(bounds.size);
|
||||||
|
|
||||||
|
var width = Mathf.NextPowerOfTwo((int) (bounds.size.x * pixelDensity));
|
||||||
|
var height = Mathf.NextPowerOfTwo((int) (bounds.size.y * pixelDensity));
|
||||||
|
|
||||||
|
Debug.Log($"width = {width}");
|
||||||
|
Debug.Log($"height = {height}");
|
||||||
|
|
||||||
|
_camera.orthographicSize = height / pixelDensity / 2;
|
||||||
|
_camera.aspect = (float) width / height;
|
||||||
|
|
||||||
|
_cameraObject.transform.localPosition = new Vector3(bounds.center.x, bounds.center.y, -1);
|
||||||
|
|
||||||
|
width = Mathf.Min(width, MaxTextureSize);
|
||||||
|
height = Mathf.Min(height, MaxTextureSize);
|
||||||
|
|
||||||
|
// setup texture
|
||||||
|
var texture = new Texture2D(width, height, TextTextureFormat, true);
|
||||||
|
_targetMaterial.mainTexture = texture;
|
||||||
|
|
||||||
|
// setup render texture
|
||||||
|
renderTex = RenderTexture.GetTemporary(width, height, 0, TextRenderTextureFormat, RenderTextureReadWrite.Linear, 1);
|
||||||
|
renderTex.autoGenerateMips = true;
|
||||||
|
_camera.targetTexture = renderTex;
|
||||||
|
|
||||||
|
// setup material
|
||||||
|
_blitMaterial.mainTexture = _tmp.font.atlas;
|
||||||
|
|
||||||
|
// draw the mesh
|
||||||
|
Graphics.DrawMesh(mesh, _tmp.renderer.localToWorldMatrix, _blitMaterial, 0, _camera, 0);
|
||||||
|
_camera.Render();
|
||||||
|
|
||||||
|
yield return null;
|
||||||
|
|
||||||
|
RenderTexture.active = renderTex;
|
||||||
|
texture.ReadPixels(new Rect(0, 0, width, height), 0, 0, true);
|
||||||
|
texture.Apply(false, true);
|
||||||
|
|
||||||
|
RenderTexture.ReleaseTemporary(renderTex);
|
||||||
|
}
|
||||||
|
}
|
51
Assets/Shaders/TMPBlit.shader
Normal file
51
Assets/Shaders/TMPBlit.shader
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
Shader "ConformalDecals/TMP_Blit"
|
||||||
|
{
|
||||||
|
Properties
|
||||||
|
{
|
||||||
|
_MainTex("_MainTex (RGB spec(A))", 2D) = "white" {}
|
||||||
|
|
||||||
|
}
|
||||||
|
SubShader
|
||||||
|
{
|
||||||
|
Tags { "Queue" = "Transparent" }
|
||||||
|
Cull Off
|
||||||
|
ZWrite Off
|
||||||
|
|
||||||
|
Pass
|
||||||
|
{
|
||||||
|
Blend One One
|
||||||
|
|
||||||
|
CGPROGRAM
|
||||||
|
#pragma vertex vert
|
||||||
|
#pragma fragment frag
|
||||||
|
|
||||||
|
sampler2D _MainTex;
|
||||||
|
|
||||||
|
#include "UnityCG.cginc"
|
||||||
|
#include "Lighting.cginc"
|
||||||
|
#include "AutoLight.cginc"
|
||||||
|
|
||||||
|
struct v2f {
|
||||||
|
float4 pos : SV_POSITION;
|
||||||
|
float2 uv : TEXCOORD0;
|
||||||
|
};
|
||||||
|
|
||||||
|
v2f vert(float4 vertex : POSITION, float2 uv : TEXCOORD0) {
|
||||||
|
v2f o;
|
||||||
|
o.pos = UnityObjectToClipPos(vertex);
|
||||||
|
o.uv = uv;
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
fixed4 frag (v2f i) : SV_Target {
|
||||||
|
fixed4 c = 0;
|
||||||
|
|
||||||
|
c.r = tex2D(_MainTex,(i.uv)).a;
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
ENDCG
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -52,6 +52,9 @@
|
|||||||
<Reference Include="UnityEngine.PhysicsModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
|
<Reference Include="UnityEngine.PhysicsModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
|
||||||
<HintPath>dlls\UnityEngine.PhysicsModule.dll</HintPath>
|
<HintPath>dlls\UnityEngine.PhysicsModule.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
|
||||||
|
<HintPath>dlls\UnityEngine.UI.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="DecalConfig.cs" />
|
<Compile Include="DecalConfig.cs" />
|
||||||
@ -70,6 +73,7 @@
|
|||||||
<Compile Include="Text\DecalFont.cs" />
|
<Compile Include="Text\DecalFont.cs" />
|
||||||
<Compile Include="Text\FontLoader.cs" />
|
<Compile Include="Text\FontLoader.cs" />
|
||||||
<Compile Include="Text\TextRenderer.cs" />
|
<Compile Include="Text\TextRenderer.cs" />
|
||||||
|
<Compile Include="Text\TextSettings.cs" />
|
||||||
<Compile Include="Util\Logging.cs" />
|
<Compile Include="Util\Logging.cs" />
|
||||||
<Compile Include="Util\OrientedBounds.cs" />
|
<Compile Include="Util\OrientedBounds.cs" />
|
||||||
<Compile Include="Util\TextureUtils.cs" />
|
<Compile Include="Util\TextureUtils.cs" />
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
using ConformalDecals.Text;
|
||||||
using ConformalDecals.Util;
|
using ConformalDecals.Util;
|
||||||
using TMPro;
|
using TMPro;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
@ -1,146 +1,112 @@
|
|||||||
using System;
|
using System;
|
||||||
using ConformalDecals.Util;
|
using System.Collections;
|
||||||
using TMPro;
|
using TMPro;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using UnityEngine.Rendering;
|
||||||
|
|
||||||
namespace ConformalDecals {
|
namespace ConformalDecals.Text {
|
||||||
public class TextRenderer {
|
[KSPAddon(KSPAddon.Startup.FlightAndEditor, true)]
|
||||||
private struct GlyphInfo {
|
public class TextRenderer : MonoBehaviour {
|
||||||
public TMP_Glyph glyph;
|
public static TextRenderer Instance {
|
||||||
public Vector2Int size;
|
get {
|
||||||
public Vector2Int position;
|
if (!_instance._isSetup) {
|
||||||
public int fontIndex;
|
_instance.Setup();
|
||||||
public bool needsResample;
|
}
|
||||||
|
|
||||||
|
return _instance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct FontInfo {
|
public const TextureFormat TextTextureFormat = TextureFormat.Alpha8;
|
||||||
public TMP_FontAsset font;
|
public const RenderTextureFormat TextRenderTextureFormat = RenderTextureFormat.R8;
|
||||||
public Texture2D fontAtlas;
|
|
||||||
public Color32[] fontAtlasColors;
|
private const string BlitShader = "ConformalDecals/TMP_Blit";
|
||||||
|
private const int MaxTextureSize = 4096;
|
||||||
|
|
||||||
|
private static TextRenderer _instance;
|
||||||
|
|
||||||
|
private bool _isSetup;
|
||||||
|
private TextMeshPro _tmp;
|
||||||
|
private GameObject _cameraObject;
|
||||||
|
private Camera _camera;
|
||||||
|
private Material _blitMaterial;
|
||||||
|
|
||||||
|
private void Start() {
|
||||||
|
if (_instance._isSetup) {
|
||||||
|
Debug.Log("[ConformalDecals] Duplicate TextRenderer created???");
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Log("[ConformalDecals] Creating TextRenderer Object");
|
||||||
|
_instance = this;
|
||||||
|
DontDestroyOnLoad(gameObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Texture2D RenderToTexture(TMP_FontAsset font, string text) {
|
public void Setup() {
|
||||||
Debug.Log($"Rendering text: {text}");
|
if (_isSetup) return;
|
||||||
var charArray = text.ToCharArray();
|
|
||||||
var glyphInfoArray = new GlyphInfo[charArray.Length];
|
|
||||||
var fontInfoArray = new FontInfo[charArray.Length];
|
|
||||||
|
|
||||||
var baseScale = font.fontInfo.Scale;
|
|
||||||
|
|
||||||
var padding = (int) font.fontInfo.Padding;
|
Debug.Log("[ConformalDecals] Setting Up TextRenderer Object");
|
||||||
var ascender = (int) font.fontInfo.Ascender;
|
|
||||||
var descender = (int) font.fontInfo.Descender;
|
|
||||||
var baseline = (int) baseScale * (descender + padding);
|
|
||||||
Debug.Log($"baseline: {baseline}");
|
|
||||||
Debug.Log($"ascender: {ascender}");
|
|
||||||
Debug.Log($"descender: {descender}");
|
|
||||||
Debug.Log($"baseScale: {baseScale}");
|
|
||||||
|
|
||||||
fontInfoArray[0].font = font;
|
_tmp = gameObject.AddComponent<TextMeshPro>();
|
||||||
|
_tmp.renderer.enabled = false; // dont automatically render
|
||||||
|
|
||||||
int xAdvance = 0;
|
_cameraObject = new GameObject("ConformalDecals text camera");
|
||||||
for (var i = 0; i < charArray.Length; i++) {
|
_cameraObject.transform.parent = transform;
|
||||||
|
_cameraObject.transform.SetPositionAndRotation(Vector3.back, Quaternion.identity);
|
||||||
|
|
||||||
var glyphFont = TMP_FontUtilities.SearchForGlyph(font, charArray[i], out var glyph);
|
_camera = _cameraObject.AddComponent<Camera>();
|
||||||
|
_camera.enabled = false; // dont automatically render
|
||||||
|
_camera.orthographic = true;
|
||||||
|
_camera.depthTextureMode = DepthTextureMode.None;
|
||||||
|
_camera.nearClipPlane = 0.1f;
|
||||||
|
_camera.farClipPlane = 2f;
|
||||||
|
_isSetup = true;
|
||||||
|
|
||||||
if (glyphFont == font) {
|
_blitMaterial = new Material(Shabby.Shabby.FindShader(BlitShader));
|
||||||
glyphInfoArray[i].fontIndex = 0;
|
}
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (int f = 1; i < charArray.Length; i++) {
|
|
||||||
if (fontInfoArray[f].font == null) {
|
|
||||||
fontInfoArray[f].font = glyphFont;
|
|
||||||
glyphInfoArray[i].fontIndex = f;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fontInfoArray[f].font == glyphFont) {
|
public Texture2D RenderToTexture(Texture2D texture2D, TMP_FontAsset font, string text, float fontSize, float pixelDensity) {
|
||||||
glyphInfoArray[i].fontIndex = f;
|
// generate text mesh
|
||||||
break;
|
_tmp.SetText(text);
|
||||||
}
|
_tmp.font = font;
|
||||||
}
|
_tmp.fontSize = fontSize;
|
||||||
}
|
_tmp.ForceMeshUpdate();
|
||||||
|
|
||||||
Debug.Log($"getting font info for character: '{charArray[i]}'");
|
// calculate camera and texture size
|
||||||
Debug.Log($"character font: {glyphFont.name}");
|
var mesh = _tmp.mesh;
|
||||||
|
var bounds = mesh.bounds;
|
||||||
|
|
||||||
glyphInfoArray[i].glyph = glyph;
|
var width = Mathf.NextPowerOfTwo((int) (bounds.size.x * pixelDensity));
|
||||||
glyphInfoArray[i].needsResample = false;
|
var height = Mathf.NextPowerOfTwo((int) (bounds.size.y * pixelDensity));
|
||||||
|
|
||||||
float elementScale = glyph.scale;
|
_camera.orthographicSize = height / pixelDensity / 2;
|
||||||
|
_camera.aspect = (float) width / height;
|
||||||
|
|
||||||
if (glyphFont == font) {
|
_cameraObject.transform.localPosition = new Vector3(bounds.center.x, bounds.center.y, -1);
|
||||||
if (!Mathf.Approximately(elementScale, 1)) {
|
|
||||||
glyphInfoArray[i].needsResample = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
elementScale *= baseScale;
|
width = Mathf.Min(width, MaxTextureSize);
|
||||||
}
|
height = Mathf.Max(height, MaxTextureSize);
|
||||||
else {
|
|
||||||
var fontScale = glyphFont.fontInfo.Scale / glyphFont.fontInfo.PointSize;
|
|
||||||
if (!Mathf.Approximately(fontScale, baseScale)) {
|
|
||||||
glyphInfoArray[i].needsResample = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
elementScale *= fontScale;
|
// setup render texture
|
||||||
}
|
var renderTex = RenderTexture.GetTemporary(width, height, 0, TextRenderTextureFormat, RenderTextureReadWrite.Linear, 1);
|
||||||
|
_camera.targetTexture = renderTex;
|
||||||
|
|
||||||
Debug.Log($"character scale: {glyphFont.fontInfo.Scale / glyphFont.fontInfo.PointSize}");
|
// setup material
|
||||||
Debug.Log($"character needs resampling: {glyphInfoArray[i].needsResample}");
|
_blitMaterial.SetTexture(PropertyIDs._MainTex, font.atlas);
|
||||||
|
_blitMaterial.SetPass(0);
|
||||||
|
|
||||||
glyphInfoArray[i].size.x = (int) ((glyph.width + (padding * 2)) * elementScale);
|
// draw the mesh
|
||||||
glyphInfoArray[i].size.y = (int) ((glyph.height + (padding * 2)) * elementScale);
|
Graphics.DrawMeshNow(mesh, _tmp.renderer.localToWorldMatrix);
|
||||||
glyphInfoArray[i].position.x = (int) ((xAdvance + glyph.xOffset - padding) * elementScale);
|
|
||||||
glyphInfoArray[i].position.y = (int) ((baseline + glyph.yOffset - padding) * elementScale);
|
var request = AsyncGPUReadback.Request(renderTex, 0, TextTextureFormat);
|
||||||
|
|
||||||
Debug.Log($"character size: {glyphInfoArray[i].size}");
|
request.WaitForCompletion();
|
||||||
Debug.Log($"character position: {glyphInfoArray[i].position}");
|
|
||||||
|
if (request.hasError) {
|
||||||
|
throw new Exception("[ConformalDecals] Error encountered trying to request render texture data from the GPU!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate texture bounds
|
|
||||||
int xOffset = glyphInfoArray[0].position.x;
|
|
||||||
var textureWidth = (glyphInfoArray[charArray.Length - 1].position.x + glyphInfoArray[charArray.Length - 1].size.x) - xOffset;
|
|
||||||
var textureHeight = (int) baseScale * (ascender + descender + padding * 2);
|
|
||||||
|
|
||||||
// ensure texture sizes are powers of 2
|
|
||||||
textureWidth = Mathf.NextPowerOfTwo(textureWidth);
|
|
||||||
textureHeight = Mathf.NextPowerOfTwo(textureHeight);
|
|
||||||
Debug.Log($"texture is {textureWidth} x {textureHeight}");
|
|
||||||
|
|
||||||
var texture = new Texture2D(textureWidth, textureHeight, TextureFormat.Alpha8, true);
|
|
||||||
|
|
||||||
var colors = new Color32[textureWidth * textureHeight];
|
|
||||||
|
|
||||||
for (var i = 0; i < fontInfoArray.Length; i++) {
|
|
||||||
if (fontInfoArray[i].font == null) break;
|
|
||||||
fontInfoArray[i].fontAtlas = fontInfoArray[i].font.atlas;
|
|
||||||
fontInfoArray[i].fontAtlasColors = fontInfoArray[i].fontAtlas.GetPixels32();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < charArray.Length; i++) {
|
|
||||||
var glyphInfo = glyphInfoArray[i];
|
|
||||||
var glyph = glyphInfo.glyph;
|
|
||||||
var fontInfo = fontInfoArray[glyphInfo.fontIndex];
|
|
||||||
|
|
||||||
var srcPos = new Vector2Int((int) glyph.x, (int) glyph.y);
|
|
||||||
var dstPos = glyphInfo.position;
|
|
||||||
dstPos.x += xOffset;
|
|
||||||
var dstSize = glyphInfo.size;
|
|
||||||
|
|
||||||
Debug.Log($"rendering character number {i}");
|
|
||||||
|
|
||||||
if (glyphInfo.needsResample) {
|
|
||||||
var srcSize = new Vector2(glyph.width, glyph.height);
|
|
||||||
TextureUtils.BlitRectBilinearAlpha(fontInfo.fontAtlas, srcPos, srcSize, texture, colors, dstPos, dstSize, TextureUtils.BlitMode.Add);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
TextureUtils.BlitRectAlpha(fontInfo.fontAtlas, fontInfo.fontAtlasColors, srcPos, texture, colors, dstPos, dstSize, TextureUtils.BlitMode.Add);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
texture.Apply(true);
|
|
||||||
|
|
||||||
return texture;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
5
Source/ConformalDecals/Text/TextSettings.cs
Normal file
5
Source/ConformalDecals/Text/TextSettings.cs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
namespace ConformalDecals.Text {
|
||||||
|
public struct TextSettings {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user