17 Commits
0.2.5 ... 0.2.7

Author SHA1 Message Date
938babb41b Merge branch 'main' into release 2020-12-19 17:08:13 -08:00
3173dd914a Update version and remove debug statement 2020-12-19 17:06:52 -08:00
5f6712f476 Revert changes to ModuleConformalDecal.cs 2020-12-19 16:58:15 -08:00
6b7996fdd7 Text render simplification and small optimizations 2020-12-19 16:11:56 -08:00
5feb16dcfb Only update text once per frame
Fixed text re-rendering several times in a single frame when pasting in text
2020-12-18 21:14:34 -08:00
bf8e98caf0 Fix text rendering for some non-ascii strings 2020-12-17 16:14:33 -08:00
b634eb1e8e Update version and changelog 2020-12-16 13:04:46 -08:00
dadf38acd5 go back to using temporary rendertexs 2020-12-16 01:28:57 -08:00
f42e0d78d6 Don't reuse textures, and don't keep them in RAM
Hopefully fixes #28
2020-12-16 01:11:32 -08:00
1e7c6d81c9 Merge branch 'main' into release 2020-11-29 16:31:55 -08:00
dda988db17 Quick bugfix 2020-11-29 16:31:41 -08:00
900061f7f6 Merge branch 'main' into release 2020-11-29 16:09:03 -08:00
e56278c6cb Fix stock flags being stretched 2020-11-29 16:02:45 -08:00
2793f5fcb1 Fix DLL copying 2020-11-26 19:17:31 -08:00
833ec43a52 Update project files 2020-11-26 17:47:42 -08:00
e6856124e7 Another attempt at fixing the planet glitch 2020-11-25 02:44:04 -08:00
8ed7a130ab Alternate rendertex handling 2020-11-22 20:18:16 -08:00
18 changed files with 176 additions and 358 deletions

1
.gitignore vendored
View File

@ -50,3 +50,4 @@ Source/ConformalDecals/bin
*.sublime* *.sublime*
.idea .idea
obj obj
*.swp

View File

@ -30,6 +30,8 @@ public class TextRenderTest : MonoBehaviour {
Debug.Log("starting..."); Debug.Log("starting...");
StartCoroutine(OnRender()); StartCoroutine(OnRender());
var thing = new GameObject();
thing.AddComponent<TextMeshPro>();
} }
// Update is called once per frame // Update is called once per frame

View File

@ -6,14 +6,14 @@
{ {
"MAJOR":0, "MAJOR":0,
"MINOR":2, "MINOR":2,
"PATCH":5, "PATCH":7,
"BUILD":0 "BUILD":0
}, },
"KSP_VERSION": "KSP_VERSION":
{ {
"MAJOR":1, "MAJOR":1,
"MINOR":10, "MINOR":11,
"PATCH":1 "PATCH":0
}, },
"KSP_VERSION_MIN":{ "KSP_VERSION_MIN":{
"MAJOR":1, "MAJOR":1,
@ -22,7 +22,7 @@
}, },
"KSP_VERSION_MAX":{ "KSP_VERSION_MAX":{
"MAJOR":1, "MAJOR":1,
"MINOR":10, "MINOR":11,
"PATCH":99 "PATCH":99
} }
} }

View File

