[OriginShift] Initial Fuckup

This commit is contained in:
NotAKidoS 2024-06-18 12:22:10 -05:00
parent 14ab184db7
commit 2375678a59
33 changed files with 2107 additions and 29 deletions

View file

@ -0,0 +1,133 @@
using UnityEngine;
#if !UNITY_EDITOR
using UnityEngine.SceneManagement;
using NAK.OriginShift.Utility;
#endif
// Creator Exposed component
namespace NAK.OriginShift.Components
{
public class OriginShiftController : MonoBehaviour
{
public static OriginShiftController Instance { get; private set; }
#region Serialized Fields
[Header("Config / Shift Params")]
[SerializeField] private bool _shiftVertical = true;
[SerializeField] [Range(10f, 2500f)] private float _shiftThreshold = 15f;
[Header("Config / Scene Objects")]
[SerializeField] private bool _autoMoveSceneRoots = true;
[SerializeField] private Transform[] _toShiftTransforms = Array.Empty<Transform>();
[Header("Config / Additive Objects")]
[SerializeField] private bool _shiftRemotePlayers = true;
[SerializeField] private bool _shiftSpawnedObjects = true;
#endregion Serialized Fields
#region Internal Fields
internal bool IsForced { get; set; }
#endregion Internal Fields
#if !UNITY_EDITOR
public static float ORIGIN_SHIFT_THRESHOLD = 15f;
#region Unity Events
private void Awake()
{
if (Instance != null
&& Instance != this)
{
Destroy(this);
OriginShiftMod.Logger.Error("Only one OriginShiftController can exist in a scene.");
return;
}
Instance = this;
}
private void Start()
{
// set threshold (we can not support dynamic threshold change)
ORIGIN_SHIFT_THRESHOLD = _shiftThreshold;
OriginShiftManager.OnOriginShifted += OnOriginShifted;
OriginShiftManager.Instance.SetupManager(IsForced);
// if auto, we will just move everything :)
if (_autoMoveSceneRoots)
GetAllSceneRootTransforms();
// if we have scene roots, we will anchor all static renderers
if (_toShiftTransforms.Length != 0)
AnchorAllStaticRenderers();
}
private void OnDestroy()
{
OriginShiftManager.OnOriginShifted -= OnOriginShifted;
OriginShiftManager.Instance.ResetManager();
}
#endregion Unity Events
#region Private Methods
private void GetAllSceneRootTransforms()
{
Scene scene = gameObject.scene;
var sceneRoots = scene.GetRootGameObjects();
_toShiftTransforms = new Transform[sceneRoots.Length + 1]; // +1 for the static batch anchor
for (var i = 0; i < sceneRoots.Length; i++) _toShiftTransforms[i] = sceneRoots[i].transform;
}
private void AnchorAllStaticRenderers()
{
// create an anchor object at 0,0,0
Transform anchor = new GameObject("NAK.StaticBatchAnchor").transform;
anchor.SetPositionAndRotation(Vector3.zero, Quaternion.identity);
anchor.localScale = Vector3.one;
// add to end of root transforms
_toShiftTransforms[^1] = anchor;
// crawl all children and find Renderers part of static batch
foreach (Transform toShiftTransform in _toShiftTransforms)
{
var renderers = toShiftTransform.GetComponentsInChildren<Renderer>(true);
foreach (Renderer renderer in renderers)
{
if (renderer.isPartOfStaticBatch) // access staticBatchRootTransform using reflection and override it
RendererReflectionUtility.SetStaticBatchRootTransform(renderer, anchor);
}
}
}
#endregion Private Methods
#region Origin Shift Events
private void OnOriginShifted(Vector3 shift)
{
foreach (Transform toShiftTransform in _toShiftTransforms)
{
if (toShiftTransform == null) continue; // skip nulls
toShiftTransform.position += shift;
}
}
#endregion Origin Shift Events
#endif
}
}

View file

