-
Notifications
You must be signed in to change notification settings - Fork 2
/
HeadCullingMask.cs
151 lines (137 loc) · 6.25 KB
/
HeadCullingMask.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
using NeosModLoader;
using HarmonyLib;
using FrooxEngine;
using FrooxEngine.CommonAvatar;
using System.Collections.Generic;
using BaseX;
using UnityNeos;
using System.Reflection;
using UnityEngine;
using System.Linq;
namespace HeadCullingMask
{
public class HeadCullingMask : NeosMod
{
public override string Name => "HeadCullingMask";
public override string Author => "hantabaru1014";
public override string Version => "1.2.0";
public override string Link => "https://github.com/hantabaru1014/HeadCullingMask";
public const string TARGET_COMMENT_TEXT = "net.hantabaru1014.HeadCullingMask.TargetSlot";
public const byte USE_LAYER = 10;
private static readonly FieldInfo forceLayerFieldInfo = AccessTools.Field(typeof(SlotConnector), "forceLayer");
private static readonly MethodInfo updateLayerMethodInfo = AccessTools.Method(typeof(SlotConnector), "UpdateLayer");
private static Dictionary<RefID, Slot> avatars;
public override void OnEngineInit()
{
avatars = new Dictionary<RefID, Slot>();
Harmony harmony = new Harmony("net.hantabaru1014.HeadCullingMask");
harmony.PatchAll();
var headOutputs = Resources.FindObjectsOfTypeAll<HeadOutput>()
.Where(c => c.gameObject.hideFlags != HideFlags.NotEditable && c.gameObject.hideFlags != HideFlags.HideAndDontSave);
foreach (var headOutput in headOutputs)
{
foreach (var cam in headOutput.cameras)
{
cam.cullingMask &= ~(1 << USE_LAYER);
Msg($"Patch Camera cullingMask : {cam.name}");
}
}
}
[HarmonyPatch(typeof(ComponentBase<FrooxEngine.Component>), "OnStart")]
class ComponentBase_OnStart_Patch
{
static void Postfix(FrooxEngine.Component __instance)
{
if (!(__instance is AvatarObjectSlot)) return;
if ((__instance.Slot?.ActiveUser?.IsLocalUser ?? false) && (__instance.Slot.Name?.StartsWith("User") ?? false))
{
AvatarObjectSlot avatarObjSlot = __instance as AvatarObjectSlot;
avatarObjSlot.Equipped.OnTargetChange += Equipped_OnTargetChange;
__instance.Disposing += Worker_Disposing;
Msg($"Found AvatarObjectSlot. RefID: {avatarObjSlot.ReferenceID}");
}
}
private static void Worker_Disposing(Worker obj)
{
avatars.Remove(obj.ReferenceID);
Msg($"Dispose AvatarObjectSlot. RefID: {obj.ReferenceID}");
}
private static void Equipped_OnTargetChange(SyncRef<IAvatarObject> avatarObj)
{
if (avatarObj?.Target?.Node != BodyNode.Root) return;
if (avatars.TryGetValue(avatarObj.Worker.ReferenceID, out var oldAvatar))
{
if (!(oldAvatar is null))
{
Msg($"Avatar Dequip : {oldAvatar.Name}");
UpdateLayer(oldAvatar, 0, false);
avatars[avatarObj.Worker.ReferenceID] = null;
}
}
if (avatarObj.State == ReferenceState.Available)
{
Msg($"Avatar Equip : {avatarObj.Target.Slot.Name}");
UpdateLayer(avatarObj.Target.Slot, USE_LAYER, true);
avatars[avatarObj.Worker.ReferenceID] = avatarObj.Target.Slot;
}
}
}
[HarmonyPatch(typeof(ScreenController), "OnInputUpdate")]
class ScreenController_OnInputUpdate_Patch
{
static void Postfix(ScreenController __instance, ScreenInputs ____input, SyncRef<FirstPersonTargettingController> ____firstPerson)
{
if (__instance.World.IsUserspace()) return;
if (____input.ToggleFirstAndThirdPerson.Pressed || ____input.ToggleFreeformCamera.Pressed)
{
var avatarRoot = __instance.Slot.GetComponent<AvatarObjectSlot>(
avObj => avObj.Equipped.Target?.Node == BodyNode.Root && avObj.Equipped.State == ReferenceState.Available
)?.Equipped.Target?.Slot;
if (avatarRoot is null) return;
if (__instance.ActiveTargetting.Target != ____firstPerson.Target)
{
Msg($"Toggle to ThirdPerson.");
UpdateLayer(avatarRoot, 0, false, true);
}
else
{
Msg($"Toggle to FirstPerson.");
UpdateLayer(avatarRoot, USE_LAYER, true, true);
}
}
}
}
[HarmonyPatch(typeof(HeadOutput), "Awake")]
class HeadOutput_Awake_Patch
{
static void Postfix(List<UnityEngine.Camera> ___cameras)
{
foreach (var cam in ___cameras)
{
cam.cullingMask &= ~(1 << USE_LAYER);
Msg($"Patch Camera cullingMask : {cam.name}");
}
}
}
[HarmonyPatch(typeof(CameraPortal), "RenderReflection")]
class CameraPortal_RenderReflection_Patch
{
static void Prefix(UnityEngine.Camera reflectionCamera)
{
reflectionCamera.cullingMask |= (1 << USE_LAYER);
}
}
private static void UpdateLayer(Slot avatarRootSlot, byte layer, bool excludeDisabled, bool forceUpdate = false)
{
List<Comment> list = Pool.BorrowList<Comment>();
avatarRootSlot.GetComponentsInChildren<Comment>(list, (Comment c) => (!excludeDisabled || c.Enabled) && c.Text.Value == TARGET_COMMENT_TEXT, false, false);
foreach (var comment in list)
{
var connector = (SlotConnector)comment.Slot.Connector;
forceLayerFieldInfo.SetValue(connector, layer);
if (forceUpdate) updateLayerMethodInfo.Invoke(connector, null);
Msg($"SetLayer : {comment.Slot.Name} to {(int)layer}");
}
}
}
}