@ -1,5 +1,5 @@
# Conformal Decals v0.2.5 # Conformal Decals v0.2.7
[![Build Status](https://travis-ci.org/drewcassidy/KSP-Conformal-Decals.svg?branch=release)](https://travis-ci.org/drewcassidy/KSP-Conformal-Decals) [![Art: CC BY-SA 4.0](https://img.shields.io/badge/Art%20License-CC%20BY--SA%204.0-orange.svg)](https://creativecommons.org/licenses/by-sa/4.0/) [![Code: GPL v3](https://img.shields.io/badge/Code%20License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) [![Build Status](https://travis-ci.com/drewcassidy/KSP-Conformal-Decals.svg?branch=release)](https://travis-ci.com/drewcassidy/KSP-Conformal-Decals) [![Art: CC BY-SA 4.0](https://img.shields.io/badge/Art%20License-CC%20BY--SA%204.0-orange.svg)](https://creativecommons.org/licenses/by-sa/4.0/) [![Code: GPL v3](https://img.shields.io/badge/Code%20License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
![Screenshot](http://pileof.rocks/KSP/images/ConformalDecalsHeader.png) ![Screenshot](http://pileof.rocks/KSP/images/ConformalDecalsHeader.png)

View File

@ -1,6 +1,6 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConformalDecals", "ConformalDecals/ConformalDecals.csproj", "{1ea983f9-42e5-494e-9683-fdac9c9121f4}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConformalDecals", "ConformalDecals/ConformalDecals.csproj", "{1EA983F9-42E5-494E-9683-FDAC9C9121F4}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -8,9 +8,9 @@ Global
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1ea983f9-42e5-494e-9683-fdac9c9121f4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1EA983F9-42E5-494E-9683-FDAC9C9121F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1ea983f9-42e5-494e-9683-fdac9c9121f4}.Debug|Any CPU.Build.0 = Debug|Any CPU {1EA983F9-42E5-494E-9683-FDAC9C9121F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1ea983f9-42e5-494e-9683-fdac9c9121f4}.Release|Any CPU.ActiveCfg = Release|Any CPU {1EA983F9-42E5-494E-9683-FDAC9C9121F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1ea983f9-42e5-494e-9683-fdac9c9121f4}.Release|Any CPU.Build.0 = Release|Any CPU {1EA983F9-42E5-494E-9683-FDAC9C9121F4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal

View File

@ -1,115 +1,56 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)/$(MSBuildToolsVersion)/Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)/$(MSBuildToolsVersion)/Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <TargetFramework>net48</TargetFramework>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{1ea983f9-42e5-494e-9683-fdac9c9121f4}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<LangVersion>8</LangVersion> <LangVersion>8</LangVersion>
<RootNamespace>ConformalDecals</RootNamespace> <IsPackable>false</IsPackable>
</PropertyGroup> <PlatformTarget>x64</PlatformTarget>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <NoWarn>1701;1702;CS0649;CS1591</NoWarn>
<DebugSymbols>true</DebugSymbols> <AssemblyVersion>0.2.7</AssemblyVersion>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin/Debug/</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin/Release/</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin/Release/ConformalDecals.xml</DocumentationFile>
<NoWarn>CS1591,CS0649</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"> <Reference Include="Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
<HintPath>dlls/Assembly-CSharp.dll</HintPath> <HintPath>dlls\Assembly-CSharp.dll</HintPath>
</Reference>
<Reference Include="KSPAssets, Version=1.4.0.0, Culture=neutral, PublicKeyToken=null">
<HintPath>dlls\KSPAssets.dll</HintPath>
</Reference> </Reference>
<Reference Include="Shabby, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"> <Reference Include="Shabby, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
<HintPath>dlls/Shabby.dll</HintPath> <HintPath>dlls\Shabby.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" />
<Reference Include="UnityEngine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"> <Reference Include="UnityEngine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
<HintPath>dlls/UnityEngine.dll</HintPath> <HintPath>dlls\UnityEngine.dll</HintPath>
</Reference> </Reference>
<Reference Include="UnityEngine.AssetBundleModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"> <Reference Include="UnityEngine.AssetBundleModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
<HintPath>dlls/UnityEngine.AssetBundleModule.dll</HintPath> <HintPath>dlls\UnityEngine.AssetBundleModule.dll</HintPath>
</Reference> </Reference>
<Reference Include="UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"> <Reference Include="UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
<HintPath>dlls/UnityEngine.CoreModule.dll</HintPath> <HintPath>dlls\UnityEngine.CoreModule.dll</HintPath>
</Reference> </Reference>
<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 Include="UnityEngine.TextCoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
<HintPath>dlls/UnityEngine.TextCoreModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.TextRenderingModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
<HintPath>dlls/UnityEngine.TextRenderingModule.dll</HintPath>
</Reference> </Reference>
<Reference Include="UnityEngine.UI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"> <Reference Include="UnityEngine.UI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<HintPath>dlls/UnityEngine.UI.dll</HintPath> <HintPath>dlls\UnityEngine.UI.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UIElementsModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
<HintPath>dlls/UnityEngine.UIElementsModule.dll</HintPath>
</Reference> </Reference>
<Reference Include="UnityEngine.UIModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"> <Reference Include="UnityEngine.UIModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
<HintPath>dlls/UnityEngine.UIModule.dll</HintPath> <HintPath>dlls\UnityEngine.UIModule.dll</HintPath>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="DecalConfig.cs" /> <Compile Remove="dlls\**" />
<Compile Include="DecalIconFixer.cs" />
<Compile Include="DecalPropertyIDs.cs" />
<Compile Include="MaterialProperties/MaterialColorProperty.cs" />
<Compile Include="MaterialProperties/MaterialFloatProperty.cs" />
<Compile Include="MaterialProperties/MaterialKeywordProperty.cs" />
<Compile Include="MaterialProperties/MaterialProperty.cs" />
<Compile Include="MaterialProperties/MaterialPropertyCollection.cs" />
<Compile Include="MaterialProperties/MaterialTextureProperty.cs" />
<Compile Include="ModuleConformalFlag.cs" />
<Compile Include="ModuleConformalText.cs" />
<Compile Include="ProjectionTarget.cs" />
<Compile Include="ModuleConformalDecal.cs" />
<Compile Include="Properties/AssemblyInfo.cs" />
<Compile Include="Text/DecalFont.cs" />
<Compile Include="Text/FontLoader.cs" />
<Compile Include="Text/TextRenderer.cs" />
<Compile Include="Text/DecalText.cs" />
<Compile Include="Text\TextRenderOutput.cs" />
<Compile Include="Text\TextRenderJob.cs" />
<Compile Include="UI/ColorPickerController.cs" />
<Compile Include="UI/FontMenuController.cs" />
<Compile Include="UI/FontMenuItem.cs" />
<Compile Include="UI/TextEntryController.cs" />
<Compile Include="UI/UILoader.cs" />
<Compile Include="UI/UITag.cs" />
<Compile Include="UI\ColorBoxSlider.cs" />
<Compile Include="UI\ColorChannelSlider.cs" />
<Compile Include="Util/Logging.cs" />
<Compile Include="Util/OrientedBounds.cs" />
<Compile Include="Util/ParseUtil.cs" />
<Compile Include="UI/BoxSlider.cs" />
<Compile Include="Util\ColorHSL.cs" />
<Compile Include="Util\ColorHSV.cs" />
<Compile Include="Util\ColorUtil.cs" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)/Microsoft.CSharp.targets" />
<PropertyGroup> <ItemGroup>
<PostBuildEvent>sh -e -c "cp -v '$(TargetPath)' '$(SolutionDir)/../GameData/ConformalDecals/Plugins'"</PostBuildEvent> <EmbeddedResource Remove="dlls\**" />
</PropertyGroup> </ItemGroup>
<ItemGroup>
<None Remove="dlls\**" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="/bin/cp -v '$(OutDir)ConformalDecals.dll' '$(SolutionDir)../GameData/ConformalDecals/Plugins'" IgnoreExitCode="true" />
<!--Fuck you MSBuild stop trying to run CMD.exe on macOS-->
</Target>
</Project> </Project>