@ -0,0 +1,78 @@
using UnityEngine;
using UnityEngine.Events;
#if !UNITY_EDITOR
using ABI_RC.Core.Util.AssetFiltering;
#endif
namespace NAK.OriginShift.Components
{
public class OriginShiftEventReceiver : MonoBehaviour
{
#region Serialized Fields
[SerializeField] private UnityEvent _onOriginShifted = new();
[SerializeField] private bool _filterChunkBoundary;
[SerializeField] private Vector3 _chunkBoundaryMin = Vector3.zero;
[SerializeField] private Vector3 _chunkBoundaryMax = Vector3.one;
#endregion Serialized Fields
#if !UNITY_EDITOR
#region Private Fields
private bool _isInitialized;
#endregion Private Fields
#region Unity Events
private void OnEnable()
{
if (!_isInitialized)
{
SharedFilter.SanitizeUnityEvents("OriginShiftEventReceiver", _onOriginShifted);
_isInitialized = true;
}
OriginShiftManager.OnOriginShifted += HandleOriginShifted;
}
private void OnDisable()
{
OriginShiftManager.OnOriginShifted -= HandleOriginShifted;
}
#endregion Unity Events
#region Origin Shift Events
private void HandleOriginShifted(Vector3 shift)
{
if (_filterChunkBoundary && !IsWithinChunkBoundary(shift))
return;
// wrap user-defined event because the user can't be trusted
try
{
_onOriginShifted.Invoke();
}
catch (Exception e)
{
OriginShiftMod.Logger.Error("OriginShiftEventReceiver: Exception invoking OnOriginShifted event: " + e, this);
}
}
private bool IsWithinChunkBoundary(Vector3 shift)
{
return shift.x >= _chunkBoundaryMin.x && shift.x <= _chunkBoundaryMax.x &&
shift.y >= _chunkBoundaryMin.y && shift.y <= _chunkBoundaryMax.y &&
shift.z >= _chunkBoundaryMin.z && shift.z <= _chunkBoundaryMax.z;
}
#endregion Origin Shift Events
#endif
}
}

View file

@ -0,0 +1,57 @@
using UnityEngine;
namespace NAK.OriginShift.Components
{
public class OriginShiftParticleSystemReceiver : MonoBehaviour
{
#if !UNITY_EDITOR
// max particles count cause i said so 2
private static readonly ParticleSystem.Particle[] _tempParticles = new ParticleSystem.Particle[10000];
private ParticleSystem[] _particleSystems;
#region Unity Events
private void Start()
{
_particleSystems = GetComponentsInChildren<ParticleSystem>(true);
if (_particleSystems.Length == 0)
{
OriginShiftMod.Logger.Error("OriginShiftParticleSystemReceiver: No ParticleSystems found on GameObject: " + gameObject.name, this);
enabled = false;
}
}
private void OnEnable()
{
OriginShiftManager.OnOriginShifted += OnOriginShifted;
}
private void OnDisable()
{
OriginShiftManager.OnOriginShifted -= OnOriginShifted;
}
#endregion Unity Events
#region Origin Shift Events
private void OnOriginShifted(Vector3 offset)
{
foreach (ParticleSystem particleSystem in _particleSystems)
ShiftParticleSystem(particleSystem, offset);
}
private static void ShiftParticleSystem(ParticleSystem particleSystem, Vector3 offset)
{
int particleCount = particleSystem.GetParticles(_tempParticles);
for (int i = 0; i < particleCount; i++) _tempParticles[i].position += offset;
particleSystem.SetParticles(_tempParticles, particleCount);
}
#endregion Origin Shift Events
#endif
}
}

View file

@ -0,0 +1,46 @@
using UnityEngine;
namespace NAK.OriginShift.Components
{
public class OriginShiftRigidbodyReceiver : MonoBehaviour
{
#if !UNITY_EDITOR
private Rigidbody _rigidbody;
#region Unity Events
private void Start()
{
_rigidbody = GetComponentInChildren<Rigidbody>();
if (_rigidbody == null)
{
OriginShiftMod.Logger.Error("OriginShiftRigidbodyReceiver: No Rigidbody found on GameObject: " + gameObject.name, this);
enabled = false;
}
}
private void OnEnable()
{
OriginShiftManager.OnOriginShifted += OnOriginShifted;
}
private void OnDisable()
{
OriginShiftManager.OnOriginShifted -= OnOriginShifted;
}
#endregion Unity Events
#region Origin Shift Events
private void OnOriginShifted(Vector3 shift)
{
_rigidbody.position += shift;
}
#endregion Origin Shift Events
#endif
}
}

