mirror of
https://github.com/drewcassidy/KSP-Conformal-Decals.git
synced 2024-09-01 18:23:54 +00:00
First working text rendering
This commit is contained in:
@ -1,16 +1,54 @@
|
||||
using System;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ConformalDecals.Text {
|
||||
public struct DecalText {
|
||||
public string text;
|
||||
public DecalFont font;
|
||||
public FontStyles style;
|
||||
public bool vertical;
|
||||
public class DecalText : IEquatable<DecalText> {
|
||||
public string Text { get; }
|
||||
|
||||
public Color color;
|
||||
public Color outlineColor;
|
||||
public float outlineWidth;
|
||||
public DecalFont Font { get; }
|
||||
|
||||
public DecalTextStyle Style { get; }
|
||||
|
||||
public string FormattedText {
|
||||
get {
|
||||
if (Style.Vertical) {
|
||||
return Regex.Replace(Text, @"(.)", "$1\n");
|
||||
}
|
||||
else {
|
||||
return Text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public DecalText(string text, DecalFont font, DecalTextStyle style) {
|
||||
Text = text;
|
||||
Font = font;
|
||||
Style = style;
|
||||
}
|
||||
|
||||
public bool Equals(DecalText other) {
|
||||
return other != null && (Text == other.Text && Equals(Font, other.Font) && Style.Equals(other.Style));
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
return obj is DecalText other && Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
unchecked {
|
||||
var hashCode = (Text != null ? Text.GetHashCode() : 0);
|
||||
hashCode = (hashCode * 397) ^ (Font != null ? Font.GetHashCode() : 0);
|
||||
hashCode = (hashCode * 397) ^ Style.GetHashCode();
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(DecalText left, DecalText right) {
|
||||
return left != null && left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(DecalText left, DecalText right) {
|
||||
return left != null && !left.Equals(right);
|
||||
}
|
||||
}
|
||||
}
|
75
Source/ConformalDecals/Text/DecalTextStyle.cs
Normal file
75
Source/ConformalDecals/Text/DecalTextStyle.cs
Normal file
@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ConformalDecals.Text {
|
||||
public struct DecalTextStyle : IEquatable<DecalTextStyle> {
|
||||
public FontStyles FontStyle { get; set; }
|
||||
|
||||
public bool Bold {
|
||||
get => (FontStyle & FontStyles.Bold) != 0;
|
||||
set {
|
||||
if (value) FontStyle |= FontStyles.Bold;
|
||||
else FontStyle &= ~FontStyles.Bold;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Italic {
|
||||
get => (FontStyle & FontStyles.Italic) != 0;
|
||||
set {
|
||||
if (value) FontStyle |= FontStyles.Italic;
|
||||
else FontStyle &= ~FontStyles.Italic;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Underline {
|
||||
get => (FontStyle & FontStyles.Underline) != 0;
|
||||
set {
|
||||
if (value) FontStyle |= FontStyles.Underline;
|
||||
else FontStyle &= ~FontStyles.Underline;
|
||||
}
|
||||
}
|
||||
|
||||
public bool SmallCaps {
|
||||
get => (FontStyle & FontStyles.SmallCaps) != 0;
|
||||
set {
|
||||
if (value) FontStyle |= FontStyles.SmallCaps;
|
||||
else FontStyle &= ~FontStyles.SmallCaps;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Vertical { get; set; }
|
||||
|
||||
public float LineSpacing { get; set; }
|
||||
|
||||
public float CharacterSpacing { get; set; }
|
||||
|
||||
public bool Equals(DecalTextStyle other) {
|
||||
return FontStyle == other.FontStyle && Vertical == other.Vertical &&
|
||||
Mathf.Approximately(LineSpacing, other.LineSpacing) &&
|
||||
Mathf.Approximately(CharacterSpacing, other.CharacterSpacing);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
return obj is DecalTextStyle other && Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
unchecked {
|
||||
var hashCode = (int) FontStyle;
|
||||
hashCode = (hashCode * 397) ^ Vertical.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ LineSpacing.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ CharacterSpacing.GetHashCode();
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(DecalTextStyle left, DecalTextStyle right) {
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(DecalTextStyle left, DecalTextStyle right) {
|
||||
return !left.Equals(right);
|
||||
}
|
||||
}
|
||||
}
|
11
Source/ConformalDecals/Text/RenderedText.cs
Normal file
11
Source/ConformalDecals/Text/RenderedText.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace ConformalDecals.Text {
|
||||
public class RenderedText : ScriptableObject {
|
||||
public Texture2D Texture { get; private set; }
|
||||
|
||||
public Rect Window { get; private set; }
|
||||
|
||||
public int UserCount { get; private set; }
|
||||
}
|
||||
}
|
@ -1,11 +1,12 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace ConformalDecals.Text {
|
||||
[KSPAddon(KSPAddon.Startup.FlightAndEditor, true)]
|
||||
[KSPAddon(KSPAddon.Startup.Instantly, true)]
|
||||
public class TextRenderer : MonoBehaviour {
|
||||
public static TextRenderer Instance {
|
||||
get {
|
||||
@ -17,22 +18,25 @@ namespace ConformalDecals.Text {
|
||||
}
|
||||
}
|
||||
|
||||
public const TextureFormat TextTextureFormat = TextureFormat.Alpha8;
|
||||
public const TextureFormat TextTextureFormat = TextureFormat.RG16;
|
||||
public const RenderTextureFormat TextRenderTextureFormat = RenderTextureFormat.R8;
|
||||
|
||||
private const string BlitShader = "ConformalDecals/TMP_Blit";
|
||||
private const string BlitShader = "ConformalDecals/Text Blit";
|
||||
private const int MaxTextureSize = 4096;
|
||||
private const float FontSize = 100;
|
||||
private const float PixelDensity = 5;
|
||||
|
||||
private static TextRenderer _instance;
|
||||
|
||||
private bool _isSetup;
|
||||
private TextMeshPro _tmp;
|
||||
private GameObject _cameraObject;
|
||||
private Camera _camera;
|
||||
private Material _blitMaterial;
|
||||
|
||||
private Dictionary<DecalText, RenderedText> _renderedTextures = new Dictionary<DecalText, RenderedText>();
|
||||
private Texture2D _lastTexture; // to reduce the number of Texture2D objects created and destroyed, keep the last one on hand
|
||||
|
||||
private void Start() {
|
||||
if (_instance._isSetup) {
|
||||
if (_instance != null) {
|
||||
Debug.Log("[ConformalDecals] Duplicate TextRenderer created???");
|
||||
}
|
||||
|
||||
@ -41,28 +45,102 @@ namespace ConformalDecals.Text {
|
||||
DontDestroyOnLoad(gameObject);
|
||||
}
|
||||
|
||||
public void Setup() {
|
||||
private void Setup() {
|
||||
if (_isSetup) return;
|
||||
|
||||
|
||||
Debug.Log("[ConformalDecals] Setting Up TextRenderer Object");
|
||||
|
||||
_tmp = gameObject.AddComponent<TextMeshPro>();
|
||||
_tmp.renderer.enabled = false; // dont automatically render
|
||||
|
||||
_cameraObject = new GameObject("ConformalDecals text camera");
|
||||
_cameraObject.transform.parent = transform;
|
||||
_cameraObject.transform.SetPositionAndRotation(Vector3.back, Quaternion.identity);
|
||||
|
||||
_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;
|
||||
|
||||
var shader = Shabby.Shabby.FindShader(BlitShader);
|
||||
if (shader == null) Debug.LogError($"[ConformalDecals] could not find text blit shader named '{shader}'");
|
||||
_blitMaterial = new Material(Shabby.Shabby.FindShader(BlitShader));
|
||||
|
||||
_isSetup = true;
|
||||
}
|
||||
|
||||
public void RenderText(DecalText text, out Texture2D texture, out Rect window) {
|
||||
// Setup TMP object for rendering
|
||||
_tmp.text = text.FormattedText;
|
||||
_tmp.font = text.Font.fontAsset;
|
||||
_tmp.fontStyle = text.Style.FontStyle | text.Font.fontStyle;
|
||||
_tmp.lineSpacing = text.Style.LineSpacing;
|
||||
_tmp.characterSpacing = text.Style.CharacterSpacing;
|
||||
|
||||
_tmp.enableKerning = true;
|
||||
_tmp.enableWordWrapping = false;
|
||||
_tmp.overflowMode = TextOverflowModes.Overflow;
|
||||
_tmp.alignment = TextAlignmentOptions.Center | TextAlignmentOptions.Baseline;
|
||||
_tmp.fontSize = FontSize;
|
||||
|
||||
// Setup blit material
|
||||
_blitMaterial.SetTexture(PropertyIDs._MainTex, text.Font.fontAsset.atlas);
|
||||
|
||||
// Generate Mesh
|
||||
_tmp.ForceMeshUpdate();
|
||||
var mesh = _tmp.mesh;
|
||||
mesh.RecalculateBounds();
|
||||
var bounds = mesh.bounds;
|
||||
|
||||
// Calculate Sizes
|
||||
var size = bounds.size * PixelDensity;
|
||||
|
||||
var textureSize = new Vector2Int {
|
||||
x = Mathf.NextPowerOfTwo((int) size.x),
|
||||
y = Mathf.NextPowerOfTwo((int) size.y)
|
||||
};
|
||||
|
||||
if (textureSize.x > MaxTextureSize) {
|
||||
textureSize.x /= textureSize.x / MaxTextureSize;
|
||||
textureSize.y /= textureSize.x / MaxTextureSize;
|
||||
}
|
||||
|
||||
if (textureSize.y > MaxTextureSize) {
|
||||
textureSize.x /= textureSize.y / MaxTextureSize;
|
||||
textureSize.y /= textureSize.y / MaxTextureSize;
|
||||
}
|
||||
|
||||
float sizeRatio = Mathf.Min(textureSize.x / size.x, textureSize.y, size.y);
|
||||
|
||||
window = new Rect {
|
||||
size = size * sizeRatio,
|
||||
center = (Vector2) textureSize / 2
|
||||
};
|
||||
|
||||
// Get Texture
|
||||
if (_lastTexture != null) {
|
||||
texture = _lastTexture;
|
||||
texture.Resize(textureSize.x, textureSize.y, TextTextureFormat, false);
|
||||
_lastTexture = null;
|
||||
}
|
||||
else {
|
||||
texture = new Texture2D(textureSize.x, textureSize.y, TextTextureFormat, false);
|
||||
}
|
||||
|
||||
// Generate Projection Matrix
|
||||
var halfSize = window.size / PixelDensity / 2;
|
||||
var matrix = Matrix4x4.Ortho(bounds.center.x - halfSize.x, bounds.center.x + halfSize.x,
|
||||
bounds.center.y - halfSize.y, bounds.center.y + halfSize.y, -1, 1);
|
||||
|
||||
// Get Rendertex
|
||||
var renderTex = RenderTexture.GetTemporary(textureSize.x, textureSize.y, 0, TextRenderTextureFormat, RenderTextureReadWrite.Linear, 1);
|
||||
renderTex.autoGenerateMips = false;
|
||||
|
||||
// Render
|
||||
Graphics.SetRenderTarget(renderTex);
|
||||
GL.PushMatrix();
|
||||
GL.LoadProjectionMatrix(matrix);
|
||||
_blitMaterial.SetPass(0);
|
||||
Graphics.DrawMeshNow(mesh, Matrix4x4.identity);
|
||||
GL.PopMatrix();
|
||||
|
||||
// Copy texture back into RAM
|
||||
RenderTexture.active = renderTex;
|
||||
texture.ReadPixels(new Rect(0, 0, textureSize.x, textureSize.y), 0, 0, false);
|
||||
texture.Apply();
|
||||
|
||||
RenderTexture.ReleaseTemporary(renderTex);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user