@ -1,11 +1,13 @@
using System ;
using System ;
using System.Collections.Generic ;
using System.Collections.Generic ;
using ConformalDecals.MaterialModifiers ;
using System.Diagnostics.CodeAnalysis ;
using ConformalDecals.MaterialProperties ;
using ConformalDecals.Util ;
using ConformalDecals.Util ;
using UnityEngine ;
using UnityEngine ;
namespace ConformalDecals {
namespace ConformalDecals {
public class ModuleConformalDecal : PartModule {
public class ModuleConformalDecal : PartModule {
[SuppressMessage("ReSharper", "InconsistentNaming")]
public enum DecalScaleMode {
public enum DecalScaleMode {
HEIGHT ,
HEIGHT ,
WIDTH ,
WIDTH ,
@ -17,43 +19,21 @@ namespace ConformalDecals {
// CONFIGURABLE VALUES
// CONFIGURABLE VALUES
/// <summary>
/// Shader name. Should be one that supports decal projection.
/// </summary>
[KSPField] public string shader = "ConformalDecals/Paint/Diffuse" ;
[KSPField] public string shader = "ConformalDecals/Paint/Diffuse" ;
/// <summary>
[KSPField] public string decalFront = "Decal-Front" ;
/// Decal front transform name. Required
[KSPField] public string decalBack = "Decal-Back" ;
/// </summary>
[KSPField] public string decalModel = "Decal-Model" ;
[KSPField] public string decalFront = "Decal-Front" ;
/// <summary>
/// Decal back transform name. Required if <see cref="updateBackScale"/> is true.
/// </summary>
[KSPField] public string decalBack = "Decal-Back" ;
/// <summary>
/// Decal model transform name. Is rescaled to preview the decal scale when unattached.
/// </summary>
/// <remarks>
/// If unspecified, the decal front transform is used instead.
/// </remarks>
[KSPField] public string decalModel = "Decal-Model" ;
/// <summary>
/// Decal projector transform name. The decal will project along the +Z axis of this transform.
/// </summary>
/// <remarks>
/// if unspecified, the part "model" transform will be used instead.
/// </remarks>
[KSPField] public string decalProjector = "Decal-Projector" ;
[KSPField] public string decalProjector = "Decal-Projector" ;
[KSPField] public string decalCollider = "Decal-Collider" ;
// Parameters
// Parameters
[KSPField] public bool scaleAdjustable = true ;
[KSPField] public bool scaleAdjustable = true ;
[KSPField] public float defaultScale = 1 ;
[KSPField] public float defaultScale = 1 ;
[KSPField] public Vector2 scaleRange = new Vector2 ( 0 , 4 ) ;
[KSPField] public Vector2 scaleRange = new Vector2 ( 0 , 4 ) ;
[KSPField] public DecalScaleMode scaleMode = DecalScaleMode . HEIGHT ;
[KSPField] public DecalScaleMode scaleMode = DecalScaleMode . HEIGHT ;
[KSPField] public bool depthAdjustable = true ;
[KSPField] public bool depthAdjustable = true ;
[KSPField] public float defaultDepth = 0.1f ;
[KSPField] public float defaultDepth = 0.1f ;
@ -75,56 +55,40 @@ namespace ConformalDecals {
[KSPField] public Vector2 tileSize ;
[KSPField] public Vector2 tileSize ;
[KSPField] public int tileIndex = - 1 ;
[KSPField] public int tileIndex = - 1 ;
/// <summary>
/// Should the back material scale be updated automatically?
/// </summary>
[KSPField] public bool updateBackScale = true ;
[KSPField] public bool updateBackScale = true ;
[KSPField] public bool selectableInFlight ;
// INTERNAL VALUES
// INTERNAL VALUES
/// <summary>
/// Decal scale factor, in meters.
/// </summary>
[ KSPField ( guiName = "#LOC_ConformalDecals_gui-scale" , guiActive = false , guiActiveEditor = true , isPersistant = true , guiFormat = "F2" , guiUnits = "m" ) ,
[ KSPField ( guiName = "#LOC_ConformalDecals_gui-scale" , guiActive = false , guiActiveEditor = true , isPersistant = true , guiFormat = "F2" , guiUnits = "m" ) ,
UI_FloatRange ( stepIncrement = 0.05f ) ]
UI_FloatRange ( stepIncrement = 0.05f ) ]
public float scale = 1.0f ;
public float scale = 1.0f ;
/// <summary>
/// Projection depth value for the decal projector, in meters.
/// </summary>
[ KSPField ( guiName = "#LOC_ConformalDecals_gui-depth" , guiActive = false , guiActiveEditor = true , isPersistant = true , guiFormat = "F2" , guiUnits = "m" ) ,
[ KSPField ( guiName = "#LOC_ConformalDecals_gui-depth" , guiActive = false , guiActiveEditor = true , isPersistant = true , guiFormat = "F2" , guiUnits = "m" ) ,
UI_FloatRange ( stepIncrement = 0.02f ) ]
UI_FloatRange ( stepIncrement = 0.02f ) ]
public float depth = 0.2f ;
public float depth = 0.2f ;
/// <summary>
/// Opacity value for the decal shader.
/// </summary>
[ KSPField ( guiName = "#LOC_ConformalDecals_gui-opacity" , guiActive = false , guiActiveEditor = true , isPersistant = true , guiFormat = "P0" ) ,
[ KSPField ( guiName = "#LOC_ConformalDecals_gui-opacity" , guiActive = false , guiActiveEditor = true , isPersistant = true , guiFormat = "P0" ) ,
UI_FloatRange ( stepIncrement = 0.05f ) ]
UI_FloatRange ( stepIncrement = 0.05f ) ]
public float opacity = 1.0f ;
public float opacity = 1.0f ;
/// <summary>
/// Alpha cutoff value for the decal shader.
/// </summary>
[ KSPField ( guiName = "#LOC_ConformalDecals_gui-cutoff" , guiActive = false , guiActiveEditor = true , isPersistant = true , guiFormat = "P0" ) ,
[ KSPField ( guiName = "#LOC_ConformalDecals_gui-cutoff" , guiActive = false , guiActiveEditor = true , isPersistant = true , guiFormat = "P0" ) ,
UI_FloatRange ( stepIncrement = 0.05f ) ]
UI_FloatRange ( stepIncrement = 0.05f ) ]
public float cutoff = 0.5f ;
public float cutoff = 0.5f ;
/// <summary>
/// Edge wear value for the decal shader. Only relevent when useBaseNormal is true and the shader is a paint shader
/// </summary>
[ KSPField ( guiName = "#LOC_ConformalDecals_gui-wear" , guiActive = false , guiActiveEditor = true , isPersistant = true , guiFormat = "F0" ) ,
[ KSPField ( guiName = "#LOC_ConformalDecals_gui-wear" , guiActive = false , guiActiveEditor = true , isPersistant = true , guiFormat = "F0" ) ,
UI_FloatRange ( ) ]
UI_FloatRange ( ) ]
public float wear = 100 ;
public float wear = 100 ;
[KSPField(isPersistant = true)] public bool projectMultiple ; // reserved for future features. do not modify
[KSPField] public MaterialPropertyCollection materialProperties ;
[KSPField] public MaterialPropertyCollection materialProperties ;
[KSPField] public Transform decalFrontTransform ;
[KSPField] public Transform decalFrontTransform ;
[KSPField] public Transform decalBackTransform ;
[KSPField] public Transform decalBackTransform ;
[KSPField] public Transform decalModelTransform ;
[KSPField] public Transform decalModelTransform ;
[KSPField] public Transform decalProjectorTransform ;
[KSPField] public Transform decalProjectorTransform ;
[KSPField] public Transform decalColliderTransform ;
[KSPField] public Material backMaterial ;
[KSPField] public Material backMaterial ;
[KSPField] public Vector2 backTextureBaseScale ;
[KSPField] public Vector2 backTextureBaseScale ;
@ -137,10 +101,10 @@ namespace ConformalDecals {
private bool _isAttached ;
private bool _isAttached ;
private Matrix4x4 _orthoMatrix ;
private Matrix4x4 _orthoMatrix ;
private Material _decalMaterial ;
private Material _decalMaterial ;
private Material _previewMaterial ;
private Material _previewMaterial ;
private BoxCollider _boundsCollid er;
private MeshRenderer _boundsRender er;
private int DecalQueue {
private int DecalQueue {
get {
get {
_decalQueueCounter + + ;
_decalQueueCounter + + ;
@ -169,43 +133,22 @@ namespace ConformalDecals {
this . Log ( "Loading module" ) ;
this . Log ( "Loading module" ) ;
try {
try {
// SETUP TRANSFORMS
// SETUP TRANSFORMS
// find front transform
decalFrontTransform = part . FindModelTransform ( decalFront ) ;
decalFrontTransform = part . FindModelTransform ( decalFront ) ;
if ( decalFrontTransform = = null ) throw new FormatException ( $"Could not find decalFront transform: '{decalFront}'." ) ;
if ( decalFrontTransform = = null ) throw new FormatException ( $"Could not find decalFront transform: '{decalFront}'." ) ;
// find back transform
decalBackTransform = part . FindModelTransform ( decalBack ) ;
if ( string . IsNullOrEmpty ( decalBack ) ) {
if ( decalBackTransform = = null ) throw new FormatException ( $"Could not find decalBack transform: '{decalBack}'." ) ;
if ( updateBackScale ) {
this . LogWarning ( "updateBackScale is true but has no specified decalBack transform!" ) ;
this . LogWarning ( "Setting updateBackScale to false." ) ;
updateBackScale = false ;
}
}
else {
decalBackTransform = part . FindModelTransform ( decalBack ) ;
if ( decalBackTransform = = null ) throw new FormatException ( $"Could not find decalBack transform: '{decalBack}'." ) ;
}
// find model transform
decalModelTransform = part . FindModelTransform ( decalModel ) ;
if ( string . IsNullOrEmpty ( decalModel ) ) {
if ( decalModelTransform = = null ) throw new FormatException ( $"Could not find decalModel transform: '{decalModel}'." ) ;
decalModelTransform = decalFrontTransform ;
}
else {
decalModelTransform = part . FindModelTransform ( decalModel ) ;
if ( decalModelTransform = = null ) throw new FormatException ( $"Could not find decalModel transform: '{decalModel}'." ) ;
}
// find projector transform
decalProjectorTransform = part . FindModelTransform ( decalProjector ) ;
if ( string . IsNullOrEmpty ( decalProjector ) ) {
if ( decalProjectorTransform = = null ) throw new FormatException ( $"Could not find decalProjector transform: '{decalProjector}'." ) ;
decalProjectorTransform = part . transform ;
}
decalColliderTransform = part . FindModelTransform ( decalCollider ) ;
else {
if ( decalColliderTransform = = null ) throw new FormatException ( $"Could not find decalCollider transform: '{decalCollider}'." ) ;
decalProjectorTransform = part . FindModelTransform ( decalProjector ) ;
if ( decalProjectorTransform = = null ) throw new FormatException ( $"Could not find decalProjector transform: '{decalProjector}'." ) ;
}
// get back material if necessary
// SETUP BACK MATERIAL
if ( updateBackScale ) {
if ( updateBackScale ) {
this . Log ( "Getting material and base scale for back material" ) ;
this . Log ( "Getting material and base scale for back material" ) ;
var backRenderer = decalBackTransform . GetComponent < MeshRenderer > ( ) ;
var backRenderer = decalBackTransform . GetComponent < MeshRenderer > ( ) ;
@ -294,17 +237,11 @@ namespace ConformalDecals {
public override void OnStart ( StartState state ) {
public override void OnStart ( StartState state ) {
this . Log ( "Starting module" ) ;
this . Log ( "Starting module" ) ;
// handle tweakables
if ( HighLogic . LoadedSceneIsEditor ) {
GameEvents . onEditorPartEvent . Add ( OnEditorEvent ) ;
GameEvents . onVariantApplied . Add ( OnVariantApplied ) ;
UpdateTweakables ( ) ;
}
materialProperties . RenderQueue = DecalQueue ;
materialProperties . RenderQueue = DecalQueue ;
_bounds Collider = decalProjectorTransform . GetComponent < BoxCollid er> ( ) ;
_bounds Renderer = decalProjectorTransform . GetComponent < MeshRender er> ( ) ;
UpdateMaterials ( ) ;
UpdateMaterials ( ) ;
@ -317,12 +254,40 @@ namespace ConformalDecals {
OnAttach ( ) ;
OnAttach ( ) ;
}
}
}
}
// handle tweakables
if ( HighLogic . LoadedSceneIsEditor ) {
GameEvents . onEditorPartEvent . Add ( OnEditorEvent ) ;
GameEvents . onVariantApplied . Add ( OnVariantApplied ) ;
UpdateTweakables ( ) ;
}
// handle flight events
if ( HighLogic . LoadedSceneIsFlight ) {
GameEvents . onPartWillDie . Add ( OnPartWillDie ) ;
Part . layerMask | = 1 < < DecalConfig . DecalLayer ;
decalColliderTransform . gameObject . layer = DecalConfig . DecalLayer ;
if ( ! selectableInFlight | | ! DecalConfig . SelectableInFlight ) {
decalColliderTransform . GetComponent < Collider > ( ) . enabled = false ;
}
if ( part . parent = = null ) part . explode ( ) ;
}
}
}
public virtual void OnDestroy ( ) {
public virtual void OnDestroy ( ) {
// remove GameEvents
// remove GameEvents
GameEvents . onEditorPartEvent . Remove ( OnEditorEvent ) ;
if ( HighLogic . LoadedSceneIsEditor ) {
GameEvents . onVariantApplied . Remove ( OnVariantApplied ) ;
GameEvents . onEditorPartEvent . Remove ( OnEditorEvent ) ;
GameEvents . onVariantApplied . Remove ( OnVariantApplied ) ;
}
if ( HighLogic . LoadedSceneIsFlight ) {
GameEvents . onPartWillDie . Remove ( OnPartWillDie ) ;
}
// remove from preCull delegate
// remove from preCull delegate
Camera . onPreCull - = Render ;
Camera . onPreCull - = Render ;
@ -381,6 +346,13 @@ namespace ConformalDecals {
}
}
}
}
protected void OnPartWillDie ( Part willDie ) {
if ( willDie = = part . parent ) {
this . Log ( "Parent part about to be destroyed! Killing decal part." ) ;
part . Die ( ) ;
}
}
protected void OnAttach ( ) {
protected void OnAttach ( ) {
if ( part . parent = = null ) {
if ( part . parent = = null ) {
this . LogError ( "Attach function called but part has no parent!" ) ;
this . LogError ( "Attach function called but part has no parent!" ) ;
@ -423,6 +395,8 @@ namespace ConformalDecals {
}
}
protected void UpdateScale ( ) {
protected void UpdateScale ( ) {
scale = Mathf . Max ( 0.01f , scale ) ;
depth = Mathf . Max ( 0.01f , depth ) ;
var aspectRatio = materialProperties . AspectRatio ;
var aspectRatio = materialProperties . AspectRatio ;
Vector2 size ;
Vector2 size ;
@ -463,7 +437,7 @@ namespace ConformalDecals {
// update projection
// update projection
foreach ( var target in _targets ) {
foreach ( var target in _targets ) {
target . Project ( _orthoMatrix , decalProjectorTransform , _bounds Collid er. bounds , useBaseNormal ) ;
target . Project ( _orthoMatrix , decalProjectorTransform , _bounds Render er. bounds , useBaseNormal ) ;
}
}
}
}
else {
else {
@ -597,7 +571,7 @@ namespace ConformalDecals {
public void Render ( Camera camera ) {
public void Render ( Camera camera ) {
if ( ! _isAttached ) return ;
if ( ! _isAttached ) return ;
// render on each target object
// render on each target object
foreach ( var target in _targets ) {
foreach ( var target in _targets ) {
target . Render ( _decalMaterial , part . mpb , camera ) ;
target . Render ( _decalMaterial , part . mpb , camera ) ;