View file

@ -0,0 +1,57 @@
using UnityEngine;
namespace NAK.OriginShift.Components
{
public class OriginShiftTrailRendererReceiver : MonoBehaviour
{
#if !UNITY_EDITOR
// max positions count cause i said so
private static readonly Vector3[] _tempPositions = new Vector3[10000];
private TrailRenderer[] _trailRenderers;
#region Unity Events
private void Start()
{
_trailRenderers = GetComponentsInChildren<TrailRenderer>(true);
if (_trailRenderers.Length == 0)
{
OriginShiftMod.Logger.Error("OriginShiftTrailRendererReceiver: No TrailRenderers found on GameObject: " + gameObject.name, this);
enabled = false;
}
}
private void OnEnable()
{
OriginShiftManager.OnOriginShifted += OnOriginShifted;
}
private void OnDisable()
{
OriginShiftManager.OnOriginShifted -= OnOriginShifted;
}
#endregion Unity Events
#region Origin Shift Events
private void OnOriginShifted(Vector3 offset)
{
foreach (TrailRenderer trailRenderer in _trailRenderers)
ShiftTrailRenderer(trailRenderer, offset);
}
private static void ShiftTrailRenderer(TrailRenderer trailRenderer, Vector3 offset)
{
trailRenderer.GetPositions(_tempPositions);
for (var i = 0; i < _tempPositions.Length; i++) _tempPositions[i] += offset;
trailRenderer.SetPositions(_tempPositions);
}
#endregion Origin Shift Events
#endif
}
}

View file

@ -0,0 +1,34 @@
using UnityEngine;
namespace NAK.OriginShift.Components
{
public class OriginShiftTransformReceiver : MonoBehaviour
{
#if !UNITY_EDITOR
#region Unity Events
private void OnEnable()
{
OriginShiftManager.OnOriginShifted += OnOriginShifted;
}
private void OnDisable()
{
OriginShiftManager.OnOriginShifted -= OnOriginShifted;
}
#endregion Unity Events
#region Origin Shift Events
private void OnOriginShifted(Vector3 shift)
{
transform.position += shift;
}
#endregion Origin Shift Events
#endif
}
}

View file

@ -0,0 +1,57 @@
using ABI_RC.Core;
using ABI_RC.Core.Player;
using ABI_RC.Systems.Movement;
using UnityEngine;
namespace NAK.OriginShift.Extensions;
public static class BetterBetterCharacterControllerExtensions
{
/// <summary>
/// Offsets the player by the given vector.
/// This is a simple move operation that does not affect velocity, grounded state, movement parent, ect.
/// </summary>
/// <param name="controller"></param>
/// <param name="offset"></param>
public static void OffsetBy(this BetterBetterCharacterController controller, Vector3 offset)
{
controller.MoveTo(PlayerSetup.Instance.GetPlayerPosition() + offset);
}
/// <summary>
/// Moves the player to the target position while keeping velocity, grounded state, movement parent, ect.
/// Allows moving the player in a way that is meant to be seamless, unlike TeleportTo or SetPosition.
/// </summary>
/// <param name="controller"></param>
/// <param name="targetPos"></param>
/// <param name="interpolate"></param>
public static void MoveTo(this BetterBetterCharacterController controller, Vector3 targetPos,
bool interpolate = false)
{
// character controller is not built to account for the player's VR offset
Vector3 vector = targetPos - PlayerSetup.Instance.GetPlayerPosition();
Vector3 vector2 = controller.GetPosition() + vector;
if (!CVRTools.IsWithinMaxBounds(vector2))
{
// yeah, ill play your game
CommonTools.LogAuto(CommonTools.LogLevelType_t.Warning,
"Attempted to move player further than the maximum allowed bounds.", "",
"OriginShift/Extensions/BetterBetterCharacterControllerExtensions.cs",
"MoveTo", 19);
return;
}
controller.TeleportPosition(vector2, interpolate); // move player
controller.SetVelocity(controller.characterMovement.velocity); // keep velocity
controller.UpdateColliderCenter(vector2, true); // update collider center
controller.characterMovement.UpdateCurrentPlatform(); // recalculate stored local offset
// invoke event so ik can update
BetterBetterCharacterController.OnMovementParentMove.Invoke(
new BetterBetterCharacterController.PlayerMoveOffset(
PlayerSetup.Instance.GetPlayerPosition(),
vector,
Quaternion.identity));
}
}

