42 Commits

Author SHA1 Message Date
f545e4c5ab Fix lightingKSP for standard/standard specular 2024-08-23 22:05:05 -07:00
cb98e78a3f New LightingKSP to solve some issues with Deferred
Deferred moves the ambient calculation to the lighting pass for very good reasons, but Unity shaders still try to do it in the base pass. Easy solution is make the GI function a no-op in deferred mode.
2024-07-30 22:40:23 -07:00
946975249d More decal shader reorganization 2024-07-29 22:32:11 -07:00
ce55f07150 Add templatized text decal shader 2024-07-26 00:03:42 -07:00
7286202be9 Reorganize assets folder and tweak shaders
Moved rim shading and underwater fog to the common cginc
2024-07-25 23:29:42 -07:00
14640c1dca Templatize decal shader 2024-07-25 00:31:16 -07:00
321b2a49a4 Reorganize UI shaders 2024-07-23 22:44:41 -07:00
f1115ef188 Tweaks to clean up shader multi-compiles and formatting 2024-07-23 21:54:39 -07:00
92818edfaa Enable debugging 2024-07-21 22:20:11 -07:00
dd40e54ef3 More aggressive shader cleanup 2024-07-17 23:32:23 -07:00
c19a0c2889 Cleaned up deferred support 2024-07-17 22:48:58 -07:00
ded36f457d Add LightingKSP.cginc dropin replacement that works with Deferred 2024-07-16 19:40:34 -07:00
1be7a98835 Mostly fix VAB decal issues 2024-06-26 22:28:41 -07:00
a3a9ad9674 Improved support for deferred rendering 2024-06-26 01:04:00 -07:00
89a1f0d872 I have a very shiny tortilla 2024-06-24 23:07:44 -07:00
6db2d6b82a Release 0.2.14
### Fixed

- Fixed parts showing as all black with the Deferred Rendering mod
2024-06-24 19:12:41 -07:00
37715a46ab Fix for Deferred support
very basic, can be improved later
2024-06-24 19:12:27 -07:00
60cc0d76bb KSP 0.12.5 2024-05-17 21:02:33 -07:00
e7307f95e9 Fix title 2024-05-17 21:01:21 -07:00
88da55123b Fix syntax error in deploy script 2024-05-17 21:00:02 -07:00
eda04235c3 I hate bash 2024-05-17 20:52:00 -07:00
65031be081 Fix missing env variables and make it error if they don't exist 2024-05-17 20:42:01 -07:00
0c7f6dd628 Update KSP version in README.md 2024-05-16 19:32:11 -07:00
883c027a1a Upload bundle in release 2024-05-16 19:23:25 -07:00
d51b240c6c Use download-artifact@v4 2024-05-16 19:17:36 -07:00
91b5e584a6 Release 0.2.13
### Fixed

- Fixed flag decals sometimes not respecting image aspect ratio
2024-05-16 19:12:10 -07:00
44a5aec21e Update github actions workflow 2024-05-15 20:49:41 -07:00
8e0a26f17c Use self-hosted shabby 2024-05-15 20:35:48 -07:00
b48db63a27 Fix flag decals not respecting aspect ratio 2024-05-15 20:25:50 -07:00
c0d20f847d Update targets on an undo 2022-11-24 14:22:38 -08:00
36732ed4d4 Release 0.2.12
### Changed

- Updated bundled Shabby to 0.3.0. Does not affect CKAN users
- Made flag aspect ratio overrides configurable with `ASPECTRATIO` nodes in the config. User flags added to Squad/Flags should now be the correct aspect ratio
- All decal aspect ratios can now be overriden with the `aspectRatio` field

### Fixed

- Reverted some changes from last version that were causing issues on launch
2022-10-31 00:15:36 -07:00
d3388a4dad better null checking 2022-10-31 00:05:31 -07:00
ece9d959fd flag aspect ratio overrides are now configurable
no longer hard coded to Squad/Flags
2022-10-30 23:58:55 -07:00
dfdf280564 Fix OnDestroy not being called 2022-10-30 21:03:27 -07:00
2cca6c37bb Update bundled Shabby to 0.3.0 2022-10-30 17:26:55 -07:00
c653c9efc8 Release 0.2.11
### Fixed

- PR by LinuxGuruGamer:
	- Fixed nullref caused when an entry in `_targets` was null
	- Fixed memory leak caused by the OnDestroy() methods not being called due to them being virtual
2022-10-29 18:15:13 -07:00
374fc8b753 Format changelog 2022-10-29 18:13:46 -07:00
88b2b4841a Merge pull request #38 from linuxgurugamer/main
Nullref fix and memory leak fix
2022-10-05 22:53:10 -07:00
b2da56b1ca A few more edits 2022-09-03 18:03:01 -04:00
14bc694588 Fixed nullref caused when an entry in _targets was null
Fixed memory leak caused by the OnDestroy() methods not being called
	due to them being virtual
2022-09-03 18:01:15 -04:00
7e1b993d20 Update README.md 2022-03-19 20:34:33 -07:00
f8d692352d Remove leftover files from old deploy system 2022-03-14 21:52:43 -07:00
86 changed files with 1859 additions and 1906 deletions

View File

