mirror of
https://github.com/drewcassidy/KSP-Conformal-Decals.git
synced 2024-09-01 18:23:54 +00:00
Merge branch 'feature-text' into develop
This commit is contained in:
commit
9f32a42e51
8
.gitignore
vendored
8
.gitignore
vendored
@ -2,15 +2,19 @@
|
||||
Assets/*
|
||||
!Assets/Shaders/
|
||||
!Assets/Textures/
|
||||
!Assets/Scripts/
|
||||
!Assets/UI/
|
||||
!Assets/ConformalDecals/
|
||||
KSP/
|
||||
Library/
|
||||
Logs/
|
||||
Packages/
|
||||
ProjectSettings/
|
||||
Temp/
|
||||
|
||||
# Unity Assetbundle Manifest Files
|
||||
GameData/ConformalDecals/Resources/Resources
|
||||
GameData/ConformalDecals/Resources/Resources.manifest
|
||||
GameData/ConformalDecals/Resources/conformaldecals.shab.manifest
|
||||
GameData/ConformalDecals/Resources/*.manifest
|
||||
|
||||
# Unity Project Files
|
||||
PartTools.cfg
|
||||
|
36
Assets/ConformalDecals/BoxSlider.cs
Normal file
36
Assets/ConformalDecals/BoxSlider.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace ConformalDecals.UI {
|
||||
[AddComponentMenu("UI/BoxSlider", 35)]
|
||||
[RequireComponent(typeof(RectTransform))]
|
||||
public class BoxSlider : Selectable, IDragHandler, IInitializePotentialDragHandler, ICanvasElement {
|
||||
[Serializable]
|
||||
public class BoxSliderEvent : UnityEvent<Vector2> { }
|
||||
|
||||
[SerializeField] private RectTransform _handleRect;
|
||||
[SerializeField] private Vector2 _value = Vector2.zero;
|
||||
|
||||
// Allow for delegate-based subscriptions for faster events than 'eventReceiver', and allowing for multiple receivers.
|
||||
[SerializeField] private BoxSliderEvent _onValueChanged = new BoxSliderEvent();
|
||||
|
||||
public BoxSliderEvent OnValueChanged {
|
||||
get => _onValueChanged;
|
||||
set => _onValueChanged = value;
|
||||
}
|
||||
|
||||
// Private fields
|
||||
public void OnDrag(PointerEventData eventData) { }
|
||||
|
||||
public void OnInitializePotentialDrag(PointerEventData eventData) { }
|
||||
|
||||
public void Rebuild(CanvasUpdate executing) { }
|
||||
|
||||
public void LayoutComplete() { }
|
||||
|
||||
public void GraphicUpdateComplete() { }
|
||||
}
|
||||
}
|
13
Assets/ConformalDecals/ColorBoxSlider.cs
Normal file
13
Assets/ConformalDecals/ColorBoxSlider.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace ConformalDecals.UI {
|
||||
public class ColorBoxSlider : MonoBehaviour{
|
||||
[SerializeField] private Vector2 _value;
|
||||
|
||||
[SerializeField] private BoxSlider _slider;
|
||||
[SerializeField] private Image _image;
|
||||
|
||||
public void OnSliderUpdate(Vector2 value) { }
|
||||
}
|
||||
}
|
18
Assets/ConformalDecals/ColorChannelSlider.cs
Normal file
18
Assets/ConformalDecals/ColorChannelSlider.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace ConformalDecals.UI {
|
||||
public class ColorChannelSlider : MonoBehaviour {
|
||||
[SerializeField] private float _value;
|
||||
[SerializeField] private int _channel;
|
||||
[SerializeField] private bool _hsv;
|
||||
|
||||
[SerializeField] private Selectable _textBox;
|
||||
[SerializeField] private Slider _slider;
|
||||
[SerializeField] private Image _image;
|
||||
|
||||
public void OnTextBoxUpdate(string text) { }
|
||||
|
||||
public void OnSliderUpdate(float value) { }
|
||||
}
|
||||
}
|
17
Assets/ConformalDecals/ColorPickerController.cs
Normal file
17
Assets/ConformalDecals/ColorPickerController.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace ConformalDecals.UI {
|
||||
public class ColorPickerController : MonoBehaviour {
|
||||
[SerializeField] private Color _value;
|
||||
[SerializeField] private Image _previewImage;
|
||||
[SerializeField] private Selectable _hexTextBox;
|
||||
|
||||
public void Close() { }
|
||||
|
||||
public void OnHexColorUpdate(string text) { }
|
||||
}
|
||||
}
|
3
Assets/ConformalDecals/ConformalDecals.asmdef
Normal file
3
Assets/ConformalDecals/ConformalDecals.asmdef
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"name": "ConformalDecals"
|
||||
}
|
10
Assets/ConformalDecals/FontMenuController.cs
Normal file
10
Assets/ConformalDecals/FontMenuController.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace ConformalDecals.UI {
|
||||
public class FontMenuController : MonoBehaviour {
|
||||
[SerializeField] private GameObject _menuItem;
|
||||
[SerializeField] private GameObject _menuList;
|
||||
|
||||
public void Close() { }
|
||||
}
|
||||
}
|
37
Assets/ConformalDecals/TextEntryController.cs
Normal file
37
Assets/ConformalDecals/TextEntryController.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace ConformalDecals.UI {
|
||||
public class TextEntryController : MonoBehaviour {
|
||||
[SerializeField] private Selectable _textBox;
|
||||
[SerializeField] private Button _fontButton;
|
||||
|
||||
[SerializeField] private Slider _lineSpacingSlider;
|
||||
[SerializeField] private Selectable _lineSpacingTextBox;
|
||||
|
||||
[SerializeField] private Slider _charSpacingSlider;
|
||||
[SerializeField] private Selectable _charSpacingTextBox;
|
||||
|
||||
[SerializeField] private Toggle _boldButton;
|
||||
[SerializeField] private Toggle _italicButton;
|
||||
[SerializeField] private Toggle _underlineButton;
|
||||
[SerializeField] private Toggle _smallCapsButton;
|
||||
[SerializeField] private Toggle _verticalButton;
|
||||
|
||||
public void Close() { }
|
||||
|
||||
public void OnTextUpdate(string text) { }
|
||||
|
||||
public void OnFontMenu() { }
|
||||
|
||||
public void OnLineSpacingUpdate(float value) { }
|
||||
public void OnLineSpacingUpdate(string text) { }
|
||||
public void OnCharSpacingUpdate(float value) { }
|
||||
public void OnCharSpacingUpdate(string text) { }
|
||||
public void OnBoldUpdate(bool state) { }
|
||||
public void OnItalicUpdate(bool state) { }
|
||||
public void OnUnderlineUpdate(bool state) { }
|
||||
public void OnSmallCapsUpdate(bool state) { }
|
||||
public void OnVerticalUpdate(bool state) { }
|
||||
}
|
||||
}
|
1
Assets/ConformalDecals/UITag.cs
Symbolic link
1
Assets/ConformalDecals/UITag.cs
Symbolic link
@ -0,0 +1 @@
|
||||
/Users/drewcassidy/Projects/KSP-Conformal-Decals/Source/ConformalDecals/UI/UITag.cs
|
117
Assets/Scripts/TextRenderTest.cs
Normal file
117
Assets/Scripts/TextRenderTest.cs
Normal file
@ -0,0 +1,117 @@
|
||||
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 = 8;
|
||||
private int MaxTextureSize = 4096;
|
||||
private static readonly int Decal = Shader.PropertyToID("_Decal");
|
||||
|
||||
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 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 = bounds.size.x * pixelDensity;
|
||||
var height = bounds.size.y * pixelDensity;
|
||||
|
||||
var widthPoT = Mathf.NextPowerOfTwo((int) width);
|
||||
var heightPoT = Mathf.NextPowerOfTwo((int) height);
|
||||
|
||||
if (widthPoT > MaxTextureSize) {
|
||||
widthPoT /= widthPoT / MaxTextureSize;
|
||||
heightPoT /= widthPoT / MaxTextureSize;
|
||||
}
|
||||
|
||||
if (heightPoT > MaxTextureSize) {
|
||||
widthPoT /= heightPoT / MaxTextureSize;
|
||||
heightPoT /= heightPoT / MaxTextureSize;
|
||||
}
|
||||
|
||||
widthPoT = Mathf.Min(widthPoT, MaxTextureSize);
|
||||
heightPoT = Mathf.Min(heightPoT, MaxTextureSize);
|
||||
|
||||
var widthRatio = widthPoT / width;
|
||||
var heightRatio = heightPoT / height;
|
||||
|
||||
var sizeRatio = Mathf.Min(widthRatio, heightRatio);
|
||||
|
||||
Debug.Log(sizeRatio);
|
||||
|
||||
int scaledHeight = (int) (sizeRatio * height);
|
||||
int scaledWidth = (int) (sizeRatio * width);
|
||||
|
||||
Debug.Log($"width = {scaledWidth}");
|
||||
Debug.Log($"height = {scaledHeight}");
|
||||
|
||||
_camera.orthographicSize = scaledHeight / pixelDensity / 2;
|
||||
_camera.aspect = (float) widthPoT / heightPoT;
|
||||
|
||||
_cameraObject.transform.localPosition = new Vector3(bounds.center.x, bounds.center.y, -1);
|
||||
|
||||
var halfHeight = heightPoT / pixelDensity / 2 / sizeRatio;
|
||||
var halfWidth = widthPoT / pixelDensity / 2 / sizeRatio;
|
||||
var matrix = Matrix4x4.Ortho(bounds.center.x - halfWidth, bounds.center.x + halfWidth,
|
||||
bounds.center.y - halfHeight, bounds.center.y + halfHeight, -1, 1);
|
||||
|
||||
// setup texture
|
||||
var texture = new Texture2D(widthPoT, heightPoT, TextTextureFormat, true);
|
||||
_targetMaterial.SetTexture(Decal, texture);
|
||||
|
||||
|
||||
// setup render texture
|
||||
renderTex = RenderTexture.GetTemporary(widthPoT, heightPoT, 0, TextRenderTextureFormat, RenderTextureReadWrite.Linear, 1);
|
||||
renderTex.autoGenerateMips = false;
|
||||
|
||||
RenderTexture.active = renderTex;
|
||||
GL.PushMatrix();
|
||||
GL.LoadProjectionMatrix(matrix);
|
||||
_blitMaterial.SetPass(0);
|
||||
Graphics.DrawMeshNow(mesh, Matrix4x4.identity);
|
||||
GL.PopMatrix();
|
||||
|
||||
// setup material
|
||||
_blitMaterial.mainTexture = _tmp.font.atlas;
|
||||
|
||||
yield return null;
|
||||
|
||||
RenderTexture.active = renderTex;
|
||||
texture.ReadPixels(new Rect(0, 0, widthPoT, heightPoT), 0, 0, true);
|
||||
texture.Apply(false, true);
|
||||
|
||||
RenderTexture.ReleaseTemporary(renderTex);
|
||||
}
|
||||
}
|
143
Assets/Shaders/ColorSlider.shader
Normal file
143
Assets/Shaders/ColorSlider.shader
Normal file
@ -0,0 +1,143 @@
|
||||
Shader "ConformalDecals/UI/Color Slider"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
_Color("Color", Color) = (0,0,0,0)
|
||||
_Radius("Radius", Float) = 4
|
||||
|
||||
_OutlineGradient("Outline Gradient Step", Range (0, 1)) = 0.6
|
||||
_OutlineOpacity("Outline Opacity", Range (0, 0.5)) = 0.1
|
||||
_OutlineWidth("Outline Width", Float) = 3
|
||||
|
||||
_StencilComp ("Stencil Comparison", Float) = 8
|
||||
_Stencil ("Stencil ID", Float) = 0
|
||||
_StencilOp ("Stencil Operation", Float) = 0
|
||||
_StencilWriteMask ("Stencil Write Mask", Float) = 255
|
||||
_StencilReadMask ("Stencil Read Mask", Float) = 255
|
||||
|
||||
_ColorMask ("Color Mask", Float) = 15
|
||||
|
||||
[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
|
||||
|
||||
[Toggle(HUE)] _Hue ("Hue", int) = 0
|
||||
[Toggle(RED)] _Red ("Red", int) = 0
|
||||
[Toggle(GREEN)] _Green ("Green", int) = 0
|
||||
[Toggle(BLUE)] _Blue ("Blue", int) = 0
|
||||
|
||||
|
||||
}
|
||||
SubShader
|
||||
{
|
||||
Tags
|
||||
{
|
||||
"Queue"="Transparent"
|
||||
"IgnoreProjector"="True"
|
||||
"RenderType"="Transparent"
|
||||
"PreviewType"="Plane"
|
||||
"CanUseSpriteAtlas"="True"
|
||||
}
|
||||
Stencil
|
||||
{
|
||||
Ref [_Stencil]
|
||||
Comp [_StencilComp]
|
||||
Pass [_StencilOp]
|
||||
ReadMask [_StencilReadMask]
|
||||
WriteMask [_StencilWriteMask]
|
||||
}
|
||||
|
||||
Cull Off
|
||||
Lighting Off
|
||||
ZWrite Off
|
||||
ZTest [unity_GUIZTestMode]
|
||||
Blend SrcAlpha OneMinusSrcAlpha
|
||||
ColorMask [_ColorMask]
|
||||
|
||||
Pass
|
||||
{
|
||||
CGPROGRAM
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
|
||||
#pragma require integers
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
#include "UnityUI.cginc"
|
||||
#include "HSL.cginc"
|
||||
#include "SDF.cginc"
|
||||
|
||||
#pragma multi_compile_local _ UNITY_UI_CLIP_RECT
|
||||
#pragma multi_compile_local _ UNITY_UI_ALPHACLIP
|
||||
#pragma multi_compile_local HUE RED GREEN BLUE
|
||||
|
||||
float4 _ClipRect;
|
||||
float _Radius;
|
||||
float4 _Color;
|
||||
|
||||
float _OutlineGradient;
|
||||
float _OutlineOpacity;
|
||||
float _OutlineWidth;
|
||||
|
||||
struct appdata
|
||||
{
|
||||
float4 vertex : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct v2f
|
||||
{
|
||||
float4 vertex : SV_POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
float4 worldPosition : TEXCOORD1;
|
||||
};
|
||||
|
||||
v2f vert (appdata v)
|
||||
{
|
||||
v2f o;
|
||||
o.vertex = UnityObjectToClipPos(v.vertex);
|
||||
o.worldPosition = v.vertex;
|
||||
o.uv = v.uv;
|
||||
return o;
|
||||
}
|
||||
|
||||
fixed4 frag (v2f i) : SV_Target
|
||||
{
|
||||
float4 color = 1;
|
||||
|
||||
#ifdef HUE
|
||||
color.rgb = HSV2RGB(float3(i.uv.y, _Color.y, _Color.z));
|
||||
#endif //HUE
|
||||
|
||||
#ifdef RED
|
||||
color.rgb = float3(i.uv.x, _Color.g, _Color.b);
|
||||
#endif //RED
|
||||
|
||||
#ifdef GREEN
|
||||
color.rgb = float3(_Color.r, i.uv.x, _Color.b);
|
||||
#endif //GREEN
|
||||
|
||||
#ifdef BLUE
|
||||
color.rgb = float3(_Color.r, _Color.g, i.uv.x);
|
||||
#endif //BLUE
|
||||
|
||||
float rrect = sdRoundedUVBox(i.uv, _Radius);
|
||||
float gradient = smoothstep(_OutlineGradient, 1 - _OutlineGradient, i.uv.y);
|
||||
float outlineOpacity = _OutlineOpacity * smoothstep(-1*_OutlineWidth, 0, rrect);
|
||||
|
||||
color.rgb = lerp(color.rgb, gradient, outlineOpacity);
|
||||
|
||||
color.a = saturate(0.5 - rrect);
|
||||
|
||||
#ifdef UNITY_UI_CLIP_RECT
|
||||
color.a *= UnityGet2DClipping(i.worldPosition.xy, _ClipRect);
|
||||
#endif
|
||||
|
||||
#ifdef UNITY_UI_ALPHACLIP
|
||||
clip (color.a - 0.001);
|
||||
#endif
|
||||
|
||||
return color;
|
||||
}
|
||||
ENDCG
|
||||
}
|
||||
}
|
||||
}
|
122
Assets/Shaders/ColorSquare.shader
Normal file
122
Assets/Shaders/ColorSquare.shader
Normal file
@ -0,0 +1,122 @@
|
||||
Shader "ConformalDecals/UI/HSV Square"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
_Color("Color", Color) = (0,0,0,0)
|
||||
_Radius("Radius", Float) = 4
|
||||
|
||||
_OutlineGradient("Outline Gradient Step", Range (0, 1)) = 0.6
|
||||
_OutlineOpacity("Outline Opacity", Range (0, 0.5)) = 0.1
|
||||
_OutlineWidth("Outline Width", Float) = 3
|
||||
|
||||
_StencilComp ("Stencil Comparison", Float) = 8
|
||||
_Stencil ("Stencil ID", Float) = 0
|
||||
_StencilOp ("Stencil Operation", Float) = 0
|
||||
_StencilWriteMask ("Stencil Write Mask", Float) = 255
|
||||
_StencilReadMask ("Stencil Read Mask", Float) = 255
|
||||
|
||||
_ColorMask ("Color Mask", Float) = 15
|
||||
|
||||
[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
|
||||
}
|
||||
SubShader
|
||||
{
|
||||
Tags
|
||||
{
|
||||
"Queue"="Transparent"
|
||||
"IgnoreProjector"="True"
|
||||
"RenderType"="Transparent"
|
||||
"PreviewType"="Plane"
|
||||
"CanUseSpriteAtlas"="True"
|
||||
}
|
||||
Stencil
|
||||
{
|
||||
Ref [_Stencil]
|
||||
Comp [_StencilComp]
|
||||
Pass [_StencilOp]
|
||||
ReadMask [_StencilReadMask]
|
||||
WriteMask [_StencilWriteMask]
|
||||
}
|
||||
|
||||
Cull Off
|
||||
Lighting Off
|
||||
ZWrite Off
|
||||
ZTest [unity_GUIZTestMode]
|
||||
Blend SrcAlpha OneMinusSrcAlpha
|
||||
ColorMask [_ColorMask]
|
||||
|
||||
Pass
|
||||
{
|
||||
CGPROGRAM
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
|
||||
#pragma require integers
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
#include "UnityUI.cginc"
|
||||
#include "HSL.cginc"
|
||||
#include "SDF.cginc"
|
||||
|
||||
#pragma multi_compile_local _ UNITY_UI_CLIP_RECT
|
||||
#pragma multi_compile_local _ UNITY_UI_ALPHACLIP
|
||||
|
||||
float4 _ClipRect;
|
||||
float _Radius;
|
||||
float4 _Color;
|
||||
|
||||
float _OutlineGradient;
|
||||
float _OutlineOpacity;
|
||||
float _OutlineWidth;
|
||||
|
||||
struct appdata
|
||||
{
|
||||
float4 vertex : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct v2f
|
||||
{
|
||||
float4 vertex : SV_POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
float4 worldPosition : TEXCOORD1;
|
||||
};
|
||||
|
||||
v2f vert (appdata v)
|
||||
{
|
||||
v2f o;
|
||||
o.vertex = UnityObjectToClipPos(v.vertex);
|
||||
o.worldPosition = v.vertex;
|
||||
o.uv = v.uv;
|
||||
return o;
|
||||
}
|
||||
|
||||
fixed4 frag (v2f i) : SV_Target
|
||||
{
|
||||
// sample the texture
|
||||
fixed4 color = 1;
|
||||
|
||||
color.a = saturate(0.5 - sdRoundedUVBox(i.uv, _Radius));
|
||||
|
||||
color.rgb = HSV2RGB(float3(_Color.x, i.uv.x, i.uv.y));
|
||||
|
||||
float rrect = sdRoundedUVBox(i.uv, _Radius);
|
||||
float gradient = smoothstep(_OutlineGradient, 1 - _OutlineGradient, i.uv.y);
|
||||
float outlineOpacity = _OutlineOpacity * smoothstep(-1*_OutlineWidth, 0, rrect);
|
||||
|
||||
color.rgb = lerp(color.rgb, gradient, outlineOpacity);
|
||||
|
||||
#ifdef UNITY_UI_CLIP_RECT
|
||||
color.a *= UnityGet2DClipping(i.worldPosition.xy, _ClipRect);
|
||||
#endif
|
||||
|
||||
#ifdef UNITY_UI_ALPHACLIP
|
||||
clip (color.a - 0.001);
|
||||
#endif
|
||||
|
||||
return color;
|
||||
}
|
||||
ENDCG
|
||||
}
|
||||
}
|
||||
}
|
120
Assets/Shaders/ColorSwatch.shader
Normal file
120
Assets/Shaders/ColorSwatch.shader
Normal file
@ -0,0 +1,120 @@
|
||||
Shader "ConformalDecals/UI/Color Swatch"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
_Color("Color", Color) = (0,0,0,0)
|
||||
_Radius("Radius", Float) = 4
|
||||
|
||||
_OutlineGradient("Outline Gradient Step", Range (0, 1)) = 0.6
|
||||
_OutlineOpacity("Outline Opacity", Range (0, 0.5)) = 0.1
|
||||
_OutlineWidth("Outline Width", Float) = 3
|
||||
|
||||
_StencilComp ("Stencil Comparison", Float) = 8
|
||||
_Stencil ("Stencil ID", Float) = 0
|
||||
_StencilOp ("Stencil Operation", Float) = 0
|
||||
_StencilWriteMask ("Stencil Write Mask", Float) = 255
|
||||
_StencilReadMask ("Stencil Read Mask", Float) = 255
|
||||
|
||||
_ColorMask ("Color Mask", Float) = 15
|
||||
|
||||
[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
|
||||
}
|
||||
SubShader
|
||||
{
|
||||
Tags
|
||||
{
|
||||
"Queue"="Transparent"
|
||||
"IgnoreProjector"="True"
|
||||
"RenderType"="Transparent"
|
||||
"PreviewType"="Plane"
|
||||
"CanUseSpriteAtlas"="True"
|
||||
}
|
||||
Stencil
|
||||
{
|
||||
Ref [_Stencil]
|
||||
Comp [_StencilComp]
|
||||
Pass [_StencilOp]
|
||||
ReadMask [_StencilReadMask]
|
||||
WriteMask [_StencilWriteMask]
|
||||
}
|
||||
|
||||
Cull Off
|
||||
Lighting Off
|
||||
ZWrite Off
|
||||
ZTest [unity_GUIZTestMode]
|
||||
Blend SrcAlpha OneMinusSrcAlpha
|
||||
ColorMask [_ColorMask]
|
||||
|
||||
Pass
|
||||
{
|
||||
CGPROGRAM
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
|
||||
#pragma require integers
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
#include "UnityUI.cginc"
|
||||
#include "HSL.cginc"
|
||||
#include "SDF.cginc"
|
||||
|
||||
#pragma multi_compile_local _ UNITY_UI_CLIP_RECT
|
||||
#pragma multi_compile_local _ UNITY_UI_ALPHACLIP
|
||||
#pragma multi_compile_local HUE RED GREEN BLUE
|
||||
|
||||
float4 _ClipRect;
|
||||
float _Radius;
|
||||
float4 _Color;
|
||||
|
||||
float _OutlineGradient;
|
||||
float _OutlineOpacity;
|
||||
float _OutlineWidth;
|
||||
|
||||
struct appdata
|
||||
{
|
||||
float4 vertex : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct v2f
|
||||
{
|
||||
float4 vertex : SV_POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
float4 worldPosition : TEXCOORD1;
|
||||
};
|
||||
|
||||
v2f vert (appdata v)
|
||||
{
|
||||
v2f o;
|
||||
o.vertex = UnityObjectToClipPos(v.vertex);
|
||||
o.worldPosition = v.vertex;
|
||||
o.uv = v.uv;
|
||||
return o;
|
||||
}
|
||||
|
||||
fixed4 frag (v2f i) : SV_Target
|
||||
{
|
||||
fixed4 color = _Color;
|
||||
|
||||
color.a = saturate(0.5 - sdRoundedUVBox(i.uv, _Radius));
|
||||
|
||||
float rrect = sdRoundedUVBox(i.uv, _Radius);
|
||||
float gradient = smoothstep(_OutlineGradient, 1 - _OutlineGradient, i.uv.y);
|
||||
float outlineOpacity = _OutlineOpacity * smoothstep(-1*_OutlineWidth, 0, rrect);
|
||||
|
||||
color.rgb = lerp(color.rgb, gradient, outlineOpacity);
|
||||
|
||||
#ifdef UNITY_UI_CLIP_RECT
|
||||
color.a *= UnityGet2DClipping(i.worldPosition.xy, _ClipRect);
|
||||
#endif
|
||||
|
||||
#ifdef UNITY_UI_ALPHACLIP
|
||||
clip (color.a - 0.001);
|
||||
#endif
|
||||
|
||||
return color;
|
||||
}
|
||||
ENDCG
|
||||
}
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
#include "AutoLight.cginc"
|
||||
#include "Lighting.cginc"
|
||||
|
||||
#define CLIP_MARGIN 0.1
|
||||
#define CLIP_MARGIN 0.05
|
||||
#define EDGE_MARGIN 0.01
|
||||
|
||||
// UNIFORM VARIABLES
|
||||
@ -85,7 +85,7 @@ struct DecalSurfaceInput
|
||||
|
||||
#ifdef DECAL_BASE_NORMAL
|
||||
float3 normal;
|
||||
#endif
|
||||
#endif //DECAL_BASE_NORMAL
|
||||
|
||||
float3 vertex_normal;
|
||||
float3 viewDir;
|
||||
@ -150,14 +150,7 @@ inline float BoundsDist(float3 p, float3 normal, float3 projNormal) {
|
||||
float dist = max(max(q.x, q.y), q.z); // pseudo SDF
|
||||
float ndist = EDGE_MARGIN - dot(normal, projNormal); // SDF to normal
|
||||
return 10 * max(dist, ndist); // return intersection
|
||||
#endif
|
||||
}
|
||||
|
||||
inline float SDFAA(float dist) {
|
||||
float ddist = length(float2(ddx(dist), ddy(dist)));
|
||||
float pixelDist = dist / ddist;
|
||||
return saturate(0.5-pixelDist);
|
||||
return saturate(0.5 - dist);
|
||||
#endif //DECAL_PREVIEW
|
||||
}
|
||||
|
||||
#endif
|
17
Assets/Shaders/HSL.cginc
Normal file
17
Assets/Shaders/HSL.cginc
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef HSL_INCLUDED
|
||||
#define HSL_INCLUDED
|
||||
|
||||
inline float3 HSL2RGB(float3 hsl) {
|
||||
int3 n = int3(0, 8, 4);
|
||||
float3 k = (n + hsl.x * 12) % 12;
|
||||
float a = hsl.y * min(hsl.z, 1 - hsl.z);
|
||||
return hsl.z - a * max(-1, min(k - 3, min(9 - k, 1)));
|
||||
}
|
||||
|
||||
inline float3 HSV2RGB(float3 hsv) {
|
||||
int3 n = int3(5, 3, 1);
|
||||
float3 k = (n + hsv.x * 6) % 6;
|
||||
return hsv.z - hsv.z * hsv.y * max(0, min(1, min(k, 4 - k)));
|
||||
}
|
||||
|
||||
#endif
|
45
Assets/Shaders/SDF.cginc
Normal file
45
Assets/Shaders/SDF.cginc
Normal file
@ -0,0 +1,45 @@
|
||||
#ifndef SDF_INCLUDED
|
||||
#define SDF_INCLUDED
|
||||
|
||||
// based on functions by Inigo Quilez
|
||||
// https://iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm
|
||||
|
||||
// SDF of a box
|
||||
float sdBox( in float2 p, in float2 b ) {
|
||||
float2 d = abs(p)-b;
|
||||
return length(max(d,0.0)) + min(max(d.x,d.y),0.0);
|
||||
}
|
||||
|
||||
// SDF of a box with corner radius r
|
||||
float sdRoundedBox( in float2 p, in float2 b, in float r ) {
|
||||
float2 d = abs(p)-b+r;
|
||||
return min(max(d.x,d.y),0.0) + length(max(d,0.0)) - r;
|
||||
}
|
||||
|
||||
// SDF of a box with corner radius r, based on the current UV position
|
||||
// UV must be ∈ (0,1), with (0,0) on one corner
|
||||
float sdRoundedUVBox( float2 uv, float r ) {
|
||||
float dx = ddx(uv.x);
|
||||
float dy = ddy(uv.y);
|
||||
|
||||
float2 dim = abs(float2(1/dx, 1/dy));
|
||||
float2 halfDim = dim / 2;
|
||||
float2 pos = (dim * uv) - halfDim;
|
||||
|
||||
return sdRoundedBox(pos, halfDim, r);
|
||||
}
|
||||
|
||||
inline float SDFdDist(float dist) {
|
||||
return length(float2(ddx(dist), ddy(dist)));
|
||||
}
|
||||
|
||||
inline float SDFAA(float dist, float ddist) {
|
||||
float pixelDist = dist / ddist;
|
||||
return saturate(0.5-pixelDist);
|
||||
}
|
||||
|
||||
inline float SDFAA(float dist) {
|
||||
return SDFAA(dist, SDFdDist(dist));
|
||||
}
|
||||
|
||||
#endif
|
@ -69,6 +69,7 @@
|
||||
#include "UnityCG.cginc"
|
||||
#include "DecalsCommon.cginc"
|
||||
#include "DecalsSurface.cginc"
|
||||
#include "SDF.cginc"
|
||||
#include "StandardDecal.cginc"
|
||||
|
||||
ENDCG
|
||||
@ -98,6 +99,7 @@
|
||||
#include "UnityCG.cginc"
|
||||
#include "DecalsCommon.cginc"
|
||||
#include "DecalsSurface.cginc"
|
||||
#include "SDF.cginc"
|
||||
#include "StandardDecal.cginc"
|
||||
|
||||
ENDCG
|
||||
|
73
Assets/Shaders/TextBlit.shader
Normal file
73
Assets/Shaders/TextBlit.shader
Normal file
@ -0,0 +1,73 @@
|
||||
Shader "ConformalDecals/Text Blit"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
_MainTex("_MainTex (RGB spec(A))", 2D) = "white" {}
|
||||
|
||||
_WeightNormal("Weight Normal", float) = 0
|
||||
_WeightBold("Weight Bold", float) = 0.5
|
||||
|
||||
_ScaleRatioA("Scale RatioA", float) = 1
|
||||
_ScaleRatioB("Scale RatioB", float) = 1
|
||||
_ScaleRatioC("Scale RatioC", float) = 1
|
||||
}
|
||||
|
||||
SubShader
|
||||
{
|
||||
Tags { "Queue" = "Transparent" }
|
||||
Cull Off
|
||||
ZWrite Off
|
||||
|
||||
Pass
|
||||
{
|
||||
BlendOp Max
|
||||
|
||||
CGPROGRAM
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
|
||||
sampler2D _MainTex;
|
||||
|
||||
// font weights to fake bold
|
||||
float _WeightNormal;
|
||||
float _WeightBold;
|
||||
|
||||
// no idea what these do
|
||||
float _ScaleRatioA;
|
||||
float _ScaleRatioB;
|
||||
float _ScaleRatioC;
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
#include "Lighting.cginc"
|
||||
#include "AutoLight.cginc"
|
||||
|
||||
struct v2f {
|
||||
float4 pos : SV_POSITION;
|
||||
float4 uv : TEXCOORD0; // u, v, bias, 0
|
||||
};
|
||||
|
||||
v2f vert(float4 vertex : POSITION, float2 uv0 : TEXCOORD0, float2 uv1 : TEXCOORD1) {
|
||||
float bold = step(uv1.y, 0);
|
||||
float weight = lerp(_WeightNormal, _WeightBold, bold) * _ScaleRatioA / 8.0;
|
||||
float bias = 1 - weight;
|
||||
|
||||
v2f o;
|
||||
o.pos = UnityObjectToClipPos(vertex);
|
||||
o.uv = float4(uv0.x, uv0.y, bias, weight);
|
||||
return o;
|
||||
}
|
||||
|
||||
fixed4 frag (v2f i) : SV_Target {
|
||||
float2 uv = i.uv.xy;
|
||||
float bias = i.uv.z;
|
||||
float weight = i.uv.w;
|
||||
|
||||
fixed4 c = 0;
|
||||
c.r = saturate(tex2D(_MainTex,(uv)).a + weight);
|
||||
return c;
|
||||
}
|
||||
|
||||
ENDCG
|
||||
}
|
||||
}
|
||||
}
|
49
Assets/Shaders/TextDecal.cginc
Normal file
49
Assets/Shaders/TextDecal.cginc
Normal file
@ -0,0 +1,49 @@
|
||||
float4 _DecalColor;
|
||||
|
||||
float4 _OutlineColor;
|
||||
float _OutlineWidth;
|
||||
|
||||
void surf(DecalSurfaceInput IN, inout SurfaceOutput o) {
|
||||
float4 color = _DecalColor;
|
||||
float dist = _Cutoff - tex2D(_Decal, IN.uv_decal).r; // text distance
|
||||
|
||||
#ifdef DECAL_OUTLINE
|
||||
// Outline
|
||||
float outlineOffset = _OutlineWidth * 0.25;
|
||||
float outlineRadius = _OutlineWidth * 0.5;
|
||||
|
||||
#ifdef DECAL_FILL
|
||||
// Outline and Fill
|
||||
float outlineDist = -dist - outlineOffset;
|
||||
float outlineFactor = SDFAA(outlineDist);
|
||||
dist -= outlineOffset;
|
||||
color = lerp(_DecalColor, _OutlineColor, outlineFactor);
|
||||
#else
|
||||
// Outline Only
|
||||
float outlineDist = abs(dist) - outlineOffset;
|
||||
dist = outlineDist;
|
||||
color = _OutlineColor;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
dist = max(dist, BoundsDist(IN.uv, IN.vertex_normal, _DecalNormal));
|
||||
float ddist = SDFdDist(dist); // distance gradient magnitude
|
||||
o.Alpha = _DecalOpacity * SDFAA(dist, ddist);
|
||||
o.Albedo = UnderwaterFog(IN.worldPosition, color).rgb;
|
||||
|
||||
#ifdef DECAL_BASE_NORMAL
|
||||
float3 normal = IN.normal;
|
||||
float wearFactor = 1 - normal.z;
|
||||
float wearFactorAlpha = saturate(_EdgeWearStrength * wearFactor);
|
||||
o.Alpha *= saturate(1 + _EdgeWearOffset - saturate(_EdgeWearStrength * wearFactor));
|
||||
#endif
|
||||
|
||||
#ifdef DECAL_SPECMAP
|
||||
float4 specular = tex2D(_SpecMap, IN.uv_specmap);
|
||||
o.Gloss = specular.r;
|
||||
o.Specular = _Shininess;
|
||||
#endif
|
||||
|
||||
half rim = 1.0 - saturate(dot(normalize(IN.viewDir), o.Normal));
|
||||
o.Emission = (_RimColor.rgb * pow(rim, _RimFalloff)) * _RimColor.a;
|
||||
}
|
112
Assets/Shaders/TextDecal.shader
Normal file
112
Assets/Shaders/TextDecal.shader
Normal file
@ -0,0 +1,112 @@
|
||||
Shader "ConformalDecals/Decal/Text"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
[Header(Decal)]
|
||||
[Toggle(DECAL_FILL)] _Fill ("Fill", int) = 0
|
||||
_Decal("Decal Texture", 2D) = "gray" {}
|
||||
_DecalColor("Decal Color", Color) = (1,1,1,1)
|
||||
|
||||
_Weight("Text Weight", Range(0,1)) = 0
|
||||
|
||||
[Header(Outline)]
|
||||
[Toggle(DECAL_OUTLINE)] _Outline ("Outline", int) = 0
|
||||
_OutlineColor("Outline Color", Color) = (0,0,0,1)
|
||||
_OutlineWidth("Outline Width", Range(0,1)) = 0.1
|
||||
|
||||
[Header(Normal)]
|
||||
[Toggle(DECAL_BASE_NORMAL)] _BaseNormal ("Use Base Normal", int) = 0
|
||||
_BumpMap("Bump Map", 2D) = "bump" {}
|
||||
_EdgeWearStrength("Edge Wear Strength", Range(0,500)) = 100
|
||||
_EdgeWearOffset("Edge Wear Offset", Range(0,1)) = 0.1
|
||||
|
||||
[Header(Specularity)]
|
||||
[Toggle(DECAL_SPECMAP)] _Decal_SpecMap ("Has SpecMap", int) = 0
|
||||
_SpecMap ("Specular Map)", 2D) = "black" {}
|
||||
_SpecColor ("_SpecColor", Color) = (0.25, 0.25, 0.25, 1)
|
||||
_Shininess ("Shininess", Range (0.03, 10)) = 0.3
|
||||
|
||||
_Cutoff ("Alpha cutoff", Range(0,1)) = 0.5
|
||||
_DecalOpacity("Opacity", Range(0,1) ) = 1
|
||||
_Background("Background Color", Color) = (0.9,0.9,0.9,0.7)
|
||||
|
||||
[Enum(UnityEngine.Rendering.CullMode)] _Cull ("Cull", int) = 2
|
||||
[Toggle] _ZWrite ("ZWrite", Float) = 1.0
|
||||
|
||||
[Toggle(DECAL_PREVIEW)] _Preview ("Preview", int) = 0
|
||||
|
||||
[Header(Effects)]
|
||||
[PerRendererData]_Opacity("_Opacity", Range(0,1) ) = 1
|
||||
_Color("_Color", Color) = (1,1,1,1)
|
||||
[PerRendererData]_RimFalloff("_RimFalloff", Range(0.01,5) ) = 0.1
|
||||
[PerRendererData]_RimColor("_RimColor", Color) = (0,0,0,0)
|
||||
[PerRendererData]_UnderwaterFogFactor ("Underwater Fog Factor", Range(0,1)) = 0
|
||||
}
|
||||
SubShader
|
||||
{
|
||||
Tags { "Queue" = "Geometry+100" "IgnoreProjector" = "true" "DisableBatching" = "true"}
|
||||
Cull [_Cull]
|
||||
|
||||
Pass
|
||||
{
|
||||
Name "FORWARD"
|
||||
Tags { "LightMode" = "ForwardBase" }
|
||||
ZWrite [_ZWrite]
|
||||
ZTest LEqual
|
||||
Blend SrcAlpha OneMinusSrcAlpha
|
||||
|
||||
CGPROGRAM
|
||||
#pragma vertex vert_forward
|
||||
#pragma fragment frag_forward
|
||||
|
||||
#pragma multi_compile_fwdbase nolightmap nodirlightmap nodynlightmap
|
||||
#pragma skip_variants SHADOWS_DEPTH SHADOWS_CUBE SHADOWS_SHADOWMASK LIGHTMAP_SHADOW_MIXING POINT_COOKIE
|
||||
#pragma multi_compile_local __ DECAL_PREVIEW
|
||||
#pragma multi_compile_local __ DECAL_BASE_NORMAL
|
||||
#pragma multi_compile_local __ DECAL_SPECMAP
|
||||
#pragma multi_compile_local __ DECAL_OUTLINE
|
||||
#pragma multi_compile_local __ DECAL_FILL
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
#include "DecalsCommon.cginc"
|
||||
#include "DecalsSurface.cginc"
|
||||
#include "SDF.cginc"
|
||||
#include "TextDecal.cginc"
|
||||
|
||||
ENDCG
|
||||
}
|
||||
|
||||
Pass
|
||||
{
|
||||
Name "FORWARD"
|
||||
Tags { "LightMode" = "ForwardAdd" }
|
||||
ZWrite Off
|
||||
ZTest LEqual
|
||||
Blend One One
|
||||
Offset -1, -1
|
||||
|
||||
CGPROGRAM
|
||||
#pragma vertex vert_forward
|
||||
#pragma fragment frag_forward
|
||||
|
||||
#pragma multi_compile_fwdadd nolightmap nodirlightmap nodynlightmap
|
||||
#pragma skip_variants SHADOWS_DEPTH SHADOWS_CUBE SHADOWS_SHADOWMASK LIGHTMAP_SHADOW_MIXING POINT_COOKIE
|
||||
#pragma multi_compile_local __ DECAL_PREVIEW
|
||||
#pragma multi_compile_local __ DECAL_BASE_NORMAL
|
||||
#pragma multi_compile_local __ DECAL_SPECMAP
|
||||
#pragma multi_compile_local __ DECAL_OUTLINE
|
||||
#pragma multi_compile_local __ DECAL_FILL
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
#include "DecalsCommon.cginc"
|
||||
#include "DecalsSurface.cginc"
|
||||
#include "SDF.cginc"
|
||||
#include "TextDecal.cginc"
|
||||
|
||||
ENDCG
|
||||
}
|
||||
|
||||
// shadow casting support
|
||||
UsePass "Legacy Shaders/VertexLit/SHADOWCASTER"
|
||||
}
|
||||
}
|
BIN
Assets/Textures/UI.png
Normal file
BIN
Assets/Textures/UI.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.2 KiB |
93
Assets/UI/BSlider.mat
Normal file
93
Assets/UI/BSlider.mat
Normal file
@ -0,0 +1,93 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!21 &2100000
|
||||
Material:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: BSlider
|
||||
m_Shader: {fileID: 4800000, guid: a6b2542ba8ea844e7b0526fab69d88ed, type: 3}
|
||||
m_ShaderKeywords: BLUE
|
||||
m_LightmapFlags: 4
|
||||
m_EnableInstancingVariants: 0
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: -1
|
||||
stringTagMap: {}
|
||||
disabledShaderPasses: []
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _BumpMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailAlbedoMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailMask:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailNormalMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _EmissionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MetallicGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _OcclusionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _ParallaxMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Floats:
|
||||
- _Blue: 1
|
||||
- _BumpScale: 1
|
||||
- _ColorMask: 15
|
||||
- _Cutoff: 0.5
|
||||
- _DetailNormalMapScale: 1
|
||||
- _DstBlend: 0
|
||||
- _GlossMapScale: 1
|
||||
- _Glossiness: 0.5
|
||||
- _GlossyReflections: 1
|
||||
- _GradientStep: 0.331
|
||||
- _Green: 0
|
||||
- _Hue: 0
|
||||
- _Metallic: 0
|
||||
- _Mode: 0
|
||||
- _OcclusionStrength: 1
|
||||
- _OutlineGradient: 0.7
|
||||
- _OutlineOpacity: 0.15
|
||||
- _OutlineWidth: 2.5
|
||||
- _Parallax: 0.02
|
||||
- _Radius: 4
|
||||
- _Red: 0
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SpecularHighlights: 1
|
||||
- _SrcBlend: 1
|
||||
- _Stencil: 0
|
||||
- _StencilComp: 8
|
||||
- _StencilOp: 0
|
||||
- _StencilReadMask: 255
|
||||
- _StencilWriteMask: 255
|
||||
- _UVSec: 0
|
||||
- _UseUIAlphaClip: 0
|
||||
- _ZWrite: 1
|
||||
m_Colors:
|
||||
- _Color: {r: 0, g: 0, b: 0, a: 1}
|
||||
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
5492
Assets/UI/ColorPickerPanel.prefab
Normal file
5492
Assets/UI/ColorPickerPanel.prefab
Normal file
File diff suppressed because it is too large
Load Diff
1048
Assets/UI/FontMenuPanel.prefab
Normal file
1048
Assets/UI/FontMenuPanel.prefab
Normal file
File diff suppressed because it is too large
Load Diff
93
Assets/UI/GSlider.mat
Normal file
93
Assets/UI/GSlider.mat
Normal file
@ -0,0 +1,93 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!21 &2100000
|
||||
Material:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: GSlider
|
||||
m_Shader: {fileID: 4800000, guid: a6b2542ba8ea844e7b0526fab69d88ed, type: 3}
|
||||
m_ShaderKeywords: GREEN
|
||||
m_LightmapFlags: 4
|
||||
m_EnableInstancingVariants: 0
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: -1
|
||||
stringTagMap: {}
|
||||
disabledShaderPasses: []
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _BumpMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailAlbedoMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailMask:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailNormalMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _EmissionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MetallicGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _OcclusionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _ParallaxMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Floats:
|
||||
- _Blue: 0
|
||||
- _BumpScale: 1
|
||||
- _ColorMask: 15
|
||||
- _Cutoff: 0.5
|
||||
- _DetailNormalMapScale: 1
|
||||
- _DstBlend: 0
|
||||
- _GlossMapScale: 1
|
||||
- _Glossiness: 0.5
|
||||
- _GlossyReflections: 1
|
||||
- _GradientStep: 0.2
|
||||
- _Green: 1
|
||||
- _Hue: 0
|
||||
- _Metallic: 0
|
||||
- _Mode: 0
|
||||
- _OcclusionStrength: 1
|
||||
- _OutlineGradient: 0.7
|
||||
- _OutlineOpacity: 0.15
|
||||
- _OutlineWidth: 2.5
|
||||
- _Parallax: 0.02
|
||||
- _Radius: 4
|
||||
- _Red: 0
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SpecularHighlights: 1
|
||||
- _SrcBlend: 1
|
||||
- _Stencil: 0
|
||||
- _StencilComp: 8
|
||||
- _StencilOp: 0
|
||||
- _StencilReadMask: 255
|
||||
- _StencilWriteMask: 255
|
||||
- _UVSec: 0
|
||||
- _UseUIAlphaClip: 0
|
||||
- _ZWrite: 1
|
||||
m_Colors:
|
||||
- _Color: {r: 0, g: 0, b: 0, a: 1}
|
||||
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
93
Assets/UI/HSLSlider.mat
Normal file
93
Assets/UI/HSLSlider.mat
Normal file
@ -0,0 +1,93 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!21 &2100000
|
||||
Material:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: HSLSlider
|
||||
m_Shader: {fileID: 4800000, guid: a6b2542ba8ea844e7b0526fab69d88ed, type: 3}
|
||||
m_ShaderKeywords: HUE
|
||||
m_LightmapFlags: 4
|
||||
m_EnableInstancingVariants: 0
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: -1
|
||||
stringTagMap: {}
|
||||
disabledShaderPasses: []
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _BumpMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailAlbedoMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailMask:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailNormalMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _EmissionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MetallicGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _OcclusionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _ParallaxMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Floats:
|
||||
- _Blue: 0
|
||||
- _BumpScale: 1
|
||||
- _ColorMask: 15
|
||||
- _Cutoff: 0.5
|
||||
- _DetailNormalMapScale: 1
|
||||
- _DstBlend: 0
|
||||
- _GlossMapScale: 1
|
||||
- _Glossiness: 0.5
|
||||
- _GlossyReflections: 1
|
||||
- _GradientStep: 0.2
|
||||
- _Green: 0
|
||||
- _Hue: 1
|
||||
- _Metallic: 0
|
||||
- _Mode: 0
|
||||
- _OcclusionStrength: 1
|
||||
- _OutlineGradient: 0.55
|
||||
- _OutlineOpacity: 0.15
|
||||
- _OutlineWidth: 2.5
|
||||
- _Parallax: 0.02
|
||||
- _Radius: 4
|
||||
- _Red: 0
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SpecularHighlights: 1
|
||||
- _SrcBlend: 1
|
||||
- _Stencil: 0
|
||||
- _StencilComp: 8
|
||||
- _StencilOp: 0
|
||||
- _StencilReadMask: 255
|
||||
- _StencilWriteMask: 255
|
||||
- _UVSec: 0
|
||||
- _UseUIAlphaClip: 0
|
||||
- _ZWrite: 1
|
||||
m_Colors:
|
||||
- _Color: {r: 0, g: 1, b: 1, a: 1}
|
||||
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
89
Assets/UI/HSLSquare.mat
Normal file
89
Assets/UI/HSLSquare.mat
Normal file
@ -0,0 +1,89 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!21 &2100000
|
||||
Material:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: HSLSquare
|
||||
m_Shader: {fileID: 4800000, guid: 41b82117f67243a4851d2ce6bbed0d6a, type: 3}
|
||||
m_ShaderKeywords:
|
||||
m_LightmapFlags: 4
|
||||
m_EnableInstancingVariants: 0
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: -1
|
||||
stringTagMap: {}
|
||||
disabledShaderPasses: []
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _BumpMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailAlbedoMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailMask:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailNormalMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _EmissionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MetallicGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _OcclusionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _ParallaxMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Floats:
|
||||
- _BumpScale: 1
|
||||
- _ColorMask: 15
|
||||
- _Cutoff: 0.5
|
||||
- _DetailNormalMapScale: 1
|
||||
- _DstBlend: 0
|
||||
- _GlossMapScale: 1
|
||||
- _Glossiness: 0.5
|
||||
- _GlossyReflections: 1
|
||||
- _Hue: 0.566
|
||||
- _Metallic: 0
|
||||
- _Mode: 0
|
||||
- _OcclusionStrength: 1
|
||||
- _OutlineGradient: 0.55
|
||||
- _OutlineOpacity: 0.15
|
||||
- _OutlineWidth: 2.5
|
||||
- _Parallax: 0.02
|
||||
- _Radius: 4
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SpecularHighlights: 1
|
||||
- _SrcBlend: 1
|
||||
- _Stencil: 0
|
||||
- _StencilComp: 8
|
||||
- _StencilOp: 0
|
||||
- _StencilReadMask: 255
|
||||
- _StencilWriteMask: 255
|
||||
- _UVSec: 0
|
||||
- _UseUIAlphaClip: 0
|
||||
- _ZWrite: 1
|
||||
m_Colors:
|
||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
93
Assets/UI/RSlider.mat
Normal file
93
Assets/UI/RSlider.mat
Normal file
@ -0,0 +1,93 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!21 &2100000
|
||||
Material:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: RSlider
|
||||
m_Shader: {fileID: 4800000, guid: a6b2542ba8ea844e7b0526fab69d88ed, type: 3}
|
||||
m_ShaderKeywords: RED
|
||||
m_LightmapFlags: 4
|
||||
m_EnableInstancingVariants: 0
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: -1
|
||||
stringTagMap: {}
|
||||
disabledShaderPasses: []
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _BumpMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailAlbedoMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailMask:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailNormalMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _EmissionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MetallicGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _OcclusionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _ParallaxMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Floats:
|
||||
- _Blue: 0
|
||||
- _BumpScale: 1
|
||||
- _ColorMask: 15
|
||||
- _Cutoff: 0.5
|
||||
- _DetailNormalMapScale: 1
|
||||
- _DstBlend: 0
|
||||
- _GlossMapScale: 1
|
||||
- _Glossiness: 0.5
|
||||
- _GlossyReflections: 1
|
||||
- _GradientStep: 0.2
|
||||
- _Green: 0
|
||||
- _Hue: 0
|
||||
- _Metallic: 0
|
||||
- _Mode: 0
|
||||
- _OcclusionStrength: 1
|
||||
- _OutlineGradient: 0.7
|
||||
- _OutlineOpacity: 0.15
|
||||
- _OutlineWidth: 2.5
|
||||
- _Parallax: 0.02
|
||||
- _Radius: 4
|
||||
- _Red: 1
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SpecularHighlights: 1
|
||||
- _SrcBlend: 1
|
||||
- _Stencil: 0
|
||||
- _StencilComp: 8
|
||||
- _StencilOp: 0
|
||||
- _StencilReadMask: 255
|
||||
- _StencilWriteMask: 255
|
||||
- _UVSec: 0
|
||||
- _UseUIAlphaClip: 0
|
||||
- _ZWrite: 1
|
||||
m_Colors:
|
||||
- _Color: {r: 0, g: 0, b: 0, a: 1}
|
||||
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
92
Assets/UI/Swatch.mat
Normal file
92
Assets/UI/Swatch.mat
Normal file
@ -0,0 +1,92 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!21 &2100000
|
||||
Material:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Swatch
|
||||
m_Shader: {fileID: 4800000, guid: a6e04e87fe864ed6a3f6a3ce52f57024, type: 3}
|
||||
m_ShaderKeywords: RED
|
||||
m_LightmapFlags: 4
|
||||
m_EnableInstancingVariants: 0
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: -1
|
||||
stringTagMap: {}
|
||||
disabledShaderPasses: []
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _BumpMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailAlbedoMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailMask:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailNormalMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _EmissionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MetallicGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _OcclusionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _ParallaxMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Floats:
|
||||
- _Blue: 0
|
||||
- _BumpScale: 1
|
||||
- _ColorMask: 15
|
||||
- _Cutoff: 0.5
|
||||
- _DetailNormalMapScale: 1
|
||||
- _DstBlend: 0
|
||||
- _GlossMapScale: 1
|
||||
- _Glossiness: 0.5
|
||||
- _GlossyReflections: 1
|
||||
- _Green: 0
|
||||
- _Hue: 0
|
||||
- _Metallic: 0
|
||||
- _Mode: 0
|
||||
- _OcclusionStrength: 1
|
||||
- _OutlineGradient: 0.3
|
||||
- _OutlineOpacity: 0.15
|
||||
- _OutlineWidth: 2.5
|
||||
- _Parallax: 0.02
|
||||
- _Radius: 4
|
||||
- _Red: 1
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SpecularHighlights: 1
|
||||
- _SrcBlend: 1
|
||||
- _Stencil: 0
|
||||
- _StencilComp: 8
|
||||
- _StencilOp: 0
|
||||
- _StencilReadMask: 255
|
||||
- _StencilWriteMask: 255
|
||||
- _UVSec: 0
|
||||
- _UseUIAlphaClip: 0
|
||||
- _ZWrite: 1
|
||||
m_Colors:
|
||||
- _Color: {r: 0.5176471, g: 0.5019608, b: 0.5019608, a: 1}
|
||||
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
5193
Assets/UI/TextEntryPanel.prefab
Normal file
5193
Assets/UI/TextEntryPanel.prefab
Normal file
File diff suppressed because it is too large
Load Diff
@ -18,6 +18,14 @@ Localization
|
||||
#LOC_ConformalDecals_gui-aspectratio = Aspect Ratio
|
||||
#LOC_ConformalDecals_gui-select-flag = Select Flag
|
||||
#LOC_ConformalDecals_gui-reset-flag = Reset Flag
|
||||
#LOC_ConformalDecals_gui-set-text = Set Text
|
||||
#LOC_ConformalDecals_gui-group-fill = Fill
|
||||
#LOC_ConformalDecals_gui-group-outline = Outline
|
||||
#LOC_ConformalDecals_gui-fill = Fill
|
||||
#LOC_ConformalDecals_gui-set-fill-color = Set Fill Color
|
||||
#LOC_ConformalDecals_gui-outline = Outline
|
||||
#LOC_ConformalDecals_gui-outline-width = Outline Width
|
||||
#LOC_ConformalDecals_gui-set-outline-color = Set Outline Color
|
||||
|
||||
// PARTS
|
||||
|
||||
@ -26,6 +34,11 @@ Localization
|
||||
#LOC_ConformalDecals_flag-description = A simple switchable flag. Can either use the mission flag or select a specific flag to use.
|
||||
#LOC_ConformalDecals_flag-tags = conformal decal sticker flag
|
||||
|
||||
// Text
|
||||
#LOC_ConformalDecals_text-title = CDL-T Text Decal
|
||||
#LOC_ConformalDecals_text-description = A decal that can display custom text in a variety of fonts
|
||||
#LOC_ConformalDecals_text-tags = conformal decal sticker text font
|
||||
|
||||
// Generic Decals
|
||||
#LOC_ConformalDecals_generic-title = CDL-1 Generic Decal
|
||||
#LOC_ConformalDecals_generic-description = A set of generic warning decals and signs to add to your vehicles.
|
||||
|
63
GameData/ConformalDecals/Parts/decal-text.cfg
Normal file
63
GameData/ConformalDecals/Parts/decal-text.cfg
Normal file
@ -0,0 +1,63 @@
|
||||
PART
|
||||
{
|
||||
name = conformaldecals-text
|
||||
module = Part
|
||||
author = Andrew Cassidy
|
||||
MODEL
|
||||
{
|
||||
model = ConformalDecals/Assets/decal-blank
|
||||
scale = 1.0, 1.0, 1.0
|
||||
}
|
||||
rescaleFactor = 1
|
||||
|
||||
// Attachment
|
||||
attachRules = 1,1,0,0,1
|
||||
node_attach = 0.0, 0.0, 0.1, 0.0, 0.0, -1.0
|
||||
|
||||
// Tech
|
||||
TechRequired = start
|
||||
|
||||
// Info
|
||||
cost = 75
|
||||
category = Structural
|
||||
|
||||
// CDL-T Text Decal
|
||||
title = #LOC_ConformalDecals_text-title
|
||||
|
||||
|
||||
// Peel-N-Stik Adhesive Decals
|
||||
manufacturer = #LOC_ConformalDecals_agent-peel-n-stick_title
|
||||
|
||||
// A decal that can display custom text in a variety of fonts
|
||||
description = #LOC_ConformalDecals_text-description
|
||||
|
||||
|
||||
// conformal decal sticker text font
|
||||
tags = #LOC_ConformalDecals_text-tags
|
||||
|
||||
bulkheadProfiles = srf
|
||||
|
||||
// Parameters
|
||||
mass = 0.0005
|
||||
dragModel = NONE
|
||||
angularDrag = 0.0
|
||||
crashTolerance = 10
|
||||
maxTemp = 2000
|
||||
breakingForce = 350
|
||||
breakingTorque = 150
|
||||
physicalSignificance = NONE
|
||||
|
||||
MODULE
|
||||
{
|
||||
name = ModuleConformalText
|
||||
|
||||
text = Text
|
||||
|
||||
shader = ConformalDecals/Decal/Text
|
||||
useBaseNormal = true
|
||||
scaleMode = MINIMUM
|
||||
|
||||
defaultDepth = 0.2
|
||||
defaultCutoff = 0.5
|
||||
}
|
||||
}
|
Binary file not shown.
@ -1,6 +1,7 @@
|
||||
// Custom category, requires WildBlueTools to work
|
||||
MODCAT
|
||||
{
|
||||
name = conformaldecals
|
||||
title = #LOC_ConformalDecals_category-decals_title // Decals
|
||||
|
||||
folderName = ConformalDecals
|
||||
|
@ -20,4 +20,41 @@ CONFORMALDECALS {
|
||||
shader = KSP/Specular (Transparent)
|
||||
shader = Solid Color (Alpha)
|
||||
}
|
||||
|
||||
FONT {
|
||||
name = LiberationSans SDF
|
||||
title = Liberation Sans
|
||||
}
|
||||
|
||||
FONT {
|
||||
name = Calibri SDF
|
||||
title = Calibri
|
||||
}
|
||||
|
||||
FONT {
|
||||
name = NotoSans-Regular SDF
|
||||
title = Noto Sans
|
||||
}
|
||||
|
||||
FONT {
|
||||
name = Waukegan LDO Extended SDF
|
||||
title = Waukegan Extended
|
||||
}
|
||||
|
||||
FONT {
|
||||
name = Nasalization SDF
|
||||
title = Nasalization
|
||||
}
|
||||
|
||||
FONT {
|
||||
name = Helvetica SDF
|
||||
title = Helvetica
|
||||
}
|
||||
|
||||
FONT {
|
||||
name = amarurgt SDF
|
||||
title = Amarillo USAF
|
||||
style = 32
|
||||
styleMask = 4
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8580f3c17793051fdc08447401fb7920665ad681ff352588508044b662a3240b
|
||||
size 364900
|
Binary file not shown.
BIN
GameData/ConformalDecals/Resources/ui.conformaldecals
Normal file
BIN
GameData/ConformalDecals/Resources/ui.conformaldecals
Normal file
Binary file not shown.
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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')" />
|
||||
<Import Project="$(MSBuildExtensionsPath)/$(MSBuildToolsVersion)/Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)/$(MSBuildToolsVersion)/Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
@ -17,7 +17,7 @@
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<OutputPath>bin/Debug/</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
@ -25,12 +25,12 @@
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<OutputPath>bin/Release/</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<DocumentationFile>bin\Release\ConformalDecals.xml</DocumentationFile>
|
||||
<NoWarn>CS1591</NoWarn>
|
||||
<DocumentationFile>bin/Release/ConformalDecals.xml</DocumentationFile>
|
||||
<NoWarn>CS1591,CS0649</NoWarn>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
|
||||
@ -41,35 +41,73 @@
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="UnityEngine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
|
||||
<HintPath>dlls\UnityEngine.dll</HintPath>
|
||||
<HintPath>dlls/UnityEngine.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.AssetBundleModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
|
||||
<HintPath>dlls/UnityEngine.AssetBundleModule.dll</HintPath>
|
||||
</Reference>
|
||||
<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 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 Include="UnityEngine.UI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
|
||||
<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 Include="UnityEngine.UIModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
|
||||
<HintPath>dlls/UnityEngine.UIModule.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="DecalConfig.cs" />
|
||||
<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="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="Test\TestLayers.cs" />
|
||||
<Compile Include="Util\Logging.cs" />
|
||||
<Compile Include="Util\OrientedBounds.cs" />
|
||||
<Compile Include="Util\ParseUtil.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="Test/TestLayers.cs" />
|
||||
<Compile Include="Text\DecalTextStyle.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/TextureUtils.cs" />
|
||||
<Compile Include="Util/ParseUtil.cs" />
|
||||
<Compile Include="UI/BoxSlider.cs" />
|
||||
<Compile Include="Util\ColorHSL.cs" />
|
||||
<Compile Include="Util\ColorHSV.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(MSBuildToolsPath)/Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>sh -e -c "cp -v '$(TargetPath)' '$(SolutionDir)/../GameData/ConformalDecals/Plugins'"</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
|
@ -1,11 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ConformalDecals.Text;
|
||||
using ConformalDecals.Util;
|
||||
using TMPro;
|
||||
using UniLinq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ConformalDecals {
|
||||
public static class DecalConfig {
|
||||
private static Texture2D _blankNormal;
|
||||
private static List<string> _shaderBlacklist;
|
||||
private static Dictionary<string, DecalFont> _fontList;
|
||||
private static int _decalLayer = 31;
|
||||
private static bool _selectableInFlight;
|
||||
|
||||
@ -37,12 +42,17 @@ namespace ConformalDecals {
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
public static Texture2D BlankNormal => _blankNormal;
|
||||
|
||||
public static int DecalLayer => _decalLayer;
|
||||
|
||||
public static bool SelectableInFlight => _selectableInFlight;
|
||||
|
||||
public static IEnumerable<DecalFont> Fonts => _fontList.Values;
|
||||
|
||||
public static DecalFont FallbackFont { get; private set; }
|
||||
|
||||
public static bool IsBlacklisted(Shader shader) {
|
||||
return IsBlacklisted(shader.name);
|
||||
}
|
||||
@ -63,14 +73,36 @@ namespace ConformalDecals {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static DecalFont GetFont(string name) {
|
||||
if (_fontList.TryGetValue(name, out var font)) {
|
||||
return font;
|
||||
}
|
||||
else {
|
||||
throw new KeyNotFoundException($"Font {name} not found");
|
||||
}
|
||||
}
|
||||
|
||||
private static void ParseConfig(ConfigNode node) {
|
||||
|
||||
ParseUtil.ParseIntIndirect(ref _decalLayer, node, "decalLayer");
|
||||
ParseUtil.ParseBoolIndirect(ref _selectableInFlight, node, "selectableInFlight");
|
||||
|
||||
foreach (var blacklist in node.GetNodes("SHADERBLACKLIST")) {
|
||||
foreach (var shaderName in blacklist.GetValuesList("shader")) {
|
||||
_shaderBlacklist.Add(shaderName);
|
||||
}
|
||||
}
|
||||
|
||||
ParseUtil.ParseIntIndirect(ref _decalLayer, node, "decalLayer");
|
||||
ParseUtil.ParseBoolIndirect(ref _selectableInFlight, node, "selectableInFlight");
|
||||
var allFonts = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
|
||||
|
||||
foreach (var fontNode in node.GetNodes("FONT")) {
|
||||
try {
|
||||
var font = new DecalFont(fontNode, allFonts);
|
||||
_fontList.Add(font.Name, font);
|
||||
}
|
||||
catch (Exception e) {
|
||||
Debug.LogException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,6 +128,7 @@ namespace ConformalDecals {
|
||||
// ReSharper disable once UnusedMember.Global
|
||||
public static void ModuleManagerPostLoad() {
|
||||
_shaderBlacklist = new List<string>();
|
||||
_fontList = new Dictionary<string, DecalFont>();
|
||||
|
||||
var configs = GameDatabase.Instance.GetConfigs("CONFORMALDECALS");
|
||||
|
||||
|
@ -19,7 +19,10 @@ namespace ConformalDecals.MaterialProperties {
|
||||
[SerializeField] private Vector2 _textureOffset;
|
||||
[SerializeField] private Vector2 _textureScale = Vector2.one;
|
||||
|
||||
public Texture2D Texture => _texture;
|
||||
public Texture2D Texture {
|
||||
get => _texture;
|
||||
set => _texture = value;
|
||||
}
|
||||
|
||||
public string TextureUrl {
|
||||
get => _textureUrl;
|
||||
|
@ -246,8 +246,6 @@ namespace ConformalDecals {
|
||||
|
||||
_boundsRenderer = decalProjectorTransform.GetComponent<MeshRenderer>();
|
||||
|
||||
UpdateMaterials();
|
||||
|
||||
// handle tweakables
|
||||
if (HighLogic.LoadedSceneIsEditor) {
|
||||
GameEvents.onEditorPartEvent.Add(OnEditorEvent);
|
||||
@ -360,7 +358,7 @@ namespace ConformalDecals {
|
||||
}
|
||||
}
|
||||
|
||||
protected void OnAttach() {
|
||||
protected virtual void OnAttach() {
|
||||
if (part.parent == null) {
|
||||
this.LogError("Attach function called but part has no parent!");
|
||||
_isAttached = false;
|
||||
@ -383,7 +381,7 @@ namespace ConformalDecals {
|
||||
UpdateScale();
|
||||
}
|
||||
|
||||
protected void OnDetach() {
|
||||
protected virtual void OnDetach() {
|
||||
_isAttached = false;
|
||||
|
||||
// unhide model
|
||||
@ -456,7 +454,7 @@ namespace ConformalDecals {
|
||||
}
|
||||
}
|
||||
|
||||
protected void UpdateMaterials() {
|
||||
protected virtual void UpdateMaterials() {
|
||||
materialProperties.UpdateMaterials();
|
||||
materialProperties.SetOpacity(opacity);
|
||||
materialProperties.SetCutoff(cutoff);
|
||||
@ -500,7 +498,7 @@ namespace ConformalDecals {
|
||||
}
|
||||
}
|
||||
|
||||
protected void UpdateTweakables() {
|
||||
protected virtual void UpdateTweakables() {
|
||||
// setup tweakable fields
|
||||
var scaleField = Fields[nameof(scale)];
|
||||
var depthField = Fields[nameof(depth)];
|
||||
@ -514,7 +512,7 @@ namespace ConformalDecals {
|
||||
cutoffField.guiActiveEditor = cutoffAdjustable;
|
||||
wearField.guiActiveEditor = useBaseNormal;
|
||||
|
||||
var steps = 20;
|
||||
var steps = 40;
|
||||
|
||||
if (scaleAdjustable) {
|
||||
var minValue = Mathf.Max(Mathf.Epsilon, scaleRange.x);
|
||||
|
292
Source/ConformalDecals/ModuleConformalText.cs
Normal file
292
Source/ConformalDecals/ModuleConformalText.cs
Normal file
@ -0,0 +1,292 @@
|
||||
using ConformalDecals.MaterialProperties;
|
||||
using ConformalDecals.Text;
|
||||
using ConformalDecals.UI;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ConformalDecals {
|
||||
public class ModuleConformalText : ModuleConformalDecal, ISerializationCallbackReceiver {
|
||||
[KSPField(isPersistant = true)] public string text = "Text";
|
||||
[KSPField(isPersistant = true)] public Color fillColor = Color.black;
|
||||
[KSPField(isPersistant = true)] public Color outlineColor = Color.white;
|
||||
|
||||
[KSPField] public Vector2 lineSpacingRange = new Vector2(-50, 50);
|
||||
[KSPField] public Vector2 charSpacingRange = new Vector2(-50, 50);
|
||||
|
||||
// serialization-only fields. do not use except in serialization functions
|
||||
[KSPField(isPersistant = true)] public string fontName = "Calibri SDF";
|
||||
[KSPField(isPersistant = true)] public int style;
|
||||
[KSPField(isPersistant = true)] public bool vertical;
|
||||
[KSPField(isPersistant = true)] public float lineSpacing;
|
||||
[KSPField(isPersistant = true)] public float charSpacing;
|
||||
|
||||
// KSP TWEAKABLES
|
||||
|
||||
[KSPEvent(guiName = "#LOC_ConformalDecals_gui-set-text", guiActive = false, guiActiveEditor = true)]
|
||||
public void SetText() {
|
||||
if (_textEntryController == null) {
|
||||
_textEntryController = TextEntryController.Create(text, _font, _style, lineSpacingRange, charSpacingRange, OnTextUpdate);
|
||||
}
|
||||
else {
|
||||
_textEntryController.Close();
|
||||
}
|
||||
}
|
||||
|
||||
// FILL
|
||||
|
||||
[KSPField(guiName = "#LOC_ConformalDecals_gui-fill", groupName = "decal-fill", groupDisplayName = "#LOC_ConformalDecals_gui-group-fill",
|
||||
guiActive = false, guiActiveEditor = true, isPersistant = true),
|
||||
UI_Toggle()]
|
||||
public bool fillEnabled = true;
|
||||
|
||||
[KSPEvent(guiName = "#LOC_ConformalDecals_gui-set-fill-color", groupName = "decal-fill", groupDisplayName = "#LOC_ConformalDecals_gui-group-fill",
|
||||
guiActive = false, guiActiveEditor = true)]
|
||||
public void SetFillColor() {
|
||||
if (_fillColorPickerController == null) {
|
||||
_fillColorPickerController = ColorPickerController.Create(fillColor, OnFillColorUpdate);
|
||||
}
|
||||
else {
|
||||
_fillColorPickerController.Close();
|
||||
}
|
||||
}
|
||||
|
||||
// OUTLINE
|
||||
|
||||
[KSPField(guiName = "#LOC_ConformalDecals_gui-outline", groupName = "decal-outline", groupDisplayName = "#LOC_ConformalDecals_gui-group-outline",
|
||||
guiActive = false, guiActiveEditor = true, isPersistant = true),
|
||||
UI_Toggle()]
|
||||
public bool outlineEnabled;
|
||||
|
||||
[KSPField(guiName = "#LOC_ConformalDecals_gui-outline-width", groupName = "decal-outline", groupDisplayName = "#LOC_ConformalDecals_gui-group-outline",
|
||||
guiActive = false, guiActiveEditor = true, isPersistant = true, guiFormat = "F2"),
|
||||
UI_FloatRange(stepIncrement = 0.05f)]
|
||||
public float outlineWidth = 0.1f;
|
||||
|
||||
[KSPEvent(guiName = "#LOC_ConformalDecals_gui-set-outline-color", groupName = "decal-outline", groupDisplayName = "#LOC_ConformalDecals_gui-group-outline",
|
||||
guiActive = false, guiActiveEditor = true)]
|
||||
public void SetOutlineColor() {
|
||||
if (_outlineColorPickerController == null) {
|
||||
_outlineColorPickerController = ColorPickerController.Create(outlineColor, OnOutlineColorUpdate);
|
||||
}
|
||||
else {
|
||||
_outlineColorPickerController.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private DecalTextStyle _style;
|
||||
private DecalFont _font;
|
||||
|
||||
private TextEntryController _textEntryController;
|
||||
private ColorPickerController _fillColorPickerController;
|
||||
private ColorPickerController _outlineColorPickerController;
|
||||
|
||||
private MaterialTextureProperty _decalTextureProperty;
|
||||
|
||||
private MaterialKeywordProperty _fillEnabledProperty;
|
||||
private MaterialColorProperty _fillColorProperty;
|
||||
|
||||
private MaterialKeywordProperty _outlineEnabledProperty;
|
||||
private MaterialColorProperty _outlineColorProperty;
|
||||
private MaterialFloatProperty _outlineWidthProperty;
|
||||
|
||||
private TextRenderJob _currentJob;
|
||||
private DecalText _currentText;
|
||||
|
||||
public override void OnLoad(ConfigNode node) {
|
||||
base.OnLoad(node);
|
||||
OnAfterDeserialize();
|
||||
|
||||
UpdateTextRecursive();
|
||||
}
|
||||
|
||||
public override void OnSave(ConfigNode node) {
|
||||
OnBeforeSerialize();
|
||||
base.OnSave(node);
|
||||
}
|
||||
|
||||
public override void OnStart(StartState state) {
|
||||
base.OnStart(state);
|
||||
|
||||
UpdateTextRecursive();
|
||||
}
|
||||
|
||||
public override void OnAwake() {
|
||||
base.OnAwake();
|
||||
|
||||
_decalTextureProperty = materialProperties.AddOrGetTextureProperty("_Decal", true);
|
||||
|
||||
_fillEnabledProperty = materialProperties.AddOrGetProperty<MaterialKeywordProperty>("DECAL_FILL");
|
||||
_fillColorProperty = materialProperties.AddOrGetProperty<MaterialColorProperty>("_DecalColor");
|
||||
|
||||
_outlineEnabledProperty = materialProperties.AddOrGetProperty<MaterialKeywordProperty>("DECAL_OUTLINE");
|
||||
_outlineColorProperty = materialProperties.AddOrGetProperty<MaterialColorProperty>("_OutlineColor");
|
||||
_outlineWidthProperty = materialProperties.AddOrGetProperty<MaterialFloatProperty>("_OutlineWidth");
|
||||
}
|
||||
|
||||
public void OnTextUpdate(string newText, DecalFont newFont, DecalTextStyle newStyle) {
|
||||
text = newText;
|
||||
_font = newFont;
|
||||
_style = newStyle;
|
||||
UpdateTextRecursive();
|
||||
}
|
||||
|
||||
public void OnFillColorUpdate(Color rgb, Util.ColorHSV hsv) {
|
||||
fillColor = rgb;
|
||||
UpdateMaterials();
|
||||
|
||||
foreach (var counterpart in part.symmetryCounterparts) {
|
||||
var decal = counterpart.GetComponent<ModuleConformalText>();
|
||||
decal.fillColor = fillColor;
|
||||
decal.UpdateMaterials();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnOutlineColorUpdate(Color rgb, Util.ColorHSV hsv) {
|
||||
outlineColor = rgb;
|
||||
UpdateMaterials();
|
||||
|
||||
foreach (var counterpart in part.symmetryCounterparts) {
|
||||
var decal = counterpart.GetComponent<ModuleConformalText>();
|
||||
decal.outlineColor = outlineColor;
|
||||
decal.UpdateMaterials();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnFillToggle(BaseField field, object obj) {
|
||||
// fill and outline cant both be disabled
|
||||
outlineEnabled = outlineEnabled || (!outlineEnabled && !fillEnabled);
|
||||
|
||||
UpdateTweakables();
|
||||
UpdateMaterials();
|
||||
|
||||
foreach (var counterpart in part.symmetryCounterparts) {
|
||||
var decal = counterpart.GetComponent<ModuleConformalText>();
|
||||
decal.fillEnabled = fillEnabled;
|
||||
decal.outlineEnabled = outlineEnabled;
|
||||
decal.UpdateTweakables();
|
||||
decal.UpdateMaterials();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnOutlineToggle(BaseField field, object obj) {
|
||||
// fill and outline cant both be disabled
|
||||
fillEnabled = fillEnabled || (!fillEnabled && !outlineEnabled);
|
||||
|
||||
UpdateTweakables();
|
||||
UpdateMaterials();
|
||||
|
||||
foreach (var counterpart in part.symmetryCounterparts) {
|
||||
var decal = counterpart.GetComponent<ModuleConformalText>();
|
||||
decal.fillEnabled = fillEnabled;
|
||||
decal.outlineEnabled = outlineEnabled;
|
||||
decal.UpdateTweakables();
|
||||
decal.UpdateMaterials();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnOutlineWidthUpdate(BaseField field, object obj) {
|
||||
UpdateMaterials();
|
||||
|
||||
foreach (var counterpart in part.symmetryCounterparts) {
|
||||
var decal = counterpart.GetComponent<ModuleConformalText>();
|
||||
decal.outlineWidth = outlineWidth;
|
||||
decal.UpdateMaterials();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnBeforeSerialize() {
|
||||
fontName = _font.Name;
|
||||
style = (int) _style.FontStyle;
|
||||
vertical = _style.Vertical;
|
||||
lineSpacing = _style.LineSpacing;
|
||||
charSpacing = _style.CharSpacing;
|
||||
}
|
||||
|
||||
public void OnAfterDeserialize() {
|
||||
_font = DecalConfig.GetFont(fontName);
|
||||
_style = new DecalTextStyle((FontStyles) style, vertical, lineSpacing, charSpacing);
|
||||
}
|
||||
|
||||
public override void OnDestroy() {
|
||||
if (HighLogic.LoadedSceneIsGame && _currentText != null) TextRenderer.UnregisterText(_currentText);
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
protected override void OnDetach() {
|
||||
// close all UIs
|
||||
if (_textEntryController != null) _textEntryController.Close();
|
||||
if (_fillColorPickerController != null) _fillColorPickerController.Close();
|
||||
if (_outlineColorPickerController != null) _outlineColorPickerController.Close();
|
||||
|
||||
base.OnDetach();
|
||||
}
|
||||
|
||||
private void UpdateTextRecursive() {
|
||||
UpdateText();
|
||||
|
||||
foreach (var counterpart in part.symmetryCounterparts) {
|
||||
var decal = counterpart.GetComponent<ModuleConformalText>();
|
||||
decal.text = text;
|
||||
decal._font = _font;
|
||||
decal._style = _style;
|
||||
|
||||
decal._currentJob = _currentJob;
|
||||
decal._currentText = _currentText;
|
||||
decal.UpdateText();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateText() {
|
||||
// Render text
|
||||
var newText = new DecalText(text, _font, _style);
|
||||
var output = TextRenderer.UpdateTextNow(_currentText, newText);
|
||||
_currentText = newText;
|
||||
|
||||
UpdateTexture(output);
|
||||
|
||||
// TODO: ASYNC RENDERING
|
||||
// var newText = new DecalText(text, _font, _style);
|
||||
// _currentJob = TextRenderer.UpdateText(_currentText, newText, UpdateTexture);
|
||||
// _currentText = newText;
|
||||
}
|
||||
|
||||
public void UpdateTexture(TextRenderOutput output) {
|
||||
_decalTextureProperty.Texture = output.Texture;
|
||||
_decalTextureProperty.SetTile(output.Window);
|
||||
|
||||
UpdateMaterials();
|
||||
UpdateScale();
|
||||
}
|
||||
|
||||
protected override void UpdateMaterials() {
|
||||
_fillEnabledProperty.value = fillEnabled;
|
||||
_fillColorProperty.color = fillColor;
|
||||
|
||||
_outlineEnabledProperty.value = outlineEnabled;
|
||||
_outlineColorProperty.color = outlineColor;
|
||||
_outlineWidthProperty.value = outlineWidth;
|
||||
|
||||
base.UpdateMaterials();
|
||||
}
|
||||
|
||||
protected override void UpdateTweakables() {
|
||||
var fillEnabledField = Fields[nameof(fillEnabled)];
|
||||
var fillColorEvent = Events["SetFillColor"];
|
||||
|
||||
var outlineEnabledField = Fields[nameof(outlineEnabled)];
|
||||
var outlineWidthField = Fields[nameof(outlineWidth)];
|
||||
var outlineColorEvent = Events["SetOutlineColor"];
|
||||
|
||||
fillColorEvent.guiActiveEditor = fillEnabled;
|
||||
outlineWidthField.guiActiveEditor = outlineEnabled;
|
||||
outlineColorEvent.guiActiveEditor = outlineEnabled;
|
||||
|
||||
((UI_Toggle) fillEnabledField.uiControlEditor).onFieldChanged = OnFillToggle;
|
||||
((UI_Toggle) outlineEnabledField.uiControlEditor).onFieldChanged = OnOutlineToggle;
|
||||
((UI_FloatRange) outlineWidthField.uiControlEditor).onFieldChanged = OnOutlineWidthUpdate;
|
||||
|
||||
base.UpdateTweakables();
|
||||
}
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ 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("Conformal Decals")]
|
||||
[assembly: AssemblyTitle("ConformalDecals")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Cineboxandrew")]
|
||||
|
94
Source/ConformalDecals/Text/DecalFont.cs
Normal file
94
Source/ConformalDecals/Text/DecalFont.cs
Normal file
@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ConformalDecals.Util;
|
||||
using TMPro;
|
||||
using UniLinq;
|
||||
|
||||
namespace ConformalDecals.Text {
|
||||
public class DecalFont : IEquatable<DecalFont> {
|
||||
public string Title { get; }
|
||||
|
||||
public TMP_FontAsset FontAsset { get; }
|
||||
|
||||
public string Name => FontAsset.name;
|
||||
|
||||
|
||||
public FontStyles FontStyle { get; }
|
||||
|
||||
public bool Bold => (FontStyle & FontStyles.Bold) != 0;
|
||||
|
||||
public bool Italic => (FontStyle & FontStyles.Italic) != 0;
|
||||
|
||||
public bool Underline => (FontStyle & FontStyles.Underline) != 0;
|
||||
|
||||
public bool SmallCaps => (FontStyle & FontStyles.SmallCaps) != 0;
|
||||
|
||||
|
||||
public FontStyles FontStyleMask { get; }
|
||||
|
||||
public bool BoldMask => (FontStyleMask & FontStyles.Bold) != 0;
|
||||
|
||||
public bool ItalicMask => (FontStyleMask & FontStyles.Italic) != 0;
|
||||
|
||||
public bool UnderlineMask => (FontStyleMask & FontStyles.Underline) != 0;
|
||||
|
||||
public bool SmallCapsMask => (FontStyleMask & FontStyles.SmallCaps) != 0;
|
||||
|
||||
|
||||
public DecalFont(ConfigNode node, IEnumerable<TMP_FontAsset> fontAssets) {
|
||||
if (node == null) throw new ArgumentNullException(nameof(node));
|
||||
if (fontAssets == null) throw new ArgumentNullException(nameof(fontAssets));
|
||||
|
||||
var name = ParseUtil.ParseString(node, "name");
|
||||
FontAsset = fontAssets.First(o => o.name == name);
|
||||
if (FontAsset == null) {
|
||||
throw new FormatException($"Could not find font asset named {name}");
|
||||
}
|
||||
|
||||
Title = ParseUtil.ParseString(node, "title", true, name);
|
||||
FontStyle = (FontStyles) ParseUtil.ParseInt(node, "style", true);
|
||||
FontStyleMask = (FontStyles) ParseUtil.ParseInt(node, "styleMask", true);
|
||||
}
|
||||
|
||||
|
||||
public void SetupSample(TMP_Text tmp) {
|
||||
if (tmp == null) throw new ArgumentNullException(nameof(tmp));
|
||||
if (FontAsset == null) throw new InvalidOperationException("DecalFont has not been initialized and Font is null.");
|
||||
|
||||
tmp.text = Title;
|
||||
tmp.font = FontAsset;
|
||||
tmp.fontStyle = FontStyle;
|
||||
}
|
||||
|
||||
public bool Equals(DecalFont other) {
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return Title == other.Title && Equals(FontAsset, other.FontAsset) && FontStyle == other.FontStyle && FontStyleMask == other.FontStyleMask;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((DecalFont) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
unchecked {
|
||||
var hashCode = (Title != null ? Title.GetHashCode() : 0);
|
||||
hashCode = (hashCode * 397) ^ (FontAsset != null ? FontAsset.GetHashCode() : 0);
|
||||
hashCode = (hashCode * 397) ^ (int) FontStyle;
|
||||
hashCode = (hashCode * 397) ^ (int) FontStyleMask;
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(DecalFont left, DecalFont right) {
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(DecalFont left, DecalFont right) {
|
||||
return !Equals(left, right);
|
||||
}
|
||||
}
|
||||
}
|
60
Source/ConformalDecals/Text/DecalText.cs
Normal file
60
Source/ConformalDecals/Text/DecalText.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ConformalDecals.Text {
|
||||
public class DecalText : IEquatable<DecalText> {
|
||||
public string Text { get; }
|
||||
|
||||
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) {
|
||||
if (font == null) throw new ArgumentNullException(nameof(font));
|
||||
Text = text;
|
||||
Font = font;
|
||||
Style = style;
|
||||
}
|
||||
|
||||
public bool Equals(DecalText other) {
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return Text == other.Text && Equals(Font, other.Font) && Style.Equals(other.Style);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((DecalText) obj);
|
||||
}
|
||||
|
||||
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 Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(DecalText left, DecalText right) {
|
||||
return !Equals(left, right);
|
||||
}
|
||||
}
|
||||
}
|
100
Source/ConformalDecals/Text/DecalTextStyle.cs
Normal file
100
Source/ConformalDecals/Text/DecalTextStyle.cs
Normal file
@ -0,0 +1,100 @@
|
||||
using System;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
// ReSharper disable NonReadonlyMemberInGetHashCode
|
||||
|
||||
namespace ConformalDecals.Text {
|
||||
public struct DecalTextStyle : IEquatable<DecalTextStyle> {
|
||||
private FontStyles _fontStyle;
|
||||
private bool _vertical;
|
||||
private float _lineSpacing;
|
||||
private float _charSpacing;
|
||||
|
||||
public FontStyles FontStyle {
|
||||
get => _fontStyle;
|
||||
set => _fontStyle = value;
|
||||
}
|
||||
|
||||
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 => _vertical;
|
||||
set => _vertical = value;
|
||||
}
|
||||
|
||||
public float LineSpacing {
|
||||
get => _lineSpacing;
|
||||
set => _lineSpacing = value;
|
||||
}
|
||||
|
||||
public float CharSpacing {
|
||||
get => _charSpacing;
|
||||
set => _charSpacing = value;
|
||||
}
|
||||
|
||||
public DecalTextStyle(FontStyles fontStyle, bool vertical, float lineSpacing, float charSpacing) {
|
||||
_fontStyle = fontStyle;
|
||||
_vertical = vertical;
|
||||
_lineSpacing = lineSpacing;
|
||||
_charSpacing = charSpacing;
|
||||
}
|
||||
|
||||
public bool Equals(DecalTextStyle other) {
|
||||
return FontStyle == other.FontStyle && Vertical == other.Vertical &&
|
||||
Mathf.Approximately(LineSpacing, other.LineSpacing) &&
|
||||
Mathf.Approximately(CharSpacing, other.CharSpacing);
|
||||
}
|
||||
|
||||
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) ^ CharSpacing.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);
|
||||
}
|
||||
}
|
||||
}
|
36
Source/ConformalDecals/Text/FontLoader.cs
Normal file
36
Source/ConformalDecals/Text/FontLoader.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using UniLinq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ConformalDecals.Text {
|
||||
[DatabaseLoaderAttrib(new[] {"kspfont"})]
|
||||
public class FontLoader : DatabaseLoader<GameDatabase.TextureInfo> {
|
||||
private const string FallbackName = "NotoSans-Regular SDF";
|
||||
private static TMP_FontAsset _fallbackFont;
|
||||
|
||||
public override IEnumerator Load(UrlDir.UrlFile urlFile, FileInfo fileInfo) {
|
||||
if (_fallbackFont == null) {
|
||||
_fallbackFont = Resources.FindObjectsOfTypeAll<TMP_FontAsset>().First(o => o.name == FallbackName);
|
||||
if (_fallbackFont == null) Debug.LogError($"Could not find fallback font '{FallbackName}'");
|
||||
}
|
||||
|
||||
Debug.Log($"[ConformalDecals] '{urlFile.fullPath}'");
|
||||
var bundle = AssetBundle.LoadFromFile(urlFile.fullPath);
|
||||
if (!bundle) {
|
||||
Debug.Log($"[ConformalDecals] could not load font asset {urlFile.fullPath}");
|
||||
}
|
||||
else {
|
||||
var loadedFonts = bundle.LoadAllAssets<TMP_FontAsset>();
|
||||
foreach (var font in loadedFonts) {
|
||||
Debug.Log($"[ConformalDecals] adding font {font.name}");
|
||||
font.fallbackFontAssets.Add(_fallbackFont);
|
||||
}
|
||||
}
|
||||
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
35
Source/ConformalDecals/Text/TextRenderJob.cs
Normal file
35
Source/ConformalDecals/Text/TextRenderJob.cs
Normal file
@ -0,0 +1,35 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
16
Source/ConformalDecals/Text/TextRenderOutput.cs
Normal file
16
Source/ConformalDecals/Text/TextRenderOutput.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace ConformalDecals.Text {
|
||||
public class TextRenderOutput {
|
||||
public Texture2D Texture { get; private set; }
|
||||
|
||||
public Rect Window { get; private set; }
|
||||
|
||||
public int UserCount { get; set; }
|
||||
|
||||
public TextRenderOutput(Texture2D texture, Rect window) {
|
||||
Texture = texture;
|
||||
Window = window;
|
||||
}
|
||||
}
|
||||
}
|
273
Source/ConformalDecals/Text/TextRenderer.cs
Normal file
273
Source/ConformalDecals/Text/TextRenderer.cs
Normal file
@ -0,0 +1,273 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace ConformalDecals.Text {
|
||||
[KSPAddon(KSPAddon.Startup.Instantly, true)]
|
||||
public class TextRenderer : MonoBehaviour {
|
||||
public const TextureFormat TextTextureFormat = TextureFormat.RG16;
|
||||
public const RenderTextureFormat TextRenderTextureFormat = RenderTextureFormat.R8;
|
||||
|
||||
public static TextRenderer Instance {
|
||||
get {
|
||||
if (!_instance._isSetup) {
|
||||
_instance.Setup();
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class TextRenderEvent : UnityEvent<TextRenderOutput> { }
|
||||
|
||||
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 TextMeshPro _tmp;
|
||||
private Shader _blitShader;
|
||||
|
||||
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
|
||||
public static TextRenderOutput UpdateTextNow(DecalText oldText, DecalText newText) {
|
||||
if (newText == null) throw new ArgumentNullException(nameof(newText));
|
||||
|
||||
return Instance.RunJob(new TextRenderJob(oldText, newText, null), out _);
|
||||
}
|
||||
|
||||
// Unregister a user of a piece of text
|
||||
public static void UnregisterText(DecalText text) {
|
||||
Debug.Log($"[ConformalDecals] Unregistering text '{text.Text}'");
|
||||
if (RenderCache.TryGetValue(text, out var renderedText)) {
|
||||
renderedText.UserCount--;
|
||||
if (renderedText.UserCount <= 0) {
|
||||
RenderCache.Remove(text);
|
||||
Destroy(renderedText.Texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Start() {
|
||||
if (_instance != null) {
|
||||
Debug.Log("[ConformalDecals] Duplicate TextRenderer created???");
|
||||
}
|
||||
|
||||
Debug.Log("[ConformalDecals] Creating TextRenderer Object");
|
||||
_instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
}
|
||||
|
||||
private void Setup() {
|
||||
if (_isSetup) return;
|
||||
|
||||
Debug.Log("[ConformalDecals] Setting Up TextRenderer Object");
|
||||
|
||||
_tmp = gameObject.AddComponent<TextMeshPro>();
|
||||
_tmp.renderer.enabled = false; // dont automatically render
|
||||
|
||||
_blitShader = Shabby.Shabby.FindShader(ShaderName);
|
||||
if (_blitShader == null) Debug.LogError($"[ConformalDecals] 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;
|
||||
}
|
||||
|
||||
Debug.Log($"[ConformalDecals] Starting Text Rendering Job. queue depth = {RenderJobs.Count}, cache size = {RenderCache.Count}");
|
||||
foreach (var cacheitem in RenderCache) {
|
||||
Debug.Log($"[ConformalDecals] Cache item: '{cacheitem.Key.Text}' with {cacheitem.Value.UserCount} users");
|
||||
}
|
||||
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
|
||||
Debug.Log("Render output is not shared with other users, so reusing texture and removing cache slot");
|
||||
|
||||
texture = oldRender.Texture;
|
||||
RenderCache.Remove(job.OldText);
|
||||
}
|
||||
else {
|
||||
// other things are using this render output, so decriment usercount, and we'll make a new entry instead
|
||||
Debug.Log("Render output is shared with other users, so making new output");
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
public TextRenderOutput RenderText(DecalText text, Texture2D texture) {
|
||||
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;
|
||||
_tmp.fontStyle = text.Style.FontStyle | text.Font.FontStyle;
|
||||
_tmp.lineSpacing = text.Style.LineSpacing;
|
||||
_tmp.characterSpacing = text.Style.CharSpacing;
|
||||
|
||||
_tmp.extraPadding = true;
|
||||
_tmp.enableKerning = true;
|
||||
_tmp.enableWordWrapping = false;
|
||||
_tmp.overflowMode = TextOverflowModes.Overflow;
|
||||
_tmp.alignment = TextAlignmentOptions.Center;
|
||||
_tmp.fontSize = FontSize;
|
||||
|
||||
// GENERATE MESH
|
||||
_tmp.ClearMesh(false);
|
||||
_tmp.ForceMeshUpdate();
|
||||
|
||||
var meshFilters = gameObject.GetComponentsInChildren<MeshFilter>();
|
||||
var meshes = new Mesh[meshFilters.Length];
|
||||
var materials = new Material[meshFilters.Length];
|
||||
|
||||
var bounds = new Bounds();
|
||||
|
||||
// SETUP MATERIALS AND BOUNDS
|
||||
for (int i = 0; i < meshFilters.Length; i++) {
|
||||
var renderer = meshFilters[i].gameObject.GetComponent<MeshRenderer>();
|
||||
|
||||
meshes[i] = meshFilters[i].mesh;
|
||||
if (i == 0) meshes[i] = _tmp.mesh;
|
||||
|
||||
materials[i] = Instantiate(renderer.material);
|
||||
materials[i].shader = _blitShader;
|
||||
|
||||
if (renderer == null) throw new FormatException($"Object {meshFilters[i].gameObject.name} has filter but no renderer");
|
||||
if (meshes[i] == null) throw new FormatException($"Object {meshFilters[i].gameObject.name} has a null mesh");
|
||||
|
||||
if (i == 0) {
|
||||
bounds = meshes[i].bounds;
|
||||
}
|
||||
else {
|
||||
bounds.Encapsulate(meshes[i].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 == 0 || textureSize.y == 0) {
|
||||
Debug.LogWarning("[ConformalDecals] No text present or error in texture size calculation. Aborting.");
|
||||
return new TextRenderOutput(Texture2D.blackTexture, Rect.zero);
|
||||
}
|
||||
|
||||
// make sure texture isnt too big, scale it down if it is
|
||||
// this is just so you dont crash the game by pasting in the entire script of The Bee Movie
|
||||
if (textureSize.x > MaxTextureSize) {
|
||||
textureSize.y /= textureSize.x / MaxTextureSize;
|
||||
textureSize.x = MaxTextureSize;
|
||||
}
|
||||
|
||||
if (textureSize.y > MaxTextureSize) {
|
||||
textureSize.x /= textureSize.y / MaxTextureSize;
|
||||
textureSize.y = MaxTextureSize;
|
||||
}
|
||||
|
||||
// scale up everything to fit the texture for maximum usage
|
||||
float sizeRatio = Mathf.Min(textureSize.x / size.x, textureSize.y / size.y);
|
||||
|
||||
// calculate where in the texture the used area actually is
|
||||
var window = new Rect {
|
||||
size = size * sizeRatio,
|
||||
center = (Vector2) textureSize / 2
|
||||
};
|
||||
|
||||
// SETUP TEXTURE
|
||||
if (texture == null) {
|
||||
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
|
||||
var halfSize = (Vector2) textureSize / PixelDensity / 2 / sizeRatio;
|
||||
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);
|
||||
GL.Clear(false, true, Color.black);
|
||||
|
||||
for (var i = 0; i < meshes.Length; i++) {
|
||||
if (meshes[i].vertexCount >= 3) {
|
||||
materials[i].SetPass(0);
|
||||
Graphics.DrawMeshNow(meshes[i], Matrix4x4.identity);
|
||||
}
|
||||
}
|
||||
|
||||
GL.PopMatrix();
|
||||
|
||||
// COPY TEXTURE BACK INTO RAM
|
||||
RenderTexture.active = renderTex;
|
||||
texture.ReadPixels(new Rect(0, 0, textureSize.x, textureSize.y), 0, 0, true);
|
||||
texture.Apply();
|
||||
|
||||
// RELEASE RENDERTEX
|
||||
RenderTexture.ReleaseTemporary(renderTex);
|
||||
|
||||
// CLEAR SUBMESHES
|
||||
for (int i = 0; i < transform.childCount; i++) {
|
||||
Destroy(transform.GetChild(i).gameObject);
|
||||
}
|
||||
|
||||
return new TextRenderOutput(texture, window);
|
||||
}
|
||||
}
|
||||
}
|
176
Source/ConformalDecals/UI/BoxSlider.cs
Normal file
176
Source/ConformalDecals/UI/BoxSlider.cs
Normal file
@ -0,0 +1,176 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace ConformalDecals.UI {
|
||||
[AddComponentMenu("UI/BoxSlider", 35)]
|
||||
[RequireComponent(typeof(RectTransform))]
|
||||
public class BoxSlider : Selectable, IDragHandler, IInitializePotentialDragHandler, ICanvasElement {
|
||||
[Serializable]
|
||||
public class BoxSliderEvent : UnityEvent<Vector2> { }
|
||||
|
||||
[SerializeField] private RectTransform _handleRect;
|
||||
[SerializeField] private Vector2 _value = Vector2.zero;
|
||||
|
||||
public RectTransform HandleRect {
|
||||
get => _handleRect;
|
||||
set {
|
||||
if (value == null) throw new ArgumentNullException(nameof(value));
|
||||
if (value != _handleRect) {
|
||||
_handleRect = value;
|
||||
UpdateCachedReferences();
|
||||
UpdateVisuals();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2 Value {
|
||||
get => _value;
|
||||
set {
|
||||
_value = value;
|
||||
_onValueChanged.Invoke(value);
|
||||
UpdateVisuals();
|
||||
}
|
||||
}
|
||||
|
||||
[Space(6)]
|
||||
|
||||
// Allow for delegate-based subscriptions for faster events than 'eventReceiver', and allowing for multiple receivers.
|
||||
[SerializeField]
|
||||
private BoxSliderEvent _onValueChanged = new BoxSliderEvent();
|
||||
|
||||
public BoxSliderEvent OnValueChanged {
|
||||
get => _onValueChanged;
|
||||
set => _onValueChanged = value;
|
||||
}
|
||||
|
||||
// Private fields
|
||||
|
||||
private Transform _handleTransform;
|
||||
private RectTransform _handleContainerRect;
|
||||
|
||||
// The offset from handle position to mouse down position
|
||||
private Vector2 _offset = Vector2.zero;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected override void OnValidate() {
|
||||
base.OnValidate();
|
||||
|
||||
//Onvalidate is called before OnEnabled. We need to make sure not to touch any other objects before OnEnable is run.
|
||||
if (IsActive()) {
|
||||
UpdateCachedReferences();
|
||||
// Update rects since other things might affect them even if value didn't change.
|
||||
UpdateVisuals();
|
||||
}
|
||||
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
|
||||
if (!UnityEditor.PrefabUtility.IsPartOfPrefabAsset(this) && !Application.isPlaying)
|
||||
CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this);
|
||||
|
||||
#else
|
||||
var prefabType = UnityEditor.PrefabUtility.GetPrefabType(this);
|
||||
if (prefabType != UnityEditor.PrefabType.Prefab && !Application.isPlaying)
|
||||
CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this);
|
||||
#endif
|
||||
}
|
||||
#endif // if UNITY_EDITOR
|
||||
|
||||
public virtual void Rebuild(CanvasUpdate executing) {
|
||||
#if UNITY_EDITOR
|
||||
if (executing == CanvasUpdate.Prelayout)
|
||||
OnValueChanged.Invoke(Value);
|
||||
#endif
|
||||
}
|
||||
|
||||
public void LayoutComplete() { }
|
||||
|
||||
public void GraphicUpdateComplete() { }
|
||||
|
||||
protected override void OnEnable() {
|
||||
base.OnEnable();
|
||||
UpdateCachedReferences();
|
||||
// Update rects since they need to be initialized correctly.
|
||||
UpdateVisuals();
|
||||
}
|
||||
|
||||
private void UpdateCachedReferences() {
|
||||
if (_handleRect) {
|
||||
_handleTransform = _handleRect.transform;
|
||||
if (_handleTransform.parent != null)
|
||||
_handleContainerRect = _handleTransform.parent.GetComponent<RectTransform>();
|
||||
}
|
||||
else {
|
||||
_handleContainerRect = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnRectTransformDimensionsChange() {
|
||||
base.OnRectTransformDimensionsChange();
|
||||
UpdateVisuals();
|
||||
}
|
||||
|
||||
// Force-update the slider. Useful if you've changed the properties and want it to update visually.
|
||||
private void UpdateVisuals() {
|
||||
if (_handleContainerRect != null) {
|
||||
_handleRect.anchorMin = _value;
|
||||
_handleRect.anchorMax = _value;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the slider's position based on the mouse.
|
||||
private void UpdateDrag(PointerEventData eventData, Camera cam) {
|
||||
var clickRect = _handleContainerRect;
|
||||
if (clickRect != null && clickRect.rect.size[0] > 0) {
|
||||
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(clickRect, eventData.position, cam, out var localCursor))
|
||||
return;
|
||||
|
||||
var rect = clickRect.rect;
|
||||
localCursor -= rect.position;
|
||||
|
||||
Vector2 newVal;
|
||||
newVal.x = Mathf.Clamp01((localCursor - _offset).x / rect.size.x);
|
||||
newVal.y = Mathf.Clamp01((localCursor - _offset).y / rect.size.y);
|
||||
|
||||
Value = newVal;
|
||||
}
|
||||
}
|
||||
|
||||
private bool MayDrag(PointerEventData eventData) {
|
||||
return IsActive() && IsInteractable() && eventData.button == PointerEventData.InputButton.Left;
|
||||
}
|
||||
|
||||
public override void OnPointerDown(PointerEventData eventData) {
|
||||
if (!MayDrag(eventData))
|
||||
return;
|
||||
|
||||
base.OnPointerDown(eventData);
|
||||
|
||||
_offset = Vector2.zero;
|
||||
if (_handleContainerRect != null && RectTransformUtility.RectangleContainsScreenPoint(_handleRect, eventData.position, eventData.enterEventCamera)) {
|
||||
Vector2 localMousePos;
|
||||
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(_handleRect, eventData.position, eventData.pressEventCamera, out localMousePos))
|
||||
_offset = localMousePos;
|
||||
|
||||
_offset.y = -_offset.y;
|
||||
}
|
||||
else {
|
||||
// Outside the slider handle - jump to this point instead
|
||||
UpdateDrag(eventData, eventData.pressEventCamera);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void OnDrag(PointerEventData eventData) {
|
||||
if (!MayDrag(eventData))
|
||||
return;
|
||||
|
||||
UpdateDrag(eventData, eventData.pressEventCamera);
|
||||
}
|
||||
|
||||
public virtual void OnInitializePotentialDrag(PointerEventData eventData) {
|
||||
eventData.useDragThreshold = false;
|
||||
}
|
||||
}
|
||||
}
|
63
Source/ConformalDecals/UI/ColorBoxSlider.cs
Normal file
63
Source/ConformalDecals/UI/ColorBoxSlider.cs
Normal file
@ -0,0 +1,63 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace ConformalDecals.UI {
|
||||
public class ColorBoxSlider : MonoBehaviour {
|
||||
[SerializeField] public ColorPickerController.SVUpdateEvent onValueChanged = new ColorPickerController.SVUpdateEvent();
|
||||
|
||||
[SerializeField] private Vector2 _value;
|
||||
|
||||
[SerializeField] private BoxSlider _slider;
|
||||
[SerializeField] private Image _image;
|
||||
|
||||
private bool _ignoreUpdates;
|
||||
|
||||
public Vector2 Value {
|
||||
get => _value;
|
||||
set {
|
||||
_value.x = Mathf.Clamp01(value.x);
|
||||
_value.y = Mathf.Clamp01(value.y);
|
||||
UpdateSlider();
|
||||
UpdateChannels();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnColorUpdate(Color rgb, Util.ColorHSV hsv) {
|
||||
if (_ignoreUpdates) return;
|
||||
|
||||
_image.material.SetColor(PropertyIDs._Color, (Vector4) hsv);
|
||||
|
||||
_value.x = hsv.s;
|
||||
_value.y = hsv.v;
|
||||
UpdateSlider();
|
||||
}
|
||||
|
||||
public void OnSliderUpdate(Vector2 value) {
|
||||
if (_ignoreUpdates) return;
|
||||
|
||||
_value = value;
|
||||
UpdateChannels();
|
||||
}
|
||||
|
||||
private void Awake() {
|
||||
var boxSlider = gameObject.GetComponentInChildren<BoxSlider>();
|
||||
boxSlider.OnValueChanged.AddListener(OnSliderUpdate);
|
||||
}
|
||||
|
||||
private void UpdateChannels() {
|
||||
_ignoreUpdates = true;
|
||||
|
||||
onValueChanged.Invoke(_value);
|
||||
|
||||
_ignoreUpdates = false;
|
||||
}
|
||||
|
||||
private void UpdateSlider() {
|
||||
_ignoreUpdates = true;
|
||||
|
||||
_slider.Value = _value;
|
||||
|
||||
_ignoreUpdates = false;
|
||||
}
|
||||
}
|
||||
}
|
79
Source/ConformalDecals/UI/ColorChannelSlider.cs
Normal file
79
Source/ConformalDecals/UI/ColorChannelSlider.cs
Normal file
@ -0,0 +1,79 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace ConformalDecals.UI {
|
||||
public class ColorChannelSlider : MonoBehaviour {
|
||||
[SerializeField] public ColorPickerController.ChannelUpdateEvent onChannelChanged = new ColorPickerController.ChannelUpdateEvent();
|
||||
|
||||
[SerializeField] private float _value;
|
||||
[SerializeField] private int _channel;
|
||||
[SerializeField] private bool _hsv;
|
||||
|
||||
[SerializeField] private Selectable _textBox;
|
||||
[SerializeField] private Slider _slider;
|
||||
[SerializeField] private Image _image;
|
||||
|
||||
private bool _ignoreUpdates;
|
||||
|
||||
public float Value {
|
||||
get => _value;
|
||||
set {
|
||||
_value = Mathf.Clamp01(value);
|
||||
UpdateSlider();
|
||||
UpdateTextbox();
|
||||
UpdateChannel();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnColorUpdate(Color rgb, Util.ColorHSV hsv) {
|
||||
if (_ignoreUpdates) return;
|
||||
|
||||
_image.material.SetColor(PropertyIDs._Color, _hsv ? (Color) (Vector4) hsv : rgb);
|
||||
|
||||
_value = _hsv ? hsv[_channel] : rgb[_channel];
|
||||
UpdateSlider();
|
||||
UpdateTextbox();
|
||||
}
|
||||
|
||||
public void OnTextBoxUpdate(string text) {
|
||||
if (_ignoreUpdates) return;
|
||||
|
||||
if (byte.TryParse(text, out byte byteValue)) {
|
||||
_value = (float) byteValue / 255;
|
||||
UpdateSlider();
|
||||
UpdateChannel();
|
||||
}
|
||||
else {
|
||||
// value is invalid, reset value
|
||||
UpdateTextbox();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnSliderUpdate(float value) {
|
||||
if (_ignoreUpdates) return;
|
||||
|
||||
_value = value;
|
||||
UpdateTextbox();
|
||||
UpdateChannel();
|
||||
}
|
||||
|
||||
private void UpdateChannel() {
|
||||
onChannelChanged.Invoke(_value, _channel, _hsv);
|
||||
}
|
||||
|
||||
private void UpdateSlider() {
|
||||
_ignoreUpdates = true;
|
||||
_slider.value = _value;
|
||||
_ignoreUpdates = false;
|
||||
}
|
||||
|
||||
private void UpdateTextbox() {
|
||||
if (_textBox == null) return;
|
||||
|
||||
_ignoreUpdates = true;
|
||||
((TMP_InputField) _textBox).text = ((byte) (255 * _value)).ToString();
|
||||
_ignoreUpdates = false;
|
||||
}
|
||||
}
|
||||
}
|
117
Source/ConformalDecals/UI/ColorPickerController.cs
Normal file
117
Source/ConformalDecals/UI/ColorPickerController.cs
Normal file
@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using ConformalDecals.Util;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace ConformalDecals.UI {
|
||||
public class ColorPickerController : MonoBehaviour {
|
||||
[Serializable]
|
||||
public class ColorUpdateEvent : UnityEvent<Color, Util.ColorHSV> { }
|
||||
|
||||
[Serializable]
|
||||
public class ChannelUpdateEvent : UnityEvent<float, int, bool> { }
|
||||
|
||||
[Serializable]
|
||||
public class SVUpdateEvent : UnityEvent<Vector2> { }
|
||||
|
||||
[SerializeField] public ColorUpdateEvent onColorChanged = new ColorUpdateEvent();
|
||||
|
||||
[SerializeField] private Color _value;
|
||||
[SerializeField] private Image _previewImage;
|
||||
[SerializeField] private Selectable _hexTextBox;
|
||||
|
||||
private bool _ignoreUpdate;
|
||||
|
||||
public Color RGB {
|
||||
get => _value;
|
||||
set {
|
||||
_value = value;
|
||||
OnColorUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public Util.ColorHSV HSV {
|
||||
get => Util.ColorHSV.RGB2HSV(_value);
|
||||
set {
|
||||
_value = Util.ColorHSV.HSV2RGB(value);
|
||||
OnColorUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static ColorPickerController Create(Color rgb, UnityAction<Color, Util.ColorHSV> colorUpdateCallback) {
|
||||
var menu = Instantiate(UILoader.ColorPickerPrefab, MainCanvasUtil.MainCanvas.transform, true);
|
||||
menu.AddComponent<DragPanel>();
|
||||
MenuNavigation.SpawnMenuNavigation(menu, Navigation.Mode.Automatic, true);
|
||||
|
||||
var controller = menu.GetComponent<ColorPickerController>();
|
||||
controller.RGB = rgb;
|
||||
controller.onColorChanged.AddListener(colorUpdateCallback);
|
||||
return controller;
|
||||
}
|
||||
|
||||
public void Close() {
|
||||
Destroy(gameObject);
|
||||
}
|
||||
|
||||
public void OnChannelUpdate(float value, int channel, bool hsv) {
|
||||
if (hsv) {
|
||||
var newHSV = HSV;
|
||||
newHSV[channel] = value;
|
||||
HSV = newHSV;
|
||||
}
|
||||
else {
|
||||
var newRGB = RGB;
|
||||
newRGB[channel] = value;
|
||||
RGB = newRGB;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnSVUpdate(Vector2 sv) {
|
||||
var newHSV = HSV;
|
||||
newHSV.s = sv.x;
|
||||
newHSV.v = sv.y;
|
||||
HSV = newHSV;
|
||||
}
|
||||
|
||||
public void OnColorUpdate() {
|
||||
onColorChanged.Invoke(RGB, HSV);
|
||||
_previewImage.material.SetColor(PropertyIDs._Color, RGB);
|
||||
UpdateHexColor();
|
||||
}
|
||||
|
||||
public void OnHexColorUpdate(string text) {
|
||||
if (_ignoreUpdate) return;
|
||||
|
||||
if (ParseUtil.TryParseHexColor(text, out var newRGB)) {
|
||||
RGB = newRGB;
|
||||
OnColorUpdate();
|
||||
}
|
||||
else {
|
||||
UpdateHexColor();
|
||||
}
|
||||
}
|
||||
|
||||
private void Awake() {
|
||||
foreach (var slider in gameObject.GetComponentsInChildren<ColorChannelSlider>()) {
|
||||
slider.onChannelChanged.AddListener(OnChannelUpdate);
|
||||
onColorChanged.AddListener(slider.OnColorUpdate);
|
||||
}
|
||||
|
||||
foreach (var box in gameObject.GetComponentsInChildren<ColorBoxSlider>()) {
|
||||
box.onValueChanged.AddListener(OnSVUpdate);
|
||||
onColorChanged.AddListener(box.OnColorUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateHexColor() {
|
||||
_ignoreUpdate = true;
|
||||
var byteColor = (Color32) RGB;
|
||||
var hexColor = $"{byteColor.r:x2}{byteColor.g:x2}{byteColor.b:x2}";
|
||||
((TMP_InputField) _hexTextBox).text = hexColor;
|
||||
_ignoreUpdate = false;
|
||||
}
|
||||
}
|
||||
}
|
64
Source/ConformalDecals/UI/FontMenuController.cs
Normal file
64
Source/ConformalDecals/UI/FontMenuController.cs
Normal file
@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ConformalDecals.Text;
|
||||
using UniLinq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace ConformalDecals.UI {
|
||||
public class FontMenuController : MonoBehaviour {
|
||||
[Serializable]
|
||||
public class FontUpdateEvent : UnityEvent<DecalFont> { }
|
||||
|
||||
[SerializeField] public FontUpdateEvent onFontChanged = new FontUpdateEvent();
|
||||
|
||||
[SerializeField] private GameObject _menuItem;
|
||||
[SerializeField] private GameObject _menuList;
|
||||
|
||||
private DecalFont _currentFont;
|
||||
|
||||
public static FontMenuController Create(IEnumerable<DecalFont> fonts, DecalFont currentFont, UnityAction<DecalFont> fontUpdateCallback) {
|
||||
var menu = Instantiate(UILoader.FontMenuPrefab, MainCanvasUtil.MainCanvas.transform, true);
|
||||
menu.AddComponent<DragPanel>();
|
||||
MenuNavigation.SpawnMenuNavigation(menu, Navigation.Mode.Automatic, true);
|
||||
|
||||
var controller = menu.GetComponent<FontMenuController>();
|
||||
controller._currentFont = currentFont;
|
||||
controller.onFontChanged.AddListener(fontUpdateCallback);
|
||||
|
||||
controller.Populate(fonts);
|
||||
return controller;
|
||||
}
|
||||
|
||||
public void Close() {
|
||||
Destroy(gameObject);
|
||||
}
|
||||
|
||||
public void OnFontSelected(DecalFont font) {
|
||||
_currentFont = font ?? throw new ArgumentNullException(nameof(font));
|
||||
onFontChanged.Invoke(_currentFont);
|
||||
}
|
||||
|
||||
public void Populate(IEnumerable<DecalFont> fonts) {
|
||||
if (fonts == null) throw new ArgumentNullException(nameof(fonts));
|
||||
|
||||
Toggle active = null;
|
||||
|
||||
foreach (var font in fonts.OrderBy(x => x.Title)) {
|
||||
Debug.Log(font.Title);
|
||||
var listItem = GameObject.Instantiate(_menuItem, _menuList.transform);
|
||||
listItem.name = font.Title;
|
||||
listItem.SetActive(true);
|
||||
|
||||
var fontItem = listItem.AddComponent<FontMenuItem>();
|
||||
fontItem.Font = font;
|
||||
fontItem.fontSelectionCallback = OnFontSelected;
|
||||
|
||||
if (font == _currentFont) active = fontItem.toggle;
|
||||
}
|
||||
|
||||
if (active != null) active.isOn = true;
|
||||
}
|
||||
}
|
||||
}
|
36
Source/ConformalDecals/UI/FontMenuItem.cs
Normal file
36
Source/ConformalDecals/UI/FontMenuItem.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using ConformalDecals.Text;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace ConformalDecals.UI {
|
||||
public class FontMenuItem : MonoBehaviour {
|
||||
public DecalFont Font {
|
||||
get => _font;
|
||||
set {
|
||||
_font = value;
|
||||
_font.SetupSample(_label);
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void FontSelectionReceiver(DecalFont font);
|
||||
|
||||
public FontSelectionReceiver fontSelectionCallback;
|
||||
public Toggle toggle;
|
||||
|
||||
private DecalFont _font;
|
||||
private TMP_Text _label;
|
||||
|
||||
private void Awake() {
|
||||
_label = gameObject.GetComponentInChildren<TextMeshProUGUI>();
|
||||
toggle = gameObject.GetComponent<Toggle>();
|
||||
toggle.isOn = false;
|
||||
toggle.onValueChanged.AddListener(delegate { OnToggle(toggle); });
|
||||
}
|
||||
|
||||
public void OnToggle(Toggle change) {
|
||||
if (change.isOn) fontSelectionCallback?.Invoke(_font);
|
||||
}
|
||||
}
|
||||
}
|
263
Source/ConformalDecals/UI/TextEntryController.cs
Normal file
263
Source/ConformalDecals/UI/TextEntryController.cs
Normal file
@ -0,0 +1,263 @@
|
||||
using System;
|
||||
using ConformalDecals.Text;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace ConformalDecals.UI {
|
||||
public class TextEntryController : MonoBehaviour {
|
||||
[Serializable]
|
||||
public class TextUpdateEvent : UnityEvent<string, DecalFont, DecalTextStyle> { }
|
||||
|
||||
[SerializeField] public TextUpdateEvent onValueChanged = new TextUpdateEvent();
|
||||
|
||||
[SerializeField] private Selectable _textBox;
|
||||
[SerializeField] private Button _fontButton;
|
||||
|
||||
[SerializeField] private Slider _lineSpacingSlider;
|
||||
[SerializeField] private Selectable _lineSpacingTextBox;
|
||||
|
||||
[SerializeField] private Slider _charSpacingSlider;
|
||||
[SerializeField] private Selectable _charSpacingTextBox;
|
||||
|
||||
[SerializeField] private Toggle _boldButton;
|
||||
[SerializeField] private Toggle _italicButton;
|
||||
[SerializeField] private Toggle _underlineButton;
|
||||
[SerializeField] private Toggle _smallCapsButton;
|
||||
[SerializeField] private Toggle _verticalButton;
|
||||
|
||||
private string _text;
|
||||
private DecalFont _font;
|
||||
private DecalTextStyle _style;
|
||||
private Vector2 _lineSpacingRange;
|
||||
private Vector2 _charSpacingRange;
|
||||
|
||||
private FontMenuController _fontMenu;
|
||||
|
||||
private bool _ignoreUpdates;
|
||||
|
||||
public static TextEntryController Create(
|
||||
string text, DecalFont font, DecalTextStyle style,
|
||||
Vector2 lineSpacingRange, Vector2 charSpacingRange,
|
||||
UnityAction<string, DecalFont, DecalTextStyle> textUpdateCallback) {
|
||||
|
||||
var window = Instantiate(UILoader.TextEntryPrefab, MainCanvasUtil.MainCanvas.transform, true);
|
||||
window.AddComponent<DragPanel>();
|
||||
MenuNavigation.SpawnMenuNavigation(window, Navigation.Mode.Automatic, true);
|
||||
|
||||
var controller = window.GetComponent<TextEntryController>();
|
||||
controller._text = text;
|
||||
controller._font = font;
|
||||
controller._style = style;
|
||||
controller._lineSpacingRange = lineSpacingRange;
|
||||
controller._charSpacingRange = charSpacingRange;
|
||||
controller.onValueChanged.AddListener(textUpdateCallback);
|
||||
|
||||
return controller;
|
||||
}
|
||||
|
||||
public void Close() {
|
||||
if (_fontMenu != null) _fontMenu.Close();
|
||||
Destroy(gameObject);
|
||||
}
|
||||
|
||||
public void OnTextUpdate(string newText) {
|
||||
this._text = newText;
|
||||
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
public void OnFontMenu() {
|
||||
if (_fontMenu == null) _fontMenu = FontMenuController.Create(DecalConfig.Fonts, _font, OnFontUpdate);
|
||||
}
|
||||
|
||||
public void OnFontUpdate(DecalFont font) {
|
||||
if (_ignoreUpdates) return;
|
||||
|
||||
_font = font;
|
||||
font.SetupSample(_fontButton.GetComponentInChildren<TextMeshProUGUI>());
|
||||
|
||||
var textBox = ((TMP_InputField) _textBox);
|
||||
textBox.textComponent.fontStyle = _style.FontStyle | _font.FontStyle;
|
||||
textBox.fontAsset = _font.FontAsset;
|
||||
|
||||
UpdateStyleButtons();
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
public void OnLineSpacingUpdate(float value) {
|
||||
if (_ignoreUpdates) return;
|
||||
|
||||
_style.LineSpacing = Mathf.Lerp(_lineSpacingRange.x, _lineSpacingRange.y, value);
|
||||
|
||||
UpdateLineSpacing();
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
public void OnLineSpacingUpdate(string text) {
|
||||
if (_ignoreUpdates) return;
|
||||
|
||||
if (float.TryParse(text, out var value)) {
|
||||
_style.LineSpacing = Mathf.Clamp(value, _lineSpacingRange.x, _lineSpacingRange.y);
|
||||
}
|
||||
else {
|
||||
Debug.LogWarning("[ConformalDecals] line spacing value '{text}' could not be parsed.");
|
||||
}
|
||||
|
||||
UpdateLineSpacing();
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
public void OnCharSpacingUpdate(float value) {
|
||||
if (_ignoreUpdates) return;
|
||||
|
||||
_style.CharSpacing = Mathf.Lerp(_charSpacingRange.x, _charSpacingRange.y, value);
|
||||
|
||||
UpdateCharSpacing();
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
public void OnCharSpacingUpdate(string text) {
|
||||
if (_ignoreUpdates) return;
|
||||
|
||||
if (float.TryParse(text, out var value)) {
|
||||
_style.CharSpacing = Mathf.Clamp(value, _charSpacingRange.x, _charSpacingRange.y);
|
||||
}
|
||||
else {
|
||||
Debug.LogWarning("[ConformalDecals] char spacing value '{text}' could not be parsed.");
|
||||
}
|
||||
|
||||
UpdateCharSpacing();
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
public void OnBoldUpdate(bool state) {
|
||||
if (_ignoreUpdates) return;
|
||||
|
||||
_style.Bold = state;
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
public void OnItalicUpdate(bool state) {
|
||||
if (_ignoreUpdates) return;
|
||||
|
||||
_style.Italic = state;
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
public void OnUnderlineUpdate(bool state) {
|
||||
if (_ignoreUpdates) return;
|
||||
|
||||
_style.Underline = state;
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
public void OnSmallCapsUpdate(bool state) {
|
||||
if (_ignoreUpdates) return;
|
||||
|
||||
_style.SmallCaps = state;
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
public void OnVerticalUpdate(bool state) {
|
||||
if (_ignoreUpdates) return;
|
||||
|
||||
_style.Vertical = state;
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
|
||||
private void Start() {
|
||||
((TMP_InputField) _textBox).text = _text;
|
||||
|
||||
_font.SetupSample(_fontButton.GetComponentInChildren<TextMeshProUGUI>());
|
||||
|
||||
UpdateStyleButtons();
|
||||
UpdateLineSpacing();
|
||||
UpdateCharSpacing();
|
||||
}
|
||||
|
||||
private void OnValueChanged() {
|
||||
onValueChanged.Invoke(_text, _font, _style);
|
||||
}
|
||||
|
||||
private void UpdateStyleButtons() {
|
||||
_ignoreUpdates = true;
|
||||
|
||||
if (_font.Bold) {
|
||||
_boldButton.interactable = false;
|
||||
_boldButton.isOn = true;
|
||||
}
|
||||
else if (_font.BoldMask) {
|
||||
_boldButton.interactable = false;
|
||||
_boldButton.isOn = false;
|
||||
}
|
||||
else {
|
||||
_boldButton.interactable = true;
|
||||
_boldButton.isOn = _style.Bold;
|
||||
}
|
||||
|
||||
if (_font.Italic) {
|
||||
_italicButton.interactable = false;
|
||||
_italicButton.isOn = true;
|
||||
}
|
||||
else if (_font.ItalicMask) {
|
||||
_italicButton.interactable = false;
|
||||
_italicButton.isOn = false;
|
||||
}
|
||||
else {
|
||||
_italicButton.interactable = true;
|
||||
_italicButton.isOn = _style.Italic;
|
||||
}
|
||||
|
||||
if (_font.Underline) {
|
||||
_underlineButton.interactable = false;
|
||||
_underlineButton.isOn = true;
|
||||
}
|
||||
else if (_font.UnderlineMask) {
|
||||
_underlineButton.interactable = false;
|
||||
_underlineButton.isOn = false;
|
||||
}
|
||||
else {
|
||||
_underlineButton.interactable = true;
|
||||
_underlineButton.isOn = _style.Underline;
|
||||
}
|
||||
|
||||
if (_font.SmallCaps) {
|
||||
_smallCapsButton.interactable = false;
|
||||
_smallCapsButton.isOn = true;
|
||||
}
|
||||
else if (_font.SmallCapsMask) {
|
||||
_smallCapsButton.interactable = false;
|
||||
_smallCapsButton.isOn = false;
|
||||
}
|
||||
else {
|
||||
_smallCapsButton.interactable = true;
|
||||
_smallCapsButton.isOn = _style.SmallCaps;
|
||||
}
|
||||
|
||||
_verticalButton.isOn = _style.Vertical;
|
||||
|
||||
_ignoreUpdates = false;
|
||||
}
|
||||
|
||||
private void UpdateLineSpacing() {
|
||||
_ignoreUpdates = true;
|
||||
|
||||
_lineSpacingSlider.value = Mathf.InverseLerp(_lineSpacingRange.x, _lineSpacingRange.y, _style.LineSpacing);
|
||||
((TMP_InputField) _lineSpacingTextBox).text = $"{_style.LineSpacing:F1}";
|
||||
|
||||
_ignoreUpdates = false;
|
||||
}
|
||||
|
||||
private void UpdateCharSpacing() {
|
||||
_ignoreUpdates = true;
|
||||
|
||||
_charSpacingSlider.value = Mathf.InverseLerp(_charSpacingRange.x, _charSpacingRange.y, _style.CharSpacing);
|
||||
((TMP_InputField) _charSpacingTextBox).text = $"{_style.CharSpacing:F1}";
|
||||
|
||||
_ignoreUpdates = false;
|
||||
}
|
||||
}
|
||||
}
|
140
Source/ConformalDecals/UI/UILoader.cs
Normal file
140
Source/ConformalDecals/UI/UILoader.cs
Normal file
@ -0,0 +1,140 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace ConformalDecals.UI {
|
||||
[KSPAddon(KSPAddon.Startup.Instantly, true)]
|
||||
public class UILoader : MonoBehaviour {
|
||||
private static readonly string Path = KSPUtil.ApplicationRootPath + "GameData/ConformalDecals/Resources/";
|
||||
|
||||
private static GameObject _textEntryPrefab;
|
||||
private static GameObject _fontMenuPrefab;
|
||||
private static GameObject _colorPickerPrefab;
|
||||
|
||||
public static GameObject FontMenuPrefab => _fontMenuPrefab;
|
||||
public static GameObject TextEntryPrefab => _textEntryPrefab;
|
||||
public static GameObject ColorPickerPrefab => _colorPickerPrefab;
|
||||
|
||||
private void Awake() {
|
||||
var prefabs = AssetBundle.LoadFromFile(Path + "ui.conformaldecals");
|
||||
|
||||
_textEntryPrefab = prefabs.LoadAsset("TextEntryPanel") as GameObject;
|
||||
_fontMenuPrefab = prefabs.LoadAsset("FontMenuPanel") as GameObject;
|
||||
_colorPickerPrefab = prefabs.LoadAsset("ColorPickerPanel") as GameObject;
|
||||
|
||||
ProcessWindow(_textEntryPrefab);
|
||||
ProcessWindow(_fontMenuPrefab);
|
||||
ProcessWindow(_colorPickerPrefab);
|
||||
}
|
||||
|
||||
private static void ProcessWindow(GameObject window) {
|
||||
var skin = UISkinManager.defaultSkin;
|
||||
var font = UISkinManager.TMPFont;
|
||||
|
||||
var background = window.GetComponent<Image>();
|
||||
background.sprite = skin.window.normal.background;
|
||||
background.type = Image.Type.Sliced;
|
||||
|
||||
var texts = window.GetComponentsInChildren<TextMeshProUGUI>(true);
|
||||
|
||||
foreach (var text in texts) {
|
||||
ProcessText(text, font, Color.white);
|
||||
}
|
||||
|
||||
var tags = window.GetComponentsInChildren<UITag>(true);
|
||||
|
||||
foreach (var tag in tags) {
|
||||
Debug.Log($"Handling object {tag.gameObject.name}");
|
||||
switch (tag.type) {
|
||||
case UITag.UIType.Window:
|
||||
ProcessImage(tag.gameObject, skin.window);
|
||||
break;
|
||||
case UITag.UIType.Button:
|
||||
ProcessSelectable(tag.gameObject, skin.button);
|
||||
break;
|
||||
case UITag.UIType.ButtonToggle:
|
||||
ProcessToggleButton(tag.gameObject, skin.button);
|
||||
break;
|
||||
case UITag.UIType.RadioToggle:
|
||||
ProcessSelectable(tag.gameObject, skin.toggle);
|
||||
break;
|
||||
case UITag.UIType.BoxSlider:
|
||||
case UITag.UIType.Slider:
|
||||
ProcessSlider(tag.gameObject, skin.horizontalSlider, skin.horizontalSliderThumb);
|
||||
break;
|
||||
case UITag.UIType.Box:
|
||||
ProcessSelectable(tag.gameObject, skin.box);
|
||||
break;
|
||||
case UITag.UIType.Dropdown:
|
||||
ProcessDropdown(tag.gameObject, skin.button, skin.window);
|
||||
break;
|
||||
case UITag.UIType.Label:
|
||||
ProcessText(tag.GetComponent<TextMeshProUGUI>(), font, new Color(0.718f, 0.996f, 0.000f, 1.000f), 14);
|
||||
break;
|
||||
case UITag.UIType.Header:
|
||||
ProcessText(tag.GetComponent<TextMeshProUGUI>(), font, new Color(0.718f, 0.996f, 0.000f, 1.000f), 16);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ProcessImage(GameObject gameObject, UIStyle style) {
|
||||
var image = gameObject.GetComponent<Image>();
|
||||
if (image != null) {
|
||||
ProcessImage(image, style.normal);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ProcessImage(Image image, UIStyleState state) {
|
||||
image.sprite = state.background;
|
||||
image.color = Color.white;
|
||||
image.type = Image.Type.Sliced;
|
||||
}
|
||||
|
||||
private static void ProcessSelectable(GameObject gameObject, UIStyle style) {
|
||||
var selectable = gameObject.GetComponent<Selectable>();
|
||||
if (selectable == null) {
|
||||
ProcessImage(gameObject, style);
|
||||
}
|
||||
else {
|
||||
ProcessImage(selectable.image, style.normal);
|
||||
|
||||
selectable.transition = Selectable.Transition.SpriteSwap;
|
||||
|
||||
var state = selectable.spriteState;
|
||||
state.highlightedSprite = style.highlight.background;
|
||||
state.selectedSprite = style.highlight.background;
|
||||
state.pressedSprite = style.active.background;
|
||||
state.disabledSprite = style.disabled.background;
|
||||
selectable.spriteState = state;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ProcessToggleButton(GameObject gameObject, UIStyle style) {
|
||||
ProcessSelectable(gameObject, style);
|
||||
|
||||
var toggle = gameObject.GetComponent<Toggle>();
|
||||
if (toggle != null) ProcessImage(toggle.graphic as Image, style.active);
|
||||
}
|
||||
|
||||
private static void ProcessSlider(GameObject gameObject, UIStyle backgroundStyle, UIStyle thumbStyle) {
|
||||
ProcessSelectable(gameObject, thumbStyle);
|
||||
|
||||
var background = gameObject.transform.Find("Background");
|
||||
if (background != null) ProcessImage(background.gameObject, backgroundStyle);
|
||||
}
|
||||
|
||||
private static void ProcessDropdown(GameObject gameObject, UIStyle buttonStyle, UIStyle windowStyle) {
|
||||
ProcessSelectable(gameObject, buttonStyle);
|
||||
|
||||
var template = gameObject.transform.Find("Template").gameObject;
|
||||
if (template != null) ProcessImage(template, windowStyle);
|
||||
}
|
||||
|
||||
private static void ProcessText(TextMeshProUGUI text, TMP_FontAsset font, Color color, int size = -1) {
|
||||
text.font = font;
|
||||
text.color = color;
|
||||
if (size > 0) text.fontSize = size;
|
||||
}
|
||||
}
|
||||
}
|
21
Source/ConformalDecals/UI/UITag.cs
Normal file
21
Source/ConformalDecals/UI/UITag.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace ConformalDecals.UI {
|
||||
public class UITag : MonoBehaviour {
|
||||
public enum UIType {
|
||||
None,
|
||||
Window,
|
||||
Box,
|
||||
Button,
|
||||
ButtonToggle,
|
||||
RadioToggle,
|
||||
Slider,
|
||||
Dropdown,
|
||||
Label,
|
||||
Header,
|
||||
BoxSlider
|
||||
}
|
||||
|
||||
[SerializeField] public UIType type = UIType.None;
|
||||
}
|
||||
}
|
161
Source/ConformalDecals/Util/ColorHSL.cs
Normal file
161
Source/ConformalDecals/Util/ColorHSL.cs
Normal file
@ -0,0 +1,161 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ConformalDecals.Util {
|
||||
public struct ColorHSL : IEquatable<Color> {
|
||||
public float h;
|
||||
public float s;
|
||||
public float l;
|
||||
public float a;
|
||||
|
||||
public ColorHSL(float h, float s = 1, float l = 0.5f, float a = 1) {
|
||||
this.h = h;
|
||||
this.s = s;
|
||||
this.l = l;
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return $"HSLA({this.h:F3}, {this.s:F3}, {this.l:F3}, {this.a:F3})";
|
||||
}
|
||||
|
||||
public string ToString(string format) {
|
||||
return
|
||||
"HSLA(" +
|
||||
$"{this.h.ToString(format, CultureInfo.InvariantCulture.NumberFormat)}, " +
|
||||
$"{this.s.ToString(format, CultureInfo.InvariantCulture.NumberFormat)}, " +
|
||||
$"{this.l.ToString(format, CultureInfo.InvariantCulture.NumberFormat)}, " +
|
||||
$"{this.a.ToString(format, CultureInfo.InvariantCulture.NumberFormat)})";
|
||||
}
|
||||
|
||||
public bool Equals(ColorHSL other) {
|
||||
return (this.h.Equals(other.h) && this.s.Equals(other.s) && this.l.Equals(other.l) && this.a.Equals(other.a));
|
||||
}
|
||||
|
||||
public bool Equals(Color other) {
|
||||
var rgb = HSL2RGB(this);
|
||||
return rgb.Equals(other);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
if (obj is ColorHSL otherHSL) return Equals(otherHSL);
|
||||
if (obj is Color otherRGB) return Equals(otherRGB);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
return ((Vector4) this).GetHashCode();
|
||||
}
|
||||
|
||||
public float this[int index] {
|
||||
get {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return this.h;
|
||||
case 1:
|
||||
return this.s;
|
||||
case 2:
|
||||
return this.l;
|
||||
case 3:
|
||||
return this.a;
|
||||
default:
|
||||
throw new IndexOutOfRangeException("Invalid Vector3 index!");
|
||||
}
|
||||
}
|
||||
set {
|
||||
switch (index) {
|
||||
case 0:
|
||||
this.h = value;
|
||||
break;
|
||||
case 1:
|
||||
this.s = value;
|
||||
break;
|
||||
case 2:
|
||||
this.l = value;
|
||||
break;
|
||||
case 3:
|
||||
this.a = value;
|
||||
break;
|
||||
default:
|
||||
throw new IndexOutOfRangeException("Invalid Vector3 index!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(ColorHSL lhs, ColorHSL rhs) {
|
||||
return lhs.Equals(rhs);
|
||||
}
|
||||
|
||||
public static bool operator !=(ColorHSL lhs, ColorHSL rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
public static implicit operator Vector4(ColorHSL c) {
|
||||
return new Vector4(c.h, c.s, c.l, c.a);
|
||||
}
|
||||
|
||||
public static implicit operator ColorHSL(Vector4 v) {
|
||||
return new ColorHSL(v.x, v.y, v.z, v.w);
|
||||
}
|
||||
|
||||
public static implicit operator ColorHSL(Color rgb) {
|
||||
return RGB2HSL(rgb);
|
||||
}
|
||||
|
||||
public static implicit operator Color(ColorHSL hsl) {
|
||||
return HSL2RGB(hsl);
|
||||
}
|
||||
|
||||
public static Color HSL2RGB(ColorHSL hsl) {
|
||||
float a = hsl.s * Mathf.Min(hsl.l, 1 - hsl.l);
|
||||
|
||||
float Component(int n) {
|
||||
float k = (n + hsl.h * 12) % 12;
|
||||
return hsl.l - a * Mathf.Max(-1, Mathf.Min(k - 3, Mathf.Min(9 - k, 1)));
|
||||
}
|
||||
|
||||
return new Color(Component(0), Component(8), Component(4), hsl.a);
|
||||
}
|
||||
|
||||
public static ColorHSL RGB2HSL(Color rgb) {
|
||||
float h = 0;
|
||||
float s = 0;
|
||||
float l = 0;
|
||||
|
||||
if (rgb.r >= rgb.g && rgb.r >= rgb.b) {
|
||||
float xMin = Mathf.Min(rgb.g, rgb.b);
|
||||
|
||||
l = (rgb.r + xMin) / 2;
|
||||
s = (rgb.r - l) / Mathf.Min(l, 1 - l);
|
||||
|
||||
float c = rgb.r - xMin;
|
||||
if (c > Mathf.Epsilon) h = (rgb.g - rgb.b) / (6 * c);
|
||||
|
||||
}
|
||||
else if (rgb.g >= rgb.r && rgb.g >= rgb.b) {
|
||||
float xMin = Mathf.Min(rgb.r, rgb.b);
|
||||
|
||||
l = (rgb.g + xMin) / 2;
|
||||
s = (rgb.g - l) / Mathf.Min(l, 1 - l);
|
||||
|
||||
float c = rgb.g - xMin;
|
||||
if (c > Mathf.Epsilon) h = (2 + ((rgb.b - rgb.r) / c)) / 6;
|
||||
|
||||
}
|
||||
else if (rgb.b >= rgb.r && rgb.b >= rgb.g) {
|
||||
float xMin = Mathf.Min(rgb.r, rgb.g);
|
||||
|
||||
l = (rgb.b + xMin) / 2;
|
||||
s = (rgb.b - l) / Mathf.Min(l, 1 - l);
|
||||
|
||||
float c = rgb.g - xMin;
|
||||
if (c > Mathf.Epsilon) h = (4 + ((rgb.r - rgb.g) / c)) / 6;
|
||||
|
||||
}
|
||||
|
||||
return new ColorHSL(h, s, l, rgb.a);
|
||||
}
|
||||
}
|
||||
}
|
123
Source/ConformalDecals/Util/ColorHSV.cs
Normal file
123
Source/ConformalDecals/Util/ColorHSV.cs
Normal file
@ -0,0 +1,123 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ConformalDecals.Util {
|
||||
public struct ColorHSV : IEquatable<Color> {
|
||||
public float h;
|
||||
public float s;
|
||||
public float v;
|
||||
public float a;
|
||||
|
||||
public ColorHSV(float h, float s = 1, float v = 1, float a = 1) {
|
||||
this.h = h;
|
||||
this.s = s;
|
||||
this.v = v;
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return $"HSVA({this.h:F3}, {this.s:F3}, {this.v:F3}, {this.a:F3})";
|
||||
}
|
||||
|
||||
public string ToString(string format) {
|
||||
return
|
||||
"HSVA(" +
|
||||
$"{this.h.ToString(format, CultureInfo.InvariantCulture.NumberFormat)}, " +
|
||||
$"{this.s.ToString(format, CultureInfo.InvariantCulture.NumberFormat)}, " +
|
||||
$"{this.v.ToString(format, CultureInfo.InvariantCulture.NumberFormat)}, " +
|
||||
$"{this.a.ToString(format, CultureInfo.InvariantCulture.NumberFormat)})";
|
||||
}
|
||||
|
||||
public bool Equals(ColorHSL other) {
|
||||
return (this.h.Equals(other.h) && this.s.Equals(other.s) && this.v.Equals(other.l) && this.a.Equals(other.a));
|
||||
}
|
||||
|
||||
public bool Equals(Color other) {
|
||||
var rgb = HSV2RGB(this);
|
||||
return rgb.Equals(other);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
if (obj is ColorHSL otherHSL) return Equals(otherHSL);
|
||||
if (obj is Color otherRGB) return Equals(otherRGB);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
return ((Vector4) this).GetHashCode();
|
||||
}
|
||||
|
||||
public float this[int index] {
|
||||
get {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return this.h;
|
||||
case 1:
|
||||
return this.s;
|
||||
case 2:
|
||||
return this.v;
|
||||
case 3:
|
||||
return this.a;
|
||||
default:
|
||||
throw new IndexOutOfRangeException("Invalid Vector3 index!");
|
||||
}
|
||||
}
|
||||
set {
|
||||
switch (index) {
|
||||
case 0:
|
||||
this.h = value;
|
||||
break;
|
||||
case 1:
|
||||
this.s = value;
|
||||
break;
|
||||
case 2:
|
||||
this.v = value;
|
||||
break;
|
||||
case 3:
|
||||
this.a = value;
|
||||
break;
|
||||
default:
|
||||
throw new IndexOutOfRangeException("Invalid Vector3 index!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(ColorHSV lhs, ColorHSV rhs) {
|
||||
return lhs.Equals(rhs);
|
||||
}
|
||||
|
||||
public static bool operator !=(ColorHSV lhs, ColorHSV rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
public static implicit operator Vector4(ColorHSV c) {
|
||||
return new Vector4(c.h, c.s, c.v, c.a);
|
||||
}
|
||||
|
||||
public static implicit operator ColorHSV(Vector4 v) {
|
||||
return new ColorHSV(v.x, v.y, v.z, v.w);
|
||||
}
|
||||
|
||||
public static implicit operator ColorHSV(Color rgb) {
|
||||
return RGB2HSV(rgb);
|
||||
}
|
||||
|
||||
public static implicit operator Color(ColorHSV hsv) {
|
||||
return HSV2RGB(hsv);
|
||||
}
|
||||
|
||||
public static Color HSV2RGB(ColorHSV hsv) {
|
||||
var rgb = Color.HSVToRGB(hsv.h, hsv.s, hsv.v, false);
|
||||
rgb.a = hsv.a;
|
||||
return rgb;
|
||||
}
|
||||
|
||||
public static ColorHSV RGB2HSV(Color rgb) {
|
||||
var hsv = new ColorHSV {a = rgb.a};
|
||||
Color.RGBToHSV(rgb, out hsv.h, out hsv.s, out hsv.v);
|
||||
return hsv;
|
||||
}
|
||||
}
|
||||
}
|
@ -139,16 +139,12 @@ namespace ConformalDecals.Util {
|
||||
throw new FormatException($"Improperly formatted {typeof(T)} value for {valueName} : '{valueString}");
|
||||
}
|
||||
|
||||
public static bool TryParseColor32(string valueString, out Color32 value) {
|
||||
public static bool TryParseHexColor(string valueString, out Color32 value) {
|
||||
value = new Color32(0, 0, 0, byte.MaxValue);
|
||||
|
||||
// HTML-style hex color
|
||||
if (valueString[0] == '#') {
|
||||
var hexColorString = valueString.Substring(1);
|
||||
if (!int.TryParse(valueString, System.Globalization.NumberStyles.HexNumber, null, out var hexColor)) return false;
|
||||
|
||||
if (!int.TryParse(hexColorString, System.Globalization.NumberStyles.HexNumber, null, out var hexColor)) return false;
|
||||
|
||||
switch (hexColorString.Length) {
|
||||
switch (valueString.Length) {
|
||||
case 8: // RRGGBBAA
|
||||
value.a = (byte) (hexColor & 0xFF);
|
||||
hexColor >>= 8;
|
||||
@ -180,6 +176,19 @@ namespace ConformalDecals.Util {
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryParseColor32(string valueString, out Color32 value) {
|
||||
value = new Color32(0, 0, 0, byte.MaxValue);
|
||||
|
||||
// hex color
|
||||
if (valueString[0] == '#') {
|
||||
var hexColorString = valueString.Substring(1);
|
||||
|
||||
if (TryParseHexColor(hexColorString, out var hexColor)) {
|
||||
value = hexColor;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// named color
|
||||
if (NamedColors.TryGetValue(valueString, out var namedColor)) {
|
||||
value = namedColor;
|
||||
|
263
Source/ConformalDecals/Util/TextureUtils.cs
Normal file
263
Source/ConformalDecals/Util/TextureUtils.cs
Normal file
@ -0,0 +1,263 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace ConformalDecals.Util {
|
||||
public static class TextureUtils {
|
||||
public enum BlitMode {
|
||||
Set,
|
||||
Add,
|
||||
}
|
||||
|
||||
public static Color32 AddColor32(Color32 color1, Color32 color2) {
|
||||
return new Color32((byte) (color1.r + color2.r), (byte) (color1.g + color2.g), (byte) (color1.b + color2.b), (byte) (color1.a + color2.a));
|
||||
}
|
||||
|
||||
public static Color32 AddColor32Clamped(Color32 color1, Color32 color2) {
|
||||
var r = color1.r + color2.r;
|
||||
var g = color1.g + color2.g;
|
||||
var b = color1.b + color2.b;
|
||||
var a = color1.a + color2.a;
|
||||
if (r > byte.MaxValue) r = byte.MaxValue;
|
||||
if (g > byte.MaxValue) g = byte.MaxValue;
|
||||
if (b > byte.MaxValue) b = byte.MaxValue;
|
||||
if (a > byte.MaxValue) a = byte.MaxValue;
|
||||
|
||||
return new Color32((byte) r, (byte) g, (byte) b, (byte) a);
|
||||
}
|
||||
|
||||
public static void ClearTexture(Color32[] colors, Color32 clearColor = default) {
|
||||
for (var i = 0; i < colors.Length; i++) {
|
||||
colors[i] = clearColor;
|
||||
}
|
||||
}
|
||||
|
||||
public static void BlitRectAlpha(
|
||||
Texture2D src, Color32[] srcColors, Vector2Int srcPos,
|
||||
Texture2D dst, Color32[] dstColors, Vector2Int dstPos,
|
||||
Vector2Int size, BlitMode mode) {
|
||||
|
||||
ClipRect(src, ref srcPos, dst, ref dstPos, ref size);
|
||||
|
||||
if (size.x <= 0 || size.y <= 0) return;
|
||||
|
||||
int srcIndex = srcPos.x + srcPos.y * src.width;
|
||||
int dstIndex = dstPos.x + dstPos.y * dst.width;
|
||||
|
||||
for (int dy = size.y - 1; dy >= 0; dy--) {
|
||||
|
||||
for (int dx = size.x - 1; dx >= 0; dx--) {
|
||||
switch (mode) {
|
||||
case BlitMode.Set:
|
||||
dstColors[dstIndex + dx].a = srcColors[srcIndex + dx].a;
|
||||
break;
|
||||
case BlitMode.Add:
|
||||
var s = srcColors[srcIndex + dx].a;
|
||||
var d = dstColors[dstIndex + dx].a;
|
||||
var sum = s + d;
|
||||
if (sum > byte.MaxValue) sum = byte.MaxValue;
|
||||
dstColors[dstIndex + dx].a = (byte) sum;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
srcIndex += src.width;
|
||||
dstIndex += dst.width;
|
||||
}
|
||||
}
|
||||
|
||||
public static void BlitRect(
|
||||
Texture2D src, Color32[] srcColors, Vector2Int srcPos,
|
||||
Texture2D dst, Color32[] dstColors, Vector2Int dstPos,
|
||||
Vector2Int size, BlitMode mode) {
|
||||
|
||||
ClipRect(src, ref srcPos, dst, ref dstPos, ref size);
|
||||
|
||||
if (size.x <= 0 || size.y <= 0) return;
|
||||
|
||||
int srcIndex = srcPos.x + srcPos.y * src.width;
|
||||
int dstIndex = dstPos.x + dstPos.y * dst.width;
|
||||
|
||||
for (int dy = 0; dy < size.y; dy++) {
|
||||
|
||||
for (int dx = 0; dx < size.x; dx++) {
|
||||
switch (mode) {
|
||||
case BlitMode.Set:
|
||||
dstColors[dstIndex + dx] = srcColors[srcIndex + dx];
|
||||
break;
|
||||
case BlitMode.Add:
|
||||
dstColors[dstIndex + dx] = AddColor32Clamped(srcColors[srcIndex + dx], dstColors[dstIndex + dx]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
srcIndex += src.width;
|
||||
dstIndex += dst.width;
|
||||
}
|
||||
}
|
||||
|
||||
public static void BlitRectBilinearAlpha(
|
||||
Texture2D src, Vector2Int srcPos, Vector2 srcSize,
|
||||
Texture2D dst, Color32[] dstColors, Vector2Int dstPos, Vector2Int dstSize,
|
||||
BlitMode mode) {
|
||||
|
||||
var sizeRatio = dstSize / srcSize;
|
||||
|
||||
ClipRect(src, ref srcPos, dst, ref dstPos, ref srcSize, ref dstSize);
|
||||
|
||||
if (dstSize.x <= 0 || dstSize.y <= 0) return;
|
||||
|
||||
var srcPixel = new Vector2(1.0f / src.width, 1.0f / src.height);
|
||||
var srcStart = (srcPos * srcPixel) + (srcPixel / 2);
|
||||
var srcStep = sizeRatio * srcPixel;
|
||||
var srcY = srcStart.y;
|
||||
|
||||
int dstIndex = dstPos.x + dstPos.y * dst.width;
|
||||
for (int dy = 0;
|
||||
dy < dstSize.y;
|
||||
dy++) {
|
||||
var srcX = srcStart.x;
|
||||
|
||||
for (int dx = 0; dx < dstSize.x; dx++) {
|
||||
switch (mode) {
|
||||
case BlitMode.Set:
|
||||
dstColors[dstIndex + dx].a = (byte) (src.GetPixelBilinear(srcX, srcY).a * byte.MaxValue);
|
||||
break;
|
||||
case BlitMode.Add:
|
||||
var s = (byte) (src.GetPixelBilinear(srcX, srcY).a * byte.MaxValue);
|
||||
var d = dstColors[dstIndex + dx].a;
|
||||
var sum = s + d;
|
||||
if (sum > byte.MaxValue) sum = byte.MaxValue;
|
||||
dstColors[dstIndex + dx].a = (byte) sum;
|
||||
break;
|
||||
}
|
||||
|
||||
srcX += srcStep.x;
|
||||
}
|
||||
|
||||
srcY += srcStep.y;
|
||||
dstIndex += dst.width;
|
||||
}
|
||||
}
|
||||
|
||||
public static void BlitRectBilinear(
|
||||
Texture2D src, Vector2Int srcPos, Vector2 srcSize,
|
||||
Texture2D dst, Color32[] dstColors, Vector2Int dstPos, Vector2Int dstSize,
|
||||
BlitMode mode) {
|
||||
|
||||
var sizeRatio = dstSize / srcSize;
|
||||
|
||||
ClipRect(src, ref srcPos, dst, ref dstPos, ref srcSize, ref dstSize);
|
||||
|
||||
if (dstSize.x <= 0 || dstSize.y <= 0) return;
|
||||
|
||||
var srcPixel = new Vector2(1.0f / src.width, 1.0f / src.height);
|
||||
var srcStart = (srcPos * srcPixel) + (srcPixel / 2);
|
||||
var srcStep = sizeRatio * srcPixel;
|
||||
var srcY = srcStart.y;
|
||||
|
||||
int dstIndex = dstPos.x + dstPos.y * dst.width;
|
||||
for (int dy = 0;
|
||||
dy < dstSize.y;
|
||||
dy++) {
|
||||
var srcX = srcStart.x;
|
||||
|
||||
for (int dx = 0; dx < dstSize.x; dx++) {
|
||||
switch (mode) {
|
||||
case BlitMode.Set:
|
||||
dstColors[dstIndex + dx] = src.GetPixelBilinear(srcX, srcY);
|
||||
break;
|
||||
case BlitMode.Add:
|
||||
dstColors[dstIndex + dx] = AddColor32Clamped(src.GetPixelBilinear(srcX, srcY), dstColors[dstIndex + dx]);
|
||||
break;
|
||||
}
|
||||
|
||||
srcX += srcStep.x;
|
||||
}
|
||||
|
||||
srcY += srcStep.y;
|
||||
dstIndex += dst.width;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ClipRect(Texture2D src, ref Vector2Int srcPos, Texture2D dst, ref Vector2Int dstPos, ref Vector2Int size) {
|
||||
if (srcPos.x < 0) {
|
||||
size.x += srcPos.x;
|
||||
dstPos.x -= srcPos.x;
|
||||
srcPos.x = 0;
|
||||
}
|
||||
|
||||
if (srcPos.y < 0) {
|
||||
size.y += srcPos.y;
|
||||
dstPos.y -= srcPos.y;
|
||||
srcPos.y = 0;
|
||||
}
|
||||
|
||||
if (dstPos.x < 0) {
|
||||
size.x += dstPos.x;
|
||||
srcPos.x -= dstPos.x;
|
||||
dstPos.x = 0;
|
||||
}
|
||||
|
||||
if (dstPos.y < 0) {
|
||||
size.y += dstPos.y;
|
||||
srcPos.y -= dstPos.y;
|
||||
dstPos.y = 0;
|
||||
}
|
||||
|
||||
if (srcPos.x + size.x > src.width) size.x = src.width - srcPos.x;
|
||||
if (srcPos.y + size.y > src.height) size.y = src.height - srcPos.y;
|
||||
if (dstPos.x + size.x > dst.width) size.x = dst.width - srcPos.x;
|
||||
if (dstPos.y + size.y > dst.height) size.y = dst.height - srcPos.y;
|
||||
}
|
||||
|
||||
private static void ClipRect(Texture2D src, ref Vector2Int srcPos, Texture2D dst, ref Vector2Int dstPos, ref Vector2 srcSize, ref Vector2Int dstSize) {
|
||||
var sizeRatio = dstSize / srcSize;
|
||||
if (srcPos.x < 0) {
|
||||
dstSize.x += (int) (srcPos.x * sizeRatio.x);
|
||||
dstPos.x -= (int) (srcPos.x * sizeRatio.x);
|
||||
srcSize.x += srcPos.x;
|
||||
srcPos.x = 0;
|
||||
}
|
||||
|
||||
if (srcPos.y < 0) {
|
||||
dstSize.y += (int) (srcPos.y * sizeRatio.y);
|
||||
dstPos.y -= (int) (srcPos.y * sizeRatio.y);
|
||||
srcSize.y += srcPos.y;
|
||||
srcPos.y = 0;
|
||||
}
|
||||
|
||||
if (dstPos.x < 0) {
|
||||
srcSize.x += dstPos.x / sizeRatio.x;
|
||||
srcPos.x -= (int) (dstPos.x / sizeRatio.x);
|
||||
dstSize.x += dstPos.x;
|
||||
dstPos.x = 0;
|
||||
}
|
||||
|
||||
if (dstPos.y < 0) {
|
||||
srcSize.y += dstPos.y / sizeRatio.y;
|
||||
srcPos.y -= (int) (dstPos.y / sizeRatio.y);
|
||||
dstSize.y += dstPos.y;
|
||||
dstPos.y = 0;
|
||||
}
|
||||
|
||||
if (srcPos.x + srcSize.x > src.width) {
|
||||
srcSize.x = src.width - srcPos.x;
|
||||
dstSize.x = (int) (srcSize.x * sizeRatio.x);
|
||||
}
|
||||
|
||||
if (srcPos.y + srcSize.y > src.height) {
|
||||
srcSize.y = src.height - srcPos.y;
|
||||
dstSize.y = (int) (srcSize.y * sizeRatio.y);
|
||||
}
|
||||
|
||||
if (dstPos.x + dstSize.x > dst.width) {
|
||||
dstSize.x = dst.width - srcPos.x;
|
||||
srcSize.x = (int) (dstSize.x / sizeRatio.x);
|
||||
}
|
||||
|
||||
if (dstPos.y + dstSize.y > dst.height) {
|
||||
dstSize.y = dst.height - srcPos.y;
|
||||
srcSize.y = (int) (dstSize.y / sizeRatio.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,15 +2,16 @@ v0.2.0
|
||||
------
|
||||
- New Parts:
|
||||
- CDL-3 Surface Base Decal: A set of conformal decals based on the symbols from the movie Moon designed by Gavin Rothery
|
||||
- CDL-T Custom Text Decal: A customizable text decal with a variety of fonts
|
||||
- Changes:
|
||||
- New "KEYWORD" material modifier, allowing for shader features to be enabled and disabled.
|
||||
- material modifiers can now be removed in variants by setting `remove = true` inside them.
|
||||
- Unified all shaders into a single "Standard" shader with variants supporting any combination of bump, specular and emissive maps, plus SDF alphas.
|
||||
- Unified all decal shaders into a single "Standard" shader with variants supporting any combination of bump, specular and emissive maps, plus SDF alphas.
|
||||
- Old shaders are remapped to Standard shader plus keywords automatically.
|
||||
- New SDF-based antialiasing for when decals extend to their borders, i.e. on opaque flags.
|
||||
- Fixes:
|
||||
- Fixed WIDTH and HEIGHT scale modes being flipped
|
||||
- Removed debug log statements
|
||||
- Removed some debug log statements
|
||||
|
||||
v0.1.4
|
||||
------
|
||||
|
Loading…
Reference in New Issue
Block a user