View file

@ -0,0 +1,19 @@
using ABI_RC.Core.Player;
using UnityEngine;
namespace NAK.OriginShift.Extensions;
public static class PlayerSetupExtensions
{
/// <summary>
/// Utility method to offset the player's currently stored avatar movement data.
/// Needs to be called, otherwise outbound net ik will be one-frame behind on teleport events.
/// </summary>
/// <param name="playerSetup"></param>
/// <param name="offset"></param>
public static void OffsetAvatarMovementData(this PlayerSetup playerSetup, Vector3 offset)
{
playerSetup._playerAvatarMovementData.RootPosition += offset;
playerSetup._playerAvatarMovementData.BodyPosition += offset; // why in world space -_-
}
}

View file

@ -0,0 +1,50 @@
using HarmonyLib;
using UnityEngine;
namespace NAK.OriginShift.Hacks;
#region Harmony Patches
internal static class OcclusionCullingPatches
{
// i wish i had this when working on the head hiding & shadow clones... would have had a much better
// method of hiding mesh that wouldn't have broken magica cloth :< (one day: make test mod to do that)
[HarmonyPostfix] // after all onprecull listeners
[HarmonyPatch(typeof(Camera), "FireOnPreCull")]
private static void OnPreCullPostfix(Camera cam)
{
OcclusionCullingHack hackInstance = cam.GetComponent<OcclusionCullingHack>();
if (hackInstance != null) hackInstance.OnPostFirePreCull(cam);
}
[HarmonyPrefix] // before all onprerender listeners
[HarmonyPatch(typeof(Camera), "FireOnPreRender")]
private static void OnPreRenderPrefix(Camera cam)
{
OcclusionCullingHack hackInstance = cam.GetComponent<OcclusionCullingHack>();
if (hackInstance != null) hackInstance.OnPreFirePreRender(cam);
}
}
#endregion Harmony Patches
/// <summary>
/// Attempted hack to fix occlusion culling for *static* objects. This does not fix dynamic objects, they will be culled
/// by the camera's frustum & original baked occlusion culling data. Nothing can be done about that. :>
/// </summary>
public class OcclusionCullingHack : MonoBehaviour
{
private Vector3 originalPosition;
internal void OnPostFirePreCull(Camera cam)
{
originalPosition = cam.transform.position;
cam.transform.position = OriginShiftManager.GetAbsolutePosition(originalPosition);
}
internal void OnPreFirePreRender(Camera cam)
{
cam.transform.position = originalPosition;
}
}

View file

@ -0,0 +1,45 @@
using UnityEngine;
namespace NAK.OriginShift.Hacks
{
public class OriginShiftOcclusionCullingDisabler : MonoBehaviour
{
private Camera _camera;
private bool _originalCullingState;
#region Unity Events
private void Start()
{
_camera = GetComponent<Camera>();
if (_camera == null)
{
Debug.LogError("OriginShiftOcclusionCullingDisabler requires a Camera component on the same GameObject.");
enabled = false;
return;
}
_originalCullingState = _camera.useOcclusionCulling;
}
private void OnEnable()
{
OriginShiftManager.OnStateChanged += OnOriginShiftStateChanged;
}
private void OnDisable()
{
OriginShiftManager.OnStateChanged -= OnOriginShiftStateChanged;
}
#endregion Unity Events
#region Origin Shift Events
private void OnOriginShiftStateChanged(OriginShiftManager.OriginShiftState state)
{
_camera.useOcclusionCulling = state != OriginShiftManager.OriginShiftState.Forced && _originalCullingState;
}
#endregion Origin Shift Events
}
}

View file

