diff --git a/Source/ConformalDecals/ConformalDecals.csproj b/Source/ConformalDecals/ConformalDecals.csproj index 1ef5737..4e04a3b 100644 --- a/Source/ConformalDecals/ConformalDecals.csproj +++ b/Source/ConformalDecals/ConformalDecals.csproj @@ -38,7 +38,6 @@ dlls/Shabby.dll - dlls\UnityEngine.CoreModule.dll diff --git a/Source/ConformalDecals/ModuleConformalDecal.cs b/Source/ConformalDecals/ModuleConformalDecal.cs index 397ba01..7704747 100644 --- a/Source/ConformalDecals/ModuleConformalDecal.cs +++ b/Source/ConformalDecals/ModuleConformalDecal.cs @@ -1,19 +1,89 @@ using System; +using System.Collections.Generic; +using ConformalDecals.MaterialModifiers; +using UniLinq; using UnityEngine; namespace ConformalDecals { public class ModuleConformalDecal : PartModule { [KSPField] public string decalPreviewTransform = ""; + [KSPField] public string decalModelTransform = ""; + + [KSPField(guiName = "Scale", guiActive = false, guiActiveEditor = true, isPersistant = true, guiFormat = "F2", guiUnits = "m"), + UI_FloatRange(minValue = 0.05f, maxValue = 4f, stepIncrement = 0.05f)] + public float scale = 1.0f; + + [KSPField(guiName = "Depth", guiActive = false, guiActiveEditor = true, isPersistant = true, guiFormat = "F2", guiUnits = "m"), + UI_FloatRange(minValue = 0.05f, maxValue = 4f, stepIncrement = 0.05f)] + public float depth = 1.0f; + + [KSPField(guiActive = true, guiFormat = "F2", guiName = "Aspect Ratio")] + public float aspectRatio = 1.0f; + + private List _targets; + private MaterialPropertyCollection _materialProperties; + private Material _material; + + private Matrix4x4 _orthoMatrix; + private Bounds _decalBounds; + + private Transform _decalPreviewTransform; + private Transform _decalModelTransform; + + private bool IsAttached => part.parent != null; + + public override void OnLoad(ConfigNode node) { + if (HighLogic.LoadedSceneIsGame) { + try { + var materialNode = node.GetNode("MATERIAL") ?? throw new FormatException("Missing MATERIAL node in module"); + _materialProperties = new MaterialPropertyCollection(materialNode, this); + _material = _materialProperties.ParsedMaterial; + + var mainTexture = _materialProperties.MainTextureProperty; + if (mainTexture != null) { + aspectRatio = mainTexture.AspectRatio; + } + else { + aspectRatio = 1; + } + + _decalPreviewTransform = part.FindModelTransform(decalPreviewTransform); + if (_decalPreviewTransform == null) throw new FormatException("Missing decal preview reference"); + + _decalModelTransform = part.FindModelTransform(decalModelTransform); + if (_decalModelTransform == null) throw new FormatException("Missing decal mesh reference"); + } + catch (Exception e) { + this.LogException("Exception parsing partmodule", e); + } + } + } + + public override void OnStart(StartState state) { + _orthoMatrix = Matrix4x4.identity; + _orthoMatrix[0, 3] = 0.5f; + _orthoMatrix[1, 3] = 0.5f; - public void OnStart(StartState state) { if ((state & StartState.Editor) != 0) { GameEvents.onEditorPartEvent.Add(OnEditorEvent); + Fields[nameof(scale)].uiControlEditor.onFieldChanged = OnTweakEvent; + Fields[nameof(depth)].uiControlEditor.onFieldChanged = OnTweakEvent; } else { - Attach(); + if (IsAttached) Attach(); } } + public void OnDisable() { + // remove from preCull delegate + Camera.onPreCull -= Render; + } + + public void OnTweakEvent(BaseField field, object obj) { + // scale or depth values have been changed, so update the projection matrix for each target + Project(); + } + public void OnEditorEvent(ConstructionEventType eventType, Part eventPart) { if (eventPart != this.part) return; switch (eventType) { @@ -29,33 +99,65 @@ namespace ConformalDecals { Project(); break; } - } public void Attach() { - if (part.parent == null) { + if (!IsAttached) { this.LogError("Attach function called but part has no parent!"); return; } + // find all valid renderers + var renderers = part.parent.transform.GetComponentsInChildren(false).Where(o => o.GetComponent() != null); + // generate ProjectionTarget objects for each valid meshrenderer + _targets = renderers.Select(o => new ProjectionTarget(o, o.GetComponent().mesh, _materialProperties)).ToList(); + + // hide preview model + _decalModelTransform.gameObject.SetActive(false); + + // add to preCull delegate Camera.onPreCull += Render; } public void Detach() { - if (part.parent != null) { + if (IsAttached) { this.LogError("Detach function called but part still has parent!"); return; } + // unhide preview model + _decalModelTransform.gameObject.SetActive(true); + + // remove from preCull delegate Camera.onPreCull -= Render; } - public void OnDisable() { - Camera.onPreCull -= Render; + [KSPEvent(guiActive = false, guiName = "Project", guiActiveEditor =true, active = true)] + public void Project() { + if (!IsAttached) return; + + // generate orthogonal matrix scale values + _orthoMatrix[0, 0] = 1 / scale; + _orthoMatrix[1, 1] = 1 / (aspectRatio * scale); + _orthoMatrix[2, 2] = 1 / depth; + + // generate bounding box for decal for culling purposes + _decalBounds.center = Vector3.forward * (depth / 2); + _decalBounds.extents = new Vector3(scale / 2, aspectRatio * scale / 2, depth / 2); + + // project to each target object + foreach (var target in _targets) { + target.Project(_orthoMatrix, _decalBounds); + } } - public void Project() { } + public void Render(Camera camera) { + if (!IsAttached) return; - public void Render(Camera camera) { } + // render on each target object + foreach (var target in _targets) { + target.Render(_material, part.mpb, camera); + } + } } } \ No newline at end of file diff --git a/Source/ConformalDecals/ProjectionTarget.cs b/Source/ConformalDecals/ProjectionTarget.cs index 6add0c0..8f32229 100644 --- a/Source/ConformalDecals/ProjectionTarget.cs +++ b/Source/ConformalDecals/ProjectionTarget.cs @@ -48,7 +48,7 @@ namespace ConformalDecals { } } - public void Project(Matrix4x4 orthoMatrix, OrientedBounds projectorBounds) { + public void Project(Matrix4x4 orthoMatrix, Bounds projectorBounds) { var projectorToTargetMatrix = Target.worldToLocalMatrix * Projector.localToWorldMatrix; var projectionMatrix = orthoMatrix * projectorToTargetMatrix.inverse; @@ -60,7 +60,7 @@ namespace ConformalDecals { DecalMPB.SetVector(_decalTangentID, decalTangent); var targetBounds = new OrientedBounds(Target.localToWorldMatrix, _targetRenderer.bounds); - _projectionEnabled = projectorBounds.Intersects(targetBounds); + _projectionEnabled = targetBounds.Intersects(projectorBounds); } public bool Render(Material decalMaterial, MaterialPropertyBlock partMPB, Camera camera) {