From c4099d90a0288a3a343076050fdd503b4e362e37 Mon Sep 17 00:00:00 2001 From: NotAKidoS <37721153+NotAKidOnSteam@users.noreply.github.com> Date: Sat, 3 Feb 2024 03:28:59 -0600 Subject: [PATCH] [BetterShadowClone] holyshit i overcomplicated it --- BetterShadowClone/ShadowCloneHelper.cs | 18 +- .../TransformHider/FPRExclusion.cs | 13 +- .../ITransformHider/MeshTransformHider.cs | 15 +- .../ITransformHider/SkinnedTransformHider.cs | 191 +++++++++++------- .../TransformHider/TransformHiderManager.cs | 41 +--- 5 files changed, 155 insertions(+), 123 deletions(-) diff --git a/BetterShadowClone/ShadowCloneHelper.cs b/BetterShadowClone/ShadowCloneHelper.cs index 6b66017..5cb9e74 100644 --- a/BetterShadowClone/ShadowCloneHelper.cs +++ b/BetterShadowClone/ShadowCloneHelper.cs @@ -43,7 +43,7 @@ public static class ShadowCloneHelper private static void ProcessRenderers(IEnumerable renderers, Transform root, Transform headBone) { - var exclusions = CollectExclusions2(root, headBone); + IReadOnlyDictionary exclusions = CollectTransformToExclusionMap(root, headBone); foreach (Renderer renderer in renderers) { @@ -55,7 +55,8 @@ public static class ShadowCloneHelper if (clone != null) ShadowCloneManager.Instance.AddShadowClone(clone); } - TransformHiderManager.CreateTransformHider(renderer, exclusions); + ITransformHider hider = TransformHiderManager.CreateTransformHider(renderer, exclusions); + if (hider != null) TransformHiderManager.Instance.AddTransformHider(hider); } } @@ -63,7 +64,7 @@ public static class ShadowCloneHelper #region FPR Exclusion Processing - private static Dictionary CollectExclusions2(Transform root, Transform headBone) + private static Dictionary CollectTransformToExclusionMap(Component root, Transform headBone) { // add an fpr exclusion to the head bone headBone.gameObject.AddComponent().target = headBone; @@ -72,7 +73,7 @@ public static class ShadowCloneHelper var fprExclusions = root.GetComponentsInChildren(true).ToList(); // get all valid exclusion targets, and destroy invalid exclusions - Dictionary exclusionTargetRoots = new(); + Dictionary exclusionTargets = new(); for (int i = fprExclusions.Count - 1; i >= 0; i--) { FPRExclusion exclusion = fprExclusions[i]; @@ -82,7 +83,7 @@ public static class ShadowCloneHelper continue; } - exclusionTargetRoots.Add(exclusion.target, exclusion); + exclusionTargets.Add(exclusion.target, exclusion); } // process each FPRExclusion (recursive) @@ -91,14 +92,15 @@ public static class ShadowCloneHelper // log totals ShadowCloneMod.Logger.Msg($"Exclusions: {fprExclusions.Count}"); - return exclusionTargetRoots; + return exclusionTargets; void ProcessExclusion(FPRExclusion exclusion, Transform transform) { - if (exclusionTargetRoots.ContainsKey(transform) - && exclusionTargetRoots[transform] != exclusion) return; // found other exclusion root + if (exclusionTargets.ContainsKey(transform) + && exclusionTargets[transform] != exclusion) return; // found other exclusion root exclusion.affectedChildren.Add(transform); // associate with the exclusion + exclusionTargets.Add(transform, exclusion); // add to the list (yes theres duplicates) foreach (Transform child in transform) ProcessExclusion(exclusion, child); // process children diff --git a/BetterShadowClone/TransformHider/FPRExclusion.cs b/BetterShadowClone/TransformHider/FPRExclusion.cs index 7c713f5..80ba948 100644 --- a/BetterShadowClone/TransformHider/FPRExclusion.cs +++ b/BetterShadowClone/TransformHider/FPRExclusion.cs @@ -13,7 +13,7 @@ public class FPRExclusion : MonoBehaviour internal List affectedChildren = new(); [NonSerialized] - internal ITransformHider[] relevantHiders; + internal readonly List relatedTasks = new(); private void OnEnable() => SetFPRState(true); @@ -23,8 +23,13 @@ public class FPRExclusion : MonoBehaviour private void SetFPRState(bool state) { - if (relevantHiders == null) return; // no hiders to set - foreach (ITransformHider hider in relevantHiders) - hider.IsActive = state; + if (relatedTasks == null) return; // no hiders to set + foreach (IFPRExclusionTask task in relatedTasks) + task.IsActive = state; } +} + +public interface IFPRExclusionTask +{ + public bool IsActive { get; set; } } \ No newline at end of file diff --git a/BetterShadowClone/TransformHider/ITransformHider/MeshTransformHider.cs b/BetterShadowClone/TransformHider/ITransformHider/MeshTransformHider.cs index 69e174f..a498b5a 100644 --- a/BetterShadowClone/TransformHider/ITransformHider/MeshTransformHider.cs +++ b/BetterShadowClone/TransformHider/ITransformHider/MeshTransformHider.cs @@ -4,7 +4,7 @@ using UnityEngine.Rendering; namespace NAK.BetterShadowClone; -public class MeshTransformHider : ITransformHider +public class MeshTransformHider : ITransformHider, IFPRExclusionTask { // lame 2 frame init stuff private const int FrameInitCount = 0; @@ -23,8 +23,19 @@ public class MeshTransformHider : ITransformHider // anything player can touch is suspect to death public bool IsValid => _mainMesh != null && !_markedForDeath; - public MeshTransformHider(MeshRenderer renderer) + public MeshTransformHider(MeshRenderer renderer, IReadOnlyDictionary exclusions) { + Transform rootBone = renderer.transform; + + // if no key found, dispose + if (!exclusions.TryGetValue(rootBone, out FPRExclusion exclusion)) + { + Dispose(); + return; + } + + exclusion.relatedTasks.Add(this); + _mainMesh = renderer; if (_mainMesh == null diff --git a/BetterShadowClone/TransformHider/ITransformHider/SkinnedTransformHider.cs b/BetterShadowClone/TransformHider/ITransformHider/SkinnedTransformHider.cs index fc0f285..f0be524 100644 --- a/BetterShadowClone/TransformHider/ITransformHider/SkinnedTransformHider.cs +++ b/BetterShadowClone/TransformHider/ITransformHider/SkinnedTransformHider.cs @@ -19,37 +19,28 @@ public class SkinnedTransformHider : ITransformHider private bool _markedForDeath; // mesh & bone - private readonly Transform _shrinkBone; private readonly SkinnedMeshRenderer _mainMesh; private readonly Transform _rootBone; - // exclusion - private readonly FPRExclusion _exclusion; - - // hider stuff + // main hider stuff private GraphicsBuffer _graphicsBuffer; private int _bufferLayout; - private ComputeBuffer _computeBuffer; - private int _vertexCount; - private int _threadGroups; - + // subtasks + private readonly List _subTasks = new(); + #region ITransformHider Methods public bool IsActive { get; set; } = true; // default hide, but FPRExclusion can override // anything player can touch is suspect to death - public bool IsValid => _mainMesh != null && _shrinkBone != null && !_markedForDeath; + public bool IsValid => !_markedForDeath && _mainMesh != null && _rootBone != null; - public SkinnedTransformHider(SkinnedMeshRenderer renderer, FPRExclusion exclusion) + public SkinnedTransformHider(SkinnedMeshRenderer renderer, IReadOnlyDictionary exclusions) { _mainMesh = renderer; - _shrinkBone = exclusion.target; - _exclusion = exclusion; - if (_exclusion == null - || _shrinkBone == null - || _mainMesh == null + if (_mainMesh == null || _mainMesh.sharedMesh == null || _mainMesh.sharedMaterials == null || _mainMesh.sharedMaterials.Length == 0) @@ -58,20 +49,39 @@ public class SkinnedTransformHider : ITransformHider return; // no mesh or bone! } - // find the head vertices - var exclusionVerts = FindExclusionVertList(); - if (exclusionVerts.Count == 0) - { - Dispose(); - return; // no head vertices! - } - _rootBone = _mainMesh.rootBone; _rootBone ??= _mainMesh.transform; // fallback to transform if no root bone - _vertexCount = exclusionVerts.Count; - _computeBuffer = new ComputeBuffer(_vertexCount, sizeof(int)); - _computeBuffer.SetData(exclusionVerts.ToArray()); + // subtask creation + + var bones = renderer.bones; + List fprExclusions = new(); + + foreach (Transform bone in bones) + { + if (bone == null) + continue; // thanks AdvancedSafety for preventing null ref for so long... + + if (!exclusions.TryGetValue(bone, out FPRExclusion exclusion)) + continue; + + fprExclusions.Add(exclusion); + } + + List exclusionVerts; + foreach (FPRExclusion exclusion in fprExclusions) + { + exclusionVerts = SubTask.FindExclusionVertList(renderer, exclusion); + if (exclusionVerts.Count == 0) + continue; + + SubTask subTask = new(this, exclusion.target, exclusionVerts); + _subTasks.Add(subTask); + exclusion.relatedTasks.Add(subTask); + } + + if (_subTasks.Count == 0) + Dispose(); // had the bones, but not the weights :? } public bool Process() @@ -102,26 +112,19 @@ public class SkinnedTransformHider : ITransformHider _frameInitCounter++; return false; } - + public bool PostProcess() - { - return false; // not needed - } + => false; // not needed public void HideTransform() { _mainMesh.forceRenderingOff = false; - // probably fine - Vector3 pos = _rootBone.transform.InverseTransformPoint(_shrinkBone.position) * _rootBone.lossyScale.y; - _graphicsBuffer = _mainMesh.GetVertexBuffer(); - TransformHiderManager.shader.SetVector(s_Pos, pos); - TransformHiderManager.shader.SetInt(s_WeightedCount, _vertexCount); - TransformHiderManager.shader.SetInt(s_BufferLayout, _bufferLayout); - TransformHiderManager.shader.SetBuffer(0, s_WeightedVertices, _computeBuffer); - TransformHiderManager.shader.SetBuffer(0, s_VertexBuffer, _graphicsBuffer); - TransformHiderManager.shader.Dispatch(0, _threadGroups, 1, 1); + + foreach (SubTask subTask in _subTasks) + if (subTask.IsActive && subTask.IsValid) subTask.Dispatch(); + _graphicsBuffer.Release(); } @@ -133,10 +136,11 @@ public class SkinnedTransformHider : ITransformHider public void Dispose() { _markedForDeath = true; + foreach (SubTask subTask in _subTasks) + subTask.Dispose(); + _graphicsBuffer?.Dispose(); _graphicsBuffer = null; - _computeBuffer?.Dispose(); - _computeBuffer = null; } #endregion @@ -152,40 +156,83 @@ public class SkinnedTransformHider : ITransformHider if (mesh.HasVertexAttribute(VertexAttribute.Position)) _bufferLayout += 3; if (mesh.HasVertexAttribute(VertexAttribute.Normal)) _bufferLayout += 3; if (mesh.HasVertexAttribute(VertexAttribute.Tangent)) _bufferLayout += 4; - // ComputeShader is doing bitshift so we dont need to multiply by 4 - //_bufferLayout *= 4; // 4 bytes per float - - const float xThreadGroups = 64f; - _threadGroups = Mathf.CeilToInt(mesh.vertexCount / xThreadGroups); _mainMesh.vertexBufferTarget |= GraphicsBuffer.Target.Raw; } - - private List FindExclusionVertList() - { - var boneWeights = _mainMesh.sharedMesh.boneWeights; - var bones = _exclusion.affectedChildren; - - HashSet weights = new(); //get indexs of child bones - for (int i = 0; i < _mainMesh.bones.Length; i++) - if (bones.Contains(_mainMesh.bones[i])) weights.Add(i); - - List headVertices = new(); - - for (int i = 0; i < boneWeights.Length; i++) - { - BoneWeight weight = boneWeights[i]; - const float minWeightThreshold = 0.2f; - if (weights.Contains(weight.boneIndex0) && weight.weight0 > minWeightThreshold - || weights.Contains(weight.boneIndex1) && weight.weight1 > minWeightThreshold - || weights.Contains(weight.boneIndex2) && weight.weight2 > minWeightThreshold - || weights.Contains(weight.boneIndex3) && weight.weight3 > minWeightThreshold) - headVertices.Add(i); - } - - return headVertices; - } #endregion + + #region Sub Task Class + + private class SubTask : IFPRExclusionTask + { + public bool IsActive { get; set; } = true; + public bool IsValid => _computeBuffer != null; // TODO: cleanup dead tasks + + private readonly SkinnedTransformHider _parent; + private readonly Transform _shrinkBone; + private readonly int _vertexCount; + private readonly ComputeBuffer _computeBuffer; + private readonly int _threadGroups; + + public SubTask(SkinnedTransformHider parent, Transform shrinkBone, List exclusionVerts) + { + _parent = parent; + _shrinkBone = shrinkBone; + + _vertexCount = exclusionVerts.Count; + _computeBuffer = new ComputeBuffer(_vertexCount, sizeof(int)); + _computeBuffer.SetData(exclusionVerts.ToArray()); + + const float xThreadGroups = 64f; + _threadGroups = Mathf.CeilToInt(_vertexCount / xThreadGroups); + } + + public void Dispatch() + { + Vector3 pos = _parent._rootBone.transform.InverseTransformPoint(_shrinkBone.position) * _parent._rootBone.lossyScale.y; + TransformHiderManager.shader.SetVector(s_Pos, pos); + TransformHiderManager.shader.SetInt(s_WeightedCount, _vertexCount); + TransformHiderManager.shader.SetInt(s_BufferLayout, _parent._bufferLayout); + TransformHiderManager.shader.SetBuffer(0, s_WeightedVertices, _computeBuffer); + TransformHiderManager.shader.SetBuffer(0, s_VertexBuffer, _parent._graphicsBuffer); + TransformHiderManager.shader.Dispatch(0, _threadGroups, 1, 1); + } + + public void Dispose() + { + _computeBuffer?.Dispose(); + } + + #region Private Methods + + public static List FindExclusionVertList(SkinnedMeshRenderer renderer, FPRExclusion exclusion) + { + var boneWeights = renderer.sharedMesh.boneWeights; + var bones = exclusion.affectedChildren; + + HashSet weights = new(); + for (int i = 0; i < renderer.bones.Length; i++) + if (bones.Contains(renderer.bones[i])) weights.Add(i); + + List headVertices = new(); + for (int i = 0; i < boneWeights.Length; i++) + { + BoneWeight weight = boneWeights[i]; + const float minWeightThreshold = 0.2f; + if (weights.Contains(weight.boneIndex0) && weight.weight0 > minWeightThreshold + || weights.Contains(weight.boneIndex1) && weight.weight1 > minWeightThreshold + || weights.Contains(weight.boneIndex2) && weight.weight2 > minWeightThreshold + || weights.Contains(weight.boneIndex3) && weight.weight3 > minWeightThreshold) + headVertices.Add(i); + } + + return headVertices; + } + + #endregion + } + + #endregion } \ No newline at end of file diff --git a/BetterShadowClone/TransformHider/TransformHiderManager.cs b/BetterShadowClone/TransformHider/TransformHiderManager.cs index 13040e0..b820d93 100644 --- a/BetterShadowClone/TransformHider/TransformHiderManager.cs +++ b/BetterShadowClone/TransformHider/TransformHiderManager.cs @@ -1,7 +1,4 @@ -using System.Collections.Generic; -using ABI_RC.Core.Player; -using ABI_RC.Core.Savior; -using ABI_RC.Systems.Camera; +using ABI_RC.Core.Player; using MagicaCloth; using UnityEngine; @@ -190,48 +187,18 @@ public class TransformHiderManager : MonoBehaviour internal static bool IsLegacyFPRExcluded(Component renderer) => renderer.gameObject.name.Contains("[FPR]"); - internal static ITransformHider CreateTransformHider(Component renderer, Transform bone) + internal static ITransformHider CreateTransformHider(Component renderer, IReadOnlyDictionary exclusions) { if (IsLegacyFPRExcluded(renderer)) return null; return renderer switch { - //SkinnedMeshRenderer skinnedMeshRenderer => new SkinnedTransformHider(skinnedMeshRenderer, bone), - MeshRenderer meshRenderer => new MeshTransformHider(meshRenderer), + SkinnedMeshRenderer skinnedMeshRenderer => new SkinnedTransformHider(skinnedMeshRenderer, exclusions), + MeshRenderer meshRenderer => new MeshTransformHider(meshRenderer, exclusions), _ => null }; } - - internal static void CreateTransformHider(Component renderer, Dictionary exclusions) - { - if (IsLegacyFPRExcluded(renderer)) - return; - - if (renderer is SkinnedMeshRenderer skinnedMeshRenderer) - { - // get all bones for renderer - var bones = skinnedMeshRenderer.bones; - List fprExclusions = new(); - - // check if any bones are excluded - foreach (Transform bone in bones) - { - if (!exclusions.TryGetValue(bone, out FPRExclusion exclusion)) - continue; - - fprExclusions.Add(exclusion); - } - - foreach (FPRExclusion exclusion in fprExclusions) - { - ITransformHider hider = new SkinnedTransformHider(skinnedMeshRenderer, exclusion); - Instance.AddTransformHider(hider); - } - } - - return; - } #endregion } \ No newline at end of file