@ -6,15 +6,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: '3.x'
python-version: '3.12'
- name: Setup .NET Core SDK
uses: actions/setup-dotnet@v1.7.2
uses: actions/setup-dotnet@v4.0.0
with:
dotnet-version: '6.0.x'
@ -25,7 +25,7 @@ jobs:
- name: Generate Version Info
run: |
echo "VERSION_TITLE=$(yaclog show -n)" >> $GITHUB_ENV
yaclog show
python Scripts/version.py
yaclog-ksp -n "Conformal Decals"
@ -46,7 +46,7 @@ jobs:
- name: Download KSP Dependencies
run: |
wget http://pileof.rocks/KSP/Shabby_v0.2.0.zip
wget https://pileof.rocks/KSP/Shabby_v0.3.0.zip
wget https://ksp.sarbian.com/jenkins/job/ModuleManager/161/artifact/ModuleManager.4.2.1.dll
wget https://github.com/blowfishpro/B9PartSwitch/releases/download/v2.19.0/B9PartSwitch_v2.19.0.zip
wget https://github.com/KSPModdingLibs/HarmonyKSP/releases/download/2.0.4.0/HarmonyKSP_2.0.4.0_for_KSP1.8+.zip
@ -59,22 +59,22 @@ jobs:
mv HarmonyKSP/GameData/000_Harmony GameData/
- name: Upload Unbundled Build
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ConformalDecals-unbundled
path: |
GameData/ConformalDecals
GameData/ConformalDecals/
README.md
CHANGELOG.md
LICENSE-ART.md
LICENSE-SOURCE.md
- name: Upload Bundled Build
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ConformalDecals
path: |
GameData
GameData/
README.md
CHANGELOG.md
LICENSE-ART.md
@ -87,12 +87,12 @@ jobs:
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v2
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
python-version: '3.12'
- name: Install Python Tools
run: |
@ -102,13 +102,14 @@ jobs:
- name: Get version name and body
run: |
echo "VERSION_TITLE=$(yaclog show -n)" >> $GITHUB_ENV
echo "$(yaclog show -mb)" >> RELEASE.md
yaclog show
- name: Download Build Artifacts
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
- name: Zip Download Packages
run: |
set -u
mkdir bundled
mkdir unbundled
zip -r bundled/ConformalDecals-$VERSION_TITLE.zip ConformalDecals/*
@ -117,23 +118,25 @@ jobs:
- name: Publish to Spacedock
run: |
set -u
curl -F "username=drewcassidy" -F "password=${{ secrets.SPACEDOCK_PASS }}" \
-c ./cookies "https://spacedock.info/api/login"
curl -c ./cookies -b ./cookies \
-F "version=$VERSION_TITLE" \
-F "changelog=$(yaclog show -mb)" \
-F "game-version=1.12.3" \
-F "game-version=1.12.5" \
-F "notify-followers=yes" \
-F "zipball=@bundled/ConformalDecals-$VERSION_TITLE.zip" \
"https://spacedock.info/api/mod/2451/update"
- name: Publish to Github
uses: softprops/action-gh-release@v1
with:
files: |
bundled/ConformalDecals-*.zip
run: |
set -u
gh release create ${{ github.ref_name }} \
--notes "$(yaclog show -mb)" \
--title "Conformal Decals $VERSION_TITLE" \
bundled/ConformalDecals-*.zip \
ConformalDecals/GameData/ConformalDecals/Versioning/ConformalDecals.version
name: Conformal Decals ${{ env.VERSION_TITLE }}
body_path: RELEASE.md
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

13
.gitignore vendored
View File

@ -1,10 +1,8 @@
# Unity Project Directories
Assets/*
!Assets/Shaders/
!Assets/Textures/
!Assets/Scripts/
!Assets/UI/
!Assets/ConformalDecals/
!Assets/ConformalDecals
Assets/ConformalDecals/Fonts
Assets/ConformalDecals/Parts
KSP/
Library/
Logs/
@ -12,6 +10,9 @@ Packages/
ProjectSettings/
Temp/
# Autogenerated shaders
Assets/ConformalDecals/Shaders/Decal/*.shader
# Unity Assetbundle Manifest Files
GameData/ConformalDecals/Resources/Resources
GameData/ConformalDecals/Resources/*.manifest
@ -52,7 +53,7 @@ Source/ConformalDecals/bin
.ds_store
*.sublime*
.idea
.vs
obj
*.swp
@thumbs

View File

@ -1 +0,0 @@
USE_SSM_CREDENTIALS: false

View File

@ -1,34 +0,0 @@
# Example annotated build data file
mod-name: ConformalDecals
package:
include-dependencies: true # Include dependencies in the package
included-gamedata: # Include these gamedata-level folders in packages:
- ConformalDecals
included-support: # Include these root-level files in packages
- README.md
- LICENSE-ART.md
- LICENSE-SOURCE.md
- changelog.txt
dependencies: # Configure dependencies
ModuleManager:
location: url
url: https://ksp.sarbian.com/jenkins/job/ModuleManager/159/artifact/ModuleManager.4.1.4.dll
zip: false
B9PartSwitch:
location: url
url: http://pileof.rocks/KSP/B9PartSwitch-v2.18.0.zip
zip: true
Shabby:
location: url
url: http://pileof.rocks/KSP/Shabby_v0.2.0.zip
zip: true
HarmonyKSP:
location: url
url: https://github.com/KSPModdingLibs/HarmonyKSP/releases/download/2.0.4.0/HarmonyKSP_2.0.4.0_for_KSP1.8+.zip
zip: true
deploy:
SpaceDock:
enabled: true # activate/deactivate this deployment script
mod-id: 2451 # The Spacedock mod ID for deployment
GitHub:
enabled: true # activate/deactivate this deployment script

View File

@ -0,0 +1,156 @@
Shader "ConformalDecals/Decal/{% block shader_name %}UNKNOWN{% endblock %}"
{
Properties
{
// Shader-specific properties
{% block properties %}
{% endblock %}
// Common decal properties
_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
[PerRendererData]_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
#pragma fragment frag_forward
#pragma multi_compile_fwdbase
#pragma skip_variants LIGHTMAP_ON DIRLIGHTMAP_COMBINED DYNAMICLIGHTMAP_ON LIGHTMAP_SHADOW_MIXING
#pragma multi_compile_local __ DECAL_PREVIEW
{% block body %}
#error No body provided
{% endblock %}
ENDCG
}
Pass
{
Name "FORWARD"
Tags
{
"LightMode" = "ForwardAdd"
}
ZWrite Off
ZTest LEqual
Blend SrcAlpha One
Offset -1, -1
CGPROGRAM
#pragma vertex vert
#pragma fragment frag_forward
#pragma multi_compile DIRECTIONAL SPOT POINT
#pragma multi_compile __ LIGHTPROBE_SH
#pragma multi_compile_local __ DECAL_PREVIEW
{{ self.body() }}
ENDCG
}
Pass
{
Name "DEFERRED_PREPASS"
Tags
{
"LightMode" = "Deferred"
}
ZWrite Off
ZTest LEqual
Offset -1, -1
Blend 1 Zero OneMinusSrcColor, Zero OneMinusSrcAlpha
Stencil
{
Ref 1
Comp Equal
Pass Keep
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag_deferred_prepass
#pragma target 3.0
#pragma multi_compile_local __ DECAL_PREVIEW
{{ self.body() }}
{% block pragmas_deferred_prepass %}
{% endblock %}
ENDCG
}
Pass
{
Name "DEFERRED"
Tags
{
"LightMode" = "Deferred"
}
ZWrite Off
ZTest LEqual
Offset -1, -1
Blend 0 SrcAlpha OneMinusSrcAlpha, Zero One
Blend 1 One One
Blend 2 SrcAlpha OneMinusSrcAlpha, Zero One
Blend 3 SrcAlpha OneMinusSrcAlpha, Zero One
Stencil
{
Ref 1
Comp Equal
Pass Keep
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag_deferred
#pragma target 3.0
#pragma multi_compile __ LIGHTPROBE_SH
#pragma multi_compile __ UNITY_HDR_ON
#pragma multi_compile_local __ DECAL_PREVIEW
{{ self.body() }}
ENDCG
}
}
}

View File

@ -0,0 +1,422 @@
#ifndef DECALS_COMMON_INCLUDED
#define DECALS_COMMON_INCLUDED
#include "AutoLight.cginc"
#include "Lighting.cginc"
#include "../LightingKSP.cginc"
#define CLIP_MARGIN 0.05
#define EDGE_MARGIN 0.01
// UNIFORM VARIABLES
// Projection matrix, normal, and tangent vectors
float4x4 _ProjectionMatrix;
float3 _DecalNormal;
float3 _DecalTangent;
// Common Shading Paramaters
float _Cutoff;
float _DecalOpacity;
float4 _Background;
sampler2D _Decal;
float4 _Decal_ST;
// Variant Shading Parameters
#ifdef DECAL_BASE_NORMAL
sampler2D _BumpMap;
float4 _BumpMap_ST;
float _EdgeWearStrength;
float _EdgeWearOffset;
#endif //DECAL_BASE_NORMAL
#ifdef DECAL_BUMPMAP
sampler2D _BumpMap;
float4 _BumpMap_ST;
#endif //DECAL_BUMPMAP
#ifdef DECAL_SPECMAP
sampler2D _SpecMap;
float4 _SpecMap_ST;
// specular color is declared in a unity CGINC for some reason??
#endif //DECAL_SPECMAP
fixed _Shininess;
#ifdef DECAL_EMISSIVE
sampler2D _Emissive;
float4 _Emissive_ST;
fixed4 _Emissive_Color;
#endif //DECAL_EMISSIVE
// KSP EFFECTS
// opacity and color
float _Opacity;
float _RimFalloff;
float4 _RimColor;
// SURFACE INPUT STRUCT
struct DecalSurfaceInput
{
float3 uv;
float2 uv_decal;
#ifdef DECAL_BUMPMAP
float2 uv_bumpmap;
#endif //DECAL_BUMPMAP
#ifdef DECAL_SPECMAP
float2 uv_specmap;
#endif //DECAL_SPECMAP
#ifdef DECAL_EMISSIVE
float2 uv_emissive;
#endif //DECAL_EMISSIVE
#ifdef DECAL_BASE_NORMAL
float3 normal;
#endif //DECAL_BASE_NORMAL
float3 vertex_normal;
float3 viewDir;
float3 worldPosition;
};
struct appdata_decal
{
float4 vertex : POSITION;
float3 normal : NORMAL;
#if defined(DECAL_BASE_NORMAL) || defined(DECAL_PREVIEW)
float4 texcoord : TEXCOORD0;
float4 tangent : TANGENT;
#endif
};
struct v2f
{
UNITY_POSITION(pos);
float3 normal : NORMAL;
float4 uv_decal : TEXCOORD0;
#ifdef DECAL_BASE_NORMAL
float2 uv_base : TEXCOORD1;
#endif //DECAL_BASE_NORMAL
float4 tSpace0 : TEXCOORD2;
float4 tSpace1 : TEXCOORD3;
float4 tSpace2 : TEXCOORD4;
#ifdef UNITY_PASS_FORWARDBASE
fixed3 vlight : TEXCOORD5;
UNITY_SHADOW_COORDS(6)
#endif //UNITY_PASS_FORWARDBASE
#ifdef UNITY_PASS_FORWARDADD
UNITY_LIGHTING_COORDS(5,6)
#endif //UNITY_PASS_FORWARDADD
#if UNITY_SHOULD_SAMPLE_SH
half3 sh : TEXCOORD7; // SH
#endif
};
inline void decalClipAlpha(float alpha) {
#ifndef DECAL_PREVIEW
clip(alpha - 0.001);
#endif
}
inline float CalcMipLevel(float2 texture_coord) {
float2 dx = ddx(texture_coord);
float2 dy = ddy(texture_coord);
float delta_max_sqr = max(dot(dx, dx), dot(dy, dy));
return 0.5 * log2(delta_max_sqr);
}
// Decal bounds distance function
// takes in a world position, world normal, and projector normal and outputs a unitless signed distance from the
inline float BoundsDist(float3 p, float3 normal, float3 projNormal) {
float3 q = abs(p - 0.5) - 0.5; // 1x1 square/cube centered at (0.5,0.5)
//float dist = length(max(q,0)) + min(max(q.x,max(q.y,q.z)),0.0); // true SDF
#ifdef DECAL_PREVIEW
return 10 * max(q.x, q.y); // 2D pseudo SDF
#else
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 //DECAL_PREVIEW
}
// declare surf function,
// this must be defined in any shader using this cginc
void surf (DecalSurfaceInput IN, inout SurfaceOutput o);
v2f vert(appdata_decal v)
{
v2f o;
UNITY_INITIALIZE_OUTPUT(v2f,o);
o.pos = UnityObjectToClipPos(v.vertex);
o.normal = v.normal;
#ifdef DECAL_PREVIEW
o.uv_decal = v.texcoord;
#else
o.uv_decal = mul (_ProjectionMatrix, v.vertex);
#endif //DECAL_PREVIEW
#ifdef DECAL_BASE_NORMAL
o.uv_base = TRANSFORM_TEX(v.texcoord, _BumpMap);
#endif //DECAL_BASE_NORMAL
float3 worldPosition = mul(unity_ObjectToWorld, v.vertex).xyz;
float3 worldNormal = UnityObjectToWorldNormal(v.normal);
#if defined(DECAL_BASE_NORMAL) || defined(DECAL_PREVIEW)
// use tangent of base geometry
fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
fixed tangentSign = v.tangent.w * unity_WorldTransformParams.w;
fixed3 worldBinormal = cross(worldNormal, worldTangent) * tangentSign;
#else
// use tangent of projector
fixed3 decalTangent = UnityObjectToWorldDir(_DecalTangent);
fixed3 worldBinormal = cross(decalTangent, worldNormal);
fixed3 worldTangent = cross(worldNormal, worldBinormal);
#endif //defined(DECAL_BASE_NORMAL) || defined(DECAL_PREVIEW)
o.tSpace0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPosition.x);
o.tSpace1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPosition.y);
o.tSpace2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPosition.z);
// forward base pass specific lighting code
#ifdef UNITY_PASS_FORWARDBASE
// SH/ambient light
#if UNITY_SHOULD_SAMPLE_SH
float3 shlight = ShadeSH9 (float4(worldNormal,1.0));
o.vlight = shlight;
#else
o.vlight = 0.0;
#endif // UNITY_SHOULD_SAMPLE_SH
// vertex light
#ifdef VERTEXLIGHT_ON
o.vlight += Shade4PointLights (
unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,
unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,
unity_4LightAtten0, worldPosition, worldNormal );
#endif // VERTEXLIGHT_ON
#endif // UNITY_PASS_FORWARDBASE
// pass shadow and, possibly, light cookie coordinates to pixel shader
UNITY_TRANSFER_LIGHTING(o, 0.0);
return o;
}
SurfaceOutput frag_common(v2f IN, out float3 worldPos, out float3 worldViewDir, out float3 viewDir) {
SurfaceOutput o;
// setup world-space TBN vectors
UNITY_EXTRACT_TBN(IN);
worldPos = float3(IN.tSpace0.w, IN.tSpace1.w, IN.tSpace2.w);
worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
viewDir = _unity_tbn_0 * worldViewDir.x + _unity_tbn_1 * worldViewDir.y + _unity_tbn_2 * worldViewDir.z;
#ifdef DECAL_PREVIEW
fixed4 uv_projected = IN.uv_decal;
#else
// perform decal projection
fixed4 uv_projected = UNITY_PROJ_COORD(IN.uv_decal);
clip(uv_projected.xyz + CLIP_MARGIN);
clip(CLIP_MARGIN + (1-uv_projected.xyz));
#endif //DECAL_PREVIEW
// declare data
DecalSurfaceInput i;
// initialize surface input
UNITY_INITIALIZE_OUTPUT(DecalSurfaceInput, i)
i.uv_decal = TRANSFORM_TEX(uv_projected, _Decal);
i.uv = uv_projected;
#ifdef DECAL_BUMPMAP
i.uv_bumpmap = TRANSFORM_TEX(uv_projected, _BumpMap);
#endif //DECAL_BUMPMAP
#ifdef DECAL_SPECMAP
i.uv_specmap = TRANSFORM_TEX(uv_projected, _SpecMap);
#endif //DECAL_SPECMAP
#ifdef DECAL_EMISSIVE
i.uv_emissive = TRANSFORM_TEX(uv_projected, _Emissive);
#endif //DECAL_EMISSIVE
#ifdef DECAL_BASE_NORMAL
#ifdef DECAL_PREVIEW
i.normal = fixed3(0,0,1);
#else
i.normal = UnpackNormalDXT5nm(tex2D(_BumpMap, IN.uv_base));
#endif //DECAL_PREVIEW
#endif //DECAL_BASE_NORMAL
i.vertex_normal = IN.normal;
i.viewDir = viewDir;
i.worldPosition = worldPos;
// initialize surface output
o.Albedo = 0.0;
o.Emission = 0.0;
o.Specular = 0.4;
o.Alpha = _DecalOpacity;
o.Gloss = 0.0;
o.Normal = fixed3(0,0,1);
// call surface function
surf(i, o);
// apply KSP fog. In the deferred pass this is a no-op
o.Albedo = UnderwaterFog(i.worldPosition, o.Albedo).rgb;
// apply KSP rim lighting
half rim = 1.0 - saturate(dot(normalize(i.viewDir), o.Normal));
o.Emission += o.Emission = (_RimColor.rgb * pow(rim, _RimFalloff)) * _RimColor.a;
// compute world normal
float3 worldN;
worldN.x = dot(_unity_tbn_0, o.Normal);
worldN.y = dot(_unity_tbn_1, o.Normal);
worldN.z = dot(_unity_tbn_2, o.Normal);
worldN = normalize(worldN);
o.Normal = worldN;
return o;
}
fixed4 frag_forward(v2f IN) : SV_Target
{
fixed4 c = 0;
float3 worldPos;
float3 worldViewDir;
float3 viewDir;
SurfaceOutput o = frag_common(IN, worldPos, worldViewDir, viewDir);
// compute lighting & shadowing factor
UNITY_LIGHT_ATTENUATION(atten, IN, worldPos)
#ifndef USING_DIRECTIONAL_LIGHT
fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));
#else
fixed3 lightDir = _WorldSpaceLightPos0.xyz;
#endif
// Setup lighting environment
UnityGI gi;
UNITY_INITIALIZE_OUTPUT(UnityGI, gi);
gi.indirect.diffuse = 0;
gi.indirect.specular = 0;
gi.light.color = _LightColor0.rgb;
gi.light.dir = lightDir;
#ifdef UNITY_PASS_FORWARDBASE
// Call GI (lightmaps/SH/reflections) lighting function
UnityGIInput giInput;
UNITY_INITIALIZE_OUTPUT(UnityGIInput, giInput);
giInput.light = gi.light;
giInput.worldPos = worldPos;
giInput.worldViewDir = worldViewDir;
giInput.atten = atten;
#if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXEL
giInput.ambient = IN.sh;
#else
giInput.ambient.rgb = 0.0;
#endif
LightingBlinnPhongKSP_GI(o, giInput, gi);
#endif
#ifdef UNITY_PASS_FORWARDADD
gi.light.color *= atten;
#endif
//call modified KSP lighting function
c += LightingBlinnPhongKSP(o, viewDir, gi);
c.rgb += o.Emission;
return c;
}
void frag_deferred (v2f IN,
out half4 outGBuffer0 : SV_Target0,
out half4 outGBuffer1 : SV_Target1,
#if defined(DECAL_BUMPMAP) || defined(DECAL_PREVIEW)
out half4 outGBuffer2 : SV_Target2,
#endif
out half4 outEmission : SV_Target3)
{
#if !(defined(DECAL_BUMPMAP) || defined(DECAL_PREVIEW))
half4 outGBuffer2 = 0; // define dummy normal buffer when we're not writing to it
#endif
float3 worldPos;
float3 worldViewDir;
float3 viewDir;
SurfaceOutput o = frag_common(IN, worldPos, worldViewDir, viewDir);
// Setup lighting environment
UnityGI gi;
UNITY_INITIALIZE_OUTPUT(UnityGI, gi);
gi.indirect.diffuse = 0;
gi.indirect.specular = 0;
gi.light.color = 0;
gi.light.dir = half3(0,1,0);
UnityGIInput giInput;
UNITY_INITIALIZE_OUTPUT(UnityGIInput, giInput);
giInput.light = gi.light;
giInput.worldPos = worldPos;
giInput.worldViewDir = worldViewDir;
giInput.atten = 1;
giInput.lightmapUV = 0.0;
#if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXEL
giInput.ambient = 0.0 * IN.sh;
#else
giInput.ambient.rgb = 0.0;
#endif
giInput.probeHDR[0] = unity_SpecCube0_HDR;
giInput.probeHDR[1] = unity_SpecCube1_HDR;
LightingBlinnPhongKSP_GI(o, giInput, gi);
outEmission = LightingBlinnPhongKSP_Deferred(o, worldViewDir, gi, outGBuffer0, outGBuffer1, outGBuffer2);
// outGBuffer0 = outEmission;
#ifndef UNITY_HDR_ON
outEmission.rgb = exp2(-outEmission.rgb);
#endif
outGBuffer0.a = o.Alpha;
outGBuffer1 *= o.Alpha;
outGBuffer2.a = o.Alpha;
outEmission.a = o.Alpha;
}
void frag_deferred_prepass(v2f IN, out half4 outGBuffer1: SV_Target1) {
float3 worldPos;
float3 worldViewDir;
float3 viewDir;
SurfaceOutput o = frag_common(IN, worldPos, worldViewDir, viewDir);
outGBuffer1 = o.Alpha;
}
#endif

View File

@ -1,28 +1,27 @@
#include "DecalsCommon.cginc"
#include "../SDF.cginc"
void surf(DecalSurfaceInput IN, inout SurfaceOutput o) {
float4 color = tex2D(_Decal, IN.uv_decal);
o.Albedo = UnderwaterFog(IN.worldPosition, color).rgb;
o.Alpha = _DecalOpacity;
o.Albedo = color.rgb;
o.Specular = 0.4;
o.Gloss = _Shininess;
#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_BUMPMAP
o.Normal = tex2D(_BumpMap, IN.uv_bumpmap);
o.Normal = UnpackNormalDXT5nm(tex2D(_BumpMap, IN.uv_bumpmap));
#endif
#ifdef DECAL_SPECMAP
float4 specular = tex2D(_SpecMap, IN.uv_specmap);
o.Gloss = specular.r;
o.Specular = _Shininess;
o.Specular = specular;
#endif
half rim = 1.0 - saturate(dot(normalize(IN.viewDir), o.Normal));
o.Emission = (_RimColor.rgb * pow(rim, _RimFalloff)) * _RimColor.a;
#ifdef DECAL_EMISSIVE
o.Emission += tex2D(_Emissive, IN.uv_emissive).rgb * _Emissive_Color.rgb * _Emissive_Color.a;
#endif
@ -34,5 +33,5 @@ void surf(DecalSurfaceInput IN, inout SurfaceOutput o) {
#else
o.Alpha *= SDFAA(dist);
o.Alpha *= color.a;
#endif
#endif
}

View File

@ -0,0 +1,40 @@
{% extends "DecalBase.shader.template" %}
{% block shader_name %}Standard{% endblock %}
{% block properties %}
[Header(Decal)]
_Decal("Decal Texture", 2D) = "gray" {}
[Toggle(DECAL_SDF_ALPHA)] _Decal_SDF_Alpha ("SDF in Alpha", int) = 0
[Header(Normal)]
[Toggle(DECAL_BASE_NORMAL)] _BaseNormal ("Use Base Normal", int) = 0
[Toggle(DECAL_BUMPMAP)] _Decal_BumpMap ("Has BumpMap", 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
[Header(Emissive)]
[Toggle(DECAL_EMISSIVE)] _Decal_Emissive ("Has Emissive", int) = 0
_Emissive("_Emissive", 2D) = "black" {}
_EmissiveColor("_EmissiveColor", Color) = (0,0,0,1)
{% endblock %}
{% block body %}
#pragma multi_compile_local __ DECAL_BASE_NORMAL DECAL_BUMPMAP
#pragma multi_compile_local __ DECAL_SPECMAP
#pragma multi_compile_local __ DECAL_EMISSIVE
#pragma multi_compile_local __ DECAL_SDF_ALPHA
#include "StandardDecal.cginc"
{% endblock %}
{% block pragmas_deferred_prepass %}
#pragma skip_variants DECAL_SPECMAP DECAL_EMISSIVE DECAL_BUMPMAP
{% endblock %}

View File

@ -1,3 +1,6 @@
#include "DecalsCommon.cginc"
#include "../SDF.cginc"
float4 _DecalColor;
float4 _OutlineColor;
@ -5,8 +8,11 @@ float _OutlineWidth;
void surf(DecalSurfaceInput IN, inout SurfaceOutput o) {
float4 color = _DecalColor;
o.Specular = 0.4;
o.Gloss = _Shininess;
float dist = _Cutoff - tex2D(_Decal, IN.uv_decal).r; // text distance
#ifdef DECAL_OUTLINE
// Outline
float outlineOffset = _OutlineWidth * 0.25;
@ -28,8 +34,8 @@ void surf(DecalSurfaceInput IN, inout SurfaceOutput o) {
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;
o.Albedo = color.rgb;
o.Alpha *= SDFAA(dist, ddist);
#ifdef DECAL_BASE_NORMAL
float3 normal = IN.normal;
@ -40,10 +46,6 @@ void surf(DecalSurfaceInput IN, inout SurfaceOutput o) {
#ifdef DECAL_SPECMAP
float4 specular = tex2D(_SpecMap, IN.uv_specmap);
o.Gloss = specular.r;
o.Specular = _Shininess;
o.Specular = specular;
#endif
half rim = 1.0 - saturate(dot(normalize(IN.viewDir), o.Normal));
o.Emission = (_RimColor.rgb * pow(rim, _RimFalloff)) * _RimColor.a;
}

View File

@ -0,0 +1,41 @@
{% extends "DecalBase.shader.template" %}
{% block shader_name %}Text{% endblock %}
{% block 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
{% endblock %}
{% block body %}
#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 "TextDecal.cginc"
{% endblock %}
{% block pragmas_deferred_prepass %}
#pragma skip_variants DECAL_SPECMAP
{% endblock %}

View File

@ -29,7 +29,7 @@ Shader "ConformalDecals/Decal Back"
CGPROGRAM
#include "LightingKSP.cginc"
#pragma surface surf BlinnPhongSmooth vertex:vert
#pragma surface surf BlinnPhongKSP vertex:vert
#pragma target 3.0
sampler2D _MainTex;
@ -63,12 +63,12 @@ Shader "ConformalDecals/Decal Back"
// 45° rotation
uv_MainTex.x = IN.uv_MainTex.x - IN.uv_MainTex.y;
uv_MainTex.y = IN.uv_MainTex.x + IN.uv_MainTex.y;
// stagger every other row
uv_MainTex.y *= 2;
int row = floor(uv_MainTex.y);
uv_MainTex.x += row * _RowOffset;
uv_MainTex.y *= 2;
float4 color = _Color * tex2D(_MainTex,(uv_MainTex));
float3 normal = UnpackNormal(tex2D(_BumpMap, uv_BumpMap));

View File

@ -0,0 +1,161 @@
// WHAT IS THIS FILE?
// this file provides a replacement for the LightingKSP.cginc file that ships with part tools for writing custom shaders.
// This version enables support for the Deferred mod
//
// HOW DO I USE IT?
// Step 1)
// replace LightingKSP.cginc in your shader folder with this file, if present. If you aren't using LightingKSP.cginc
// in your shader, add the following below `CGPROGRAM`:
// `#include "../LightingKSP.cginc"`
//
// Step 2)
// add the following above `CGPROGRAM`:
// ```
// Stencil
// {
// Ref 1
// Comp Always
// Pass Replace
// }
// ```
//
// Step 3)
// there should be a line in your shader that looks like this:
// `#pragma surface surf BlinnPhongSmooth keepalpha`
// Remove the `keepalpha` if it's there. the part after `surf` is the name of the lighting function your shader uses now.
// If the lighting function is `BlinnPhong` or `BlinnPhongSmooth`, change it to `BlinnPhongKSP`
// If the lighting function is `Standard`, change it to `StandardKSP`
// If the lighting function is `StandardSpecular`, change it to `StandardSpecularKSP`
#ifndef LIGHTING_KSP_INCLUDED
#define LIGHTING_KSP_INCLUDED
#include "UnityPBSLighting.cginc"
#define blinnPhongShininessPower 0.215
// An exact conversion from blinn-phong to PBR is impossible, but the look can be approximated perceptually
// and by observing how blinn-phong looks and feels at various settings, although it can never be perfect
// 1) The specularColor can be used as is in the PBR specular flow, just needs to be divided by PI so it sums up to 1 over the hemisphere
// 2) Blinn-phong shininess doesn't stop feeling shiny unless at very low values, like below 0.04
// while the PBR smoothness feels more linear -> map shininess to smoothness accordingly using a function
// that increases very quickly at first then slows down, I went with something like x^(1/4) or x^(1/6) then made the power configurable
// I tried various mappings from the literature but nothing really worked as well as this
// 3) Finally I noticed that some parts still looked very shiny like the AV-R8 winglet while in stock they looked rough thanks a low
// specularColor but high shininess and specularMap, so I multiplied the smoothness by the sqrt of the specularColor and that caps
// the smoothness when specularColor is low
void GetStandardSpecularPropertiesFromLegacy(float legacyShininess, float specularMap, out float3 specular,
out float smoothness)
{
float3 legacySpecularColor = saturate(_SpecColor);
smoothness = pow(legacyShininess, blinnPhongShininessPower) * specularMap;
smoothness *= sqrt(length(legacySpecularColor));
specular = legacySpecularColor * UNITY_INV_PI;
}
float4 _Color;
// LEGACY BLINN-PHONG LIGHTING FUNCTION FOR KSP WITH PBR CONVERSION FOR DEFERRED
inline float4 LightingBlinnPhongKSP(SurfaceOutput s, half3 viewDir, UnityGI gi)
{
return LightingBlinnPhong(s,viewDir, gi);
}
inline float4 LightingBlinnPhongKSP_Deferred(SurfaceOutput s, float3 worldViewDir, UnityGI gi,
out float4 outDiffuseOcclusion, out float4 outSpecSmoothness,
out float4 outNormal)
{
SurfaceOutputStandardSpecular ss;
ss.Albedo = s.Albedo;
ss.Normal = s.Normal;
ss.Emission = s.Emission;
ss.Occlusion = 1;
ss.Alpha = saturate(s.Alpha);
GetStandardSpecularPropertiesFromLegacy(s.Specular, s.Gloss, ss.Specular, ss.Smoothness);
return LightingStandardSpecular_Deferred(ss, worldViewDir, gi, outDiffuseOcclusion, outSpecSmoothness, outNormal);
}
inline void LightingBlinnPhongKSP_GI(inout SurfaceOutput s, UnityGIInput gi_input, inout UnityGI gi)
{
#ifndef UNITY_PASS_DEFERRED
gi = UnityGlobalIllumination(gi_input, 1.0, s.Normal);
#endif
}
// STANDARD UNITY LIGHTING FUNCTION FOR KSP
inline float4 LightingStandardKSP(SurfaceOutputStandard s, float3 worldViewDir, UnityGI gi)
{
return LightingStandard(s, worldViewDir, gi); // no change
}
inline float4 LightingStandardKSP_Deferred(SurfaceOutputStandard s, float3 worldViewDir, UnityGI gi,
out float4 outDiffuseOcclusion,
out float4 outSpecSmoothness, out float4 outNormal)
{
return LightingStandard_Deferred(s, worldViewDir, gi, outDiffuseOcclusion, outSpecSmoothness, outNormal);
}
inline void LightingStandardKSP_GI(inout SurfaceOutputStandard s, UnityGIInput gi_input, inout UnityGI gi)
{
#ifndef UNITY_PASS_DEFERRED
LightingStandard_GI(s, gi_input, gi);
#endif
}
// STANDARD SPECULAR UNITY LIGHTING FUNCTION FOR KSP
inline float4 LightingStandardSpecularKSP(SurfaceOutputStandardSpecular s, float3 worldViewDir, UnityGI gi)
{
return LightingStandardSpecular(s, worldViewDir, gi); // no change
}
inline float4 LightingStandardSpecularKSP_Deferred(SurfaceOutputStandardSpecular s, float3 worldViewDir, UnityGI gi,
out float4 outDiffuseOcclusion,
out float4 outSpecSmoothness, out float4 outNormal)
{
return LightingStandardSpecular_Deferred(s, worldViewDir, gi, outDiffuseOcclusion, outSpecSmoothness, outNormal);
}
inline void LightingStandardSpecularKSP_GI(inout SurfaceOutputStandardSpecular s, UnityGIInput gi_input,
inout UnityGI gi)
{
#ifndef UNITY_PASS_DEFERRED
LightingStandardSpecular_GI(s, gi_input, gi);
#endif
}
float4 _LocalCameraPos;
float4 _LocalCameraDir;
float4 _UnderwaterFogColor;
float _UnderwaterMinAlphaFogDistance;
float _UnderwaterMaxAlbedoFog;
float _UnderwaterMaxAlphaFog;
float _UnderwaterAlbedoDistanceScalar;
float _UnderwaterAlphaDistanceScalar;
float _UnderwaterFogFactor;
float4 UnderwaterFog(float3 worldPos, float3 color)
{
// skip fog in deferred mode
#ifdef UNITY_PASS_DEFERRED
return float4(color, 1);
#endif
float3 toPixel = worldPos - _LocalCameraPos.xyz;
float toPixelLength = length(toPixel); ///< Comment out the math--looks better without it.
float underwaterDetection = _UnderwaterFogFactor * _LocalCameraDir.w; ///< sign(1 - sign(_LocalCameraPos.w));
float albedoLerpValue = underwaterDetection * (_UnderwaterMaxAlbedoFog * saturate(
toPixelLength * _UnderwaterAlbedoDistanceScalar));
float alphaFactor = 1 - underwaterDetection * (_UnderwaterMaxAlphaFog * saturate(
(toPixelLength - _UnderwaterMinAlphaFogDistance) * _UnderwaterAlphaDistanceScalar));
return float4(lerp(color, _UnderwaterFogColor.rgb, albedoLerpValue), alphaFactor);
}
#endif

View File

@ -4,11 +4,11 @@ Shader "ConformalDecals/UI/Color Slider"
{
_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
@ -18,13 +18,11 @@ Shader "ConformalDecals/UI/Color Slider"
_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
{
@ -44,14 +42,14 @@ Shader "ConformalDecals/UI/Color Slider"
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
Cull Off
Lighting Off
ZWrite Off
ZTest [unity_GUIZTestMode]
Blend SrcAlpha OneMinusSrcAlpha
ColorMask [_ColorMask]
Pass
{
CGPROGRAM
@ -63,7 +61,7 @@ Shader "ConformalDecals/UI/Color Slider"
#include "UnityCG.cginc"
#include "UnityUI.cginc"
#include "HSL.cginc"
#include "SDF.cginc"
#include "../SDF.cginc"
#pragma multi_compile_local _ UNITY_UI_CLIP_RECT
#pragma multi_compile_local _ UNITY_UI_ALPHACLIP

View File

@ -8,7 +8,7 @@ Shader "ConformalDecals/UI/HSV Square"
_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
@ -37,14 +37,14 @@ Shader "ConformalDecals/UI/HSV Square"
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
Cull Off
Lighting Off
ZWrite Off
ZTest [unity_GUIZTestMode]
Blend SrcAlpha OneMinusSrcAlpha
ColorMask [_ColorMask]
Pass
{
CGPROGRAM
@ -56,7 +56,7 @@ Shader "ConformalDecals/UI/HSV Square"
#include "UnityCG.cginc"
#include "UnityUI.cginc"
#include "HSL.cginc"
#include "SDF.cginc"
#include "../SDF.cginc"
#pragma multi_compile_local _ UNITY_UI_CLIP_RECT
#pragma multi_compile_local _ UNITY_UI_ALPHACLIP

View File

@ -4,11 +4,11 @@ Shader "ConformalDecals/UI/Color Swatch"
{
_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
@ -37,14 +37,14 @@ Shader "ConformalDecals/UI/Color Swatch"
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
Cull Off
Lighting Off
ZWrite Off
ZTest [unity_GUIZTestMode]
Blend SrcAlpha OneMinusSrcAlpha
ColorMask [_ColorMask]
Pass
{
CGPROGRAM
@ -56,7 +56,7 @@ Shader "ConformalDecals/UI/Color Swatch"
#include "UnityCG.cginc"
#include "UnityUI.cginc"
#include "HSL.cginc"
#include "SDF.cginc"
#include "../SDF.cginc"
#pragma multi_compile_local _ UNITY_UI_CLIP_RECT
#pragma multi_compile_local _ UNITY_UI_ALPHACLIP

View File

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View File

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 470 KiB

After

Width:  |  Height:  |  Size: 470 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

Before

Width:  |  Height:  |  Size: 260 B

After

Width:  |  Height:  |  Size: 260 B

View File

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

Before

Width:  |  Height:  |  Size: 169 B

After

Width:  |  Height:  |  Size: 169 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -1,46 +0,0 @@
using System;
using UnityEngine;
[ExecuteInEditMode]
public class DecalProjectorTest : MonoBehaviour
{
public GameObject target = null;
public Material targetMaterial = null;
public MeshRenderer targetRenderer;
public float aspectRatio = 1.0f;
public float size = 1.0f;
public float factor = 1.0f;
private Matrix4x4 _projectionMatrix;
private Matrix4x4 _OrthoMatrix;
private int _matrixID;
private int _normalID;
public int _tangentID;
// Start is called before the first frame update
void Awake()
{
_projectionMatrix = Matrix4x4.identity;
targetRenderer = target.GetComponent<MeshRenderer>();
}
// Update is called once per frame
void Update()
{
Vector3 pos =new Vector3( 0.5f ,0.5f, 0);
Vector3 scale = new Vector3(1 / size, 1 / (aspectRatio * size), 1);
_OrthoMatrix.SetTRS(pos, Quaternion.identity, scale);
//Debug.Log(_OrthoMatrix);
var targetToProjector = transform.worldToLocalMatrix * targetRenderer.localToWorldMatrix;
var projectorToTarget = targetRenderer.worldToLocalMatrix * transform.localToWorldMatrix;
_projectionMatrix = _OrthoMatrix * targetToProjector;
targetMaterial.SetMatrix("_ProjectionMatrix", _projectionMatrix);
targetMaterial.SetVector("_DecalNormal", projectorToTarget.MultiplyVector(Vector3.back).normalized);
targetMaterial.SetVector("_DecalTangent", projectorToTarget.MultiplyVector(Vector3.right).normalized);
}
}

View File

@ -1,117 +0,0 @@
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);
}
}

View File

@ -1,156 +0,0 @@
#ifndef DECALS_COMMON_INCLUDED
#define DECALS_COMMON_INCLUDED
#include "AutoLight.cginc"
#include "Lighting.cginc"
#define CLIP_MARGIN 0.05
#define EDGE_MARGIN 0.01
// UNIFORM VARIABLES
// Projection matrix, normal, and tangent vectors
float4x4 _ProjectionMatrix;
float3 _DecalNormal;
float3 _DecalTangent;
// Common Shading Paramaters
float _Cutoff;
float _DecalOpacity;
float4 _Background;
sampler2D _Decal;
float4 _Decal_ST;
// Variant Shading Parameters
#ifdef DECAL_BASE_NORMAL
sampler2D _BumpMap;
float4 _BumpMap_ST;
float _EdgeWearStrength;
float _EdgeWearOffset;
#endif //DECAL_BASE_NORMAL
#ifdef DECAL_BUMPMAP
sampler2D _BumpMap;
float4 _BumpMap_ST;
#endif //DECAL_BUMPMAP
#ifdef DECAL_SPECMAP
sampler2D _SpecMap;
float4 _SpecMap_ST;
// specular color is declared in a unity CGINC for some reason??
fixed _Shininess;
#endif //DECAL_SPECMAP
#ifdef DECAL_EMISSIVE
sampler2D _Emissive;
float4 _Emissive_ST;
fixed4 _Emissive_Color;
#endif //DECAL_EMISSIVE
// KSP EFFECTS
// opacity and color
float _Opacity;
float4 _Color;
float _RimFalloff;
float4 _RimColor;
// fog
float4 _LocalCameraPos;
float4 _LocalCameraDir;
float4 _UnderwaterFogColor;
float _UnderwaterMinAlphaFogDistance;
float _UnderwaterMaxAlbedoFog;
float _UnderwaterMaxAlphaFog;
float _UnderwaterAlbedoDistanceScalar;
float _UnderwaterAlphaDistanceScalar;
float _UnderwaterFogFactor;
// SURFACE INPUT STRUCT
struct DecalSurfaceInput
{
float3 uv;
float2 uv_decal;
#ifdef DECAL_BUMPMAP
float2 uv_bumpmap;
#endif //DECAL_BUMPMAP
#ifdef DECAL_SPECMAP
float2 uv_specmap;
#endif //DECAL_SPECMAP
#ifdef DECAL_EMISSIVE
float2 uv_emissive;
#endif //DECAL_EMISSIVE
#ifdef DECAL_BASE_NORMAL
float3 normal;
#endif //DECAL_BASE_NORMAL
float3 vertex_normal;
float3 viewDir;
float3 worldPosition;
};
struct appdata_decal
{
float4 vertex : POSITION;
float3 normal : NORMAL;
#if defined(DECAL_BASE_NORMAL) || defined(DECAL_PREVIEW)
float4 texcoord : TEXCOORD0;
float4 tangent : TANGENT;
#endif
};
struct v2f
{
UNITY_POSITION(pos);
float3 normal : NORMAL;
float4 uv_decal : TEXCOORD0;
#ifdef DECAL_BASE_NORMAL
float2 uv_base : TEXCOORD1;
#endif //DECAL_BASE_NORMAL
float4 tSpace0 : TEXCOORD2;
float4 tSpace1 : TEXCOORD3;
float4 tSpace2 : TEXCOORD4;
#ifdef UNITY_PASS_FORWARDBASE
fixed3 vlight : TEXCOORD5;
UNITY_SHADOW_COORDS(6)
#endif //UNITY_PASS_FORWARDBASE
#ifdef UNITY_PASS_FORWARDADD
UNITY_LIGHTING_COORDS(5,6)
#endif //UNITY_PASS_FORWARDADD
};
inline void decalClipAlpha(float alpha) {
#ifndef DECAL_PREVIEW
clip(alpha - 0.001);
#endif
}
inline float CalcMipLevel(float2 texture_coord) {
float2 dx = ddx(texture_coord);
float2 dy = ddy(texture_coord);
float delta_max_sqr = max(dot(dx, dx), dot(dy, dy));
return 0.5 * log2(delta_max_sqr);
}
inline float BoundsDist(float3 p, float3 normal, float3 projNormal) {
float3 q = abs(p - 0.5) - 0.5; // 1x1 square/cube centered at (0.5,0.5)
//float dist = length(max(q,0)) + min(max(q.x,max(q.y,q.z)),0.0); // true SDF
#ifdef DECAL_PREVIEW
return 10 * max(q.x, q.y); // 2D pseudo SDF
#else
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 //DECAL_PREVIEW
}
#endif

View File

@ -1,33 +0,0 @@
#ifndef DECALS_LIGHTING_INCLUDED
#define DECALS_LIGHTING_INCLUDED
// modifed version of the KSP BlinnPhong because it does some weird things
inline fixed4 LightingBlinnPhongDecal(SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten)
{
s.Normal = normalize(s.Normal);
half3 h = normalize(lightDir + viewDir);
fixed diff = max(0, dot(s.Normal, lightDir));
float nh = max(0, dot(s.Normal, h));
float spec = pow(nh, s.Specular*128.0) * s.Gloss;
fixed4 c = 0;
c.rgb = (s.Albedo * _LightColor0.rgb * diff + _LightColor0.rgb * _SpecColor.rgb * spec) * (atten);
return c;
}
// KSP underwater fog function
float4 UnderwaterFog(float3 worldPos, float3 color)
{
float3 toPixel = worldPos - _LocalCameraPos.xyz;
float toPixelLength = length(toPixel);
float underwaterDetection = _UnderwaterFogFactor * _LocalCameraDir.w;
float albedoLerpValue = underwaterDetection * (_UnderwaterMaxAlbedoFog * saturate(toPixelLength * _UnderwaterAlbedoDistanceScalar));
float alphaFactor = 1 - underwaterDetection * (_UnderwaterMaxAlphaFog * saturate((toPixelLength - _UnderwaterMinAlphaFogDistance) * _UnderwaterAlphaDistanceScalar));
return float4(lerp(color, _UnderwaterFogColor.rgb, albedoLerpValue), alphaFactor);
}
#endif

View File

@ -1,183 +0,0 @@
#ifndef DECALS_SURFACE_INCLUDED
#define DECALS_SURFACE_INCLUDED
#include "DecalsCommon.cginc"
#include "DecalsLighting.cginc"
// declare surf function,
// this must be defined in any shader using this cginc
void surf (DecalSurfaceInput IN, inout SurfaceOutput o);
v2f vert_forward(appdata_decal v)
{
v2f o;
UNITY_INITIALIZE_OUTPUT(v2f,o);
o.pos = UnityObjectToClipPos(v.vertex);
o.normal = v.normal;
#ifdef DECAL_PREVIEW
o.uv_decal = v.texcoord;
#else
o.uv_decal = mul (_ProjectionMatrix, v.vertex);
#endif //DECAL_PREVIEW
#ifdef DECAL_BASE_NORMAL
o.uv_base = TRANSFORM_TEX(v.texcoord, _BumpMap);
#endif //DECAL_BASE_NORMAL
float3 worldPosition = mul(unity_ObjectToWorld, v.vertex).xyz;
float3 worldNormal = UnityObjectToWorldNormal(v.normal);
#if defined(DECAL_BASE_NORMAL) || defined(DECAL_PREVIEW)
// use tangent of base geometry
fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
fixed tangentSign = v.tangent.w * unity_WorldTransformParams.w;
fixed3 worldBinormal = cross(worldNormal, worldTangent) * tangentSign;
#else
// use tangent of projector
fixed3 decalTangent = UnityObjectToWorldDir(_DecalTangent);
fixed3 worldBinormal = cross(decalTangent, worldNormal);
fixed3 worldTangent = cross(worldNormal, worldBinormal);
#endif //defined(DECAL_BASE_NORMAL) || defined(DECAL_PREVIEW)
o.tSpace0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPosition.x);
o.tSpace1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPosition.y);
o.tSpace2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPosition.z);
// forward base pass specific lighting code
#ifdef UNITY_PASS_FORWARDBASE
// SH/ambient light
#if UNITY_SHOULD_SAMPLE_SH
float3 shlight = ShadeSH9 (float4(worldNormal,1.0));
o.vlight = shlight;
#else
o.vlight = 0.0;
#endif // UNITY_SHOULD_SAMPLE_SH
// vertex light
#ifdef VERTEXLIGHT_ON
o.vlight += Shade4PointLights (
unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,
unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,
unity_4LightAtten0, worldPosition, worldNormal );
#endif // VERTEXLIGHT_ON
#endif // UNITY_PASS_FORWARDBASE
// pass shadow and, possibly, light cookie coordinates to pixel shader
UNITY_TRANSFER_LIGHTING(o, 0.0);
return o;
}
fixed4 frag_forward(v2f IN) : SV_Target
{
#ifdef DECAL_PREVIEW
fixed4 uv_projected = IN.uv_decal;
#else
// perform decal projection
fixed4 uv_projected = UNITY_PROJ_COORD(IN.uv_decal);
clip(uv_projected.xyz + CLIP_MARGIN);
clip(CLIP_MARGIN + (1-uv_projected.xyz));
#endif //DECAL_PREVIEW
// declare data
DecalSurfaceInput i;
SurfaceOutput o;
// setup world-space TBN vectors
UNITY_EXTRACT_TBN(IN);
float3 worldPosition = float3(IN.tSpace0.w, IN.tSpace1.w, IN.tSpace2.w);
float3 worldTangent = float3(IN.tSpace0.x, IN.tSpace1.x, IN.tSpace2.x);
// setup world-space light and view direction vectors
#ifndef USING_DIRECTIONAL_LIGHT
fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPosition));
#else
fixed3 lightDir = _WorldSpaceLightPos0.xyz;
#endif
float3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPosition));
float3 viewDir = _unity_tbn_0 * worldViewDir.x + _unity_tbn_1 * worldViewDir.y + _unity_tbn_2 * worldViewDir.z;
// initialize surface input
UNITY_INITIALIZE_OUTPUT(DecalSurfaceInput, i)
i.uv_decal = TRANSFORM_TEX(uv_projected, _Decal);
i.uv = uv_projected;
#ifdef DECAL_BUMPMAP
i.uv_bumpmap = TRANSFORM_TEX(uv_projected, _BumpMap);
#endif //DECAL_BUMPMAP
#ifdef DECAL_SPECMAP
i.uv_specmap = TRANSFORM_TEX(uv_projected, _SpecMap);
#endif //DECAL_SPECMAP
#ifdef DECAL_EMISSIVE
i.uv_emissive = TRANSFORM_TEX(uv_projected, _Emissive);
#endif //DECAL_EMISSIVE
#ifdef DECAL_BASE_NORMAL
#ifdef DECAL_PREVIEW
i.normal = fixed3(0,0,1);
#else
i.normal = UnpackNormalDXT5nm(tex2D(_BumpMap, IN.uv_base));
#endif //DECAL_PREVIEW
#endif //DECAL_BASE_NORMAL
i.vertex_normal = IN.normal;
i.viewDir = viewDir;
i.worldPosition = worldPosition;
// initialize surface output
o.Albedo = 0.0;
o.Emission = 0.0;
o.Specular = 0.0;
o.Alpha = 0.0;
o.Gloss = 0.0;
o.Normal = fixed3(0,0,1);
// call surface function
surf(i, o);
#ifdef DECAL_PREVIEW
if (any(IN.uv_decal > 1) || any(IN.uv_decal < 0)) o.Alpha = 0;
o.Albedo = lerp(_Background.rgb, o.Albedo, o.Alpha) * _Color.rgb;
o.Normal = lerp(float3(0,0,1), o.Normal, o.Alpha);
o.Gloss = lerp(_Background.a, o.Gloss, o.Alpha);
o.Emission = lerp(0, o.Emission, o.Alpha);
o.Alpha = _Opacity;
#endif //DECAL_PREVIEW
// compute lighting & shadowing factor
UNITY_LIGHT_ATTENUATION(atten, IN, worldPosition)
// compute world normal
float3 WorldNormal;
WorldNormal.x = dot(_unity_tbn_0, o.Normal);
WorldNormal.y = dot(_unity_tbn_1, o.Normal);
WorldNormal.z = dot(_unity_tbn_2, o.Normal);
WorldNormal = normalize(WorldNormal);
o.Normal = WorldNormal;
//call modified KSP lighting function
float4 c = LightingBlinnPhongDecal(o, lightDir, worldViewDir, atten);
// Forward base emission and ambient/vertex lighting
#ifdef UNITY_PASS_FORWARDBASE
c.rgb += o.Emission;
c.rgb += o.Albedo * IN.vlight;
c.a = o.Alpha;
#endif //UNITY_PASS_FORWARDBASE
// Forward add multiply by alpha
#ifdef UNITY_PASS_FORWARDADD
c.rgb *= o.Alpha;
#endif
return c;
}
#endif

View File

@ -1,102 +0,0 @@
#ifndef LIGHTING_KSP_INCLUDED
#define LIGHTING_KSP_INCLUDED
inline fixed4 LightingBlinnPhongSmooth(SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten)
{
s.Normal = normalize(s.Normal);
half3 h = normalize(lightDir + viewDir);
fixed diff = max(0, dot(s.Normal, lightDir));
float nh = max(0, dot(s.Normal, h));
float spec = pow(nh, s.Specular*128.0) * s.Gloss;
fixed4 c;
c.rgb = (s.Albedo * _LightColor0.rgb * diff + _LightColor0.rgb * _SpecColor.rgb * spec) * (atten);
c.a = s.Alpha + _LightColor0.a * _SpecColor.a * spec * atten;
return c;
}
inline half4 LightingUnlit(SurfaceOutput s, half3 lightDir, half atten)
{
// half diff = max (0, dot (s.Normal, lightDir));
half4 c;
c.rgb = s.Albedo;
c.a = s.Alpha;
return c;
}
inline half4 LightingUnlit_PrePass(SurfaceOutput s, half4 light)
{
half4 c;
c.rgb = s.Albedo;
c.a = s.Alpha;
return c;
}
fixed4 LightingNoLighting(SurfaceOutput s, fixed3 lightDir, fixed atten) { return fixed4(0, 0, 0, 0); }
float4 _Color;
half _LightBoost;
half4 LightingLightWrapped(SurfaceOutput s, half3 lightDir, half3 viewDir, half atten) {
float3 w = _Color.rgb*0.5;
half3 NdotL = dot(s.Normal, lightDir);
//Specular term
half3 h = normalize(lightDir + viewDir);
s.Normal = normalize(s.Normal);
float NdotH = dot(s.Normal, h);
float spec = pow(max(NdotH, 0), s.Specular * 128.0) * s.Gloss;
fixed3 specColor = _SpecColor.rgb * _LightColor0.rgb;
half3 diff = NdotL * (1 - w) + w;
half4 c;
c.rgb = ((s.Albedo * _LightColor0.rgb * diff) + (specColor * spec)) * (atten * _LightBoost);
c.a = s.Alpha + (_LightColor0.a * _SpecColor.a * spec * atten);
return c;
}
float4 _LocalCameraPos;
float4 _LocalCameraDir;
float4 _UnderwaterFogColor;
float _UnderwaterMinAlphaFogDistance;
float _UnderwaterMaxAlbedoFog;
float _UnderwaterMaxAlphaFog;
float _UnderwaterAlbedoDistanceScalar;
float _UnderwaterAlphaDistanceScalar;
float _UnderwaterFogFactor;
float4 UnderwaterFog(float3 worldPos, float3 color)
{
float3 toPixel = worldPos - _LocalCameraPos.xyz;
float toPixelLength = length(toPixel); ///< Comment out the math--looks better without it.
//float angleDot = dot(_LocalCameraDir.xyz, toPixel / toPixelLength);
//angleDot = lerp(0.00000001, angleDot, saturate(sign(angleDot)));
//float waterDist = -_LocalCameraPos.w / angleDot;
//float dist = min(toPixelLength, waterDist);
float underwaterDetection = _UnderwaterFogFactor * _LocalCameraDir.w; ///< sign(1 - sign(_LocalCameraPos.w));
float albedoLerpValue = underwaterDetection * (_UnderwaterMaxAlbedoFog * saturate(toPixelLength * _UnderwaterAlbedoDistanceScalar));
float alphaFactor = 1 - underwaterDetection * (_UnderwaterMaxAlphaFog * saturate((toPixelLength - _UnderwaterMinAlphaFogDistance) * _UnderwaterAlphaDistanceScalar));
return float4(lerp(color, _UnderwaterFogColor.rgb, albedoLerpValue), alphaFactor);
}
#endif

View File

@ -1,111 +0,0 @@
Shader "ConformalDecals/Decal/Standard"
{
Properties
{
[Header(Decal)]
_Decal("Decal Texture", 2D) = "gray" {}
[Toggle(DECAL_SDF_ALPHA)] _Decal_SDF_Alpha ("SDF in Alpha", int) = 0
[Header(Normal)]
[Toggle(DECAL_BASE_NORMAL)] _BaseNormal ("Use Base Normal", int) = 0
[Toggle(DECAL_BUMPMAP)] _Decal_BumpMap ("Has BumpMap", 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
[Header(Emissive)]
[Toggle(DECAL_EMISSIVE)] _Decal_Emissive ("Has Emissive", int) = 0
_Emissive("_Emissive", 2D) = "black" {}
_EmissiveColor("_EmissiveColor", Color) = (0,0,0,1)
_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
[PerRendererData]_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 DECAL_BUMPMAP
#pragma multi_compile_local __ DECAL_SPECMAP
#pragma multi_compile_local __ DECAL_EMISSIVE
#pragma multi_compile_local __ DECAL_SDF_ALPHA
#include "UnityCG.cginc"
#include "DecalsCommon.cginc"
#include "DecalsSurface.cginc"
#include "SDF.cginc"
#include "StandardDecal.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 DECAL_BUMPMAP
#pragma multi_compile_local __ DECAL_SPECMAP
#pragma multi_compile_local __ DECAL_EMISSIVE
#pragma multi_compile_local __ DECAL_SDF_ALPHA
#include "UnityCG.cginc"
#include "DecalsCommon.cginc"
#include "DecalsSurface.cginc"
#include "SDF.cginc"
#include "StandardDecal.cginc"
ENDCG
}
// shadow casting support
UsePass "Legacy Shaders/VertexLit/SHADOWCASTER"
}
}

View File

@ -1,112 +0,0 @@
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"
}
}

View File

@ -8,6 +8,42 @@ All notable changes to this project will be documented in this file
| website | https://forum.kerbalspaceprogram.com/index.php?/topic/194802-18-111-conformal-decals |
| author | Andrew Cassidy |
## 0.2.14 - 2024-06-25
### Fixed
- Fixed parts showing as all black with the Deferred Rendering mod
## 0.2.13 - 2024-05-17
### Fixed
- Fixed flag decals sometimes not respecting image aspect ratio
## 0.2.12 - 2022-10-31
### Changed
- Updated bundled Shabby to 0.3.0. Does not affect CKAN users
- Made flag aspect ratio overrides configurable with `ASPECTRATIO` nodes in the config. User flags added to Squad/Flags should now be the correct aspect ratio
- All decal aspect ratios can now be overriden with the `aspectRatio` field
### Fixed
- Reverted some changes from last version that were causing issues on launch
## 0.2.11 - 2022-10-30
### Fixed
- PR by LinuxGuruGamer:
- Fixed nullref caused when an entry in `_targets` was null
- Fixed memory leak caused by the OnDestroy() methods not being called due to them being virtual
## 0.2.10 - 2022-03-14
### Fixed

Binary file not shown.

View File

@ -15,7 +15,7 @@ Localization
#LOC_ConformalDecals_gui-opacity = Opacity
#LOC_ConformalDecals_gui-cutoff = Cutoff
#LOC_ConformalDecals_gui-wear = Edge Wear
#LOC_ConformalDecals_gui-multiproject = Project onto Multiple
#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

View File

@ -98,7 +98,7 @@ PART
MODULE {
IDENTIFIER { name = ModuleConformalDecal }
DATA {
KEYWORD { name = DECAL_SDF_ALPHA }
shader = ConformalDecals/Paint/DiffuseSDF
tile = 0, 2, 128, 112
}
}

View File

@ -49,4 +49,309 @@ CONFORMALDECALS {
style = 32
styleMask = 4
}
ASPECTRATIO {
path = Squad/Flags/09
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Flags/Sentinel_Flag
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Flags/blorbs
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Flags/bullseye
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Flags/capsule
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Flags/circles
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Flags/default
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Flags/hexagon
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Flags/hexagonCircles
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Flags/kerbal1
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Flags/kerbal2
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Flags/kerbin
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Flags/kerbinmunflag
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Flags/line
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Flags/minimalistic
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Flags/orbit
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Flags/orbs
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Flags/retro
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Flags/rings
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Flags/rocketScience
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Flags/satellite
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Flags/spheres
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Flags/squadLogo
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Flags/squadLogo2
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Flags/stripes
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Flags/trees
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Flags/trippy
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/FlagsAgency/NASA
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/FlagsAgency/esa_dark_blue
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/FlagsAgency/uk_space_agency
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/FlagsOrganization/B612_Foundation_flag
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/FlagsOrganization/ESA_Arianespace
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/FlagsOrganization/electron
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/FlagsOrganization/rocketlab
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Agencies/C7AerospaceDivision
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Agencies/DinkelsteinKermansConstructionEmporium
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Agencies/ExperimentalEngineering
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Agencies/FlooydResearchLab
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Agencies/GoliathNationalProducts
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Agencies/IntegratedIntegrals
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Agencies/IonicSymphonicProtonicElectronics
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Agencies/JebsJunkyard
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Agencies/KerbalMotion
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Agencies/KerbinWorldFirstRecordKeepingSociety
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Agencies/Kerbodyne
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Agencies/Kerlington
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Agencies/MaxoConstructionToys
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Agencies/MovingPartsExpertsGroup
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Agencies/OMBDemolition
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Agencies/PeriapsisCo
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Agencies/Probodobodyne
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Agencies/R&D
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Agencies/ReactionSystemsLtd
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Agencies/Rockomax
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Agencies/Rokea
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Agencies/SeansCannery
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Agencies/SteadlerEngineeringCorps
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Agencies/StrutCo
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Agencies/Vac-Co
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Agencies/WinterOwl
aspectRatio = 0.625
}
ASPECTRATIO {
path = Squad/Agencies/ZaltonicElectronics
aspectRatio = 0.625
}
}

View File

@ -1,5 +1,5 @@
# Conformal Decals
[![Art: CC BY-SA 4.0](https://img.shields.io/badge/Art%20License-CC%20BY--SA%204.0-orange.svg)](https://creativecommons.org/licenses/by-sa/4.0/) [![Code: GPL v3](https://img.shields.io/badge/Code%20License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
[![Art: CC BY-SA 4.0](https://img.shields.io/badge/Art%20License-CC%20BY--SA%204.0-orange.svg)](https://creativecommons.org/licenses/by-sa/4.0/) [![Code: GPL v3](https://img.shields.io/badge/Code%20License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) [![CKAN: Indexed](https://img.shields.io/badge/CKAN-Indexed-brightgreen.svg)](https://github.com/KSP-CKAN/CKAN)
![Screenshot](http://pileof.rocks/KSP/images/ConformalDecalsHeader.png)
@ -8,7 +8,7 @@ Conformal Decals adds a set of decal stickers to KSP, as well as providing a fra
## Dependencies
Required:
- KSP (1.8.x to 1.10.x)
- KSP (1.8.x to 1.12.x)
- B9 Part Switch (2.18.0). Bundled with release.
- ModuleManager (4.1.4). Bundled with release.
- Shabby (0.2.0 unofficial build). Bundled with release.

View File

@ -1,12 +1,6 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConformalDecals", "ConformalDecals\ConformalDecals.csproj", "{1EA983F9-42E5-494E-9683-FDAC9C9121F4}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C2564402-B081-479B-B723-D5C065BC884E}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
EndProjectSection
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConformalDecals", "ConformalDecals/ConformalDecals.csproj", "{1EA983F9-42E5-494E-9683-FDAC9C9121F4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -19,28 +13,4 @@ Global
{1EA983F9-42E5-494E-9683-FDAC9C9121F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1EA983F9-42E5-494E-9683-FDAC9C9121F4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
Policies = $0
$0.DotNetNamingPolicy = $1
$1.DirectoryNamespaceAssociation = PrefixedHierarchical
$0.TextStylePolicy = $2
$2.FileWidth = 80
$2.TabsToSpaces = True
$2.scope = text/x-csharp
$2.NoTabsAfterNonTabs = True
$2.EolMarker = Unix
$0.CSharpFormattingPolicy = $3
$3.NewLinesForBracesInTypes = False
$3.NewLinesForBracesInMethods = False
$3.NewLinesForBracesInProperties = False
$3.NewLinesForBracesInAccessors = False
$3.NewLinesForBracesInAnonymousMethods = False
$3.NewLinesForBracesInControlBlocks = False
$3.NewLinesForBracesInAnonymousTypes = False
$3.NewLinesForBracesInObjectCollectionArrayInitializers = False
$3.NewLinesForBracesInLambdaExpressionBody = False
$3.scope = text/x-csharp
$3.SpaceAfterCast = True
$0.StandardHeader = $4
EndGlobalSection
EndGlobal

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?><Project Sdk="Microsoft.NET.Sdk">
<?xml version="1.0" ?><Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net48</TargetFramework>
<LangVersion>8</LangVersion>
@ -8,6 +8,11 @@
<AssemblyVersion>9.9.9</AssemblyVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DefineConstants>DEBUG;TRACE;ENABLE_PROFILER</DefineConstants>
<DebugType>portable</DebugType>
</PropertyGroup>
<ItemGroup>
<Reference Include="Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
<HintPath>dlls\Assembly-CSharp.dll</HintPath>
@ -36,23 +41,21 @@
</ItemGroup>
<ItemGroup>
<Compile Remove="dlls\**" />
<Compile Remove="dlls\**"/>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Remove="dlls\**" />
<EmbeddedResource Remove="dlls\**"/>
</ItemGroup>
<ItemGroup>
<None Remove="dlls\**" />
<None Remove="dlls\**"/>
</ItemGroup>
<ItemGroup>
<None Include="..\.editorconfig" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="/bin/cp -v '$(OutDir)ConformalDecals.dll' '$(SolutionDir)../GameData/ConformalDecals/Plugins/ConformalDecals.dll'" IgnoreExitCode="true" />
<Exec Command="/bin/cp -v '$(OutDir)ConformalDecals.dll' '$(SolutionDir)../GameData/ConformalDecals/Plugins/ConformalDecals.dll'" IgnoreExitCode="true"/>
<Exec Command="/bin/cp -v '$(OutDir)ConformalDecals.pdb' '$(SolutionDir)../GameData/ConformalDecals/Plugins/ConformalDecals.pdb'" IgnoreExitCode="true"/>
<!--Fuck you MSBuild stop trying to run CMD.exe on macOS-->
</Target>
</Project>
</Project>

View File

@ -4,6 +4,7 @@ using System.Text.RegularExpressions;
using ConformalDecals.Text;
using ConformalDecals.Util;
using TMPro;
using UniLinq;
using UnityEngine;
namespace ConformalDecals {
@ -14,6 +15,7 @@ namespace ConformalDecals {
private static Dictionary<string, DecalFont> _fontList;
private static int _decalLayer = 31;
private static bool _selectableInFlight;
private static Dictionary<string, float> _aspectRatios;
private struct LegacyShaderEntry {
public string name;
@ -52,6 +54,8 @@ namespace ConformalDecals {
public static IEnumerable<DecalFont> Fonts => _fontList.Values;
public static Dictionary<string, float> AspectRatios => _aspectRatios;
public static bool IsBlacklisted(Shader shader) {
return IsBlacklisted(shader.name);
}
@ -85,8 +89,7 @@ namespace ConformalDecals {
public static DecalFont GetFont(string name) {
if (_fontList.TryGetValue(name, out var font)) {
return font;
}
else {
} else {
throw new KeyNotFoundException($"Font {name} not found");
}
}
@ -110,10 +113,19 @@ namespace ConformalDecals {
foreach (var fontNode in node.GetNodes("FONT")) {
try {
var font = DecalFont.Parse(fontNode, allFonts);
var font = new DecalFont(fontNode, allFonts);
_fontList.Add(font.Name, font);
} catch (Exception e) {
Debug.LogException(e);
}
catch (Exception e) {
}
foreach (var ratioNode in node.GetNodes("ASPECTRATIO")) {
try {
var path = ParseUtil.ParseString(ratioNode, "path");
var ratio = ParseUtil.ParseFloat(ratioNode, "aspectRatio");
_aspectRatios[path] = ratio;
} catch (Exception e) {
Debug.LogException(e);
}
}
@ -127,8 +139,12 @@ namespace ConformalDecals {
var colors = new[] {color, color, color, color};
var tex = new Texture2D(width, height, TextureFormat.RGBA32, false);
for (var x = 0; x <= width; x++) {
for (var y = 0; y < height; y++) {
tex.SetPixels32(colors);
}
}
tex.SetPixels32(colors);
tex.Apply();
return tex;
@ -139,6 +155,7 @@ namespace ConformalDecals {
_shaderBlacklist = new List<string>();
_shaderRegexBlacklist = new List<Regex>();
_fontList = new Dictionary<string, DecalFont>();
_aspectRatios = new Dictionary<string, float>();
var configs = GameDatabase.Instance.GetConfigs("CONFORMALDECALS");
@ -160,4 +177,4 @@ namespace ConformalDecals {
_blankNormal = MakeBlankNormal();
}
}
}
}

View File

@ -8,7 +8,7 @@ namespace ConformalDecals.MaterialProperties {
public override void ParseNode(ConfigNode node) {
base.ParseNode(node);
value = ParseUtil.ParseBool(node, "value", true, true);
ParseUtil.ParseBoolIndirect(ref value, node, "value");
}
public override void Modify(Material material) {

View File

@ -45,6 +45,7 @@ namespace ConformalDecals.MaterialProperties {
_decalMaterial.SetInt(DecalPropertyIDs._Cull, (int) CullMode.Off);
_decalMaterial.SetInt(DecalPropertyIDs._ZWrite, 0);
_decalMaterial.renderQueue = RenderQueue;
_decalMaterial.SetShaderPassEnabled("SHADOWCASTER", false);
}
return _decalMaterial;
@ -59,6 +60,7 @@ namespace ConformalDecals.MaterialProperties {
_previewMaterial.EnableKeyword("DECAL_PREVIEW");
_previewMaterial.SetInt(DecalPropertyIDs._Cull, (int) CullMode.Back);
_previewMaterial.SetInt(DecalPropertyIDs._ZWrite, 1);
_previewMaterial.SetShaderPassEnabled("DEFERRED_PREPASS", false);
}
return _previewMaterial;
@ -75,7 +77,12 @@ namespace ConformalDecals.MaterialProperties {
}
}
public float AspectRatio => MainTexture == null ? 1 : MainTexture.AspectRatio;
public float AspectRatio {
get => MainTexture == null ? 1 : MainTexture.AspectRatio;
set {
if (MainTexture != null) MainTexture.AspectRatio = value;
}
}
public void OnBeforeSerialize() {
if (_materialProperties == null) throw new SerializationException("Tried to serialize an uninitialized MaterialPropertyCollection");
@ -95,7 +102,7 @@ namespace ConformalDecals.MaterialProperties {
var property = MaterialProperty.Instantiate(_serializedProperties[i]);
_materialProperties.Add(_serializedNames[i], property);
if (property is MaterialTextureProperty {isMain: true} textureProperty) {
if (property is MaterialTextureProperty textureProperty && textureProperty.isMain) {
_mainTexture = textureProperty;
}
}
@ -105,28 +112,6 @@ namespace ConformalDecals.MaterialProperties {
_materialProperties ??= new Dictionary<string, MaterialProperty>();
}
public void Load(ConfigNode node) {
// add keyword nodes
foreach (var keywordNode in node.GetNodes("KEYWORD")) {
ParseProperty<MaterialKeywordProperty>(keywordNode);
}
// add texture nodes
foreach (var textureNode in node.GetNodes("TEXTURE")) {
ParseProperty<MaterialTextureProperty>(textureNode);
}
// add float nodes
foreach (var floatNode in node.GetNodes("FLOAT")) {
ParseProperty<MaterialTextureProperty>(floatNode);
}
// add color nodes
foreach (var colorNode in node.GetNodes("COLOR")) {
ParseProperty<MaterialColorProperty>(colorNode);
}
}
public void OnDestroy() {
if (_decalMaterial != null) Destroy(_decalMaterial);
if (_previewMaterial != null) Destroy(_previewMaterial);
@ -158,8 +143,7 @@ namespace ConformalDecals.MaterialProperties {
public T GetProperty<T>(string propertyName) where T : MaterialProperty {
if (_materialProperties.ContainsKey(propertyName) && _materialProperties[propertyName] is T property) {
return property;
}
else {
} else {
return null;
}
}
@ -167,8 +151,7 @@ namespace ConformalDecals.MaterialProperties {
public T AddOrGetProperty<T>(string propertyName) where T : MaterialProperty {
if (_materialProperties.ContainsKey(propertyName) && _materialProperties[propertyName] is T property) {
return property;
}
else {
} else {
return AddProperty<T>(propertyName);
}
}
@ -178,9 +161,10 @@ namespace ConformalDecals.MaterialProperties {
foreach (var material in Materials) {
property.Remove(material);
}
_materialProperties.Remove(propertyName);
Destroy(property);
return true;
}
@ -214,7 +198,7 @@ namespace ConformalDecals.MaterialProperties {
var newProperty = AddOrGetProperty<T>(propertyName);
newProperty.ParseNode(node);
if (newProperty is MaterialTextureProperty {isMain: true} textureProperty) {
if (newProperty is MaterialTextureProperty textureProperty && textureProperty.isMain) {
_mainTexture = textureProperty;
}
@ -226,8 +210,7 @@ namespace ConformalDecals.MaterialProperties {
if (_shader == null) {
Logging.Log("Using default decal shader");
shaderName = "ConformalDecals/Decal/Standard";
}
else {
} else {
return;
}
}
@ -252,7 +235,7 @@ namespace ConformalDecals.MaterialProperties {
public void UpdateScale(Vector2 scale) {
foreach (var entry in _materialProperties) {
if (entry.Value is MaterialTextureProperty {autoScale: true} textureProperty) {
if (entry.Value is MaterialTextureProperty textureProperty && textureProperty.autoScale) {
textureProperty.SetScale(scale);
}
}
@ -263,7 +246,7 @@ namespace ConformalDecals.MaterialProperties {
var mainTexSize = _mainTexture.Dimensions;
foreach (var entry in _materialProperties) {
if (entry.Value is MaterialTextureProperty {autoTile: true} textureProperty) {
if (entry.Value is MaterialTextureProperty textureProperty && textureProperty.autoTile) {
textureProperty.SetTile(tile, mainTexSize);
}
}
@ -280,6 +263,20 @@ namespace ConformalDecals.MaterialProperties {
UpdateTile(tile);
}
public void SetOpacity(float opacity) {
DecalMaterial.SetFloat(DecalPropertyIDs._DecalOpacity, opacity);
PreviewMaterial.SetFloat(DecalPropertyIDs._DecalOpacity, opacity);
}
public void SetCutoff(float cutoff) {
DecalMaterial.SetFloat(DecalPropertyIDs._Cutoff, cutoff);
PreviewMaterial.SetFloat(DecalPropertyIDs._Cutoff, cutoff);
}
public void SetWear(float wear) {
DecalMaterial.SetFloat(DecalPropertyIDs._EdgeWearStrength, wear);
}
public void UpdateMaterials() {
foreach (var material in Materials) {
UpdateMaterial(material);

View File

@ -19,6 +19,8 @@ namespace ConformalDecals.MaterialProperties {
[SerializeField] private Vector2 _textureOffset;
[SerializeField] private Vector2 _textureScale = Vector2.one;
[SerializeField] private float _aspectRatioOverride = -1.0f;
public Texture2D Texture {
get => _texture;
set => _texture = value;
@ -27,7 +29,6 @@ namespace ConformalDecals.MaterialProperties {
public string TextureUrl {
get => _textureUrl;
set {
if (_textureUrl == value) return; // URL hasnt changed
_texture = LoadTexture(value, isNormal);
_textureUrl = value;
}
@ -44,10 +45,12 @@ namespace ConformalDecals.MaterialProperties {
public float AspectRatio {
get {
if (_aspectRatioOverride > 0) return _aspectRatioOverride;
if (_texture == null) return 1;
if (_textureUrl?.Contains("Squad/Flags") == true) return 0.625f; // squad flags are slightly stretched, so unstretch them
return MaskedHeight / (float) MaskedWidth;
}
set => _aspectRatioOverride = value;
}
public override void ParseNode(ConfigNode node) {
@ -65,7 +68,7 @@ namespace ConformalDecals.MaterialProperties {
if (ParseUtil.ParseStringIndirect(ref _textureUrl, node, "textureUrl")) {
_texture = LoadTexture(_textureUrl, isNormal);
}
if (_texture == null) {
_texture = isNormal ? DecalConfig.BlankNormal : Texture2D.whiteTexture;
}

View File

@ -2,9 +2,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using ConformalDecals.MaterialProperties;
using ConformalDecals.Targets;
using ConformalDecals.Util;
using UniLinq;
using UnityEngine;
namespace ConformalDecals {
@ -37,6 +35,8 @@ namespace ConformalDecals {
[KSPField] public DecalScaleMode scaleMode = DecalScaleMode.HEIGHT;
[KSPField] public float aspectRatio = -1.0f; // < 0 = use texture
[KSPField] public bool depthAdjustable = true;
[KSPField] public float defaultDepth = 0.1f;
[KSPField] public Vector2 depthRange = new Vector2(0, 2);
@ -73,23 +73,15 @@ namespace ConformalDecals {
UI_FloatRange()]
public float opacity = 1.0f;
private MaterialFloatProperty _opacityProperty;
[KSPField(guiName = "#LOC_ConformalDecals_gui-cutoff", guiActive = false, guiActiveEditor = true, isPersistant = true, guiFormat = "P0"),
UI_FloatRange()]
public float cutoff = 0.5f;
private MaterialFloatProperty _cutoffProperty;
[KSPField(guiName = "#LOC_ConformalDecals_gui-wear", guiActive = false, guiActiveEditor = true, isPersistant = true, guiFormat = "F0"),
UI_FloatRange()]
public float wear = 100;
private MaterialFloatProperty _wearProperty;
[KSPField(guiName = "#LOC_ConformalDecals_gui-multiproject", guiActive = false, guiActiveEditor = true, isPersistant = true),
UI_Toggle()]
public bool projectMultiple = true;
[KSPField(isPersistant = true)] public bool projectMultiple; // reserved for future features. do not modify
[KSPField] public MaterialPropertyCollection materialProperties;
@ -106,7 +98,7 @@ namespace ConformalDecals {
private const int DecalQueueMax = 2400;
private static int _decalQueueCounter = -1;
private readonly Dictionary<Part, ProjectionPartTarget> _targets = new Dictionary<Part, ProjectionPartTarget>();
private List<ProjectionTarget> _targets;
private bool _isAttached;
private Matrix4x4 _orthoMatrix;
@ -126,8 +118,6 @@ namespace ConformalDecals {
}
}
// EVENTS
/// <inheritdoc />
public override void OnAwake() {
base.OnAwake();
@ -137,244 +127,12 @@ namespace ConformalDecals {
} else {
materialProperties = ScriptableObject.Instantiate(materialProperties);
}
_opacityProperty = materialProperties.AddOrGetProperty<MaterialFloatProperty>("_DecalOpacity");
_cutoffProperty = materialProperties.AddOrGetProperty<MaterialFloatProperty>("_Cutoff");
_wearProperty = materialProperties.AddOrGetProperty<MaterialFloatProperty>("_EdgeWearStrength");
}
/// <inheritdoc />
public override void OnLoad(ConfigNode node) {
// Load
try {
LoadDecal(node);
} catch (Exception e) {
this.LogException("Error loading decal", e);
}
// Setup
try {
SetupDecal();
} catch (Exception e) {
this.LogException("Error setting up decal", e);
}
}
/// <inheritdoc />
public override void OnIconCreate() {
UpdateAll();
}
/// <inheritdoc />
public override void OnStart(StartState state) {
materialProperties.RenderQueue = DecalQueue;
_boundsRenderer = decalProjectorTransform.GetComponent<MeshRenderer>();
// handle tweakables
if (HighLogic.LoadedSceneIsEditor) {
GameEvents.onEditorPartEvent.Add(OnEditorEvent);
GameEvents.onVariantApplied.Add(OnVariantApplied);
UpdateTweakables();
}
}
/// Called after OnStart is finished for all parts
/// This is mostly used to make sure all B9 variants are already in place for the rest of the vessel
public override void OnStartFinished(StartState state) {
// handle game events
if (HighLogic.LoadedSceneIsGame) {
// set initial attachment state
if (part.parent == null) {
OnDetach();
} else {
OnAttach();
}
}
// handle flight events
if (HighLogic.LoadedSceneIsFlight) {
GameEvents.onPartWillDie.Add(OnPartWillDie);
if (part.parent == null) part.explode();
Part.layerMask |= 1 << DecalConfig.DecalLayer;
decalColliderTransform.gameObject.layer = DecalConfig.DecalLayer;
if (!selectableInFlight || !DecalConfig.SelectableInFlight) {
decalColliderTransform.GetComponent<Collider>().enabled = false;
_boundsRenderer.enabled = false;
}
}
}
/// Called by Unity when the decal is destroyed
public virtual void OnDestroy() {
// remove GameEvents
if (HighLogic.LoadedSceneIsEditor) {
GameEvents.onEditorPartEvent.Remove(OnEditorEvent);
GameEvents.onVariantApplied.Remove(OnVariantApplied);
}
if (HighLogic.LoadedSceneIsFlight) {
GameEvents.onPartWillDie.Remove(OnPartWillDie);
}
// remove from preCull delegate
Camera.onPreCull -= Render;
// destroy material properties object
Destroy(materialProperties);
}
/// Called when the decal's projection and scale is modified through a tweakable
protected void OnProjectionTweakEvent(BaseField field, object obj) {
// scale or depth values have been changed, so update scale
// and update projection matrices if attached
UpdateProjection();
foreach (var counterpart in part.symmetryCounterparts) {
var decal = counterpart.GetComponent<ModuleConformalDecal>();
decal.UpdateProjection();
}
}
/// Called when the decal's material is modified through a tweakable
protected void OnMaterialTweakEvent(BaseField field, object obj) {
UpdateMaterials();
foreach (var counterpart in part.symmetryCounterparts) {
var decal = counterpart.GetComponent<ModuleConformalDecal>();
decal.UpdateMaterials();
}
}
/// Called by KSP when a new variant is applied in the editor
protected void OnVariantApplied(Part eventPart, PartVariant variant) {
if (_isAttached && eventPart != null && (projectMultiple || eventPart == part.parent)) {
_targets.Remove(eventPart);
UpdateProjection();
}
}
/// Called by KSP when an editor event occurs
protected void OnEditorEvent(ConstructionEventType eventType, Part eventPart) {
switch (eventType) {
case ConstructionEventType.PartAttached:
OnPartAttached(eventPart);
break;
case ConstructionEventType.PartDetached:
OnPartDetached(eventPart);
break;
case ConstructionEventType.PartOffsetting:
case ConstructionEventType.PartRotating:
OnPartTransformed(eventPart);
break;
}
}
/// Called when a part is transformed in the editor
protected void OnPartTransformed(Part eventPart, bool firstcall = true) {
if (part == eventPart || (firstcall && part.symmetryCounterparts.Contains(eventPart))) {
// if this is the top-level call (original event part is a decal) then update symmetry counterparts, otherwise just update this
UpdateProjection();
} else if (_isAttached) {
UpdatePartTarget(eventPart, _boundsRenderer.bounds);
// recursively call for child parts
foreach (var child in eventPart.children) {
OnPartTransformed(child, false);
}
}
}
/// Called when a part is attached in the editor
protected void OnPartAttached(Part eventPart, bool firstcall = true) {
if (part == eventPart || (firstcall && part.symmetryCounterparts.Contains(eventPart))) {
// if this is the top-level call (original event part is a decal) then update symmetry counterparts, otherwise just update this
OnAttach();
} else {
UpdatePartTarget(eventPart, _boundsRenderer.bounds);
// recursively call for child parts
foreach (var child in eventPart.children) {
OnPartAttached(child, false);
}
}
}
/// Called when a part is detached in the editor
protected void OnPartDetached(Part eventPart, bool firstcall = true) {
if (part == eventPart || (firstcall && part.symmetryCounterparts.Contains(eventPart))) {
// if this is the top-level call (original event part is a decal) then update symmetry counterparts, otherwise just update this
OnDetach();
} else if (_isAttached) {
_targets.Remove(eventPart);
// recursively call for child parts
foreach (var child in eventPart.children) {
OnPartDetached(child, false);
}
}
}
/// Called when part `willDie` will be destroyed
protected void OnPartWillDie(Part willDie) {
if (willDie == part.parent && willDie != null) {
this.Log("Parent part about to be destroyed! Killing decal part.");
part.Die();
} else if (_isAttached && projectMultiple) {
_targets.Remove(willDie);
}
}
/// Called when decal is attached to a new part
protected virtual void OnAttach() {
if (_isAttached) return;
if (part.parent == null) {
this.LogError("Attach function called but part has no parent!");
_isAttached = false;
return;
}
_isAttached = true;
_targets.Clear();
// hide model
decalModelTransform.gameObject.SetActive(false);
// unhide projector
decalProjectorTransform.gameObject.SetActive(true);
// add to preCull delegate
Camera.onPreCull += Render;
UpdateMaterials();
UpdateProjection();
}
/// Called when decal is detached from its parent part
protected virtual void OnDetach() {
if (!_isAttached) return;
_isAttached = false;
// unhide model
decalModelTransform.gameObject.SetActive(true);
// hide projector
decalProjectorTransform.gameObject.SetActive(false);
// remove from preCull delegate
Camera.onPreCull -= Render;
UpdateMaterials();
UpdateProjection();
}
// FUNCTIONS
/// Load any settings from the decal config
protected virtual void LoadDecal(ConfigNode node) {
// PARSE TRANSFORMS
if (!HighLogic.LoadedSceneIsGame) {
// SETUP TRANSFORMS
decalFrontTransform = part.FindModelTransform(decalFront);
if (decalFrontTransform == null) throw new FormatException($"Could not find decalFront transform: '{decalFront}'.");
@ -406,37 +164,65 @@ namespace ConformalDecals {
}
}
}
// PARSE MATERIAL PROPERTIES
// set shader
materialProperties.SetShader(shader);
materialProperties.AddOrGetProperty<MaterialKeywordProperty>("DECAL_BASE_NORMAL").value = useBaseNormal;
// add keyword nodes
foreach (var keywordNode in node.GetNodes("KEYWORD")) {
materialProperties.ParseProperty<MaterialKeywordProperty>(keywordNode);
}
// add texture nodes
foreach (var textureNode in node.GetNodes("TEXTURE")) {
materialProperties.ParseProperty<MaterialTextureProperty>(textureNode);
}
// add float nodes
foreach (var floatNode in node.GetNodes("FLOAT")) {
materialProperties.ParseProperty<MaterialTextureProperty>(floatNode);
}
// add color nodes
foreach (var colorNode in node.GetNodes("COLOR")) {
materialProperties.ParseProperty<MaterialColorProperty>(colorNode);
}
// handle texture tiling parameters
var tileString = node.GetValue("tile");
if (!string.IsNullOrEmpty(tileString)) {
var tileValid = ParseExtensions.TryParseRect(tileString, out tileRect);
if (!tileValid) throw new FormatException($"Invalid rect value for tile '{tileString}'");
}
if (tileRect.x >= 0) {
materialProperties.UpdateTile(tileRect);
} else if (tileIndex >= 0) {
materialProperties.UpdateTile(tileIndex, tileSize);
}
// handle aspect ratio overrides
materialProperties.AspectRatio = aspectRatio;
} catch (Exception e) {
this.LogException("Exception parsing partmodule", e);
}
// PARSE MATERIAL PROPERTIES
// set shader
materialProperties.SetShader(shader);
materialProperties.AddOrGetProperty<MaterialKeywordProperty>("DECAL_BASE_NORMAL").value = useBaseNormal;
materialProperties.Load(node);
UpdateMaterials();
// handle texture tiling parameters
var tileString = node.GetValue("tile");
if (!string.IsNullOrEmpty(tileString)) {
var tileValid = ParseExtensions.TryParseRect(tileString, out tileRect);
if (!tileValid) throw new FormatException($"Invalid rect value for tile '{tileString}'");
foreach (var keyword in _decalMaterial.shaderKeywords) {
this.Log($"keyword: {keyword}");
}
if (tileRect.x >= 0) {
materialProperties.UpdateTile(tileRect);
} else if (tileIndex >= 0) {
materialProperties.UpdateTile(tileIndex, tileSize);
}
}
/// Setup decal by calling update functions relevent for the current situation
protected virtual void SetupDecal() {
if (HighLogic.LoadedSceneIsEditor) {
// Update tweakables in editor mode
UpdateTweakables();
}
if (HighLogic.LoadedSceneIsGame) {
UpdateAll();
UpdateScale();
} else {
scale = defaultScale;
depth = defaultDepth;
@ -444,14 +230,283 @@ namespace ConformalDecals {
cutoff = defaultCutoff;
wear = defaultWear;
UpdateAll();
// QUEUE PART FOR ICON FIXING IN VAB
DecalIconFixer.QueuePart(part.name);
}
}
/// Update decal editor tweakables
/// <inheritdoc />
public override void OnIconCreate() {
UpdateScale();
}
/// <inheritdoc />
public override void OnStart(StartState state) {
materialProperties.RenderQueue = DecalQueue;
_boundsRenderer = decalProjectorTransform.GetComponent<MeshRenderer>();
// handle tweakables
if (HighLogic.LoadedSceneIsEditor) {
GameEvents.onEditorPartEvent.Add(OnEditorEvent);
GameEvents.onVariantApplied.Add(OnVariantApplied);
GameEvents.onEditorUndo.Add(OnEditorUndo);
UpdateTweakables();
}
}
public override void OnStartFinished(StartState state) {
// handle game events
if (HighLogic.LoadedSceneIsGame) {
// set initial attachment state
if (part.parent == null) {
OnDetach();
} else {
OnAttach();
}
}
// handle flight events
if (HighLogic.LoadedSceneIsFlight) {
GameEvents.onPartWillDie.Add(OnPartWillDie);
if (part.parent == null) part.explode();
Part.layerMask |= 1 << DecalConfig.DecalLayer;
decalColliderTransform.gameObject.layer = DecalConfig.DecalLayer;
if (!selectableInFlight || !DecalConfig.SelectableInFlight) {
decalColliderTransform.GetComponent<Collider>().enabled = false;
_boundsRenderer.enabled = false;
}
}
}
public virtual void OnDestroy() {
// remove GameEvents
if (HighLogic.LoadedSceneIsEditor) {
GameEvents.onEditorPartEvent.Remove(OnEditorEvent);
GameEvents.onVariantApplied.Remove(OnVariantApplied);
GameEvents.onEditorUndo.Remove(OnEditorUndo);
}
if (HighLogic.LoadedSceneIsFlight) {
GameEvents.onPartWillDie.Remove(OnPartWillDie);
}
// remove from preCull delegate
Camera.onPreCull -= Render;
// destroy material properties object
Destroy(materialProperties);
}
protected void OnSizeTweakEvent(BaseField field, object obj) {
// scale or depth values have been changed, so update scale
// and update projection matrices if attached
UpdateScale();
foreach (var counterpart in part.symmetryCounterparts) {
var decal = counterpart.GetComponent<ModuleConformalDecal>();
decal.UpdateScale();
}
}
protected void OnMaterialTweakEvent(BaseField field, object obj) {
materialProperties.SetOpacity(opacity);
materialProperties.SetCutoff(cutoff);
if (useBaseNormal) {
materialProperties.SetWear(wear);
}
foreach (var counterpart in part.symmetryCounterparts) {
var decal = counterpart.GetComponent<ModuleConformalDecal>();
decal.materialProperties.SetOpacity(opacity);
decal.materialProperties.SetCutoff(cutoff);
if (useBaseNormal) {
decal.materialProperties.SetWear(wear);
}
}
}
protected void OnVariantApplied(Part eventPart, PartVariant variant) {
if (_isAttached && eventPart == part.parent) {
UpdateTargets();
}
}
protected void OnEditorEvent(ConstructionEventType eventType, Part eventPart) {
if (this.part != eventPart && !part.symmetryCounterparts.Contains(eventPart)) return;
switch (eventType) {
case ConstructionEventType.PartAttached:
OnAttach();
break;
case ConstructionEventType.PartDetached:
OnDetach();
break;
case ConstructionEventType.PartOffsetting:
case ConstructionEventType.PartRotating:
UpdateScale();
break;
}
}
protected void OnEditorUndo(ShipConstruct sc) {
UpdateTargets();
}
protected void OnPartWillDie(Part willDie) {
if (willDie == part.parent) {
this.Log("Parent part about to be destroyed! Killing decal part.");
part.Die();
}
}
protected virtual void OnAttach() {
if (part.parent == null) {
this.LogError("Attach function called but part has no parent!");
_isAttached = false;
return;
}
_isAttached = true;
// hide model
decalModelTransform.gameObject.SetActive(false);
// unhide projector
decalProjectorTransform.gameObject.SetActive(true);
// add to preCull delegate
Camera.onPreCull += Render;
UpdateMaterials();
UpdateTargets();
UpdateScale();
}
protected virtual void OnDetach() {
_isAttached = false;
// unhide model
decalModelTransform.gameObject.SetActive(true);
// hide projector
decalProjectorTransform.gameObject.SetActive(false);
// remove from preCull delegate
Camera.onPreCull -= Render;
UpdateMaterials();
UpdateScale();
}
protected void UpdateScale() {
scale = Mathf.Max(0.01f, scale);
depth = Mathf.Max(0.01f, depth);
var sizeRatio = Mathf.Max(0.01f, materialProperties.AspectRatio);
Vector2 size;
switch (scaleMode) {
default:
case DecalScaleMode.HEIGHT:
size = new Vector2(scale / sizeRatio, scale);
break;
case DecalScaleMode.WIDTH:
size = new Vector2(scale, scale * sizeRatio);
break;
case DecalScaleMode.AVERAGE:
var width1 = 2 * scale / (1 + sizeRatio);
size = new Vector2(width1, width1 * sizeRatio);
break;
case DecalScaleMode.AREA:
var width2 = Mathf.Sqrt(scale / sizeRatio);
size = new Vector2(width2, width2 * sizeRatio);
break;
case DecalScaleMode.MINIMUM:
if (sizeRatio > 1) goto case DecalScaleMode.WIDTH;
else goto case DecalScaleMode.HEIGHT;
case DecalScaleMode.MAXIMUM:
if (sizeRatio > 1) goto case DecalScaleMode.HEIGHT;
else goto case DecalScaleMode.WIDTH;
}
// update material scale
materialProperties.UpdateScale(size);
if (_isAttached) {
// update orthogonal matrix
_orthoMatrix = Matrix4x4.identity;
_orthoMatrix[0, 3] = 0.5f;
_orthoMatrix[1, 3] = 0.5f;
decalProjectorTransform.localScale = new Vector3(size.x, size.y, depth);
// update projection
foreach (var target in _targets) {
if (target.target == null) {
_targets.Remove(target);
} else {
target.Project(_orthoMatrix, decalProjectorTransform, _boundsRenderer.bounds, useBaseNormal);
}
}
} else {
// rescale preview model
decalModelTransform.localScale = new Vector3(size.x, size.y, (size.x + size.y) / 2);
// update back material scale
if (updateBackScale) {
backMaterial.SetTextureScale(PropertyIDs._MainTex, new Vector2(size.x * backTextureBaseScale.x, size.y * backTextureBaseScale.y));
}
}
}
protected virtual void UpdateMaterials() {
materialProperties.UpdateMaterials();
materialProperties.SetOpacity(opacity);
materialProperties.SetCutoff(cutoff);
if (useBaseNormal) {
materialProperties.SetWear(wear);
}
_decalMaterial = materialProperties.DecalMaterial;
_previewMaterial = materialProperties.PreviewMaterial;
if (!_isAttached) decalFrontTransform.GetComponent<MeshRenderer>().material = _previewMaterial;
}
protected void UpdateTargets() {
if (_targets == null) {
_targets = new List<ProjectionTarget>();
} else {
_targets.Clear();
}
// find all valid renderers
var renderers = part.parent.FindModelComponents<MeshRenderer>();
foreach (var renderer in renderers) {
// skip disabled renderers
if (renderer.gameObject.activeInHierarchy == false) continue;
// skip blacklisted shaders
if (DecalConfig.IsBlacklisted(renderer.material.shader)) continue;
var meshFilter = renderer.GetComponent<MeshFilter>();
if (meshFilter == null) continue; // object has a meshRenderer with no filter, invalid
var mesh = meshFilter.mesh;
if (mesh == null) continue; // object has a null mesh, invalid
// create new ProjectionTarget to represent the renderer
var target = new ProjectionTarget(renderer, mesh);
// add the target to the list
_targets.Add(target);
}
}
protected virtual void UpdateTweakables() {
// setup tweakable fields
var scaleField = Fields[nameof(scale)];
@ -459,7 +514,6 @@ namespace ConformalDecals {
var opacityField = Fields[nameof(opacity)];
var cutoffField = Fields[nameof(cutoff)];
var wearField = Fields[nameof(wear)];
var multiprojectField = Fields[nameof(projectMultiple)];
scaleField.guiActiveEditor = scaleAdjustable;
depthField.guiActiveEditor = depthAdjustable;
@ -477,7 +531,7 @@ namespace ConformalDecals {
scaleEditor.minValue = minValue;
scaleEditor.maxValue = maxValue;
scaleEditor.stepIncrement = 0.01f; //1cm
scaleEditor.onFieldChanged = OnProjectionTweakEvent;
scaleEditor.onFieldChanged = OnSizeTweakEvent;
}
if (depthAdjustable) {
@ -488,7 +542,7 @@ namespace ConformalDecals {
depthEditor.minValue = minValue;
depthEditor.maxValue = maxValue;
depthEditor.stepIncrement = 0.01f; //1cm
depthEditor.onFieldChanged = OnProjectionTweakEvent;
depthEditor.onFieldChanged = OnSizeTweakEvent;
}
if (opacityAdjustable) {
@ -525,128 +579,15 @@ namespace ConformalDecals {
wearEditor.stepIncrement = (maxValue - minValue) / steps;
wearEditor.onFieldChanged = OnMaterialTweakEvent;
}
var multiprojectEditor = (UI_Toggle) multiprojectField.uiControlEditor;
multiprojectEditor.onFieldChanged = OnProjectionTweakEvent;
}
/// Updates materials, scale and targets
protected virtual void UpdateAll() {
UpdateMaterials();
UpdateProjection();
}
/// Update decal materials
protected virtual void UpdateMaterials() {
_opacityProperty.value = opacity;
_cutoffProperty.value = cutoff;
_wearProperty.value = wear;
materialProperties.UpdateMaterials();
_decalMaterial = materialProperties.DecalMaterial;
_previewMaterial = materialProperties.PreviewMaterial;
if (!_isAttached) decalFrontTransform.GetComponent<MeshRenderer>().material = _previewMaterial;
}
/// Update decal scale and projection
protected void UpdateProjection() {
// Update scale and depth
scale = Mathf.Max(0.01f, scale);
depth = Mathf.Max(0.01f, depth);
var aspectRatio = Mathf.Max(0.01f, materialProperties.AspectRatio);
Vector2 size;
switch (scaleMode) {
default:
case DecalScaleMode.HEIGHT:
size = new Vector2(scale / aspectRatio, scale);
break;
case DecalScaleMode.WIDTH:
size = new Vector2(scale, scale * aspectRatio);
break;
case DecalScaleMode.AVERAGE:
var width1 = 2 * scale / (1 + aspectRatio);
size = new Vector2(width1, width1 * aspectRatio);
break;
case DecalScaleMode.AREA:
var width2 = Mathf.Sqrt(scale / aspectRatio);
size = new Vector2(width2, width2 * aspectRatio);
break;
case DecalScaleMode.MINIMUM:
if (aspectRatio > 1) goto case DecalScaleMode.WIDTH;
else goto case DecalScaleMode.HEIGHT;
case DecalScaleMode.MAXIMUM:
if (aspectRatio > 1) goto case DecalScaleMode.HEIGHT;
else goto case DecalScaleMode.WIDTH;
}
// update material scale
materialProperties.UpdateScale(size);
decalProjectorTransform.localScale = new Vector3(size.x, size.y, depth);
if (_isAttached) {
// update orthogonal matrix
_orthoMatrix = Matrix4x4.identity;
_orthoMatrix[0, 3] = 0.5f;
_orthoMatrix[1, 3] = 0.5f;
var projectionBounds = _boundsRenderer.bounds;
// disable all targets
foreach (var target in _targets.Values) {
target.enabled = false;
}
// collect list of potential targets
IEnumerable<Part> targetParts;
if (projectMultiple) {
targetParts = HighLogic.LoadedSceneIsFlight ? part.vessel.parts : EditorLogic.fetch.ship.parts;
} else {
targetParts = new[] {part.parent};
}
foreach (var targetPart in targetParts) {
UpdatePartTarget(targetPart, projectionBounds);
}
} else {
// rescale preview model
decalModelTransform.localScale = new Vector3(size.x, size.y, (size.x + size.y) / 2);
// update back material scale
if (updateBackScale) {
backMaterial.SetTextureScale(PropertyIDs._MainTex, new Vector2(size.x * backTextureBaseScale.x, size.y * backTextureBaseScale.y));
}
}
}
protected void UpdatePartTarget(Part targetPart, Bounds projectionBounds) {
if (targetPart.GetComponent<ModuleConformalDecal>() != null) return; // skip other decals
if (!_targets.TryGetValue(targetPart, out var target)) {
var rendererList = targetPart.FindModelComponents<MeshRenderer>();
if (rendererList.Any(o => projectionBounds.Intersects(o.bounds))) {
target = new ProjectionPartTarget(targetPart, useBaseNormal);
_targets.Add(targetPart, target);
} else {
return;
}
}
target.Project(_orthoMatrix, decalProjectorTransform, projectionBounds);
}
/// Render the decal
public void Render(Camera camera) {
if (!_isAttached) return;
// render on each target object
foreach (var target in _targets.Values) {
target.Render(_decalMaterial, part.mpb, camera);
foreach (var target in _targets) {
if (ReferenceEquals(target.target, null)) _targets.Remove(target);
else target.Render(_decalMaterial, part.mpb, camera);
}
}
}

View File

@ -1,3 +1,4 @@
using ConformalDecals.Util;
using UnityEngine;
namespace ConformalDecals {
@ -8,6 +9,7 @@ namespace ConformalDecals {
[KSPField(isPersistant = true)] public bool useCustomFlag;
// The URL of the flag for the current mission or agency
public string MissionFlagUrl {
get {
if (HighLogic.LoadedSceneIsEditor) {
@ -23,16 +25,28 @@ namespace ConformalDecals {
}
}
public override void OnLoad(ConfigNode node) {
base.OnLoad(node);
// Since OnLoad is called for all modules, we only need to update this module
// Updating symmetry counterparts would be redundent
UpdateFlag();
}
public override void OnStart(StartState state) {
base.OnStart(state);
if (HighLogic.LoadedSceneIsEditor) {
// Register flag change event
GameEvents.onMissionFlagSelect.Add(OnEditorFlagSelected);
// Register reset button event
Events[nameof(ResetFlagButton)].guiActiveEditor = useCustomFlag;
Events[nameof(ResetFlag)].guiActiveEditor = useCustomFlag;
}
base.OnStart(state);
// Since OnStart is called for all modules, we only need to update this module
// Updating symmetry counterparts would be redundent
UpdateFlag();
}
public override void OnDestroy() {
@ -45,7 +59,7 @@ namespace ConformalDecals {
}
[KSPEvent(guiActive = false, guiActiveEditor = true, guiName = "#LOC_ConformalDecals_gui-select-flag")]
public void SelectFlagButton() {
public void SelectFlag() {
// Button for selecting a flag
// This is a bit of a hack to bring up the stock flag selection menu
// When its done, it calls OnCustomFlagSelected()
@ -56,65 +70,67 @@ namespace ConformalDecals {
}
[KSPEvent(guiActive = false, guiActiveEditor = true, guiName = "#LOC_ConformalDecals_gui-reset-flag")]
public void ResetFlagButton() {
public void ResetFlag() {
// we are no longer using a custom flag, so instead use the mission or agency flag
SetFlag("Mission", false, true);
useCustomFlag = false;
flagUrl = "Mission";
UpdateFlag(true);
// disable the reset button, since it no longer makes sense
Events[nameof(ResetFlagButton)].guiActiveEditor = false;
Events[nameof(ResetFlag)].guiActiveEditor = false;
}
private void OnCustomFlagSelected(FlagBrowser.FlagEntry newFlagEntry) {
// Callback for when a flag is selected in the menu spawned by SelectFlag()
// we are now using a custom flag with the URL of the new flag entry
SetFlag(newFlagEntry.textureInfo.name, true, true);
useCustomFlag = true;
flagUrl = newFlagEntry.textureInfo.name;
UpdateFlag(true);
// make sure the reset button is now available
Events[nameof(ResetFlagButton)].guiActiveEditor = true;
Events[nameof(ResetFlag)].guiActiveEditor = true;
}
private void OnEditorFlagSelected(string newFlagUrl) {
// Callback for when a new mission flag is selected in the editor
// Since this callback is called for all modules, we only need to update this module
// Updating symmetry counterparts would be redundent
// if we are using the mission flag, update it. otherwise ignore the call
if (!useCustomFlag) {
SetFlag(newFlagUrl, false, false);
flagUrl = newFlagUrl;
// Since this callback is called for all modules, we only need to update this module
// Updating symmetry counterparts would be redundent
UpdateFlag();
}
}
private void SetFlag(string newFlagUrl, bool isCustom, bool recursive) {
// Function to set the flag URL, the custom flag
// Set values
flagUrl = newFlagUrl;
useCustomFlag = isCustom;
// Update material and projection
UpdateAll();
// Update symmetry counterparts if called to
if (recursive) {
foreach (var counterpart in part.symmetryCounterparts) {
var decal = counterpart.GetComponent<ModuleConformalFlag>();
decal.SetFlag(newFlagUrl, isCustom, false);
}
}
}
protected override void UpdateMaterials() {
// Update the displayed flag texture for this decal or optionally any symmetry counterparts
private void UpdateFlag(bool recursive = false) {
// get the decal material property for the decal texture
var textureProperty = materialProperties.AddOrGetTextureProperty("_Decal", true);
if (useCustomFlag) { // set the texture to the custom flag
textureProperty.TextureUrl = flagUrl;
} else { // set the texture to the mission flag
textureProperty.TextureUrl = MissionFlagUrl;
string textureURL = useCustomFlag ? flagUrl : MissionFlagUrl;
textureProperty.TextureUrl = textureURL;
if (DecalConfig.AspectRatios.ContainsKey(textureURL)) {
var ratio = DecalConfig.AspectRatios[textureURL];
this.Log($"Overriding aspect ratio for {textureURL} with {ratio}");
textureProperty.AspectRatio = ratio;
} else {
textureProperty.AspectRatio = -1.0f;
}
base.UpdateMaterials();
UpdateMaterials();
UpdateScale();
if (recursive) {
// for each symmetry counterpart, copy this part's properties and update it in turn
foreach (var counterpart in part.symmetryCounterparts) {
var decal = counterpart.GetComponent<ModuleConformalFlag>();
decal.useCustomFlag = useCustomFlag;
decal.flagUrl = flagUrl;
decal.UpdateFlag();
}
}
}
}
}

View File

@ -5,7 +5,6 @@ using ConformalDecals.Text;
using ConformalDecals.UI;
using ConformalDecals.Util;
using TMPro;
using UniLinq;
using UnityEngine;
namespace ConformalDecals {
@ -29,7 +28,8 @@ namespace ConformalDecals {
public void SetText() {
if (_textEntryController == null) {
_textEntryController = TextEntryController.Create(text, font, style, vertical, lineSpacing, charSpacing, lineSpacingRange, charSpacingRange, OnTextUpdate);
} else {
}
else {
_textEntryController.Close();
}
}
@ -46,7 +46,8 @@ namespace ConformalDecals {
public void SetFillColor() {
if (_fillColorPickerController == null) {
_fillColorPickerController = ColorPickerController.Create(fillColor, OnFillColorUpdate);
} else {
}
else {
_fillColorPickerController.Close();
}
}
@ -68,7 +69,8 @@ namespace ConformalDecals {
public void SetOutlineColor() {
if (_outlineColorPickerController == null) {
_outlineColorPickerController = ColorPickerController.Create(outlineColor, OnOutlineColorUpdate);
} else {
}
else {
_outlineColorPickerController.Close();
}
}
@ -87,11 +89,41 @@ namespace ConformalDecals {
private MaterialColorProperty _outlineColorProperty;
private MaterialFloatProperty _outlineWidthProperty;
private DecalText _currentText;
private DecalText _currentText;
// EVENTS
public override void OnLoad(ConfigNode node) {
base.OnLoad(node);
string textRaw = "";
if (ParseUtil.ParseStringIndirect(ref textRaw, node, "text")) {
text = WebUtility.UrlDecode(textRaw);
}
string fontName = "";
if (ParseUtil.ParseStringIndirect(ref fontName, node, "fontName")) {
font = DecalConfig.GetFont(fontName);
}
else if (font == null) font = DecalConfig.GetFont("Calibri SDF");
int styleInt = 0;
if (ParseUtil.ParseIntIndirect(ref styleInt, node, "style")) {
style = (FontStyles) styleInt;
}
ParseUtil.ParseColor32Indirect(ref fillColor, node, "fillColor");
ParseUtil.ParseColor32Indirect(ref outlineColor, node, "outlineColor");
if (HighLogic.LoadedSceneIsGame) {
// For some reason, rendering doesnt work right on the first frame a scene is loaded
// So delay any rendering until the next frame when called in OnLoad
// This is probably a problem with Unity, not KSP
StartCoroutine(UpdateTextLate());
}
else {
UpdateText();
}
}
/// <inheritdoc />
public override void OnSave(ConfigNode node) {
node.AddValue("text", WebUtility.UrlEncode(text));
node.AddValue("fontName", font.Name);
@ -115,23 +147,13 @@ namespace ConformalDecals {
}
public void OnTextUpdate(string newText, DecalFont newFont, FontStyles newStyle, bool newVertical, float newLineSpacing, float newCharSpacing) {
text = newText;
font = newFont;
style = newStyle;
vertical = newVertical;
lineSpacing = newLineSpacing;
charSpacing = newCharSpacing;
UpdateAll();
foreach (var decal in part.symmetryCounterparts.Select(o => o.GetComponent<ModuleConformalText>())) {
decal.text = newText;
decal.font = newFont;
decal.style = newStyle;
decal.vertical = newVertical;
decal.lineSpacing = newLineSpacing;
decal.charSpacing = newCharSpacing;
decal.UpdateAll();
}
this.text = newText;
this.font = newFont;
this.style = newStyle;
this.vertical = newVertical;
this.lineSpacing = newLineSpacing;
this.charSpacing = newCharSpacing;
UpdateText(true);
}
public void OnFillColorUpdate(Color rgb, Util.ColorHSV hsv) {
@ -205,7 +227,7 @@ namespace ConformalDecals {
if (_textEntryController != null) _textEntryController.Close();
if (_fillColorPickerController != null) _fillColorPickerController.Close();
if (_outlineColorPickerController != null) _outlineColorPickerController.Close();
base.OnDestroy();
}
@ -218,58 +240,48 @@ namespace ConformalDecals {
base.OnDetach();
}
// FUNCTIONS
protected override void LoadDecal(ConfigNode node) {
base.LoadDecal(node);
string textRaw = "";
if (ParseUtil.ParseStringIndirect(ref textRaw, node, "text")) {
text = WebUtility.UrlDecode(textRaw);
}
string fontName = "";
if (ParseUtil.ParseStringIndirect(ref fontName, node, "fontName")) {
font = DecalConfig.GetFont(fontName);
} else if (font == null) font = DecalConfig.GetFont("Calibri SDF");
int styleInt = 0;
if (ParseUtil.ParseIntIndirect(ref styleInt, node, "style")) {
style = (FontStyles) styleInt;
}
ParseUtil.ParseColor32Indirect(ref fillColor, node, "fillColor");
ParseUtil.ParseColor32Indirect(ref outlineColor, node, "outlineColor");
}
protected override void SetupDecal() {
if (HighLogic.LoadedSceneIsGame) {
// For some reason text rendering fails on the first frame of a scene, so this is my workaround
StartCoroutine(UpdateTextLate());
} else {
base.SetupDecal();
}
}
private IEnumerator UpdateTextLate() {
yield return null;
UpdateAll();
UpdateText();
}
private void UpdateText(bool recursive = false) {
// Render text
var newText = new DecalText(text, font, style, vertical, lineSpacing, charSpacing);
var output = TextRenderer.UpdateText(_currentText, newText);
// update the _currentText state variable
// this is the ONLY place this variable should be set! otherwise the current state is lost
_currentText = newText;
// Update the texture with the new rendered output
UpdateTexture(output);
// If recursive, copy parameters to other parts and perform the same operation
if (recursive) {
foreach (var counterpart in part.symmetryCounterparts) {
var decal = counterpart.GetComponent<ModuleConformalText>();
decal.text = text;
decal.font = font;
decal.style = style;
decal.vertical = vertical;
decal.charSpacing = charSpacing;
decal.lineSpacing = lineSpacing;
decal.UpdateText();
}
}
}
public void UpdateTexture(TextRenderOutput output) {
_decalTextureProperty.Texture = output.Texture;
_decalTextureProperty.SetTile(output.Window);
UpdateMaterials();
UpdateScale();
}
protected override void UpdateMaterials() {
// Render text
var newText = new DecalText(text, font, style, vertical, lineSpacing, charSpacing);
if (newText != _currentText) {
var output = TextRenderer.UpdateText(_currentText, newText);
// update the _currentText state variable
// this is the ONLY place this variable should be set! otherwise the current state is lost
_currentText = newText;
_decalTextureProperty.Texture = output.Texture;
_decalTextureProperty.SetTile(output.Window);
}
_fillEnabledProperty.value = fillEnabled;
_fillColorProperty.color = fillColor;

View File

@ -0,0 +1,68 @@
using UnityEngine;
using UnityEngine.Rendering;
namespace ConformalDecals {
public class ProjectionTarget {
// Target object data
public readonly Transform target;
private readonly Renderer _targetRenderer;
private readonly Mesh _targetMesh;
private bool _projectionEnabled;
// property block
private readonly MaterialPropertyBlock _decalMPB;
public ProjectionTarget(MeshRenderer targetRenderer, Mesh targetMesh) {
target = targetRenderer.transform;
_targetRenderer = targetRenderer;
_targetMesh = targetMesh;
_decalMPB = new MaterialPropertyBlock();
}
public void Project(Matrix4x4 orthoMatrix, Transform projector, Bounds projectorBounds, bool useBaseNormal) {
if (projectorBounds.Intersects(_targetRenderer.bounds)) {
_projectionEnabled = true;
var targetMaterial = _targetRenderer.sharedMaterial;
var projectorToTargetMatrix = target.worldToLocalMatrix * projector.localToWorldMatrix;
var projectionMatrix = orthoMatrix * projectorToTargetMatrix.inverse;
var decalNormal = projectorToTargetMatrix.MultiplyVector(Vector3.back).normalized;
var decalTangent = projectorToTargetMatrix.MultiplyVector(Vector3.right).normalized;
_decalMPB.SetMatrix(DecalPropertyIDs._ProjectionMatrix, projectionMatrix);
_decalMPB.SetVector(DecalPropertyIDs._DecalNormal, decalNormal);
_decalMPB.SetVector(DecalPropertyIDs._DecalTangent, decalTangent);
if (useBaseNormal && targetMaterial.HasProperty(DecalPropertyIDs._BumpMap)) {
_decalMPB.SetTexture(DecalPropertyIDs._BumpMap, targetMaterial.GetTexture(DecalPropertyIDs._BumpMap));
var normalScale = targetMaterial.GetTextureScale(DecalPropertyIDs._BumpMap);
var normalOffset = targetMaterial.GetTextureOffset(DecalPropertyIDs._BumpMap);
_decalMPB.SetVector(DecalPropertyIDs._BumpMap_ST, new Vector4(normalScale.x, normalScale.y, normalOffset.x, normalOffset.y));
}
else {
_decalMPB.SetTexture(DecalPropertyIDs._BumpMap, DecalConfig.BlankNormal);
}
}
else {
_projectionEnabled = false;
}
}
public bool Render(Material decalMaterial, MaterialPropertyBlock partMPB, Camera camera) {
if (_projectionEnabled) {
_decalMPB.SetFloat(PropertyIDs._RimFalloff, partMPB.GetFloat(PropertyIDs._RimFalloff));
_decalMPB.SetColor(PropertyIDs._RimColor, partMPB.GetColor(PropertyIDs._RimColor));
Graphics.DrawMesh(_targetMesh, target.localToWorldMatrix, decalMaterial, 0, camera, 0, _decalMPB, ShadowCastingMode.Off, true);
return true;
}
return false;
}
}
}

View File

@ -1,8 +0,0 @@
using UnityEngine;
namespace ConformalDecals.Targets {
public interface IProjectionTarget {
bool Project(Matrix4x4 orthoMatrix, Transform projector, Bounds projectionBounds);
void Render(Material decalMaterial, MaterialPropertyBlock partMPB, Camera camera);
}
}

View File

@ -1,93 +0,0 @@
using UnityEngine;
using UnityEngine.Rendering;
namespace ConformalDecals.Targets {
public class ProjectionMeshTarget : IProjectionTarget {
public const string NodeName = "MESH_TARGET";
// enabled flag
public bool enabled;
// Target object data
public readonly Transform target;
public readonly Transform root;
public readonly Mesh mesh;
public readonly MeshRenderer renderer;
// Projection data
private Matrix4x4 _decalMatrix;
private Vector3 _decalNormal;
private Vector3 _decalTangent;
// property block
private readonly MaterialPropertyBlock _decalMPB;
public ProjectionMeshTarget(Transform target, Transform root, MeshRenderer renderer, Mesh mesh, bool useBaseNormal) {
this.root = root;
this.target = target;
this.renderer = renderer;
this.mesh = mesh;
_decalMPB = new MaterialPropertyBlock();
SetNormalMap(renderer.sharedMaterial, useBaseNormal);
}
private void SetNormalMap(Material targetMaterial, bool useBaseNormal) {
if (useBaseNormal && targetMaterial.HasProperty(DecalPropertyIDs._BumpMap)) {
_decalMPB.SetTexture(DecalPropertyIDs._BumpMap, targetMaterial.GetTexture(DecalPropertyIDs._BumpMap));
var normalScale = targetMaterial.GetTextureScale(DecalPropertyIDs._BumpMap);
var normalOffset = targetMaterial.GetTextureOffset(DecalPropertyIDs._BumpMap);
_decalMPB.SetVector(DecalPropertyIDs._BumpMap_ST, new Vector4(normalScale.x, normalScale.y, normalOffset.x, normalOffset.y));
}
else {
_decalMPB.SetTexture(DecalPropertyIDs._BumpMap, DecalConfig.BlankNormal);
}
}
public bool Project(Matrix4x4 orthoMatrix, Transform projector, Bounds projectionBounds) {
if (projectionBounds.Intersects(renderer.bounds)) {
enabled = true;
var projectorToTargetMatrix = target.worldToLocalMatrix * projector.localToWorldMatrix;
_decalMatrix = orthoMatrix * projectorToTargetMatrix.inverse;
_decalNormal = projectorToTargetMatrix.MultiplyVector(Vector3.back).normalized;
_decalTangent = projectorToTargetMatrix.MultiplyVector(Vector3.right).normalized;
_decalMPB.SetMatrix(DecalPropertyIDs._ProjectionMatrix, _decalMatrix);
_decalMPB.SetVector(DecalPropertyIDs._DecalNormal, _decalNormal);
_decalMPB.SetVector(DecalPropertyIDs._DecalTangent, _decalTangent);
}
else {
enabled = false;
}
return enabled;
}
public void Render(Material decalMaterial, MaterialPropertyBlock partMPB, Camera camera) {
if (!enabled) return;
_decalMPB.SetFloat(PropertyIDs._RimFalloff, partMPB.GetFloat(PropertyIDs._RimFalloff));
_decalMPB.SetColor(PropertyIDs._RimColor, partMPB.GetColor(PropertyIDs._RimColor));
Graphics.DrawMesh(mesh, target.localToWorldMatrix, decalMaterial, 0, camera, 0, _decalMPB, ShadowCastingMode.Off, true);
}
public static bool ValidateTarget(Transform target, MeshRenderer renderer, MeshFilter filter) {
if (renderer == null) return false;
if (filter == null) return false;
if (!target.gameObject.activeInHierarchy) return false;
var material = renderer.material;
if (material == null) return false;
if (DecalConfig.IsBlacklisted(material.shader)) return false;
if (filter.sharedMesh == null) return false;
return true;
}
}
}

View File

@ -1,50 +0,0 @@
using System.Collections.Generic;
using UnityEngine;
namespace ConformalDecals.Targets {
public class ProjectionPartTarget : IProjectionTarget {
public const string NodeName = "PART_TARGET";
// enabled flag
public bool enabled;
public readonly Part part;
public readonly List<ProjectionMeshTarget> meshTargets = new List<ProjectionMeshTarget>();
public ProjectionPartTarget(Part part, bool useBaseNormal) {
this.part = part;
foreach (var renderer in part.FindModelComponents<MeshRenderer>()) {
var target = renderer.transform;
var filter = target.GetComponent<MeshFilter>();
// check if the target has any missing data
if (!ProjectionMeshTarget.ValidateTarget(target, renderer, filter)) continue;
// create new ProjectionTarget to represent the renderer
var projectionTarget = new ProjectionMeshTarget(target, part.transform, renderer, filter.sharedMesh, useBaseNormal);
// add the target to the list
meshTargets.Add(projectionTarget);
}
}
public bool Project(Matrix4x4 orthoMatrix, Transform projector, Bounds projectionBounds) {
enabled = false;
foreach (var meshTarget in meshTargets) {
enabled |= meshTarget.Project(orthoMatrix, projector, projectionBounds);
}
return enabled;
}
public void Render(Material decalMaterial, MaterialPropertyBlock partMPB, Camera camera) {
if (!enabled) return;
foreach (var target in meshTargets) {
target.Render(decalMaterial, partMPB, camera);
}
}
}
}

View File

@ -46,26 +46,22 @@ namespace ConformalDecals.Text {
public bool SmallCapsMask => (_fontStyleMask & FontStyles.SmallCaps) != 0;
public static DecalFont Parse(ConfigNode node, IEnumerable<TMP_FontAsset> fontAssets) {
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 font = ScriptableObject.CreateInstance<DecalFont>();
var name = ParseUtil.ParseString(node, "name");
var fontAsset = fontAssets.First(o => o.name == name);
if (fontAsset == null) {
_fontAsset = fontAssets.First(o => o.name == name);
if (FontAsset == null) {
throw new FormatException($"Could not find font asset named {name}");
}
font._fontAsset = fontAsset;
font._title = ParseUtil.ParseString(node, "title", true, name);
font._fontStyle = (FontStyles) ParseUtil.ParseInt(node, "style", true);
font._fontStyleMask = (FontStyles) ParseUtil.ParseInt(node, "styleMask", true);
return font;
_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.");

View File

@ -1,5 +1,6 @@
using System.IO;
using System.Collections;
using System.Collections.Generic;
using ConformalDecals.Util;
using TMPro;
using UniLinq;

View File

@ -1,33 +0,0 @@
using System;
using ConformalDecals.Util;
using UnityEngine;
namespace ConformalDecals.Tweakables {
[AttributeUsage(AttributeTargets.Field)]
public abstract class TweakableData : System.Attribute, ISerializationCallbackReceiver {
public string name;
public bool adjustable = true;
public string adjustableKey;
// public string fieldChangedCallback;
public bool useSymmetry = true;
protected TweakableData(string name) {
this.name = name;
adjustableKey = name + "Adjustable";
}
public virtual void Load(ConfigNode node) {
ParseUtil.ParseBoolIndirect(ref adjustable, node, adjustableKey);
}
public virtual void Apply(BaseField baseField, PartModule module) {
baseField.guiActiveEditor = adjustable;
}
public void OnBeforeSerialize() { }
public void OnAfterDeserialize() { }
}
}

View File

@ -1,30 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UniLinq;
using UnityEngine;
namespace ConformalDecals.Tweakables {
public class TweakableDataCollection : IEnumerable<TweakableData>, ISerializationCallbackReceiver {
public readonly Dictionary<string, TweakableData> tweakables = new Dictionary<string, TweakableData>();
[SerializeField] private TweakableData[] _serializedTweakables;
public IEnumerator<TweakableData> GetEnumerator() {
return tweakables.Values.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
public void OnBeforeSerialize() {
_serializedTweakables = tweakables.Values.ToArray();
}
public void OnAfterDeserialize() {
foreach (var tweakable in _serializedTweakables) {
tweakables.Add(tweakable.name, tweakable);
}
}
}
}

View File

@ -1,56 +0,0 @@
using System;
using ConformalDecals.Util;
using UnityEngine;
namespace ConformalDecals.Tweakables {
[AttributeUsage(AttributeTargets.Field)]
public class TweakableSlider : TweakableData {
// The default value for the slider
public float defaultValue;
public string defaultValueKey;
// The range of the slider as a vector of <min, max>
public float min = 0;
public float max = 1;
public string rangeKey;
// The step size of the slider
public float step;
public string stepKey;
public TweakableSlider(string name) : base(name) {
defaultValueKey = name + "Default";
rangeKey = name + "Range";
stepKey = name + "Step";
}
public override void Load(ConfigNode node) {
base.Load(node);
var range = new Vector2(min, max);
ParseUtil.ParseVector2Indirect(ref range, node, rangeKey);
min = Mathf.Max(Mathf.Epsilon, range.x);
max = Mathf.Max(min, range.y);
ParseUtil.ParseFloatIndirect(ref step, node, stepKey);
if (!HighLogic.LoadedSceneIsEditor && !HighLogic.LoadedSceneIsFlight) {
ParseUtil.ParseFloatIndirect(ref defaultValue, node, defaultValueKey);
}
}
public override void Apply(BaseField baseField, PartModule module) {
base.Apply(baseField, module);
var uiControlEditor = (UI_FloatRange) baseField.uiControlEditor;
uiControlEditor.minValue = min;
uiControlEditor.maxValue = max;
uiControlEditor.stepIncrement = step;
// Set the default value on first load
if (!HighLogic.LoadedSceneIsEditor && !HighLogic.LoadedSceneIsFlight) {
baseField.FieldInfo.SetValue(module, defaultValue);
}
}
}
}

View File

@ -1,34 +0,0 @@
using System;
using System.Reflection;
using ConformalDecals.Util;
namespace ConformalDecals.Tweakables {
[AttributeUsage(AttributeTargets.Field)]
public class TweakableToggle : TweakableData {
// The default value for the toggle
public bool defaultValue;
public string defaultValueKey;
public TweakableToggle(string name) : base(name) {
defaultValueKey = name + "Default";
}
public override void Load(ConfigNode node) {
base.Load(node);
// Set the default value on first load
if (!HighLogic.LoadedSceneIsEditor && !HighLogic.LoadedSceneIsFlight) {
ParseUtil.ParseBoolIndirect(ref defaultValue, node, defaultValueKey);
}
}
public override void Apply(BaseField baseField, PartModule module) {
base.Apply(baseField, module);
// Set the default value on first load
if (!HighLogic.LoadedSceneIsEditor && !HighLogic.LoadedSceneIsFlight) {
baseField.FieldInfo.SetValue(module, defaultValue);
}
}
}
}

View File

@ -31,7 +31,7 @@ namespace ConformalDecals.Util {
public static string ParseString(ConfigNode node, string valueName, bool isOptional = false, string defaultValue = "") {
if (node.HasValue(valueName)) return node.GetValue(valueName);
if (isOptional) {
return defaultValue;
}
@ -74,14 +74,6 @@ namespace ConformalDecals.Util {
return ParseValueIndirect(ref value, node, valueName, int.TryParse);
}
public static uint ParseUint(ConfigNode node, string valueName, bool isOptional = false, uint defaultValue = 0) {
return ParseValue(node, valueName, uint.TryParse, isOptional, defaultValue);
}
public static bool ParseUintIndirect(ref uint value, ConfigNode node, string valueName) {
return ParseValueIndirect(ref value, node, valueName, uint.TryParse);
}
public static Color32 ParseColor32(ConfigNode node, string valueName, bool isOptional = false, Color32 defaultValue = default) {
return ParseValue(node, valueName, TryParseColor32, isOptional, defaultValue);
}
@ -114,14 +106,6 @@ namespace ConformalDecals.Util {
return ParseValueIndirect(ref value, node, valueName, ParseExtensions.TryParseVector3);
}
public static Matrix4x4 ParseMatrix4x4(ConfigNode node, string valueName, bool isOptional = false, Matrix4x4 defaultValue = default) {
return ParseValue(node, valueName, ParseUtil.TryParseMatrix4x4, isOptional, defaultValue);
}
public static bool ParseMatrix4x4Indirect(ref Matrix4x4 value, ConfigNode node, string valueName) {
return ParseValueIndirect(ref value, node, valueName, ParseUtil.TryParseMatrix4x4);
}
public static T ParseValue<T>(ConfigNode node, string valueName, TryParseDelegate<T> tryParse, bool isOptional = false, T defaultValue = default) {
string valueString = node.GetValue(valueName);
@ -161,26 +145,6 @@ namespace ConformalDecals.Util {
throw new FormatException($"Improperly formatted {typeof(T)} value for {valueName} : '{valueString}");
}
public static bool TryParseMatrix4x4(string valueString, out Matrix4x4 value) {
value = new Matrix4x4();
var split = valueString.Split(Separator, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < split.Length; i++) {
split[i] = split[i].Trim();
}
if (split.Length != 16) return false;
int index = 0;
for (int row = 0; row < 4; row++) {
for (int col = 0; col < 4; col++) {
if (!float.TryParse(split[index], out float component)) return false;
value[row, col] = component;
}
}
return true;
}
public static bool TryParseHexColor(string valueString, out Color32 value) {
value = new Color32(0, 0, 0, byte.MaxValue);