@ -0,0 +1,254 @@
using System;
using ABI_RC.Core.Base;
using ABI_RC.Core.Player;
using ABI.CCK.Components;
using JetBrains.Annotations;
using MagicaCloth;
using NAK.OriginShift.Components;
using NAK.OriginShift.Utility;
using UnityEngine;
namespace NAK.OriginShift;
public class OriginShiftManager : MonoBehaviour
{
#region Singleton
private static OriginShiftManager _instance;
public static OriginShiftManager Instance
{
get
{
if (_instance) return _instance;
_instance = new GameObject("NAKOriginShiftManager").AddComponent<OriginShiftManager>();
DontDestroyOnLoad(_instance.gameObject);
return _instance;
}
}
private static bool _useOriginShift;
public static bool UseOriginShift
{
get => _useOriginShift;
set
{
if (_useOriginShift == value)
return;
if (value)
{
PlayerSetup.Instance.gameObject.AddComponentIfMissing<OriginShiftMonitor>();
}
else
{
Instance.ResetOrigin();
if (PlayerSetup.Instance.TryGetComponent(out OriginShiftMonitor originShiftMonitor))
DestroyImmediate(originShiftMonitor);
}
_useOriginShift = value;
}
}
private static bool _compatibilityMode = true;
public static bool CompatibilityMode
{
get => _useOriginShift && _compatibilityMode;
set => _compatibilityMode = value;
}
#endregion Singleton
#region Shader Globals
private static readonly int s_OriginShiftChunkOffset = Shader.PropertyToID("_OriginShiftChunkOffset"); // Vector3
private static readonly int s_OriginShiftChunkPosition = Shader.PropertyToID("_OriginShiftChunkPosition"); // Vector3
private static readonly int s_OriginShiftChunkThreshold = Shader.PropertyToID("_OriginShiftChunkThreshold"); // float
private static void SetShaderGlobals(Vector3 chunk, float threshold)
{
Shader.SetGlobalVector(s_OriginShiftChunkOffset, chunk);
Shader.SetGlobalFloat(s_OriginShiftChunkThreshold, threshold);
Shader.SetGlobalVector(s_OriginShiftChunkPosition, chunk * threshold);
}
private static void ResetShaderGlobals()
{
Shader.SetGlobalVector(s_OriginShiftChunkOffset, Vector3.zero);
Shader.SetGlobalFloat(s_OriginShiftChunkThreshold, -1f);
Shader.SetGlobalVector(s_OriginShiftChunkPosition, Vector3.zero);
}
#endregion Shader Globals
#region Actions
public static Action<OriginShiftState> OnStateChanged = delegate { };
public static Action<Vector3> OnOriginShifted = delegate { };
public static Action<Vector3> OnPostOriginShifted = delegate { };
#endregion Actions
#region Public Properties
[PublicAPI] public bool IsOriginShifted => ChunkOffset != Vector3.zero;
[PublicAPI] public Vector3 ChunkOffset { get; internal set; } = Vector3.zero;
[PublicAPI] public Vector3 ChunkPosition => ChunkOffset * OriginShiftController.ORIGIN_SHIFT_THRESHOLD;
public enum OriginShiftState
{
Inactive, // world is not using Origin Shift
Active, // world is using Origin Shift
Forced // temp for this session, force world to use Origin Shift
}
private OriginShiftState _currentState = OriginShiftState.Inactive;
[PublicAPI] public OriginShiftState CurrentState
{
get => _currentState;
private set
{
if (_currentState == value)
return;
_currentState = value;
OnStateChanged.Invoke(value);
}
}
#endregion Public Properties
#region Manager Lifecycle
private OriginShiftController _forceController;
public void ForceManager()
{
if (CVRWorld.Instance == null)
{
OriginShiftMod.Logger.Error("Cannot force Origin Shift without a world.");
return;
}
OriginShiftMod.Logger.Msg("Forcing Origin Shift...");
_forceController = CVRWorld.Instance.gameObject.AddComponentIfMissing<OriginShiftController>();
_forceController.IsForced = true;
}
public void SetupManager(bool isForced = false)
{
if (CVRWorld.Instance == null)
{
OriginShiftMod.Logger.Error("Cannot setup Origin Shift without a world.");
return;
}
OriginShiftMod.Logger.Msg("Setting up Origin Shift...");
CurrentState = isForced ? OriginShiftState.Forced : OriginShiftState.Active;
UseOriginShift = true;
}
public void ResetManager()
{
OriginShiftMod.Logger.Msg("Resetting Origin Shift...");
if (_forceController) Destroy(_forceController);
CurrentState = OriginShiftState.Inactive;
ResetOrigin();
ResetShaderGlobals();
UseOriginShift = false;
}
#endregion Manager Lifecycle
#region Public Methods
// Called by OriginShiftMonitor when the local player needs to shit
public void ShiftOrigin(Vector3 rawPosition)
{
if (!_useOriginShift) return;
// create stopwatch
StopWatch stopwatch = new();
stopwatch.Start();
// normalize
float halfThreshold = (OriginShiftController.ORIGIN_SHIFT_THRESHOLD / 2);
rawPosition += new Vector3(halfThreshold, halfThreshold, halfThreshold);
// add to chunk
Vector3 chunkDifference;
Vector3 calculatedChunk = chunkDifference = ChunkOffset;
calculatedChunk.x += Mathf.FloorToInt(rawPosition.x / OriginShiftController.ORIGIN_SHIFT_THRESHOLD);
calculatedChunk.y += Mathf.FloorToInt(rawPosition.y / OriginShiftController.ORIGIN_SHIFT_THRESHOLD);
calculatedChunk.z += Mathf.FloorToInt(rawPosition.z / OriginShiftController.ORIGIN_SHIFT_THRESHOLD);
// get offset
chunkDifference = (ChunkOffset - calculatedChunk) * OriginShiftController.ORIGIN_SHIFT_THRESHOLD;
// store & invoke
ChunkOffset = calculatedChunk;
OnOriginShifted.Invoke(chunkDifference);
OnPostOriginShifted.Invoke(chunkDifference);
// set shader globals
SetShaderGlobals(ChunkOffset, OriginShiftController.ORIGIN_SHIFT_THRESHOLD);
// log
stopwatch.Stop();
OriginShiftMod.Logger.Msg($"Shifted Origin: {chunkDifference} in {stopwatch.ElapsedMilliseconds:F11}ms");
}
public void ResetOrigin()
{
if (!_useOriginShift) return;
ShiftOrigin(-ChunkPosition);
ChunkOffset = Vector3.zero;
}
#endregion Origin Shift Implementation
#region Utility Methods
public static Vector3 GetAbsolutePosition(Vector3 localizedPosition)
{
// absolute coordinates can be reconstructed using current chunk and threshold
localizedPosition += Instance.ChunkOffset * OriginShiftController.ORIGIN_SHIFT_THRESHOLD;
return localizedPosition;
}
public static Vector3 GetLocalizedPosition(Vector3 absolutePosition)
{
return absolutePosition - (Instance.ChunkOffset * OriginShiftController.ORIGIN_SHIFT_THRESHOLD);
}
#endregion Utility Methods
#region Unity Events
#if !UNITY_EDITOR
private void Update()
{
if (Input.GetKeyDown(KeyCode.P)) // press p to print chunk
OriginShiftMod.Logger.Msg($"Current Chunk: {ChunkOffset}");
// press o to toggle debug
if (Input.GetKeyDown(KeyCode.O))
{
if (TryGetComponent(out DebugTextDisplay debugTextDisplay))
Destroy(debugTextDisplay);
else
gameObject.AddComponent<DebugTextDisplay>();
}
}
#endif
#endregion Unity Events
}

View file

@ -0,0 +1,68 @@
using Unity.Mathematics;
using UnityEngine;
using Zettai;
namespace NAK.OriginShift;
public class OriginShiftDbAvatarReceiver : MonoBehaviour
{
private DbJobsAvatarManager _avatarManager;
private void OnEnable()
{
OriginShiftManager.OnOriginShifted += OnOriginShifted;
_avatarManager = GetComponent<DbJobsAvatarManager>();
}
private void OnDisable()
{
OriginShiftManager.OnOriginShifted -= OnOriginShifted;
}
private void OnOriginShifted(Vector3 shift)
{
// get all particles
var particles = _avatarManager.particlesArray;
for (var index = 0; index < particles.Length; index++)
{
ParticleStruct particle = particles[index];
float3 position = particle.m_Position;
position.x += shift.x;
position.y += shift.y;
position.z += shift.z;
particle.m_Position = position;
position = particle.m_PrevPosition;
position.x += shift.x;
position.y += shift.y;
position.z += shift.z;
particle.m_PrevPosition = position;
particles[index] = particle;
}
_avatarManager.particlesArray = particles;
// get all transforminfo
var transformInfos = _avatarManager.transformInfoArray;
for (var index = 0; index < transformInfos.Length; index++)
{
TransformInfo transformInfo = transformInfos[index];
float3 position = transformInfo.position;
position.x += shift.x;
position.y += shift.y;
position.z += shift.z;
transformInfo.position = position;
position = transformInfo.prevPosition;
position.x += shift.x;
position.y += shift.y;
position.z += shift.z;
transformInfo.prevPosition = position;
transformInfos[index] = transformInfo;
}
_avatarManager.transformInfoArray = transformInfos;
}
}

View file

@ -0,0 +1,93 @@
using System.Collections;
using ABI_RC.Core;
using ABI_RC.Core.Player;
using NAK.OriginShift.Components;
using NAK.OriginShift.Extensions;
using UnityEngine;
#if !UNITY_EDITOR
using ABI_RC.Systems.Movement;
#endif
namespace NAK.OriginShift
{
[DefaultExecutionOrder(int.MaxValue)]
public class OriginShiftMonitor : MonoBehaviour
{
#if !UNITY_EDITOR
private PlayerSetup _playerSetup;
private BetterBetterCharacterController _characterController;
#endif
#region Unity Events
private void Start()
{
#if !UNITY_EDITOR
_playerSetup = GetComponent<PlayerSetup>();
_characterController = GetComponent<BetterBetterCharacterController>();
#endif
OriginShiftManager.OnPostOriginShifted += OnPostOriginShifted;
StartCoroutine(FixedUpdateCoroutine());
}
private void OnDestroy()
{
OriginShiftManager.OnPostOriginShifted -= OnPostOriginShifted;
StopAllCoroutines();
}
private void LateFixedUpdate()
{
// in CVR use GetPlayerPosition to account for VR offset
Vector3 position = PlayerSetup.Instance.GetPlayerPosition();
// respawn height check
Vector3 absPosition = OriginShiftManager.GetAbsolutePosition(position);
if (absPosition.y < BetterBetterCharacterController.Instance.respawnHeight)
{
RootLogic.Instance.Respawn();
return;
}
float halfThreshold = OriginShiftController.ORIGIN_SHIFT_THRESHOLD / 2; // i keep forgetting this
if (Mathf.Abs(position.x) > halfThreshold
|| Mathf.Abs(position.y) > halfThreshold
|| Mathf.Abs(position.z) > halfThreshold)
OriginShiftManager.Instance.ShiftOrigin(position);
}
#endregion Unity Events
#region Origin Shift Events
private void OnPostOriginShifted(Vector3 shift)
{
#if UNITY_EDITOR
// shift our transform back
transform.position += shift;
#else
_characterController.OffsetBy(shift);
_playerSetup.OffsetAvatarMovementData(shift);
#endif
}
#endregion Origin Shift Events
#region LateFixedUpdate Implementation
private readonly YieldInstruction _fixedUpdateYield = new WaitForFixedUpdate();
private IEnumerator FixedUpdateCoroutine()
{
while (true)
{
yield return _fixedUpdateYield;
LateFixedUpdate(); // we need to run after all physics (specifically, character controller)
}
// ReSharper disable once IteratorNeverReturns
}
#endregion LateFixedUpdate Implementation
}
}

View file

@ -0,0 +1,47 @@
using ABI_RC.Core.Player;
using UnityEngine;
namespace NAK.OriginShift;
public class OriginShiftNetIkReceiver : MonoBehaviour
{
private PuppetMaster _puppetMaster;
#region Unity Events
private void Start()
{
_puppetMaster = GetComponent<PuppetMaster>();
if (_puppetMaster == null)
{
OriginShiftMod.Logger.Error("OriginShiftNetIkReceiver: No PuppetMaster found on GameObject: " + gameObject.name, this);
enabled = false;
}
}
private void OnEnable()
{
OriginShiftManager.OnOriginShifted += OnOriginShifted;
}
private void OnDisable()
{
OriginShiftManager.OnOriginShifted -= OnOriginShifted;
}
#endregion Unity Events
#region Origin Shift Events
private void OnOriginShifted(Vector3 shift)
{
// would be nice if these were relative positions like sub-syncs :)
_puppetMaster._playerAvatarMovementDataCurrent.RootPosition += shift;
_puppetMaster._playerAvatarMovementDataCurrent.BodyPosition += shift;
_puppetMaster._playerAvatarMovementDataPast.RootPosition += shift;
_puppetMaster._playerAvatarMovementDataPast.BodyPosition += shift;
// later in frame puppetmaster will update remote player position
}
#endregion Origin Shift Events
}

View file

@ -0,0 +1,99 @@
#if !UNITY_EDITOR
using ABI_RC.Core.Player;
using NAK.OriginShift.Components;
using UnityEngine;
namespace NAK.OriginShift.Utility;
public class DebugTextDisplay : MonoBehaviour
{
#region Private Variables
private string _debugText = "Initializing...";
private Color _textColor = Color.white;
private bool _originShiftEventOccurred;
private const float _blendDuration = 1.0f;
private float _blendTime;
#endregion Private Variables
#region Unity Events
private void OnEnable()
{
OriginShiftManager.OnPostOriginShifted += OnPostOriginShifted;
}
private void OnDisable()
{
OriginShiftManager.OnPostOriginShifted -= OnPostOriginShifted;
}
private void OnGUI()
{
GUIStyle style = new()
{
fontSize = 25,
normal = { textColor = _textColor },
alignment = TextAnchor.UpperRight
};
float screenWidth = Screen.width;
var xPosition = screenWidth - 10;
float yPosition = 10;
GUI.Label(new Rect(xPosition - 490, yPosition, 500, 150), _debugText, style);
}
#endregion Unity Events
#region Public Methods
public void UpdateDebugText(string newText)
{
_debugText = newText;
}
#endregion Public Methods
private void Update()
{
Vector3 currentChunk = OriginShiftManager.Instance.ChunkOffset;
Vector3 localCoordinates = PlayerSetup.Instance.GetPlayerPosition();
Vector3 absoluteCoordinates = localCoordinates;
// absolute coordinates can be reconstructed using current chunk and threshold
absoluteCoordinates += currentChunk * OriginShiftController.ORIGIN_SHIFT_THRESHOLD;
// Update the debug text with the current coordinates
UpdateDebugText($"Local Coordinates:\n{localCoordinates}\n\n" +
$"Absolute Coordinates:\n{absoluteCoordinates}\n\n" +
$"Current Chunk:\n{currentChunk}");
// Blend back to white if the origin shift event occurred
if (_originShiftEventOccurred)
{
_blendTime += Time.deltaTime;
_textColor = Color.Lerp(Color.red, Color.white, _blendTime / _blendDuration);
if (_blendTime >= _blendDuration)
{
_originShiftEventOccurred = false;
_blendTime = 0.0f;
_textColor = Color.white;
}
}
}
#region Origin Shift Events
private void OnPostOriginShifted(Vector3 _)
{
_originShiftEventOccurred = true;
_textColor = Color.green;
_blendTime = 0.0f;
}
#endregion Origin Shift Events
}
#endif

View file

@ -0,0 +1,33 @@
using System.Reflection;
using UnityEngine;
namespace NAK.OriginShift.Utility;
/// <summary>
/// Dumb little utility class to access the private staticBatchRootTransform property in the Renderer class.
/// Using this we can move static batched objects with the scene! :)
/// </summary>
public static class RendererReflectionUtility
{
private static readonly PropertyInfo _staticBatchRootTransformProperty;
static RendererReflectionUtility()
{
Type rendererType = typeof(Renderer);
_staticBatchRootTransformProperty = rendererType.GetProperty("staticBatchRootTransform", BindingFlags.NonPublic | BindingFlags.Instance);
if (_staticBatchRootTransformProperty == null) OriginShiftMod.Logger.Error("Property staticBatchRootTransform not found in Renderer class.");
}
public static void SetStaticBatchRootTransform(Renderer renderer, Transform newTransform)
{
if (_staticBatchRootTransformProperty != null)
_staticBatchRootTransformProperty.SetValue(renderer, newTransform);
}
public static Transform GetStaticBatchRootTransform(Renderer renderer)
{
if (_staticBatchRootTransformProperty != null)
return (Transform)_staticBatchRootTransformProperty.GetValue(renderer);
return null;
}
}