Skip to content

Commit

Permalink
50% functional re-written rendering patches.
Browse files Browse the repository at this point in the history
  • Loading branch information
Epicguru committed Apr 14, 2024
1 parent d0f7e98 commit 08cdd43
Show file tree
Hide file tree
Showing 13 changed files with 124 additions and 111 deletions.
1 change: 1 addition & 0 deletions Source/AnimationMod.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=Lerp/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Lightsaber/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=minecraft/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=parms/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=pathfinding/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=pathing/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Renderers/@EntryIndexedValue">True</s:Boolean>
Expand Down
8 changes: 2 additions & 6 deletions Source/AnimationMod/AnimRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -912,14 +912,12 @@ protected void DrawPawns(Action<Pawn, Vector2> 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;
Expand Down Expand Up @@ -972,15 +970,13 @@ protected void DrawPawns(Action<Pawn, Vector2> 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.
Expand Down
4 changes: 2 additions & 2 deletions Source/AnimationMod/Heads/HeadInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
}
Expand Down
14 changes: 10 additions & 4 deletions Source/AnimationMod/Patches/PatchMaster.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ namespace AM.Patches;

/// <summary>
/// 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.
/// </summary>
[HarmonyPatch(typeof(PawnRenderUtility), nameof(PawnRenderUtility.DrawEquipmentAiming))]
public static class Patch_PawnRenderer_DrawEquipment
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using HarmonyLib;
using JetBrains.Annotations;
using Verse;

namespace AM.Patches;

/// <summary>
/// Suppresses shadow draw done by PawnRenderer's DrawShadowInternal.
/// </summary>
[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;
}
108 changes: 82 additions & 26 deletions Source/AnimationMod/Patches/Patch_PawnRenderer_RenderPawnAt.cs
Original file line number Diff line number Diff line change
@@ -1,57 +1,113 @@
using AM.Grappling;
using HarmonyLib;
using UnityEngine;
using Verse;

namespace AM.Patches;

/// <summary>
/// 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 <see cref="PawnRenderTree.ParallelPreDraw(PawnDrawParms)"/> with the arguments that I want in order to modify how the pawn will actually be displayed.
/// </summary>
[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;
}
}
}

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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@ namespace AM.Patches;
/// and body angle. Driven by the active animation.
/// Also used to render severed heads.
/// </summary>
[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;
Expand Down
23 changes: 0 additions & 23 deletions Source/AnimationMod/Patches/Patch_PawnUtility_GetPosture.cs

This file was deleted.

4 changes: 1 addition & 3 deletions Source/AnimationMod/Patches/Patch_PawnUtility_IsInvisible.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,14 @@ 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)
{
if (!Core.Settings.AllowInvisiblePawns)
return true;

var anim = PatchMaster.GetAnimator(pawn);
if (anim != null && !IsRendering)
if (anim != null)
{
__result = true;
return false;
Expand Down

0 comments on commit 08cdd43

Please sign in to comment.