From c1a7750ad5d34bb31f7d1becc552b594d433a3a7 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Wed, 1 Jun 2022 13:08:17 -0400 Subject: [PATCH] DebugRaycaster now reports a plane perpendicular to its normal and rotated so that the plane's positive y is as close to 'up' relative to the planet as possible --- .../Utility/DebugMenu/DebugMenuNomaiText.cs | 149 +++++++----------- .../Utility/DebugUtilities/DebugPropPlacer.cs | 8 +- .../DebugUtilities/DebugRaycastData.cs | 14 +- .../Utility/DebugUtilities/DebugRaycaster.cs | 74 ++++++++- 4 files changed, 135 insertions(+), 110 deletions(-) diff --git a/NewHorizons/Utility/DebugMenu/DebugMenuNomaiText.cs b/NewHorizons/Utility/DebugMenu/DebugMenuNomaiText.cs index e30dc74b..f9fb8442 100644 --- a/NewHorizons/Utility/DebugMenu/DebugMenuNomaiText.cs +++ b/NewHorizons/Utility/DebugMenu/DebugMenuNomaiText.cs @@ -19,14 +19,21 @@ namespace NewHorizons.Utility.DebugMenu class DebugMenuNomaiText : DebugSubmenu { - private Vector2 recentPropsScrollPosition = Vector2.zero; - private HashSet favoriteProps = new HashSet(); - public static readonly char separatorCharacter = '☧'; // since no chars are illegal in game object names, I picked one that's extremely unlikely to be used to be a separator - private static readonly string favoritePropsPlayerPrefKey = "FavoriteProps"; - - internal DebugPropPlacer _dpp; internal DebugRaycaster _drc; + class NomaiTextTree + { + public GameObject text; + public int variation; + public float arcLengthLocationOnParent; + + public List children = new List(); + } + + List textTrees = new List(); + NomaiTextTree currentTree; + + internal override string SubmenuName() { return "Text Placer"; @@ -34,15 +41,12 @@ namespace NewHorizons.Utility.DebugMenu internal override void OnInit(DebugMenu menu) { - _dpp = menu.GetComponent(); _drc = menu.GetComponent(); } internal override void OnAwake(DebugMenu menu) { - _dpp = menu.GetComponent(); _drc = menu.GetComponent(); - LoadFavoriteProps(); } internal override void OnBeginLoadMod(DebugMenu debugMenu) @@ -52,73 +56,26 @@ namespace NewHorizons.Utility.DebugMenu internal override void LoadConfigFile(DebugMenu menu, PlanetConfig config) { - _dpp.FindAndRegisterPropsFromConfig(config); - } - - private void LoadFavoriteProps() - { - string favoritePropsPlayerPref = PlayerPrefs.GetString(favoritePropsPlayerPrefKey); - - if (favoritePropsPlayerPref == null || favoritePropsPlayerPref == "") return; - - var favoritePropPaths = favoritePropsPlayerPref.Split(separatorCharacter); - foreach (string favoriteProp in favoritePropPaths) - { - DebugPropPlacer.RecentlyPlacedProps.Add(favoriteProp); - this.favoriteProps.Add(favoriteProp); - } + // TODO: populate textTrees } internal override void OnGUI(DebugMenu menu) { - // - // DebugPropPlacer - // - GUILayout.Label("Currently placing: "); - _dpp.SetCurrentObject(GUILayout.TextArea(_dpp.currentObject)); - GUILayout.Space(5); - // List of recently placed objects - GUILayout.Label("Recently placed objects"); - recentPropsScrollPosition = GUILayout.BeginScrollView(recentPropsScrollPosition, GUILayout.Width(menu.EditorMenuSize.x), GUILayout.Height(500)); - foreach (string propPath in DebugPropPlacer.RecentlyPlacedProps) - { - GUILayout.BeginHorizontal(); - - var propPathElements = propPath[propPath.Length-1] == '/' - ? propPath.Substring(0, propPath.Length-1).Split('/') - : propPath.Split('/'); - string propName = propPathElements[propPathElements.Length - 1]; - - string favoriteButtonIcon = favoriteProps.Contains(propPath) ? "★" : "☆"; - if (GUILayout.Button(favoriteButtonIcon, GUILayout.ExpandWidth(false))) - { - if (favoriteProps.Contains(propPath)) - { - favoriteProps.Remove(propPath); - } - else - { - favoriteProps.Add(propPath); - } - - string[] favoritePropsArray = favoriteProps.ToArray(); - PlayerPrefs.SetString(favoritePropsPlayerPrefKey, string.Join(separatorCharacter + "", favoritePropsArray)); - } - - if (GUILayout.Button(propName)) - { - _dpp.SetCurrentObject(propPath); - } - - GUILayout.EndHorizontal(); - } - GUILayout.EndScrollView(); - GUILayout.Space(5); } + void DrawSpiralControls(int indentationLevel, NomaiTextTree tree) + { + GUILayout.BeginHorizontal(); + GUILayout.Space(5*indentationLevel); + + GUILayout.EndHorizontal(); + + tree.children.ForEach(child => DrawSpiralControls(indentationLevel+1, child)); + } + internal override void PreSave(DebugMenu menu) { UpdateLoadedConfigsForRecentSystem(menu); @@ -126,46 +83,46 @@ namespace NewHorizons.Utility.DebugMenu private void UpdateLoadedConfigsForRecentSystem(DebugMenu menu) { - var newDetails = _dpp.GetPropsConfigByBody(); + //var newDetails = _dpp.GetPropsConfigByBody(); - Logger.Log("Updating config files. New Details Counts by planet: " + string.Join(", ", newDetails.Keys.Select(x => x + $" ({newDetails[x].Length})"))); + //Logger.Log("Updating config files. New Details Counts by planet: " + string.Join(", ", newDetails.Keys.Select(x => x + $" ({newDetails[x].Length})"))); - Dictionary planetToConfigPath = new Dictionary(); + //Dictionary planetToConfigPath = new Dictionary(); - // Get all configs - foreach (var filePath in menu.loadedConfigFiles.Keys) - { - Logger.Log("potentially updating copy of config at " + filePath); + //// Get all configs + //foreach (var filePath in menu.loadedConfigFiles.Keys) + //{ + // Logger.Log("potentially updating copy of config at " + filePath); - if (menu.loadedConfigFiles[filePath].starSystem != Main.Instance.CurrentStarSystem) return; - if (menu.loadedConfigFiles[filePath].name == null || AstroObjectLocator.GetAstroObject(menu.loadedConfigFiles[filePath].name) == null) { Logger.Log("Failed to update copy of config at " + filePath); continue; } + // if (menu.loadedConfigFiles[filePath].starSystem != Main.Instance.CurrentStarSystem) return; + // if (menu.loadedConfigFiles[filePath].name == null || AstroObjectLocator.GetAstroObject(menu.loadedConfigFiles[filePath].name) == null) { Logger.Log("Failed to update copy of config at " + filePath); continue; } - var astroObjectName = DebugPropPlacer.GetAstroObjectName(menu.loadedConfigFiles[filePath].name); - planetToConfigPath[astroObjectName] = filePath; + // var astroObjectName = DebugPropPlacer.GetAstroObjectName(menu.loadedConfigFiles[filePath].name); + // planetToConfigPath[astroObjectName] = filePath; - if (!newDetails.ContainsKey(astroObjectName)) continue; + // if (!newDetails.ContainsKey(astroObjectName)) continue; - if (menu.loadedConfigFiles[filePath].Props == null) menu.loadedConfigFiles[filePath].Props = new External.Modules.PropModule(); - menu.loadedConfigFiles[filePath].Props.details = newDetails[astroObjectName]; + // if (menu.loadedConfigFiles[filePath].Props == null) menu.loadedConfigFiles[filePath].Props = new External.Modules.PropModule(); + // menu.loadedConfigFiles[filePath].Props.details = newDetails[astroObjectName]; - Logger.Log("successfully updated copy of config file for " + astroObjectName); - } + // Logger.Log("successfully updated copy of config file for " + astroObjectName); + //} - // find all new planets that do not yet have config paths - var planetsThatDoNotHaveConfigFiles = newDetails.Keys.Where(x => !planetToConfigPath.ContainsKey(x)).ToList(); - foreach (var astroObjectName in planetsThatDoNotHaveConfigFiles) - { - Logger.Log("Fabricating new config file for " + astroObjectName); + //// find all new planets that do not yet have config paths + //var planetsThatDoNotHaveConfigFiles = newDetails.Keys.Where(x => !planetToConfigPath.ContainsKey(x)).ToList(); + //foreach (var astroObjectName in planetsThatDoNotHaveConfigFiles) + //{ + // Logger.Log("Fabricating new config file for " + astroObjectName); - var filepath = "planets/" + Main.Instance.CurrentStarSystem + "/" + astroObjectName + ".json"; - PlanetConfig c = new PlanetConfig(); - c.starSystem = Main.Instance.CurrentStarSystem; - c.name = astroObjectName; - c.Props = new PropModule(); - c.Props.details = newDetails[astroObjectName]; + // var filepath = "planets/" + Main.Instance.CurrentStarSystem + "/" + astroObjectName + ".json"; + // PlanetConfig c = new PlanetConfig(); + // c.starSystem = Main.Instance.CurrentStarSystem; + // c.name = astroObjectName; + // c.Props = new PropModule(); + // c.Props.details = newDetails[astroObjectName]; - menu.loadedConfigFiles[filepath] = c; - } + // menu.loadedConfigFiles[filepath] = c; + //} } } } diff --git a/NewHorizons/Utility/DebugUtilities/DebugPropPlacer.cs b/NewHorizons/Utility/DebugUtilities/DebugPropPlacer.cs index 592cdc11..171a24c6 100644 --- a/NewHorizons/Utility/DebugUtilities/DebugPropPlacer.cs +++ b/NewHorizons/Utility/DebugUtilities/DebugPropPlacer.cs @@ -94,9 +94,9 @@ namespace NewHorizons.Utility.DebugUtilities // TODO: implement sectors // if this hits a sector, store that sector and add a config file option for it - if (!data.hitObject.name.EndsWith("_Body")) + if (!data.hitBodyGameObject.name.EndsWith("_Body")) { - Logger.Log("Cannot place object on non-body object: " + data.hitObject.name); + Logger.Log("Cannot place object on non-body object: " + data.hitBodyGameObject.name); } try @@ -106,7 +106,7 @@ namespace NewHorizons.Utility.DebugUtilities SetCurrentObject(DEFAULT_OBJECT); } - GameObject prop = DetailBuilder.MakeDetail(data.hitObject, data.hitObject.GetComponentInChildren(), currentObject, data.pos, data.norm, 1, false); + GameObject prop = DetailBuilder.MakeDetail(data.hitBodyGameObject, data.hitBodyGameObject.GetComponentInChildren(), currentObject, data.pos, data.norm, 1, false); PropPlacementData propData = RegisterProp_WithReturn(data.bodyName, prop); // align with surface normal @@ -131,7 +131,7 @@ namespace NewHorizons.Utility.DebugUtilities } catch { - Logger.Log($"Failed to place object {currentObject} on body ${data.hitObject} at location ${data.pos}."); + Logger.Log($"Failed to place object {currentObject} on body ${data.hitBodyGameObject} at location ${data.pos}."); } } diff --git a/NewHorizons/Utility/DebugUtilities/DebugRaycastData.cs b/NewHorizons/Utility/DebugUtilities/DebugRaycastData.cs index 467bdda6..e38bcd84 100644 --- a/NewHorizons/Utility/DebugUtilities/DebugRaycastData.cs +++ b/NewHorizons/Utility/DebugUtilities/DebugRaycastData.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -12,9 +12,19 @@ namespace NewHorizons.Utility.DebugUtilities public bool hit; public Vector3 pos; public Vector3 norm; + public Plane plane; public string bodyName; public string bodyPath; - public GameObject hitObject; + public GameObject hitBodyGameObject; + public GameObject hitObject; + } + + struct Plane + { + public Vector3 origin; + public Vector3 normal; + public Vector3 u; // the "+x" direction on the plane + public Vector3 v; // the "+y" direction on the plane } } diff --git a/NewHorizons/Utility/DebugUtilities/DebugRaycaster.cs b/NewHorizons/Utility/DebugUtilities/DebugRaycaster.cs index 1c34448b..6592397c 100644 --- a/NewHorizons/Utility/DebugUtilities/DebugRaycaster.cs +++ b/NewHorizons/Utility/DebugUtilities/DebugRaycaster.cs @@ -1,4 +1,4 @@ -using UnityEngine; +using UnityEngine; using UnityEngine.InputSystem; namespace NewHorizons.Utility.DebugUtilities @@ -9,7 +9,13 @@ namespace NewHorizons.Utility.DebugUtilities private OWRigidbody _rb; private GameObject _surfaceSphere; private GameObject _normalSphere1; - private GameObject _normalSphere2; + private GameObject _normalSphere2; + + private GameObject _planeUpRightSphere; + private GameObject _planeUpLeftSphere; + private GameObject _planeDownRightSphere; + private GameObject _planeDownLeftSphere; + private void Awake() { @@ -35,19 +41,36 @@ namespace NewHorizons.Utility.DebugUtilities DebugRaycastData data = Raycast(); var posText = $"{{\"x\": {data.pos.x}, \"y\": {data.pos.y}, \"z\": {data.pos.z}}}"; var normText = $"{{\"x\": {data.norm.x}, \"y\": {data.norm.y}, \"z\": {data.norm.z}}}"; - + if(_surfaceSphere != null) GameObject.Destroy(_surfaceSphere); if(_normalSphere1 != null) GameObject.Destroy(_normalSphere1); if(_normalSphere2 != null) GameObject.Destroy(_normalSphere2); + if(_planeUpRightSphere != null) GameObject.Destroy(_planeUpRightSphere ); + if(_planeUpLeftSphere != null) GameObject.Destroy(_planeUpLeftSphere ); + if(_planeDownLeftSphere != null) GameObject.Destroy(_planeDownLeftSphere ); + if(_planeDownRightSphere != null) GameObject.Destroy(_planeDownRightSphere); - _surfaceSphere = AddDebugShape.AddSphere(data.hitObject, 0.1f, Color.green); - _normalSphere1 = AddDebugShape.AddSphere(data.hitObject, 0.01f, Color.red); - _normalSphere2 = AddDebugShape.AddSphere(data.hitObject, 0.01f, Color.red); + _surfaceSphere = AddDebugShape.AddSphere(data.hitBodyGameObject, 0.1f, Color.green); + _normalSphere1 = AddDebugShape.AddSphere(data.hitBodyGameObject, 0.01f, Color.red); + _normalSphere2 = AddDebugShape.AddSphere(data.hitBodyGameObject, 0.01f, Color.red); _surfaceSphere.transform.localPosition = data.pos; _normalSphere1.transform.localPosition = data.pos + data.norm * 0.5f; _normalSphere2.transform.localPosition = data.pos + data.norm; - + + // plane corners + var planeSize = 0.5f; + var planePointSize = 0.05f; + _planeUpRightSphere = AddDebugShape.AddSphere(data.hitBodyGameObject, planePointSize, Color.green); + _planeUpLeftSphere = AddDebugShape.AddSphere(data.hitBodyGameObject, planePointSize, Color.cyan) ; + _planeDownLeftSphere = AddDebugShape.AddSphere(data.hitBodyGameObject, planePointSize, Color.blue) ; + _planeDownRightSphere = AddDebugShape.AddSphere(data.hitBodyGameObject, planePointSize, Color.cyan) ; + + _planeUpRightSphere .transform.localPosition = data.plane.origin + data.plane.u*1*planeSize + data.plane.v*1*planeSize; + _planeUpLeftSphere .transform.localPosition = data.plane.origin + data.plane.u*-1*planeSize + data.plane.v*1*planeSize; + _planeDownLeftSphere .transform.localPosition = data.plane.origin + data.plane.u*-1*planeSize + data.plane.v*-1*planeSize; + _planeDownRightSphere.transform.localPosition = data.plane.origin + data.plane.u*1*planeSize + data.plane.v*-1*planeSize; + Logger.Log($"Raycast hit \"position\": {posText}, \"normal\": {normText} on [{data.bodyName}] at [{data.bodyPath}]"); } internal DebugRaycastData Raycast() @@ -65,14 +88,49 @@ namespace NewHorizons.Utility.DebugUtilities data.pos = hitInfo.transform.InverseTransformPoint(hitInfo.point); data.norm = hitInfo.transform.InverseTransformDirection(hitInfo.normal); var o = hitInfo.transform.gameObject; + + var hitAstroObject = o.GetComponent() ?? o.GetComponentInParent(); data.bodyName = o.name; data.bodyPath = SearchUtilities.GetPath(o.transform); - data.hitObject = hitInfo.transform.gameObject; + data.hitObject = o; + data.hitBodyGameObject = hitAstroObject?.gameObject; + data.plane = ConstructPlane(data); } _rb.EnableCollisionDetection(); return data; + } + + + internal Plane ConstructPlane(DebugRaycastData data) + { + var U = data.pos - Vector3.zero; // U is the local "up" direction. the direction directly away from the center of the planet at this point. // pos is always relative to the body, so the body is considered to be at 0,0,0. + var R = data.pos; // R is our origin point for the plane + var N = data.norm.normalized; // N is the normal for this plane + + if (Vector3.Cross(U, N) == Vector3.zero) U = new Vector3(0, 0, 1); + if (Vector3.Cross(U, N) == Vector3.zero) U = new Vector3(0, 1, 0); // if 0,0,1 was actually the same vector U already was (lol), try (0,1,0) instead + + Logger.Log("U is " + U.ToString()); + + // stackoverflow.com/a/9605695 + // I don't know exactly how this works, but I'm projecting a point that is located above the plane's origin, relative to the planet, onto the plane. this gets us our v vector + var q = (2*U)-R; + var dist = Vector3.Dot(N, q); + var v_raw = 2*U - dist*N; + var v = (R-v_raw).normalized; + + var u = Vector3.Cross(N, v); + + Plane p = new Plane() + { + origin = R, + normal = N, + u = u, + v = v + }; + return p; } } }