From 08cdd43ff217e55dbcfa985b1895804e085b3bd2 Mon Sep 17 00:00:00 2001 From: James Billy Date: Sun, 14 Apr 2024 11:25:56 +0200 Subject: [PATCH] 50% functional re-written rendering patches. --- Source/AnimationMod.sln.DotSettings | 1 + Source/AnimationMod/AnimRenderer.cs | 8 +- Source/AnimationMod/Heads/HeadInstance.cs | 4 +- Source/AnimationMod/Patches/PatchMaster.cs | 14 ++- ...lTextureAtlasManager_TryGetPawnFrameSet.cs | 28 ----- .../Patch_PawnGenerator_GeneratePawn.cs | 2 +- .../Patch_PawnRenderer_DrawEquipmentAiming.cs | 2 + .../Patch_PawnRenderer_DrawInvisibleShadow.cs | 17 --- .../Patch_PawnRenderer_DrawShadowInternal.cs | 18 +++ .../Patch_PawnRenderer_RenderPawnAt.cs | 108 +++++++++++++----- .../Patch_PawnRenderer_RenderPawnInternal.cs | 6 +- .../Patches/Patch_PawnUtility_GetPosture.cs | 23 ---- .../Patches/Patch_PawnUtility_IsInvisible.cs | 4 +- 13 files changed, 124 insertions(+), 111 deletions(-) delete mode 100644 Source/AnimationMod/Patches/Patch_GlobalTextureAtlasManager_TryGetPawnFrameSet.cs delete mode 100644 Source/AnimationMod/Patches/Patch_PawnRenderer_DrawInvisibleShadow.cs create mode 100644 Source/AnimationMod/Patches/Patch_PawnRenderer_DrawShadowInternal.cs delete mode 100644 Source/AnimationMod/Patches/Patch_PawnUtility_GetPosture.cs diff --git a/Source/AnimationMod.sln.DotSettings b/Source/AnimationMod.sln.DotSettings index 0e1e6115..f63c8b6d 100644 --- a/Source/AnimationMod.sln.DotSettings +++ b/Source/AnimationMod.sln.DotSettings @@ -23,6 +23,7 @@ True True True + True True True True diff --git a/Source/AnimationMod/AnimRenderer.cs b/Source/AnimationMod/AnimRenderer.cs index 87c40965..a4738a98 100644 --- a/Source/AnimationMod/AnimRenderer.cs +++ b/Source/AnimationMod/AnimRenderer.cs @@ -912,14 +912,12 @@ protected void DrawPawns(Action labelDraw = null) Patch_PawnRenderer_RenderPawnInternal.AllowNext = true; Patch_PawnRenderer_RenderPawnInternal.DoNotModify = true; // Don't use animation position/rotation. Patch_PawnRenderer_RenderPawnInternal.NextDrawMode = Patch_PawnRenderer_RenderPawnInternal.DrawMode.Full; - Patch_PawnUtility_IsInvisible.IsRendering = true; PrePawnSpecialRender?.Invoke(pawn, this); pawn.DrawNowAt(pawn.DrawPosHeld ?? pawn.DrawPos); PostPawnSpecialRender?.Invoke(pawn, this); Patch_PawnRenderer_RenderPawnInternal.DoNotModify = false; - Patch_PawnUtility_IsInvisible.IsRendering = false; // Draw label. Vector3 drawPos2 = pawn.DrawPos; @@ -972,15 +970,13 @@ protected void DrawPawns(Action labelDraw = null) ? Patch_PawnRenderer_RenderPawnInternal.DrawMode.Full : i == 0 ? Patch_PawnRenderer_RenderPawnInternal.DrawMode.BodyOnly : Patch_PawnRenderer_RenderPawnInternal.DrawMode.HeadOnly; - Patch_PawnUtility_IsInvisible.IsRendering = true; - Patch_PawnRenderer_DrawInvisibleShadow.Suppress = suppressShadow; // In 1.4 shadow rendering is baked into RenderPawnAt and may need to be prevented. + Patch_PawnRenderer_DrawShadowInternal.Suppress = suppressShadow; // In 1.4 shadow rendering is baked into RenderPawnAt and may need to be prevented. PrePawnSpecialRender?.Invoke(pawn, this); pawn.Drawer.renderer.RenderPawnAt(pos, dir, true); // This direction here is not the final one. PostPawnSpecialRender?.Invoke(pawn, this); - Patch_PawnRenderer_DrawInvisibleShadow.Suppress = false; - Patch_PawnUtility_IsInvisible.IsRendering = false; + Patch_PawnRenderer_DrawShadowInternal.Suppress = false; } // Render shadow. diff --git a/Source/AnimationMod/Heads/HeadInstance.cs b/Source/AnimationMod/Heads/HeadInstance.cs index 9bf2415b..fedc995a 100644 --- a/Source/AnimationMod/Heads/HeadInstance.cs +++ b/Source/AnimationMod/Heads/HeadInstance.cs @@ -36,7 +36,7 @@ public bool Render() Patch_PawnRenderer_RenderPawnInternal.NextDrawMode = Patch_PawnRenderer_RenderPawnInternal.DrawMode.HeadStandalone; Patch_PawnRenderer_RenderPawnInternal.HeadRotation = Direction; Patch_PawnRenderer_RenderPawnInternal.StandaloneHeadRotation = Rotation; - Patch_PawnRenderer_DrawInvisibleShadow.Suppress = true; // In 1.4 shadow rendering is baked into RenderPawnAt and may need to be prevented. + Patch_PawnRenderer_DrawShadowInternal.Suppress = true; // In 1.4 shadow rendering is baked into RenderPawnAt and may need to be prevented. Patch_PawnRenderer_RenderPawnInternal.AllowNext = true; try @@ -46,7 +46,7 @@ public bool Render() finally { Patch_PawnRenderer_RenderPawnInternal.NextDrawMode = Patch_PawnRenderer_RenderPawnInternal.DrawMode.Full; - Patch_PawnRenderer_DrawInvisibleShadow.Suppress = false; + Patch_PawnRenderer_DrawShadowInternal.Suppress = false; } return true; } diff --git a/Source/AnimationMod/Patches/PatchMaster.cs b/Source/AnimationMod/Patches/PatchMaster.cs index ba6d5f03..1d274191 100644 --- a/Source/AnimationMod/Patches/PatchMaster.cs +++ b/Source/AnimationMod/Patches/PatchMaster.cs @@ -4,16 +4,22 @@ namespace AM.Patches; public static class PatchMaster { + private static readonly object key = new object(); + private static Pawn lastPawn; private static AnimRenderer lastRenderer; public static AnimRenderer GetAnimator(Pawn pawn) { - if (pawn == lastPawn && lastRenderer is { IsDestroyed: false }) - return lastRenderer; + lock (key) + { + if (pawn == lastPawn && lastRenderer is { IsDestroyed: false }) + return lastRenderer; + + lastPawn = pawn; + lastRenderer = AnimRenderer.TryGetAnimator(pawn); + } - lastPawn = pawn; - lastRenderer = AnimRenderer.TryGetAnimator(pawn); return lastRenderer; } } diff --git a/Source/AnimationMod/Patches/Patch_GlobalTextureAtlasManager_TryGetPawnFrameSet.cs b/Source/AnimationMod/Patches/Patch_GlobalTextureAtlasManager_TryGetPawnFrameSet.cs deleted file mode 100644 index 8d0a2b97..00000000 --- a/Source/AnimationMod/Patches/Patch_GlobalTextureAtlasManager_TryGetPawnFrameSet.cs +++ /dev/null @@ -1,28 +0,0 @@ -using HarmonyLib; -using Verse; - -namespace AM.Patches; - -/// -/// Disables the texture caching introduced in Rimworld 1.3. -/// Only applies when a pawn in being animated, or when they have been beheaded. -/// -[HarmonyPatch(typeof(GlobalTextureAtlasManager), nameof(GlobalTextureAtlasManager.TryGetPawnFrameSet))] -public static class Patch_GlobalTextureAtlasManager_TryGetPawnFrameSet -{ - [HarmonyPriority(Priority.First)] - public static bool Prefix(Pawn pawn, ref bool createdNew, ref bool __result) - { - var anim = PatchMaster.GetAnimator(pawn); - if (anim == null) - { - var isBeheaded = AnimationManager.PawnToHeadInstance.TryGetValue(pawn, out _); - if (!isBeheaded) - return true; - } - - createdNew = false; - __result = false; - return false; - } -} \ No newline at end of file diff --git a/Source/AnimationMod/Patches/Patch_PawnGenerator_GeneratePawn.cs b/Source/AnimationMod/Patches/Patch_PawnGenerator_GeneratePawn.cs index b838c4f7..cb68fb7d 100644 --- a/Source/AnimationMod/Patches/Patch_PawnGenerator_GeneratePawn.cs +++ b/Source/AnimationMod/Patches/Patch_PawnGenerator_GeneratePawn.cs @@ -55,7 +55,7 @@ private static bool HasSkillToUseLasso(Pawn pawn) private static void GiveLasso(Pawn pawn, ThingDef lasso) { - if (pawn == null || lasso == null) + if (pawn?.apparel == null || lasso == null) return; var thing = ThingMaker.MakeThing(lasso) as Apparel; diff --git a/Source/AnimationMod/Patches/Patch_PawnRenderer_DrawEquipmentAiming.cs b/Source/AnimationMod/Patches/Patch_PawnRenderer_DrawEquipmentAiming.cs index 0ce9295d..9f5bba92 100644 --- a/Source/AnimationMod/Patches/Patch_PawnRenderer_DrawEquipmentAiming.cs +++ b/Source/AnimationMod/Patches/Patch_PawnRenderer_DrawEquipmentAiming.cs @@ -7,6 +7,8 @@ namespace AM.Patches; /// /// Used to override drawing melee weapons. +/// Prevents the standard (or modded) melee weapon from rendering, +/// and also notifies the IdleControllerComp that it is supposed to be rendering. /// [HarmonyPatch(typeof(PawnRenderUtility), nameof(PawnRenderUtility.DrawEquipmentAiming))] public static class Patch_PawnRenderer_DrawEquipment diff --git a/Source/AnimationMod/Patches/Patch_PawnRenderer_DrawInvisibleShadow.cs b/Source/AnimationMod/Patches/Patch_PawnRenderer_DrawInvisibleShadow.cs deleted file mode 100644 index 01c801f9..00000000 --- a/Source/AnimationMod/Patches/Patch_PawnRenderer_DrawInvisibleShadow.cs +++ /dev/null @@ -1,17 +0,0 @@ -using HarmonyLib; -using JetBrains.Annotations; -using Verse; - -namespace AM.Patches; - -/// -/// Suppresses shadow draw which was added in 1.4 in the . -/// -[HarmonyPatch(typeof(PawnRenderer), nameof(PawnRenderer.RenderShadowOnlyAt))] -[UsedImplicitly] -public class Patch_PawnRenderer_DrawInvisibleShadow -{ - public static bool Suppress = false; - - public static bool Prefix() => !Suppress; -} \ No newline at end of file diff --git a/Source/AnimationMod/Patches/Patch_PawnRenderer_DrawShadowInternal.cs b/Source/AnimationMod/Patches/Patch_PawnRenderer_DrawShadowInternal.cs new file mode 100644 index 00000000..2104a919 --- /dev/null +++ b/Source/AnimationMod/Patches/Patch_PawnRenderer_DrawShadowInternal.cs @@ -0,0 +1,18 @@ +using HarmonyLib; +using JetBrains.Annotations; +using Verse; + +namespace AM.Patches; + +/// +/// Suppresses shadow draw done by PawnRenderer's DrawShadowInternal. +/// +[HarmonyPatch(typeof(PawnRenderer), nameof(PawnRenderer.DrawShadowInternal))] +[UsedImplicitly] +public class Patch_PawnRenderer_DrawShadowInternal +{ + public static bool Suppress = false; + + [HarmonyPriority(Priority.First)] + public static bool Prefix() => !Suppress; +} \ No newline at end of file diff --git a/Source/AnimationMod/Patches/Patch_PawnRenderer_RenderPawnAt.cs b/Source/AnimationMod/Patches/Patch_PawnRenderer_RenderPawnAt.cs index eafb1a0b..90015703 100644 --- a/Source/AnimationMod/Patches/Patch_PawnRenderer_RenderPawnAt.cs +++ b/Source/AnimationMod/Patches/Patch_PawnRenderer_RenderPawnAt.cs @@ -1,57 +1,113 @@ using AM.Grappling; using HarmonyLib; +using UnityEngine; using Verse; namespace AM.Patches; /// -/// Simply prevents the regular RenderPawnAt method from running while a pawn in being animated. -/// This disables the regular rendering whenever a pawn in being animated. -/// Additionally, modifies PawnRenderer.results to make sure that the RenderPawnInternal method gets called every frame -/// while in an animation. +/// When a pawn is being animated, RenderPawnAt needs to be modified: +/// first, the private field 'results' needs to be modified in order to ensure that RenderPawnDynamic is called. +/// Next, call with the arguments that I want in order to modify how the pawn will actually be displayed. /// [HarmonyPatch(typeof(PawnRenderer), nameof(PawnRenderer.RenderPawnAt))] public static class Patch_PawnRenderer_RenderPawnAt { public static bool AllowNext; - private static PawnRenderer.PreRenderResults? storedResultsTemp; - [HarmonyPriority(Priority.First)] - public static bool Prefix(Pawn ___pawn, ref PawnRenderer.PreRenderResults ___results) + public static bool Prefix(Pawn ___pawn, PawnRenderTree ___renderTree, ref PawnRenderer.PreRenderResults ___results, ref PawnRenderer.PreRenderResults? __state) { - var anim = PatchMaster.GetAnimator(___pawn); + __state = null; // Harmony requires this to be initialized. - if (anim != null && !AllowNext) - { - return false; - } + // Draw the ropes that bind the pawn when being grappled. + DrawGrappleRopeIfRequired(___pawn); - var job = ___pawn.CurJob; - if (job?.def == AM_DefOf.AM_GrapplePawn) + // Try to get an active animator for this pawn. + var animator = PatchMaster.GetAnimator(___pawn); + if (animator == null) { - JobDriver_GrapplePawn.DrawEnsnaringRope(___pawn, job); + return true; } - if (AllowNext) + // Ok, the pawn is in an active animation, but we want to suppress the regular draw call + // in order to draw only when we want to. This is controlled by the AllowNext flag. + if (!AllowNext) { - storedResultsTemp = ___results; - ___results.showBody = true; - ___results.useCached = false; - ___results.valid = true; + return false; } + // Auto-reset AllowNext flag. AllowNext = false; - return true; + + // Pawn is being animated, so the 'results' field must be modified to + // ensure that RenderPawnInternal gets called, as opposed to cached rendering. + // Store the current value of results in 'state', so that is can be restored in the postfix. + __state = ___results; + + // Setting these values ensures that RenderPawnInternal gets called: + ___results.valid = true; + ___results.draw = true; + ___results.useCached = false; + + // Make are the arguments that get passed into RenderPawnInternal and ParallelPreDraw. + // Note that the original results.parms is used as a base, which is useful because we can inherit some + // flags like 'coveredInFoam'. + MakeDrawArgs(animator, ___pawn, ref ___results.parms); + + // Most importantly, need to call ParallelPreRender on the renderTree to actually + // set up all the matrices and whatnot that gets used when renderTree draw is called. + ___renderTree.ParallelPreDraw(___results.parms); + + // Do run the regular RenderPawnAt method now. + return true; } [HarmonyPriority(Priority.First)] - public static void Postfix(ref PawnRenderer.PreRenderResults ___results) + public static void Postfix(PawnRenderer.PreRenderResults? __state, ref PawnRenderer.PreRenderResults ___results) { - if (storedResultsTemp != null) + // Restore private field 'results' to original value before modification, + // if required. + if (__state != null) { - ___results = storedResultsTemp.Value; - storedResultsTemp = null; + ___results = __state.Value; } } -} \ No newline at end of file + + private static void DrawGrappleRopeIfRequired(Pawn pawn) + { + var job = pawn.CurJob; + if (job?.def == AM_DefOf.AM_GrapplePawn) + { + JobDriver_GrapplePawn.DrawEnsnaringRope(pawn, job); + } + } + + public static void MakeDrawArgs(AnimRenderer animator, Pawn pawn, ref PawnDrawParms parms) + { + // Try to get the pawn snapshot. + var part = animator.GetPawnBody(pawn); + var snapshot = animator.GetSnapshot(part); + + // Dead pawns should not be animated, but make sure that it is not dead just in case. + parms.dead = false; + + // Debug: + parms.tint = Color.magenta; + + // Must be standing. + parms.posture = RimWorld.PawnPosture.Standing; + + // Remove invisible flag. + parms.flags &= ~PawnRenderFlags.Invisible; + + // Set facing direction: + parms.facing = snapshot.GetWorldDirection(); + + // New transform matrix calculation: + Vector3 worldPos = snapshot.GetWorldPosition(); + float worldAngle = snapshot.GetWorldRotation(); + Vector3 scale = snapshot.LocalScale; // Just use local part as scale, not currently used anyway but might be useful in future. + parms.matrix = Matrix4x4.TRS(worldPos + pawn.ageTracker.CurLifeStage.bodyDrawOffset, Quaternion.Euler(0, worldAngle, 0), scale); + } +} diff --git a/Source/AnimationMod/Patches/Patch_PawnRenderer_RenderPawnInternal.cs b/Source/AnimationMod/Patches/Patch_PawnRenderer_RenderPawnInternal.cs index 6d5e1d48..7d6a72ff 100644 --- a/Source/AnimationMod/Patches/Patch_PawnRenderer_RenderPawnInternal.cs +++ b/Source/AnimationMod/Patches/Patch_PawnRenderer_RenderPawnInternal.cs @@ -11,9 +11,13 @@ namespace AM.Patches; /// and body angle. Driven by the active animation. /// Also used to render severed heads. /// -[HarmonyPatch(typeof(PawnRenderer), nameof(PawnRenderer.RenderPawnInternal))] +//[HarmonyPatch(typeof(PawnRenderer), nameof(PawnRenderer.RenderPawnInternal))] static class Patch_PawnRenderer_RenderPawnInternal { + /* + * Steps to ensure that I can render exactly how I want: + */ + public static bool AllowNext; public static bool DoNotModify = false; public static DrawMode NextDrawMode = DrawMode.Full; diff --git a/Source/AnimationMod/Patches/Patch_PawnUtility_GetPosture.cs b/Source/AnimationMod/Patches/Patch_PawnUtility_GetPosture.cs deleted file mode 100644 index db570786..00000000 --- a/Source/AnimationMod/Patches/Patch_PawnUtility_GetPosture.cs +++ /dev/null @@ -1,23 +0,0 @@ -using HarmonyLib; -using RimWorld; -using Verse; - -namespace AM.Patches; - -/// -/// Makes it so that pawns that are being animated stand up, ignoring regular posture calculation. -/// -[HarmonyPatch(typeof(PawnUtility), nameof(PawnUtility.GetPosture))] -public static class Patch_PawnUtility_GetPosture -{ - [HarmonyPriority(Priority.First)] - public static bool Prefix(Pawn p, ref PawnPosture __result) - { - var anim = PatchMaster.GetAnimator(p); - if (anim == null) - return true; - - __result = PawnPosture.Standing; - return false; - } -} \ No newline at end of file diff --git a/Source/AnimationMod/Patches/Patch_PawnUtility_IsInvisible.cs b/Source/AnimationMod/Patches/Patch_PawnUtility_IsInvisible.cs index 2e0c0c27..a626ab15 100644 --- a/Source/AnimationMod/Patches/Patch_PawnUtility_IsInvisible.cs +++ b/Source/AnimationMod/Patches/Patch_PawnUtility_IsInvisible.cs @@ -16,8 +16,6 @@ namespace AM.Patches; [HarmonyPatch(typeof(InvisibilityUtility), nameof(InvisibilityUtility.IsPsychologicallyInvisible))] public static class Patch_PawnUtility_IsInvisible { - public static bool IsRendering; - [HarmonyPriority(Priority.First)] public static bool Prefix(Pawn pawn, ref bool __result) { @@ -25,7 +23,7 @@ public static bool Prefix(Pawn pawn, ref bool __result) return true; var anim = PatchMaster.GetAnimator(pawn); - if (anim != null && !IsRendering) + if (anim != null) { __result = true; return false;