diff --git a/GameData/ConformalDecals/Plugins/ConformalDecals.dll b/GameData/ConformalDecals/Plugins/ConformalDecals.dll index 9c63257..18c06f8 100644 Binary files a/GameData/ConformalDecals/Plugins/ConformalDecals.dll and b/GameData/ConformalDecals/Plugins/ConformalDecals.dll differ diff --git a/Source/ConformalDecals/ModuleConformalDecal.cs b/Source/ConformalDecals/ModuleConformalDecal.cs index 64bc2a8..12e8875 100644 --- a/Source/ConformalDecals/ModuleConformalDecal.cs +++ b/Source/ConformalDecals/ModuleConformalDecal.cs @@ -89,8 +89,10 @@ namespace ConformalDecals { [KSPField] public Transform decalProjectorTransform; [KSPField] public Transform decalColliderTransform; - [KSPField] public Material backMaterial; - [KSPField] public Vector2 backTextureBaseScale; + [KSPField] public MeshRenderer boundsRenderer; + [KSPField] public MeshRenderer frontRenderer; + [KSPField] public Material backMaterial; + [KSPField] public Vector2 backTextureBaseScale; private const int DecalQueueMin = 2100; private const int DecalQueueMax = 2400; @@ -101,9 +103,8 @@ namespace ConformalDecals { private bool _isAttached; private Matrix4x4 _orthoMatrix; - private Material _decalMaterial; - private Material _previewMaterial; - private MeshRenderer _boundsRenderer; + private Material _decalMaterial; + private Material _previewMaterial; private int DecalQueue { get { @@ -132,24 +133,33 @@ namespace ConformalDecals { public override void OnLoad(ConfigNode node) { try { // SETUP TRANSFORMS - decalFrontTransform = part.FindModelTransform(decalFront); - if (decalFrontTransform == null) throw new FormatException($"Could not find decalFront transform: '{decalFront}'."); - - decalBackTransform = part.FindModelTransform(decalBack); - if (decalBackTransform == null) throw new FormatException($"Could not find decalBack transform: '{decalBack}'."); + // Projector transform, where the decal is projected from when attached + decalProjectorTransform = part.FindModelTransform(decalProjector); + if (decalProjectorTransform == null) throw new FormatException($"Could not find decalProjector transform: '{decalProjector}'."); + // Model transform, containing all visible elements of the decal when not attached decalModelTransform = part.FindModelTransform(decalModel); if (decalModelTransform == null) throw new FormatException($"Could not find decalModel transform: '{decalModel}'."); - decalProjectorTransform = part.FindModelTransform(decalProjector); - if (decalProjectorTransform == null) throw new FormatException($"Could not find decalProjector transform: '{decalProjector}'."); + // Front transform, shows a preview of the decal when unattached + decalFrontTransform = part.FindModelTransform(decalFront); + if (decalFrontTransform == null) throw new FormatException($"Could not find decalFront transform: '{decalFront}'."); + + frontRenderer = decalFrontTransform.GetComponent(); + // Collider transform, selectable area and shows where the decal is projecting onto decalColliderTransform = part.FindModelTransform(decalCollider); if (decalColliderTransform == null) throw new FormatException($"Could not find decalCollider transform: '{decalCollider}'."); - // SETUP BACK MATERIAL + boundsRenderer = decalColliderTransform.GetComponent(); + + // SETUP BACK if (updateBackScale) { + decalBackTransform = part.FindModelTransform(decalBack); + if (decalBackTransform == null) throw new FormatException($"Could not find decalBack transform: '{decalBack}'."); + var backRenderer = decalBackTransform.GetComponent(); + if (backRenderer == null) { this.LogError($"Specified decalBack transform {decalBack} has no renderer attached! Setting updateBackScale to false."); updateBackScale = false; @@ -244,7 +254,8 @@ namespace ConformalDecals { public override void OnStart(StartState state) { materialProperties.RenderQueue = DecalQueue; - _boundsRenderer = decalProjectorTransform.GetComponent(); + boundsRenderer = decalProjectorTransform.GetComponent(); + frontRenderer = decalFrontTransform.GetComponent(); // handle tweakables if (HighLogic.LoadedSceneIsEditor) { @@ -278,7 +289,7 @@ namespace ConformalDecals { if (!selectableInFlight || !DecalConfig.SelectableInFlight) { decalColliderTransform.GetComponent().enabled = false; - _boundsRenderer.enabled = false; + boundsRenderer.enabled = false; } } } @@ -440,7 +451,7 @@ namespace ConformalDecals { // update projection foreach (var target in _targets) { - target.Project(_orthoMatrix, decalProjectorTransform, _boundsRenderer.bounds, useBaseNormal); + target.Project(_orthoMatrix, decalProjectorTransform, boundsRenderer.bounds, useBaseNormal); } } else { @@ -465,7 +476,7 @@ namespace ConformalDecals { _decalMaterial = materialProperties.DecalMaterial; _previewMaterial = materialProperties.PreviewMaterial; - if (!_isAttached) decalFrontTransform.GetComponent().material = _previewMaterial; + if (!_isAttached) frontRenderer.material = _previewMaterial; } protected void UpdateTargets() { diff --git a/Source/ConformalDecals/ModuleConformalText.cs b/Source/ConformalDecals/ModuleConformalText.cs index 0d6d027..c5db737 100644 --- a/Source/ConformalDecals/ModuleConformalText.cs +++ b/Source/ConformalDecals/ModuleConformalText.cs @@ -89,7 +89,6 @@ namespace ConformalDecals { private MaterialColorProperty _outlineColorProperty; private MaterialFloatProperty _outlineWidthProperty; - private TextRenderJob _currentJob; private DecalText _currentText; public override void OnLoad(ConfigNode node) { @@ -253,7 +252,6 @@ namespace ConformalDecals { decal.charSpacing = charSpacing; decal.lineSpacing = lineSpacing; - decal._currentJob = _currentJob; decal._currentText = _currentText; decal.UpdateText(); } @@ -267,7 +265,7 @@ namespace ConformalDecals { private void UpdateText() { // Render text var newText = new DecalText(text, font, style, vertical, lineSpacing, charSpacing); - var output = TextRenderer.UpdateTextNow(_currentText, newText); + var output = TextRenderer.UpdateText(_currentText, newText); _currentText = newText; UpdateTexture(output); diff --git a/Source/ConformalDecals/Text/TextRenderJob.cs b/Source/ConformalDecals/Text/TextRenderJob.cs deleted file mode 100644 index 475de9b..0000000 --- a/Source/ConformalDecals/Text/TextRenderJob.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using UnityEngine.Events; - -namespace ConformalDecals.Text { - public class TextRenderJob { - public DecalText OldText { get; } - public DecalText NewText { get; } - public bool Needed { get; private set; } - public bool IsStarted { get; private set; } - public bool IsDone { get; private set; } - - public readonly TextRenderer.TextRenderEvent onRenderFinished = new TextRenderer.TextRenderEvent(); - - public TextRenderJob(DecalText oldText, DecalText newText, UnityAction renderFinishedCallback) { - OldText = oldText; - NewText = newText ?? throw new ArgumentNullException(nameof(newText)); - Needed = true; - - if (renderFinishedCallback != null) onRenderFinished.AddListener(renderFinishedCallback); - } - - public void Cancel() { - Needed = false; - } - - public void Start() { - IsStarted = true; - } - - public void Finish(TextRenderOutput output) { - IsDone = true; - onRenderFinished.Invoke(output); - } - } -} \ No newline at end of file diff --git a/Source/ConformalDecals/Text/TextRenderer.cs b/Source/ConformalDecals/Text/TextRenderer.cs index 7a527ea..a807873 100644 --- a/Source/ConformalDecals/Text/TextRenderer.cs +++ b/Source/ConformalDecals/Text/TextRenderer.cs @@ -3,16 +3,12 @@ using System.Collections.Generic; using ConformalDecals.Util; using TMPro; using UnityEngine; -using UnityEngine.Events; using UnityEngine.Rendering; +using Object = UnityEngine.Object; namespace ConformalDecals.Text { - // TODO: Testing shows the job system is unnecessary, so remove job system code. - /// Class handing text rendering. - /// Is a singleton referencing a single gameobject in the scene which contains the TextMeshPro component - [KSPAddon(KSPAddon.Startup.Instantly, true)] - public class TextRenderer : MonoBehaviour { + public static class TextRenderer { /// Texture format used for returned textures. /// Unfortunately due to how Unity textures work, this cannot be R8 or Alpha8, /// so theres always a superfluous green channel using memory @@ -22,142 +18,55 @@ namespace ConformalDecals.Text { /// Overriden below to be ARGB32 on DirectX because DirectX is dumb public static RenderTextureFormat textRenderTextureFormat = RenderTextureFormat.R8; - /// The text renderer object within the scene which contains the TextMeshPro component used for rendering. - public static TextRenderer Instance { - get { - if (!_instance._isSetup) { - _instance.Setup(); - } - - return _instance; - } - } - - /// Text Render unityevent, used with the job system to signal render completion - [Serializable] - public class TextRenderEvent : UnityEvent { } - private const string ShaderName = "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 Shader _blitShader; + private static Shader _blitShader; + private static Texture2D _blankTexture; private static readonly Dictionary RenderCache = new Dictionary(); - private static readonly Queue RenderJobs = new Queue(); - private static Texture2D _blankTexture; - - /// Update text using the job queue - public static TextRenderJob UpdateText(DecalText oldText, DecalText newText, UnityAction renderFinishedCallback) { + /// Update text immediately without using job queue + public static TextRenderOutput UpdateText(DecalText oldText, DecalText newText) { if (newText == null) throw new ArgumentNullException(nameof(newText)); + Logging.Log($"Rendering text {newText}"); - var job = new TextRenderJob(oldText, newText, renderFinishedCallback); - RenderJobs.Enqueue(job); - return job; - } + if (!(oldText is null)) UnregisterText(oldText); - /// Update text immediately without using job queue - public static TextRenderOutput UpdateTextNow(DecalText oldText, DecalText newText) { - if (newText == null) throw new ArgumentNullException(nameof(newText)); + // now that all old references are handled, begin rendering the new output + if (!RenderCache.TryGetValue(newText, out var renderOutput)) { + renderOutput = RenderText(newText); + RenderCache.Add(newText, renderOutput); + } - return Instance.RunJob(new TextRenderJob(oldText, newText, null), out _); + renderOutput.UserCount++; + return renderOutput; } /// Unregister a user of a piece of text public static void UnregisterText(DecalText text) { if (text == null) throw new ArgumentNullException(nameof(text)); - + if (RenderCache.TryGetValue(text, out var renderedText)) { renderedText.UserCount--; if (renderedText.UserCount <= 0) { RenderCache.Remove(text); var texture = renderedText.Texture; - if (texture != _blankTexture) Destroy(texture); + if (texture != _blankTexture) Object.Destroy(texture); } } } - private void Start() { - if (_instance != null) { - Logging.LogError("Duplicate TextRenderer created???"); - } - - Logging.Log("Creating TextRenderer Object"); - _instance = this; - DontDestroyOnLoad(gameObject); - if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11 || SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D12) { - textRenderTextureFormat = RenderTextureFormat.ARGB32; // DirectX is dumb - } - - if (!SystemInfo.SupportsTextureFormat(textTextureFormat)) { - Logging.LogError($"Text texture format {textTextureFormat} not supported on this platform."); - } - - if (!SystemInfo.SupportsRenderTextureFormat(textRenderTextureFormat)) { - Logging.LogError($"Text texture format {textRenderTextureFormat} not supported on this platform."); - } - - _blankTexture = Texture2D.blackTexture; - } - - /// Setup this text renderer instance for rendering - private void Setup() { - if (_isSetup) return; - - Logging.Log("Setting Up TextRenderer Object"); - - // _tmp = gameObject.AddComponent(); - // _tmp.renderer.enabled = false; // dont automatically render - - _blitShader = Shabby.Shabby.FindShader(ShaderName); - if (_blitShader == null) Logging.LogError($"Could not find text blit shader named '{ShaderName}'"); - - _isSetup = true; - } - - /// Run a text render job - private TextRenderOutput RunJob(TextRenderJob job, out bool renderNeeded) { - if (!job.Needed) { - renderNeeded = false; - return null; - } - - Logging.Log($"Rendering text {job.NewText}"); - - job.Start(); - if (!(job.OldText is null)) UnregisterText(job.OldText); - - // now that all old references are handled, begin rendering the new output - - if (RenderCache.TryGetValue(job.NewText, out var renderOutput)) { - renderNeeded = false; - } - else { - renderNeeded = true; - - renderOutput = RenderText(job.NewText); - RenderCache.Add(job.NewText, renderOutput); - } - - renderOutput.UserCount++; - - job.Finish(renderOutput); - return renderOutput; - } /// Render a piece of text to a given texture - public TextRenderOutput RenderText(DecalText text) { + public static TextRenderOutput RenderText(DecalText text) { + if (text == null) throw new ArgumentNullException(nameof(text)); + var tmpObject = new GameObject("Text Mesh Pro renderer"); var tmp = tmpObject.AddComponent(); - if (text == null) throw new ArgumentNullException(nameof(text)); - if (tmp == null) throw new InvalidOperationException("TextMeshPro object not yet created."); - // SETUP TMP OBJECT FOR RENDERING tmp.text = text.FormattedText; tmp.font = text.Font.FontAsset; @@ -189,7 +98,7 @@ namespace ConformalDecals.Text { meshes[i] = meshFilters[i].mesh; if (i == 0) meshes[i] = tmp.mesh; - materials[i] = Instantiate(renderer.material); + materials[i] = Object.Instantiate(renderer.material); materials[i].shader = _blitShader; if (renderer == null) throw new FormatException($"Object {meshFilters[i].gameObject.name} has filter but no renderer"); @@ -215,6 +124,7 @@ namespace ConformalDecals.Text { if (textureSize.x == 0 || textureSize.y == 0) { Logging.LogError("No text present or error in texture size calculation. Aborting."); + Object.Destroy(tmpObject); return new TextRenderOutput(_blankTexture, Rect.zero); } @@ -276,10 +186,30 @@ namespace ConformalDecals.Text { // RELEASE RENDERTEX RenderTexture.ReleaseTemporary(renderTex); - // CLEAR SUBMESHES - Destroy(tmpObject); + // DESTROY THE RENDERER OBJECT + Object.Destroy(tmpObject); return new TextRenderOutput(texture, window); } + + /// Setup shader and texture + public static void ModuleManagerPostLoad() { + if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11 || SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D12) { + textRenderTextureFormat = RenderTextureFormat.ARGB32; // DirectX is dumb + } + + if (!SystemInfo.SupportsTextureFormat(textTextureFormat)) { + Logging.LogError($"Text texture format {textTextureFormat} not supported on this platform."); + } + + if (!SystemInfo.SupportsRenderTextureFormat(textRenderTextureFormat)) { + Logging.LogError($"Text texture format {textRenderTextureFormat} not supported on this platform."); + } + + _blankTexture = Texture2D.blackTexture; + _blitShader = Shabby.Shabby.FindShader(ShaderName); + if (_blitShader == null) Logging.LogError($"Could not find text blit shader named '{ShaderName}'"); + } + } } \ No newline at end of file diff --git a/changelog.txt b/changelog.txt index b9077f1..a53a127 100644 --- a/changelog.txt +++ b/changelog.txt @@ -3,6 +3,8 @@ v0.2.7 - Fixes: - Fixed certain non-ascii strings not rendering correctly under certain circumstances. - Yet another attempted fix for the planet text glitch. +- Changes: + - Small optimizations and simplified text rendering. v0.2.6 ------