mirror of
https://github.com/Outer-Wilds-New-Horizons/new-horizons.git
synced 2025-12-11 20:15:44 +01:00
Merge pull request #205 from xen-42/prop-placer-polar-controls
Prop placer menu prop positioning controls
This commit is contained in:
commit
d04f36881b
@ -12,14 +12,28 @@ namespace NewHorizons.Utility.DebugMenu
|
||||
{
|
||||
class DebugMenuPropPlacer : DebugSubmenu
|
||||
{
|
||||
private Vector2 recentPropsScrollPosition = Vector2.zero;
|
||||
private HashSet<string> favoriteProps = new HashSet<string>();
|
||||
private List<string> propsLoadedFromConfig = new List<string>();
|
||||
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;
|
||||
|
||||
// misc
|
||||
private GameObject mostRecentlyPlacedProp;
|
||||
private Vector3 mostRecentlyPlacedPropSphericalPos;
|
||||
|
||||
// menu params
|
||||
private Vector2 recentPropsScrollPosition = Vector2.zero;
|
||||
private bool propsCollapsed = false;
|
||||
private bool propPositioningCollapsed = false;
|
||||
private Vector3 propPosDelta = new Vector3(0.1f, 0.1f, 0.1f);
|
||||
private Vector3 propRotDelta = new Vector3(0.1f, 0.1f, 0.1f);
|
||||
private Vector3 propSphericalPosDelta = new Vector3(0.1f, 0.1f, 0.1f);
|
||||
private float propRotationAboutLocalUpDelta = 0.1f;
|
||||
private float propScaleDelta = 0.1f;
|
||||
|
||||
internal override string SubmenuName()
|
||||
{
|
||||
return "Prop Placer";
|
||||
@ -55,7 +69,7 @@ namespace NewHorizons.Utility.DebugMenu
|
||||
|
||||
internal override void LoadConfigFile(DebugMenu menu, PlanetConfig config)
|
||||
{
|
||||
_dpp.FindAndRegisterPropsFromConfig(config);
|
||||
_dpp.FindAndRegisterPropsFromConfig(config, propsLoadedFromConfig);
|
||||
}
|
||||
|
||||
private void LoadFavoriteProps()
|
||||
@ -81,9 +95,167 @@ namespace NewHorizons.Utility.DebugMenu
|
||||
_dpp.SetCurrentObject(GUILayout.TextArea(_dpp.currentObject));
|
||||
|
||||
GUILayout.Space(5);
|
||||
GUILayout.Space(5);
|
||||
|
||||
|
||||
var arrow = propsCollapsed ? " > " : " v ";
|
||||
if (GUILayout.Button(arrow + "Recently placed objects", menu._tabBarStyle)) propsCollapsed = !propsCollapsed;
|
||||
if (!propsCollapsed) DrawPropsList(menu);
|
||||
GUILayout.Space(5);
|
||||
|
||||
if (_dpp.mostRecentlyPlacedPropGO != null)
|
||||
{
|
||||
arrow = propPositioningCollapsed ? " > " : " v ";
|
||||
if (GUILayout.Button(arrow + "Position last placed prop", menu._tabBarStyle)) propPositioningCollapsed = !propPositioningCollapsed;
|
||||
if (!propPositioningCollapsed) DrawPropsAdustmentControls(menu);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawPropsAdustmentControls(DebugMenu menu)
|
||||
{
|
||||
var propPath = _dpp.mostRecentlyPlacedPropPath;
|
||||
var propPathElements = propPath[propPath.Length-1] == '/'
|
||||
? propPath.Substring(0, propPath.Length-1).Split('/')
|
||||
: propPath.Split('/');
|
||||
string propName = propPathElements[propPathElements.Length - 1];
|
||||
GUILayout.Label($"Reposition {propName}: ");
|
||||
|
||||
|
||||
Vector3 latestPropPosDelta = VectorInput(_dpp.mostRecentlyPlacedPropGO.transform.localPosition, propPosDelta, out propPosDelta, "x", "y", "z");
|
||||
_dpp.mostRecentlyPlacedPropGO.transform.localPosition += latestPropPosDelta;
|
||||
if (latestPropPosDelta != Vector3.zero) mostRecentlyPlacedPropSphericalPos = DeltaSphericalPosition(mostRecentlyPlacedProp, Vector3.zero);
|
||||
|
||||
//GUILayout.Space(5);
|
||||
//Vector3 latestPropRotDelta = VectorInput(_dpp.mostRecentlyPlacedPropGO.transform.localEulerAngles, propRotDelta, out propRotDelta, "x", "y", "z");
|
||||
//_dpp.mostRecentlyPlacedPropGO.transform.localEulerAngles += latestPropRotDelta;
|
||||
|
||||
GUILayout.Space(5);
|
||||
GUILayout.Space(5);
|
||||
|
||||
|
||||
if (mostRecentlyPlacedProp != _dpp.mostRecentlyPlacedPropGO)
|
||||
{
|
||||
mostRecentlyPlacedProp = _dpp.mostRecentlyPlacedPropGO;
|
||||
mostRecentlyPlacedPropSphericalPos = DeltaSphericalPosition(mostRecentlyPlacedProp, Vector3.zero);
|
||||
}
|
||||
|
||||
Vector3 latestPropSphericalPosDelta = VectorInput(mostRecentlyPlacedPropSphericalPos, propSphericalPosDelta, out propSphericalPosDelta, "lat ", "lon ", "height");
|
||||
if (latestPropSphericalPosDelta != Vector3.zero)
|
||||
{
|
||||
DeltaSphericalPosition(mostRecentlyPlacedProp, latestPropSphericalPosDelta);
|
||||
mostRecentlyPlacedPropSphericalPos = mostRecentlyPlacedPropSphericalPos+latestPropSphericalPosDelta;
|
||||
}
|
||||
|
||||
GUILayout.Space(5);
|
||||
GUILayout.Space(5);
|
||||
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Label("Rotate about up: ", GUILayout.Width(50));
|
||||
float deltaRot = 0;
|
||||
if (GUILayout.Button("+", GUILayout.ExpandWidth(false))) deltaRot += propRotationAboutLocalUpDelta;
|
||||
if (GUILayout.Button("-", GUILayout.ExpandWidth(false))) deltaRot -= propRotationAboutLocalUpDelta;
|
||||
propRotationAboutLocalUpDelta = float.Parse(GUILayout.TextField(propRotationAboutLocalUpDelta+"", GUILayout.Width(100)));
|
||||
|
||||
if (deltaRot != 0)
|
||||
{
|
||||
Transform astroObject = mostRecentlyPlacedProp.transform.parent.parent;
|
||||
mostRecentlyPlacedProp.transform.RotateAround(mostRecentlyPlacedProp.transform.position, mostRecentlyPlacedProp.transform.up, deltaRot);
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Label("scale: ", GUILayout.Width(50));
|
||||
var scaleString = mostRecentlyPlacedProp.transform.localScale.x+"";
|
||||
var newScaleString = GUILayout.TextField(scaleString , GUILayout.Width(50));
|
||||
var parsedScaleString = mostRecentlyPlacedProp.transform.localScale.x; try { parsedScaleString = float.Parse(newScaleString); } catch {}
|
||||
float deltaScale = scaleString == newScaleString ? 0 : parsedScaleString - mostRecentlyPlacedProp.transform.localScale.x;
|
||||
if (GUILayout.Button("+", GUILayout.ExpandWidth(false))) deltaScale += propScaleDelta;
|
||||
if (GUILayout.Button("-", GUILayout.ExpandWidth(false))) deltaScale -= propScaleDelta;
|
||||
propScaleDelta = float.Parse(GUILayout.TextField(propScaleDelta+"", GUILayout.Width(100)));
|
||||
|
||||
if (deltaScale != 0)
|
||||
{
|
||||
float newScale = mostRecentlyPlacedProp.transform.localScale.x + deltaScale;
|
||||
mostRecentlyPlacedProp.transform.localScale = new Vector3(newScale, newScale, newScale);
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
private Vector3 DeltaSphericalPosition(GameObject prop, Vector3 deltaSpherical)
|
||||
{
|
||||
Transform astroObject = prop.transform.parent.parent;
|
||||
Transform sector = prop.transform.parent;
|
||||
Vector3 originalLocalPos = astroObject.InverseTransformPoint(prop.transform.position); // parent is the sector, this gives localPos relative to the astroobject (what the DetailBuilder asks for)
|
||||
Vector3 sphericalPos = CoordinateUtilities.CartesianToSpherical(originalLocalPos);
|
||||
|
||||
if (deltaSpherical == Vector3.zero) return sphericalPos;
|
||||
Vector3 newSpherical = sphericalPos+deltaSpherical;
|
||||
|
||||
Vector3 finalLocalPosition = CoordinateUtilities.SphericalToCartesian(newSpherical);
|
||||
Vector3 finalAbsolutePosition = astroObject.TransformPoint(finalLocalPosition);
|
||||
prop.transform.localPosition = prop.transform.parent.InverseTransformPoint(finalAbsolutePosition);
|
||||
// prop.transform.rotation = Quaternion.FromToRotation(originalLocalPos.normalized, finalLocalPosition.normalized) * prop.transform.rotation;
|
||||
|
||||
// first, rotate the object by the astroObject's rotation, that means anything afterwards is relative to this rotation (ie, we can pretend the astroObject has 0 rotation)
|
||||
// then, rotate by the difference in position, basically accounting for the curvature of a sphere
|
||||
// then re-apply the local rotations of the hierarchy down to the prop (apply the sector local rotation, then the prop local rotation)
|
||||
|
||||
// since we're doing all rotation relative to the astro object, we start with its absolute rotation
|
||||
// then we apply the rotation about the astroobject using FromTooRotation
|
||||
// then we reapply the local rotations down through the hierarchy
|
||||
prop.transform.rotation = astroObject.rotation * Quaternion.FromToRotation(originalLocalPos.normalized, finalLocalPosition.normalized) * sector.localRotation * prop.transform.localRotation;
|
||||
|
||||
return newSpherical;
|
||||
}
|
||||
|
||||
private Vector3 VectorInput(Vector3 input, Vector3 deltaControls, out Vector3 deltaControlsOut, string labelX, string labelY, string labelZ)
|
||||
{
|
||||
var dx = deltaControls.x;
|
||||
var dy = deltaControls.y;
|
||||
var dz = deltaControls.z;
|
||||
|
||||
// x
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Label(labelX+": ", GUILayout.Width(50));
|
||||
var xString = input.x+"";
|
||||
var newXString = GUILayout.TextField(xString, GUILayout.Width(50));
|
||||
var parsedXString = input.x; try { parsedXString = float.Parse(newXString); } catch {}
|
||||
float deltaX = xString == newXString ? 0 : parsedXString - input.x;
|
||||
if (GUILayout.Button("+", GUILayout.ExpandWidth(false))) deltaX += dx;
|
||||
if (GUILayout.Button("-", GUILayout.ExpandWidth(false))) deltaX -= dx;
|
||||
dx = float.Parse(GUILayout.TextField(dx+"", GUILayout.Width(100)));
|
||||
GUILayout.EndHorizontal();
|
||||
// y
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Label(labelY+": ", GUILayout.Width(50));
|
||||
var yString = input.y+"";
|
||||
var newYString = GUILayout.TextField(yString, GUILayout.Width(50));
|
||||
var parsedYString = input.y; try { parsedYString = float.Parse(newYString); } catch {}
|
||||
float deltaY = yString == newYString ? 0 : parsedYString - input.y;
|
||||
if (GUILayout.Button("+", GUILayout.ExpandWidth(false))) deltaY += dy;
|
||||
if (GUILayout.Button("-", GUILayout.ExpandWidth(false))) deltaY -= dy;
|
||||
dy = float.Parse(GUILayout.TextField(dy+"", GUILayout.Width(100)));
|
||||
GUILayout.EndHorizontal();
|
||||
// z
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Label(labelZ+": ", GUILayout.Width(50));
|
||||
var zString = input.z+"";
|
||||
var newZString = GUILayout.TextField(zString, GUILayout.Width(50));
|
||||
var parsedZString = input.z; try { parsedZString = float.Parse(newZString); } catch {}
|
||||
float deltaZ = zString == newZString ? 0 : parsedZString - input.z;
|
||||
if (GUILayout.Button("+", GUILayout.ExpandWidth(false))) deltaZ += dz;
|
||||
if (GUILayout.Button("-", GUILayout.ExpandWidth(false))) deltaZ -= dz;
|
||||
dz = float.Parse(GUILayout.TextField(dz+"", GUILayout.Width(100)));
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
deltaControlsOut = new Vector3(dx, dy, dz);
|
||||
return new Vector3(deltaX, deltaY, deltaZ);
|
||||
}
|
||||
|
||||
private void DrawPropsList(DebugMenu menu)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
@ -117,9 +289,7 @@ namespace NewHorizons.Utility.DebugMenu
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
GUILayout.EndScrollView();
|
||||
|
||||
GUILayout.Space(5);
|
||||
GUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
internal override void PreSave(DebugMenu menu)
|
||||
|
||||
@ -31,7 +31,7 @@ namespace NewHorizons.Utility.DebugUtilities
|
||||
public static readonly string DEFAULT_OBJECT = "BrittleHollow_Body/Sector_BH/Sector_NorthHemisphere/Sector_NorthPole/Sector_HangingCity/Sector_HangingCity_District1/Props_HangingCity_District1/OtherComponentsGroup/Props_HangingCity_Building_10/Prefab_NOM_VaseThin";
|
||||
|
||||
public string currentObject { get; private set; } // path of the prop to be placed
|
||||
private bool hasAddedCurrentObjectToRecentsList = false;
|
||||
private bool hasAddedCurrentObjectToRecentsList = false;
|
||||
private List<PropPlacementData> props = new List<PropPlacementData>();
|
||||
private List<PropPlacementData> deletedProps = new List<PropPlacementData>();
|
||||
private DebugRaycaster _rc;
|
||||
@ -39,6 +39,8 @@ namespace NewHorizons.Utility.DebugUtilities
|
||||
public static HashSet<string> RecentlyPlacedProps = new HashSet<string>();
|
||||
|
||||
public static bool active = false;
|
||||
public GameObject mostRecentlyPlacedPropGO { get { return props.Count() <= 0 ? null : props[props.Count()-1].gameObject; } }
|
||||
public string mostRecentlyPlacedPropPath { get { return props.Count() <= 0 ? "" : props[props.Count()-1].detailInfo.path; } }
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
@ -65,7 +67,7 @@ namespace NewHorizons.Utility.DebugUtilities
|
||||
{
|
||||
UndoDelete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetCurrentObject(string s)
|
||||
{
|
||||
@ -153,7 +155,7 @@ namespace NewHorizons.Utility.DebugUtilities
|
||||
return astroObjectName;
|
||||
}
|
||||
|
||||
public void FindAndRegisterPropsFromConfig(PlanetConfig config)
|
||||
public void FindAndRegisterPropsFromConfig(PlanetConfig config, List<string> pathsList = null)
|
||||
{
|
||||
if (config.starSystem != Main.Instance.CurrentStarSystem) return;
|
||||
|
||||
@ -180,7 +182,7 @@ namespace NewHorizons.Utility.DebugUtilities
|
||||
// selectable list of placed props
|
||||
if (detail.assetBundle == null && !RecentlyPlacedProps.Contains(data.detailInfo.path))
|
||||
{
|
||||
RecentlyPlacedProps.Add(data.detailInfo.path);
|
||||
if (pathsList != null) pathsList.Add(data.detailInfo.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user