View File

@ -41,7 +41,13 @@ namespace ConformalDecals.MaterialProperties {
public Vector2 Dimensions => new Vector2(_texture.width, _texture.height); public Vector2 Dimensions => new Vector2(_texture.width, _texture.height);
public Vector2 MaskedDimensions => _hasTile ? _tileRect.size : Dimensions; public Vector2 MaskedDimensions => _hasTile ? _tileRect.size : Dimensions;
public float AspectRatio => MaskedHeight / (float) MaskedWidth; public float AspectRatio {
get {
if (_texture == null) return 1;
if (_textureUrl?.Contains("Squad/Flags") == true) return 0.625f;
return MaskedHeight / (float) MaskedWidth;
}
}
public override void ParseNode(ConfigNode node) { public override void ParseNode(ConfigNode node) {
base.ParseNode(node); base.ParseNode(node);

View File

@ -400,7 +400,7 @@ namespace ConformalDecals {
protected void UpdateScale() { protected void UpdateScale() {
scale = Mathf.Max(0.01f, scale); scale = Mathf.Max(0.01f, scale);
depth = Mathf.Max(0.01f, depth); depth = Mathf.Max(0.01f, depth);
var aspectRatio = materialProperties.AspectRatio; var aspectRatio = Mathf.Max(0.01f, materialProperties.AspectRatio);
Vector2 size; Vector2 size;
switch (scaleMode) { switch (scaleMode) {

View File

@ -89,7 +89,6 @@ namespace ConformalDecals {
private MaterialColorProperty _outlineColorProperty; private MaterialColorProperty _outlineColorProperty;
private MaterialFloatProperty _outlineWidthProperty; private MaterialFloatProperty _outlineWidthProperty;
private TextRenderJob _currentJob;
private DecalText _currentText; private DecalText _currentText;
public override void OnLoad(ConfigNode node) { public override void OnLoad(ConfigNode node) {
@ -253,7 +252,6 @@ namespace ConformalDecals {
decal.charSpacing = charSpacing; decal.charSpacing = charSpacing;
decal.lineSpacing = lineSpacing; decal.lineSpacing = lineSpacing;
decal._currentJob = _currentJob;
decal._currentText = _currentText; decal._currentText = _currentText;
decal.UpdateText(); decal.UpdateText();
} }
@ -267,7 +265,7 @@ namespace ConformalDecals {
private void UpdateText() { private void UpdateText() {
// Render text // Render text
var newText = new DecalText(text, font, style, vertical, lineSpacing, charSpacing); var newText = new DecalText(text, font, style, vertical, lineSpacing, charSpacing);
var output = TextRenderer.UpdateTextNow(_currentText, newText); var output = TextRenderer.UpdateText(_currentText, newText);
_currentText = newText; _currentText = newText;
UpdateTexture(output); UpdateTexture(output);

View File

@ -1,38 +1 @@
using System.Reflection; [assembly: KSPAssembly("ConformalDecals", 0, 2)]
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("ConformalDecals")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Cineboxandrew")]
[assembly: AssemblyProduct("ConformalDecals")]
[assembly: AssemblyCopyright("Copyright © Andrew Cassidy 2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("1ea983f9-42e5-494e-9683-fdac9c9121f4")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: KSPAssembly("ConformalDecals", 0, 1, 0)]

View File

@ -105,5 +105,9 @@ namespace ConformalDecals.Text {
public void OnBeforeSerialize() { } public void OnBeforeSerialize() { }
public void OnAfterDeserialize() { } public void OnAfterDeserialize() { }
public override string ToString() {
return _title;
}
} }
} }

View File

@ -86,5 +86,9 @@ namespace ConformalDecals.Text {
public static bool operator !=(DecalText left, DecalText right) { public static bool operator !=(DecalText left, DecalText right) {
return !Equals(left, right); return !Equals(left, right);
} }
public override string ToString() {
return $"{nameof(_text)}: {_text}, {nameof(_font)}: {_font}, {nameof(_style)}: {_style}, {nameof(_vertical)}: {_vertical}, {nameof(_lineSpacing)}: {_lineSpacing}, {nameof(_charSpacing)}: {_charSpacing}";
}
} }
} }

View File

@ -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<TextRenderOutput> 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);
}
}
}

View File

@ -9,7 +9,7 @@ namespace ConformalDecals.Text {
/// The rectangle that the rendered text takes up within the texture /// The rectangle that the rendered text takes up within the texture
public Rect Window { get; private set; } public Rect Window { get; private set; }
/// The number of users for this render output. If 0, it can be discarded from the cache and the texture reused /// The number of users for this render output. If 0, it can be discarded from the cache
public int UserCount { get; set; } public int UserCount { get; set; }
public TextRenderOutput(Texture2D texture, Rect window) { public TextRenderOutput(Texture2D texture, Rect window) {

View File

@ -3,16 +3,12 @@ using System.Collections.Generic;
using ConformalDecals.Util; using ConformalDecals.Util;
using TMPro; using TMPro;
using UnityEngine; using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Rendering; using UnityEngine.Rendering;
using Object = UnityEngine.Object;
namespace ConformalDecals.Text { namespace ConformalDecals.Text {
// TODO: Testing shows the job system is unnecessary, so remove job system code.
/// Class handing text rendering. /// Class handing text rendering.
/// Is a singleton referencing a single gameobject in the scene which contains the TextMeshPro component public static class TextRenderer {
[KSPAddon(KSPAddon.Startup.Instantly, true)]
public class TextRenderer : MonoBehaviour {
/// Texture format used for returned textures. /// Texture format used for returned textures.
/// Unfortunately due to how Unity textures work, this cannot be R8 or Alpha8, /// Unfortunately due to how Unity textures work, this cannot be R8 or Alpha8,
/// so theres always a superfluous green channel using memory /// so theres always a superfluous green channel using memory
@ -22,163 +18,73 @@ namespace ConformalDecals.Text {
/// Overriden below to be ARGB32 on DirectX because DirectX is dumb /// Overriden below to be ARGB32 on DirectX because DirectX is dumb
public static RenderTextureFormat textRenderTextureFormat = RenderTextureFormat.R8; 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<TextRenderOutput> { }
private const string ShaderName = "ConformalDecals/Text Blit"; private const string ShaderName = "ConformalDecals/Text Blit";
private const int MaxTextureSize = 4096; private const int MaxTextureSize = 4096;
private const float FontSize = 100; private const float FontSize = 100;
private const float PixelDensity = 5; private const float PixelDensity = 5;
private static TextRenderer _instance; private static Shader _blitShader;
private static Texture2D _blankTexture;
private bool _isSetup;
private TextMeshPro _tmp;
private Shader _blitShader;
private static readonly Dictionary<DecalText, TextRenderOutput> RenderCache = new Dictionary<DecalText, TextRenderOutput>(); private static readonly Dictionary<DecalText, TextRenderOutput> RenderCache = new Dictionary<DecalText, TextRenderOutput>();
private static readonly Queue<TextRenderJob> RenderJobs = new Queue<TextRenderJob>();
/// Update text using the job queue
public static TextRenderJob UpdateText(DecalText oldText, DecalText newText, UnityAction<TextRenderOutput> renderFinishedCallback) {
if (newText == null) throw new ArgumentNullException(nameof(newText));
var job = new TextRenderJob(oldText, newText, renderFinishedCallback);
RenderJobs.Enqueue(job);
return job;
}
/// Update text immediately without using job queue /// Update text immediately without using job queue
public static TextRenderOutput UpdateTextNow(DecalText oldText, DecalText newText) { public static TextRenderOutput UpdateText(DecalText oldText, DecalText newText) {
if (newText == null) throw new ArgumentNullException(nameof(newText)); if (newText == null) throw new ArgumentNullException(nameof(newText));
return Instance.RunJob(new TextRenderJob(oldText, newText, null), out _); if (!(oldText is null)) UnregisterText(oldText);
// 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);
}
renderOutput.UserCount++;
return renderOutput;
} }
/// Unregister a user of a piece of text /// Unregister a user of a piece of text
public static void UnregisterText(DecalText text) { public static void UnregisterText(DecalText text) {
if (text == null) throw new ArgumentNullException(nameof(text));
if (RenderCache.TryGetValue(text, out var renderedText)) { if (RenderCache.TryGetValue(text, out var renderedText)) {
renderedText.UserCount--; renderedText.UserCount--;
if (renderedText.UserCount <= 0) { if (renderedText.UserCount <= 0) {
RenderCache.Remove(text); RenderCache.Remove(text);
Destroy(renderedText.Texture); var texture = renderedText.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.");
}
}
/// Setup this text renderer instance for rendering
private void Setup() {
if (_isSetup) return;
Logging.Log("Setting Up TextRenderer Object");
_tmp = gameObject.AddComponent<TextMeshPro>();
_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;
}
job.Start();
Texture2D texture = null;
if (job.OldText != null && RenderCache.TryGetValue(job.OldText, out var oldRender)) {
// old output still exists
oldRender.UserCount--;
if (oldRender.UserCount <= 0) {
// this is the only usage of this output, so we are free to re-render into the texture
texture = oldRender.Texture;
RenderCache.Remove(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, texture);
RenderCache.Add(job.NewText, renderOutput);
}
renderOutput.UserCount++;
job.Finish(renderOutput);
return renderOutput;
}
/// Render a piece of text to a given texture /// Render a piece of text to a given texture
public TextRenderOutput RenderText(DecalText text, Texture2D texture) { public static TextRenderOutput RenderText(DecalText text) {
if (text == null) throw new ArgumentNullException(nameof(text)); if (text == null) throw new ArgumentNullException(nameof(text));
if (_tmp == null) throw new InvalidOperationException("TextMeshPro object not yet created.");
var tmpObject = new GameObject("Text Mesh Pro renderer");
var tmp = tmpObject.AddComponent<TextMeshPro>();
// SETUP TMP OBJECT FOR RENDERING // SETUP TMP OBJECT FOR RENDERING
_tmp.text = text.FormattedText; tmp.text = text.FormattedText;
_tmp.font = text.Font.FontAsset; tmp.font = text.Font.FontAsset;
_tmp.fontStyle = text.Style | text.Font.FontStyle; tmp.fontStyle = text.Style | text.Font.FontStyle;
_tmp.lineSpacing = text.LineSpacing; tmp.lineSpacing = text.LineSpacing;
_tmp.characterSpacing = text.CharSpacing; tmp.characterSpacing = text.CharSpacing;
_tmp.extraPadding = true; tmp.extraPadding = true;
_tmp.enableKerning = true; tmp.enableKerning = true;
_tmp.enableWordWrapping = false; tmp.enableWordWrapping = false;
_tmp.overflowMode = TextOverflowModes.Overflow; tmp.overflowMode = TextOverflowModes.Overflow;
_tmp.alignment = TextAlignmentOptions.Center; tmp.alignment = TextAlignmentOptions.Center;
_tmp.fontSize = FontSize; tmp.fontSize = FontSize;
// GENERATE MESH // GENERATE MESH
_tmp.ClearMesh(false); tmp.ClearMesh(false);
_tmp.ForceMeshUpdate(); tmp.ForceMeshUpdate();
var meshFilters = gameObject.GetComponentsInChildren<MeshFilter>(); var meshFilters = tmpObject.GetComponentsInChildren<MeshFilter>();
var meshes = new Mesh[meshFilters.Length]; var meshes = new Mesh[meshFilters.Length];
var materials = new Material[meshFilters.Length]; var materials = new Material[meshFilters.Length];
@ -189,9 +95,9 @@ namespace ConformalDecals.Text {
var renderer = meshFilters[i].gameObject.GetComponent<MeshRenderer>(); var renderer = meshFilters[i].gameObject.GetComponent<MeshRenderer>();
meshes[i] = meshFilters[i].mesh; meshes[i] = meshFilters[i].mesh;
if (i == 0) meshes[i] = _tmp.mesh; if (i == 0) meshes[i] = tmp.mesh;
materials[i] = Instantiate(renderer.material); materials[i] = Object.Instantiate(renderer.material);
materials[i].shader = _blitShader; materials[i].shader = _blitShader;
if (renderer == null) throw new FormatException($"Object {meshFilters[i].gameObject.name} has filter but no renderer"); if (renderer == null) throw new FormatException($"Object {meshFilters[i].gameObject.name} has filter but no renderer");
@ -216,8 +122,9 @@ namespace ConformalDecals.Text {
}; };
if (textureSize.x == 0 || textureSize.y == 0) { if (textureSize.x == 0 || textureSize.y == 0) {
Logging.LogWarning("No text present or error in texture size calculation. Aborting."); Logging.LogError("No text present or error in texture size calculation. Aborting.");
return new TextRenderOutput(Texture2D.blackTexture, Rect.zero); Object.Destroy(tmpObject);
return new TextRenderOutput(_blankTexture, Rect.zero);
} }
// make sure texture isnt too big, scale it down if it is // make sure texture isnt too big, scale it down if it is
@ -242,12 +149,7 @@ namespace ConformalDecals.Text {
}; };
// SETUP TEXTURE // SETUP TEXTURE
if (texture == null) { var texture = new Texture2D(textureSize.x, textureSize.y, textTextureFormat, false);
texture = new Texture2D(textureSize.x, textureSize.y, textTextureFormat, true);
}
else if (texture.width != textureSize.x || texture.height != textureSize.y || texture.format != textTextureFormat) {
texture.Resize(textureSize.x, textureSize.y, textTextureFormat, true);
}
// GENERATE PROJECTION MATRIX // GENERATE PROJECTION MATRIX
var halfSize = (Vector2) textureSize / PixelDensity / 2 / sizeRatio; var halfSize = (Vector2) textureSize / PixelDensity / 2 / sizeRatio;
@ -255,8 +157,7 @@ namespace ConformalDecals.Text {
bounds.center.y - halfSize.y, bounds.center.y + halfSize.y, -1, 1); bounds.center.y - halfSize.y, bounds.center.y + halfSize.y, -1, 1);
// GET RENDERTEX // GET RENDERTEX
var renderTex = RenderTexture.GetTemporary(textureSize.x, textureSize.y, 0, textRenderTextureFormat, RenderTextureReadWrite.Linear, 1); var renderTex = RenderTexture.GetTemporary(textureSize.x, textureSize.y, 0, textRenderTextureFormat, RenderTextureReadWrite.Linear);
renderTex.autoGenerateMips = false;
// RENDER // RENDER
Graphics.SetRenderTarget(renderTex); Graphics.SetRenderTarget(renderTex);
@ -272,26 +173,42 @@ namespace ConformalDecals.Text {
} }
} }
// COPY TEXTURE BACK INTO RAM // COPY RENDERTEX INTO TEXTURE
var prevRT = RenderTexture.active;
RenderTexture.active = renderTex; RenderTexture.active = renderTex;
texture.ReadPixels(new Rect(0, 0, textureSize.x, textureSize.y), 0, 0, true); texture.ReadPixels(new Rect(0, 0, textureSize.x, textureSize.y), 0, 0, false);
texture.Apply(); texture.Apply(false, true);
RenderTexture.active = prevRT;
GL.Clear(false, true, Color.black); //KSP doesnt clear render textures before using them so we need to clear afterwards, as well. Thanks Squad.
GL.PopMatrix(); GL.PopMatrix();
// RELEASE RENDERTEX // RELEASE RENDERTEX
RenderTexture.ReleaseTemporary(renderTex); RenderTexture.ReleaseTemporary(renderTex);
// CLEAR SUBMESHES // DESTROY THE RENDERER OBJECT
_tmp.text = ""; Object.Destroy(tmpObject);
for (int i = 0; i < transform.childCount; i++) {
var child = transform.GetChild(i);
Destroy(child.gameObject);
}
return new TextRenderOutput(texture, window); 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}'");
}
} }
} }

View File

@ -3,7 +3,6 @@ using ConformalDecals.Text;
using ConformalDecals.Util; using ConformalDecals.Util;
using TMPro; using TMPro;
using UnityEngine; using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI; using UnityEngine.UI;
namespace ConformalDecals.UI { namespace ConformalDecals.UI {
@ -35,14 +34,15 @@ namespace ConformalDecals.UI {
private Vector2 _lineSpacingRange; private Vector2 _lineSpacingRange;
private Vector2 _charSpacingRange; private Vector2 _charSpacingRange;
private TMP_InputField _textBoxTMP; private TMP_InputField _textBoxTMP;
private FontMenuController _fontMenu;
private TextUpdateDelegate _onValueChanged; private TextUpdateDelegate _onValueChanged;
private FontMenuController _fontMenu; private static int _lockCounter;
private bool _ignoreUpdates;
private bool _isLocked; private bool _isLocked;
private string _lockString; private string _lockString;
private static int _lockCounter; private bool _ignoreUpdates;
private bool _textUpdated;
public static TextEntryController Create( public static TextEntryController Create(
string text, DecalFont font, FontStyles style, bool vertical, float linespacing, float charspacing, string text, DecalFont font, FontStyles style, bool vertical, float linespacing, float charspacing,
@ -74,7 +74,7 @@ namespace ConformalDecals.UI {
public void SetControlLock(string value = null) { public void SetControlLock(string value = null) {
if (_isLocked) return; if (_isLocked) return;
InputLockManager.SetControlLock(_lockString); InputLockManager.SetControlLock(ControlTypes.EDITOR_UI, _lockString);
_isLocked = true; _isLocked = true;
} }
@ -86,8 +86,7 @@ namespace ConformalDecals.UI {
public void OnTextUpdate(string newText) { public void OnTextUpdate(string newText) {
this._text = newText; this._text = newText;
_textUpdated = true;
OnValueChanged();
} }
public void OnFontMenu() { public void OnFontMenu() {
@ -105,7 +104,7 @@ namespace ConformalDecals.UI {
_textBoxTMP.fontAsset = _font.FontAsset; _textBoxTMP.fontAsset = _font.FontAsset;
UpdateStyleButtons(); UpdateStyleButtons();
OnValueChanged(); _textUpdated = true;
} }
public void OnLineSpacingUpdate(float value) { public void OnLineSpacingUpdate(float value) {
@ -114,7 +113,7 @@ namespace ConformalDecals.UI {
_lineSpacing = Mathf.Lerp(_lineSpacingRange.x, _lineSpacingRange.y, value); _lineSpacing = Mathf.Lerp(_lineSpacingRange.x, _lineSpacingRange.y, value);
UpdateLineSpacing(); UpdateLineSpacing();
OnValueChanged(); _textUpdated = true;
} }
public void OnLineSpacingUpdate(string text) { public void OnLineSpacingUpdate(string text) {
@ -128,7 +127,7 @@ namespace ConformalDecals.UI {
} }
UpdateLineSpacing(); UpdateLineSpacing();
OnValueChanged(); _textUpdated = true;
} }
public void OnCharSpacingUpdate(float value) { public void OnCharSpacingUpdate(float value) {
@ -137,7 +136,7 @@ namespace ConformalDecals.UI {
_charSpacing = Mathf.Lerp(_charSpacingRange.x, _charSpacingRange.y, value); _charSpacing = Mathf.Lerp(_charSpacingRange.x, _charSpacingRange.y, value);
UpdateCharSpacing(); UpdateCharSpacing();
OnValueChanged(); _textUpdated = true;
} }
public void OnCharSpacingUpdate(string text) { public void OnCharSpacingUpdate(string text) {
@ -151,7 +150,7 @@ namespace ConformalDecals.UI {
} }
UpdateCharSpacing(); UpdateCharSpacing();
OnValueChanged(); _textUpdated = true;
} }
public void OnBoldUpdate(bool state) { public void OnBoldUpdate(bool state) {
@ -163,7 +162,7 @@ namespace ConformalDecals.UI {
_style &= ~FontStyles.Bold; _style &= ~FontStyles.Bold;
_textBoxTMP.textComponent.fontStyle = _style | _font.FontStyle & ~_font.FontStyleMask; _textBoxTMP.textComponent.fontStyle = _style | _font.FontStyle & ~_font.FontStyleMask;
OnValueChanged(); _textUpdated = true;
} }
public void OnItalicUpdate(bool state) { public void OnItalicUpdate(bool state) {
@ -175,7 +174,7 @@ namespace ConformalDecals.UI {
_style &= ~FontStyles.Italic; _style &= ~FontStyles.Italic;
_textBoxTMP.textComponent.fontStyle = _style | _font.FontStyle & ~_font.FontStyleMask; _textBoxTMP.textComponent.fontStyle = _style | _font.FontStyle & ~_font.FontStyleMask;
OnValueChanged(); _textUpdated = true;
} }
public void OnUnderlineUpdate(bool state) { public void OnUnderlineUpdate(bool state) {
@ -187,7 +186,7 @@ namespace ConformalDecals.UI {
_style &= ~FontStyles.Underline; _style &= ~FontStyles.Underline;
_textBoxTMP.textComponent.fontStyle = _style | _font.FontStyle & ~_font.FontStyleMask; _textBoxTMP.textComponent.fontStyle = _style | _font.FontStyle & ~_font.FontStyleMask;
OnValueChanged(); _textUpdated = true;
} }
public void OnSmallCapsUpdate(bool state) { public void OnSmallCapsUpdate(bool state) {
@ -199,14 +198,14 @@ namespace ConformalDecals.UI {
_style &= ~FontStyles.SmallCaps; _style &= ~FontStyles.SmallCaps;
_textBoxTMP.textComponent.fontStyle = _style | _font.FontStyle & ~_font.FontStyleMask; _textBoxTMP.textComponent.fontStyle = _style | _font.FontStyle & ~_font.FontStyleMask;
OnValueChanged(); _textUpdated = true;
} }
public void OnVerticalUpdate(bool state) { public void OnVerticalUpdate(bool state) {
if (_ignoreUpdates) return; if (_ignoreUpdates) return;
_vertical = state; _vertical = state;
OnValueChanged(); _textUpdated = true;
} }
private void Start() { private void Start() {
@ -230,8 +229,11 @@ namespace ConformalDecals.UI {
RemoveControlLock(); RemoveControlLock();
} }
private void OnValueChanged() { private void LateUpdate() {
if (_textUpdated) {
_onValueChanged(_text, _font, _style, _vertical, _lineSpacing, _charSpacing); _onValueChanged(_text, _font, _style, _vertical, _lineSpacing, _charSpacing);
_textUpdated = false;
}
} }
private void UpdateStyleButtons() { private void UpdateStyleButtons() {

View File

@ -1,3 +1,18 @@
v0.2.7
------
- Supported KSP versions: 1.8.x to 1.11.x
- Notes:
- Attaching decal parts in flight using engineer kerbals is not supported.
- Fixes:
- Fixed certain non-ascii strings not rendering correctly under certain circumstances.
- Yet another attempted fix for the planet text glitch.
v0.2.6
------
- Fixes:
- Fixed stock flags appearing stretched by forcing their aspect ratio to be correct.
- Another attempted fix for the planet text glitch.
v0.2.5 v0.2.5
------ ------
- Fixes: - Fixes: