From 206d55b5aad3918f0e6767538a16cdf75f927901 Mon Sep 17 00:00:00 2001 From: kafeijao Date: Sat, 11 Mar 2023 22:33:41 +0000 Subject: [PATCH] [CVRSuperMario64] Added limit of max tris count, skip animations disabled when close to camera mario, nuke mario after dead. Fix level not updating when changing types. --- CVRSuperMario64/Components/CVRSM64Context.cs | 2 +- .../Components/CVRSM64LevelModifier.cs | 9 +++ CVRSuperMario64/Components/CVRSM64Mario.cs | 20 +++-- CVRSuperMario64/Config.cs | 12 ++- CVRSuperMario64/Utils.cs | 76 +++++++++++++------ 5 files changed, 84 insertions(+), 35 deletions(-) diff --git a/CVRSuperMario64/Components/CVRSM64Context.cs b/CVRSuperMario64/Components/CVRSM64Context.cs index 3f320b0..946f7e1 100644 --- a/CVRSuperMario64/Components/CVRSM64Context.cs +++ b/CVRSuperMario64/Components/CVRSM64Context.cs @@ -30,7 +30,7 @@ private void Awake() { // Update context's colliders Interop.StaticSurfacesLoad(Utils.GetAllStaticSurfaces()); - Config.MeIgnoreCollidersHigherThanPolygons.OnEntryValueChanged.Subscribe((oldValue, newValue) => { + Config.MeMaxMeshColliderTotalTris.OnEntryValueChanged.Subscribe((oldValue, newValue) => { if (newValue == oldValue) return; Interop.StaticSurfacesLoad(Utils.GetAllStaticSurfaces()); }); diff --git a/CVRSuperMario64/Components/CVRSM64LevelModifier.cs b/CVRSuperMario64/Components/CVRSM64LevelModifier.cs index 7e6f86a..6f97308 100644 --- a/CVRSuperMario64/Components/CVRSM64LevelModifier.cs +++ b/CVRSuperMario64/Components/CVRSM64LevelModifier.cs @@ -80,6 +80,15 @@ public static void ContextTick(List marios) { return; } + // Reset the old type + if (maxLevelModifier.modifierType != _lastModifierType) { + lock (marios) { + foreach (var mario in marios) { + Interop.SetLevelModifier(mario.MarioId, _lastModifierType, float.MinValue); + } + } + } + _lastModifierType = maxLevelModifier.modifierType; _lastLevel = highestLevel; _forceUpdate = false; diff --git a/CVRSuperMario64/Components/CVRSM64Mario.cs b/CVRSuperMario64/Components/CVRSM64Mario.cs index 06fd4b0..58a4e97 100644 --- a/CVRSuperMario64/Components/CVRSM64Mario.cs +++ b/CVRSuperMario64/Components/CVRSM64Mario.cs @@ -75,6 +75,7 @@ public class CVRSM64Mario : MonoBehaviour { [NonSerialized] private bool _wasPickedUp; [NonSerialized] private bool _initializedByRemote; [NonSerialized] private bool _isDying; + [NonSerialized] private bool _isNuked; // Bypasses [NonSerialized] private bool _wasBypassed; @@ -401,7 +402,7 @@ private void OnDisable() { public void ContextFixedUpdateSynced(List marios) { - if (!_enabled || !_initialized) return; + if (!_enabled || !_initialized || _isNuked) return; // Janky remote sync check if (!IsMine() && !_initializedByRemote) { @@ -470,10 +471,7 @@ public void ContextFixedUpdateSynced(List marios) { // Check for deaths, so we delete the prop if (!_isDying && IsDead()) { _isDying = true; - Invoke(nameof(DeleteMario), 15f); - #if DEBUG - MelonLogger.Msg($"One of our Marios died, the prop will be deleted in 15 seconds..."); - #endif + Invoke(nameof(SetMarioAsNuked), 15f); } } else { @@ -531,7 +529,7 @@ public void ContextFixedUpdateSynced(List marios) { } public void ContextUpdateSynced() { - if (!_enabled || !_initialized) return; + if (!_enabled || !_initialized || _isNuked) return; if (!IsMine() && !_initializedByRemote) return; @@ -768,9 +766,15 @@ private bool IsDead() { } } - private void DeleteMario() { + private void SetMarioAsNuked() { lock (_lock) { - spawnable.Delete(); + _isNuked = true; + var deleteMario = Config.MeDeleteMarioAfterDead.Value; + #if DEBUG + MelonLogger.Msg($"One of our Marios died, it has been 15 seconds and we're going to " + + $"{(deleteMario ? "delete the mario" : "stopping its engine updates")}."); + #endif + if (deleteMario) spawnable.Delete(); } } diff --git a/CVRSuperMario64/Config.cs b/CVRSuperMario64/Config.cs index cfd1606..5f040ec 100644 --- a/CVRSuperMario64/Config.cs +++ b/CVRSuperMario64/Config.cs @@ -9,10 +9,11 @@ public static class Config { internal static MelonPreferences_Entry MeAudioPitch; internal static MelonPreferences_Entry MeAudioVolume; internal static MelonPreferences_Entry MeGameTickMs; - internal static MelonPreferences_Entry MeIgnoreCollidersHigherThanPolygons; internal static MelonPreferences_Entry MePlayRandomMusicOnMarioJoin; internal static MelonPreferences_Entry MeSkipFarMarioDistance; internal static MelonPreferences_Entry MeMaxMariosAnimatedPerPerson; + internal static MelonPreferences_Entry MeMaxMeshColliderTotalTris; + internal static MelonPreferences_Entry MeDeleteMarioAfterDead; public static void InitializeMelonPrefs() { @@ -30,9 +31,6 @@ public static void InitializeMelonPrefs() { MeGameTickMs = _melonCategory.CreateEntry("GameTickMs", 25, description: "The game ticks frequency in Milliseconds."); - MeIgnoreCollidersHigherThanPolygons = _melonCategory.CreateEntry("IgnoreCollidersHigherThanPolygons", 10000, - description: "Ignore colliders with a poly count higher than."); - MePlayRandomMusicOnMarioJoin = _melonCategory.CreateEntry("PlayRandomMusicOnMarioJoin", true, description: "Whether to play a random music when a mario joins or not."); MePlayRandomMusicOnMarioJoin.OnEntryValueChanged.Subscribe((_, newValue) => { @@ -44,6 +42,12 @@ public static void InitializeMelonPrefs() { MeSkipFarMarioDistance = _melonCategory.CreateEntry("SkipFarMarioDistance", 5f, description: "The max distance that we're going to calculate the mario animations for other people."); + + MeMaxMeshColliderTotalTris = _melonCategory.CreateEntry("MaxMeshColliderTotalTris", 75000, + description: "The max total number of collision tris loaded from automatically generated static mesh colliders."); + + MeDeleteMarioAfterDead = _melonCategory.CreateEntry("DeleteMarioAfterDead", true, + description: "Whether to automatically delete our marios after 15 seconds of being dead or not."); } } diff --git a/CVRSuperMario64/Utils.cs b/CVRSuperMario64/Utils.cs index cdc98a1..0db7e7f 100644 --- a/CVRSuperMario64/Utils.cs +++ b/CVRSuperMario64/Utils.cs @@ -250,6 +250,8 @@ private static Vector3 ScalePoint(CapsuleCollider c, Vector3 point) { internal static Interop.SM64Surface[] GetAllStaticSurfaces() { var surfaces = new List(); + var meshColliders = new List(); + foreach (var obj in UnityEngine.Object.FindObjectsOfType()) { var cvrSM64ColliderStatic = obj.GetComponent(); @@ -276,7 +278,49 @@ internal static Interop.SM64Surface[] GetAllStaticSurfaces() { //MelonLogger.Msg($"[GoodCollider] {obj.name}"); #endif - GetTransformedSurfaces(obj, surfaces, surfaceType, terrainType, hasDedicatedComponent); + if (obj is MeshCollider meshCollider && !hasDedicatedComponent) { + // Let's do some more processing to the mesh colliders without dedicated components + meshColliders.Add(meshCollider); + } + else { + // Everything else, let's just add (probably a bad idea) + GetTransformedSurfaces(obj, surfaces, surfaceType, terrainType, hasDedicatedComponent); + } + } + + // Ignore all meshes colliders with a null shared mesh, or non-readable + var nonReadableMeshColliders = meshColliders.Where(meshCollider => meshCollider.sharedMesh == null || !meshCollider.sharedMesh.isReadable).ToList(); + #if DEBUG + foreach (var nonReadableMeshCollider in nonReadableMeshColliders) { + MelonLogger.Warning($"[MeshCollider] {nonReadableMeshCollider.name} Mesh is null or not readable, " + + "so we won't be able to use this as a collider for Mario :("); + } + #endif + meshColliders.RemoveAll(meshCollider => meshCollider.sharedMesh == null || !meshCollider.sharedMesh.isReadable); + + // Sort the meshColliders list by the length of their triangles array in ascending order + meshColliders.Sort((a, b) => a.sharedMesh.triangles.Length.CompareTo(b.sharedMesh.triangles.Length)); + + // Add the mesh colliders until we reach the max mesh collider polygon limit + var totalMeshColliderTris = 0; + foreach (var meshCollider in meshColliders) { + var meshTrisCount = meshCollider.sharedMesh.triangles.Length / 3; + var newTotalMeshColliderTris = totalMeshColliderTris + meshTrisCount; + if (newTotalMeshColliderTris > Config.MeMaxMeshColliderTotalTris.Value) { + #if DEBUG + MelonLogger.Warning($"[MeshCollider] {meshCollider.name} will be ignored because it exceeds the " + + $"maximum mesh collider total tris count. Tris: {meshTrisCount}, Total Tris: " + + $"{newTotalMeshColliderTris}/{Config.MeMaxMeshColliderTotalTris.Value}"); + #endif + } + else { + GetTransformedSurfaces(meshCollider, surfaces, SM64SurfaceType.Default, SM64TerrainType.Grass, false); + #if DEBUG + MelonLogger.Msg($"[MeshCollider] {meshCollider.name} will be added. Tris: {meshTrisCount}, Total " + + $"Tris: {newTotalMeshColliderTris}/{Config.MeMaxMeshColliderTotalTris.Value}"); + #endif + } + totalMeshColliderTris = newTotalMeshColliderTris; } return surfaces.ToArray(); @@ -293,24 +337,16 @@ internal static Interop.SM64Surface[] GetAllStaticSurfaces() { break; case MeshCollider meshCollider: - #if DEBUG - MelonLogger.Msg($"[MeshCollider] {meshCollider.name} Readable: {meshCollider.sharedMesh.isReadable}, SubMeshCount: {meshCollider.sharedMesh.subMeshCount}, TrisCount: {meshCollider.sharedMesh.triangles.Length}"); - #endif - - if (!meshCollider.sharedMesh.isReadable) { - MelonLogger.Warning( - $"[MeshCollider] {meshCollider.name} Mesh is not readable, so we won't be able to use this as a collider for Mario :("); - return surfaces; - } - - if (!bypassPolygonLimit && meshCollider.sharedMesh.triangles.Length > Config.MeIgnoreCollidersHigherThanPolygons.Value) { - MelonLogger.Warning($"[MeshCollider] {meshCollider.name} has {meshCollider.sharedMesh.triangles.Length} triangles, " + - $"which is more than the configured limit ({Config.MeIgnoreCollidersHigherThanPolygons.Value}), so this mesh will be ignored!"); + if (meshCollider.sharedMesh == null || !meshCollider.sharedMesh.isReadable) { + #if DEBUG + MelonLogger.Warning($"[MeshCollider] {meshCollider.name} Mesh is null or not readable, so we won't be able to use this as a collider for Mario :("); + #endif return surfaces; } // Todo: Handle when meshes are too big (colliders stop working). // Planes scaled to 60 60 60 will break. While 50 50 50 will work. + // Edit: Seems to be related how big each polygon is (cubes break sooner than planes(more polys)) TransformAndGetSurfaces(surfaces, meshCollider.sharedMesh, surfaceType, terrainType, x => meshCollider.transform.TransformPoint(x)); break; @@ -337,14 +373,10 @@ internal static Interop.SM64Surface[] GetAllStaticSurfaces() { break; case MeshCollider meshCollider: - if (!meshCollider.sharedMesh.isReadable) { - MelonLogger.Warning($"[MeshCollider] {meshCollider.name} Mesh is not readable, so we won't be able to use this as a collider for Mario :("); - return surfaces; - } - - if (!bypassPolygonLimit && meshCollider.sharedMesh.triangles.Length > Config.MeIgnoreCollidersHigherThanPolygons.Value) { - MelonLogger.Warning($"[MeshCollider] {meshCollider.name} has {meshCollider.sharedMesh.triangles.Length} triangles, " + - $"which is more than the configured limit ({Config.MeIgnoreCollidersHigherThanPolygons.Value}), so this mesh will be ignored!"); + if (meshCollider.sharedMesh == null || !meshCollider.sharedMesh.isReadable) { + #if DEBUG + MelonLogger.Warning($"[MeshCollider] {meshCollider.name} Mesh is null or not readable, so we won't be able to use this as a collider for Mario :("); + #endif return surfaces; }