From 58aa10ce509cea61203268f32aae0615eb639717 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Fri, 30 Dec 2022 19:14:32 -0500 Subject: [PATCH 01/58] created folder for all the nomai text related code, moved the old builder in there and copied over the spiral generator and auto placer from the nh-unity project --- .../Props/NomaiText/NomaiTextArcArranger.cs | 277 ++++++++++ .../Props/NomaiText/NomaiTextArcBuilder.cs | 508 ++++++++++++++++++ .../Props/{ => NomaiText}/NomaiTextBuilder.cs | 0 3 files changed, 785 insertions(+) create mode 100644 NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs create mode 100644 NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs rename NewHorizons/Builder/Props/{ => NomaiText}/NomaiTextBuilder.cs (100%) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs new file mode 100644 index 00000000..590d61ad --- /dev/null +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs @@ -0,0 +1,277 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +[ExecuteInEditMode] +public class NomaiTextArcArranger : MonoBehaviour { + public List spirals = new List(); + private Dictionary sprialOverlapResolutionPriority = new Dictionary(); + + private static int MAX_MOVE_DISTANCE = 2; + + public float maxX = 4; + public float minX = -4; + public float maxY = 5f; + public float minY = -1f; + + public static SpiralManipulator Place(GameObject spiralMeshHolder = null) { + if (spiralMeshHolder == null) + { + spiralMeshHolder = new GameObject("spiral holder"); + spiralMeshHolder.AddComponent(); + } + + var rootArc = NomaiTextArcBuilder.BuildSpiralGameObject(NomaiTextArcBuilder.adultSpiralProfile); + rootArc.transform.parent = spiralMeshHolder.transform; + rootArc.transform.localEulerAngles = new Vector3(0, 0, Random.Range(-60, 60)); + + var manip = rootArc.AddComponent(); + if (Random.value < 0.5) manip.transform.localScale = new Vector3(-1, 1, 1); // randomly mirror + spiralMeshHolder.GetComponent().spirals.Add(manip); + + return manip; + } + + private void OnDrawGizmosSelected() + { + var topLeft = new Vector3(minX, maxY) + transform.position; + var topRight = new Vector3(maxX, maxY) + transform.position; + var bottomRight = new Vector3(maxX, minY) + transform.position; + var bottomLeft = new Vector3(minX, minY) + transform.position; + Debug.DrawLine(topLeft, topRight, Color.red); + Debug.DrawLine(topRight, bottomRight, Color.red); + Debug.DrawLine(bottomRight, bottomLeft, Color.red); + Debug.DrawLine(bottomLeft, topLeft, Color.red); + } + + public int AttemptOverlapResolution(Vector2Int overlappingSpirals) + { + if (!sprialOverlapResolutionPriority.ContainsKey(overlappingSpirals.x)) sprialOverlapResolutionPriority[overlappingSpirals.x] = 0; + if (!sprialOverlapResolutionPriority.ContainsKey(overlappingSpirals.y)) sprialOverlapResolutionPriority[overlappingSpirals.y] = 0; + + int mirrorIndex = overlappingSpirals.x; + if (sprialOverlapResolutionPriority[overlappingSpirals.y] > sprialOverlapResolutionPriority[overlappingSpirals.x]) mirrorIndex = overlappingSpirals.y; + + this.spirals[mirrorIndex].Mirror(); + sprialOverlapResolutionPriority[mirrorIndex]--; + + return mirrorIndex; + } + + public Vector2Int Overlap() + { + var index = -1; + foreach (var s1 in spirals) + { + index++; + if (s1.parent == null) continue; + + var jndex = -1; + foreach (var s2 in spirals) + { + jndex++; + if (s1 == s2) continue; + if (Vector3.Distance(s1.center, s2.center) > Mathf.Max(s1.NomaiTextLine.GetWorldRadius(), s2.NomaiTextLine.GetWorldRadius())) continue; // no overlap possible - too far away + + var s1Points = s1.NomaiTextLine.GetPoints().Select(p => s1.transform.TransformPoint(p)).ToList(); + var s2Points = s2.NomaiTextLine.GetPoints().Select(p => s2.transform.TransformPoint(p)).ToList(); + var s1ThresholdForOverlap = Vector3.Distance(s1Points[0], s1Points[1]); + var s2ThresholdForOverlap = Vector3.Distance(s2Points[0], s2Points[1]); + var thresholdForOverlap = Mathf.Pow(Mathf.Max(s1ThresholdForOverlap, s2ThresholdForOverlap), 2); // square to save on computation (we'll work in distance squared from here on) + + if (s1.parent == s2) s1Points.RemoveAt(0); // don't consider the base points so that we can check if children overlap their parents + if (s2.parent == s1) s2Points.RemoveAt(0); // (note: the base point of a child is always exactly overlapping with one of the parent's points) + + foreach(var p1 in s1Points) + { + foreach(var p2 in s2Points) + { + if (Vector3.SqrMagnitude(p1-p2) <= thresholdForOverlap) return new Vector2Int(index, jndex); // s1 and s2 overlap + } + } + } + } + + return new Vector2Int(-1, -1); + } + + public void Step() { + // TODO: after setting child position on parent in Step(), check to see if this spiral exits the bounds - if so, move it away until it no longer does + // this ensures that a spiral can never be outside the bounds, it makes them rigid + + // TODO: for integration with NH - before generating spirals, seed the RNG with the hash of the XML filename for this convo + // and add an option to specify the seed + + var index = -1; + foreach (var s1 in spirals) + { + index++; + if (s1.parent == null) continue; + + // + // Calculate the force s1 should experience + // + + Vector2 force = Vector2.zero; + foreach (var s2 in spirals) + { + if (s1 == s2) continue; + if (s1.parent == s2) continue; + if (s1 == s2.parent) continue; + + // push away from other spirals + var f = (s2.center - s1.center); + force -= f / Mathf.Pow(f.magnitude, 6); + + var f2 = (s2.localPosition - s1.localPosition); + force -= f2 / Mathf.Pow(f2.magnitude, 6); + } + + + // push away from the edges + if (s1.center.y < minY+s1.transform.parent.position.y) force += new Vector2(0, Mathf.Pow(10f*minY - 10f*s1.center.y, 6)); + if (s1.center.x < minX+s1.transform.parent.position.x) force += new Vector2(Mathf.Pow(10f*minX - 10f*s1.center.x, 6), 0); + if (s1.center.y > maxY+s1.transform.parent.position.y) force -= new Vector2(0, Mathf.Pow(10f*maxY - 10f*s1.center.y, 6)); + if (s1.center.x > maxX+s1.transform.parent.position.x) force -= new Vector2(Mathf.Pow(10f*maxX - 10f*s1.center.x, 6), 0); + + // + // renormalize the force magnitude (keeps force sizes reasonable, and improves stability in the case of small forces) + // + + var avg = 1; // the size of vector required to get a medium push + var scale = 0.75f; + force = force.normalized * scale * (1 / (1 + Mathf.Exp(avg-force.magnitude)) - 1 / (1 + Mathf.Exp(avg))); // apply a sigmoid-ish smoothing operation, so only giant forces actually move the spirals + + // + // apply the forces as we go to increase stability? + // + + var spiral = s1; + var parentPoints = spiral.parent.GetComponent().GetPoints(); + + // pick the parent point that's closest to center+force, and move to there + var idealPoint = spiral.position + force; + var bestPointIndex = 0; + var bestPointDistance = 99999999f; + for (var j = SpiralManipulator.MIN_PARENT_POINT; j < SpiralManipulator.MAX_PARENT_POINT; j++) + { + // skip this point if it's already occupied by ANOTHER spiral (if it's occupied by this spiral, DO count it) + if (j != spiral._parentPointIndex && spiral.parent.occupiedParentPoints.Contains(j)) continue; + + var point = parentPoints[j]; + point = spiral.parent.transform.TransformPoint(point); + + var dist = Vector2.Distance(point, idealPoint); + if (dist < bestPointDistance) { + bestPointDistance = dist; + bestPointIndex = j; + } + } + + // + // limit the distance a spiral can move in a single step + // + + bestPointIndex = spiral._parentPointIndex + Mathf.Min(MAX_MOVE_DISTANCE, Mathf.Max(-MAX_MOVE_DISTANCE, bestPointIndex - spiral._parentPointIndex)); // minimize step size to help stability + + // + // actually move the spiral + // + + SpiralManipulator.PlaceChildOnParentPoint(spiral, spiral.parent, bestPointIndex); + } + } +} + +[ExecuteInEditMode] +public class SpiralManipulator : MonoBehaviour { + public SpiralManipulator parent; + public List children = new List(); + + public HashSet occupiedParentPoints = new HashSet(); + public int _parentPointIndex = -1; + + public static int MIN_PARENT_POINT = 3; + public static int MAX_PARENT_POINT = 26; + + + private NomaiTextLine _NomaiTextLine; + public NomaiTextLine NomaiTextLine + { + get + { + if (_NomaiTextLine == null) _NomaiTextLine = GetComponent(); + return _NomaiTextLine; + } + } + + public Vector2 center { + get { return NomaiTextLine.GetWorldCenter(); } + } + + public Vector2 localPosition { + get { return new Vector2(this.transform.localPosition.x, this.transform.localPosition.y); } + } + public Vector2 position { + get { return new Vector2(this.transform.position.x, this.transform.position.y); } + } + + public SpiralManipulator AddChild() { + var index = Random.Range(MIN_PARENT_POINT, MAX_PARENT_POINT); + var child = NomaiTextArcArranger.Place(this.transform.parent.gameObject); + PlaceChildOnParentPoint(child, this, index); + + child.GetComponent().parent = this; + this.children.Add(child.GetComponent()); + return child.GetComponent(); + } + + public void Mirror() + { + this.transform.localScale = new Vector3(-this.transform.localScale.x, 1, 1); + if (this.parent != null) SpiralManipulator.PlaceChildOnParentPoint(this, this.parent, this._parentPointIndex); + } + + public void UpdateChildren() + { + foreach(var child in this.children) + { + PlaceChildOnParentPoint(child, this, child._parentPointIndex); + } + } + + public static int PlaceChildOnParentPoint(SpiralManipulator child, SpiralManipulator parent, int parentPointIndex, bool updateChildren=true) + { + // track which points on the parent are being occupied + if (child._parentPointIndex != -1) parent.occupiedParentPoints.Remove(child._parentPointIndex); + child._parentPointIndex = parentPointIndex; // just in case this function was called without setting this value + parent.occupiedParentPoints.Add(parentPointIndex); + + // get the parent's points and make parentPointIndex valid + var _points = parent.GetComponent().GetPoints(); + parentPointIndex = Mathf.Max(0, Mathf.Min(parentPointIndex, _points.Length-1)); + + // calculate the normal at point by using the neighboring points to approximate the tangent (and account for mirroring, which means all points are actually at (-point.x, point.y) ) + var normal = _points[Mathf.Min(parentPointIndex+1, _points.Length-1)] - _points[Mathf.Max(parentPointIndex-1, 0)]; + if (parent.transform.localScale.x < 0) normal = new Vector3(-normal.x, normal.y, normal.z); + float rot = Mathf.Atan2(normal.y, normal.x) * Mathf.Rad2Deg; + if (parent.transform.localScale.x < 0) rot += 180; // account for mirroring again (without doing this, the normal points inward on mirrored spirals, instead of outward) + + // get the location the child spiral should be at (and yet again account for mirroring) + var point = _points[parentPointIndex]; + if (parent.transform.localScale.x < 0) point = new Vector3(-point.x, point.y, point.z); + + // set the child's position and rotation according to calculations + child.transform.localPosition = Quaternion.Euler(0, 0, parent.transform.localEulerAngles.z) * point + parent.transform.localPosition; + child.transform.localEulerAngles = new Vector3(0, 0, rot + parent.transform.localEulerAngles.z); + + // recursive update on all children so they move along with the parent + if (updateChildren) + { + child.UpdateChildren(); + } + + return parentPointIndex; + } +} \ No newline at end of file diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs new file mode 100644 index 00000000..822a8823 --- /dev/null +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs @@ -0,0 +1,508 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEditor; +using System.Reflection; + +public static class NomaiTextArcBuilder { + public static int i = 0; + + public static SpiralProfile spiralProfile; + public static bool removeBakedInRotationAndPosition = true; + + public static void PlaceAdult() + { + BuildSpiralGameObject(adultSpiralProfile, "Text Arc Prefab " + (i++)); + } + public static void PlaceChild() + { + BuildSpiralGameObject(childSpiralProfile, "Text Arc Prefab " + (i++)); + } + + public static GameObject BuildSpiralGameObject(SpiralProfile profile, string goName="New Nomai Spiral") + { + var g = new GameObject(goName); + g.transform.localPosition = Vector3.zero; + g.transform.localEulerAngles = Vector3.zero; + + var m = new SpiralMesh(profile); + m.Randomize(); + m.updateMesh(); + + g.AddComponent().sharedMesh = m.mesh; + g.AddComponent().sharedMaterial = new Material(Shader.Find("Sprites/Default")); + g.GetComponent().sharedMaterial.color = Color.magenta; + + var owNomaiTextLine = g.AddComponent(); + + // + // rotate mesh to face up + // + + var norm = m.skeleton[1] - m.skeleton[0]; + float r = Mathf.Atan2(-norm.y, norm.x) * Mathf.Rad2Deg; + if (m.mirror) r += 180; + var ang = m.mirror ? 90-r : -90-r; + + // using m.sharedMesh causes old meshes to disappear for some reason, idk why + var mesh = g.GetComponent().mesh; + if (removeBakedInRotationAndPosition) + { + var meshCopy = mesh; + var newVerts = meshCopy.vertices.Select(v => Quaternion.Euler(-90, 0, 0) * Quaternion.Euler(0, ang, 0) * v).ToArray(); + meshCopy.vertices = newVerts; + meshCopy.RecalculateBounds(); + } + + AssetDatabase.CreateAsset(mesh, "Assets/Spirals/"+(profile.profileName)+"spiral" + (NomaiTextArcBuilder.i) + ".asset"); + g.GetComponent().sharedMesh = AssetDatabase.LoadAssetAtPath("Assets/Spirals/"+(profile.profileName)+"spiral" + (NomaiTextArcBuilder.i) + ".asset", typeof(Mesh)) as Mesh; + NomaiTextArcBuilder.i++; + + // + // set up NomaiTextArc stuff + // + + var _points = m.skeleton + .Select((compiled) => + Quaternion.Euler(-90, 0, 0) * Quaternion.Euler(0, ang, 0) * (new Vector3(compiled.x, 0, compiled.y)) // decompile them, rotate them by ang, and then rotate them to be vertical, like the base game spirals are + ) + .ToList(); + + var _lengths = _points.Take(_points.Count()-1).Select((point, i) => Vector3.Distance(point, _points[i+1])).ToArray(); + var _totalLength = _lengths.Aggregate(0f, (acc, length) => acc + length); + var _state = NomaiTextLine.VisualState.UNREAD; + var _textLineLocation = NomaiText.Location.UNSPECIFIED; + var _center = _points.Aggregate(Vector3.zero, (acc, point) => acc + point) / (float)_points.Count(); + var _radius = _points.Aggregate(0f, (acc, point) => Mathf.Max(Vector3.Distance(_center, point), acc)); + var _active = true; + + (typeof (NomaiTextLine)).InvokeMember("_points", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _points.ToArray() }); + (typeof (NomaiTextLine)).InvokeMember("_lengths", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _lengths }); + (typeof (NomaiTextLine)).InvokeMember("_totalLength", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _totalLength }); + (typeof (NomaiTextLine)).InvokeMember("_state", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _state }); + (typeof (NomaiTextLine)).InvokeMember("_textLineLocation", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _textLineLocation }); + (typeof (NomaiTextLine)).InvokeMember("_center", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _center }); + (typeof (NomaiTextLine)).InvokeMember("_radius", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _radius }); + (typeof (NomaiTextLine)).InvokeMember("_active", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _active }); + + return g; + } + + // + // + // Handle the connection between game objects and spiral meshes + // + // + + public struct SpiralProfile { + // all of the Vector2 params here refer to a range of valid values + public string profileName; + public bool canMirror; + public Vector2 a; + public Vector2 b; + public Vector2 endS; + public Vector2 skeletonScale; + public int numSkeletonPoints; + public float uvScale; + public float innerWidth; // width at the tip + public float outerWidth; // width at the base + public Material material; + } + + public static SpiralProfile adultSpiralProfile = new SpiralProfile() { + profileName="Adult", + canMirror = false, // we don't want to mirror the actual mesh itself anymore, we'll just mirror the game object using localScale.x + a = new Vector2(0.5f, 0.5f), + b = new Vector2(0.3f, 0.6f), + endS = new Vector2(0, 50f), + skeletonScale = new Vector2(0.01f, 0.01f), + numSkeletonPoints = 51, + + innerWidth = 0.001f, + outerWidth = 0.05f, + uvScale = 4.9f, + }; + + public static SpiralProfile childSpiralProfile = new SpiralProfile() { + profileName="Child", + canMirror = false, // we don't want to mirror the actual mesh itself anymore, we'll just mirror the game object using localScale.x + a = new Vector2(0.9f, 0.9f), + b = new Vector2(0.305f, 0.4f), + endS = new Vector2(16f, 60f), + skeletonScale = new Vector2(0.002f, 0.005f), + numSkeletonPoints = 51, + + innerWidth = 0.001f/10f, + outerWidth = 2f*0.05f, + uvScale = 4.9f/3.5f, + }; + + // + // + // Construct spiral meshes from the mathematical spirals generated below + // + // + + public class SpiralMesh: MathematicalSpiral { + public List skeleton; + public List skeletonOutsidePoints; + + public int numSkeletonPoints = 51; // seems to be Mobius' default + + public float innerWidth = 0.001f; // width at the tip + public float outerWidth = 0.05f; //0.107f; // width at the base + public float uvScale = 4.9f; //2.9f; + private float baseUVScale = 1f / 300f; + public float uvOffset = 0; + + public Mesh mesh; + + public SpiralMesh(SpiralProfile profile): base(profile) { + this.numSkeletonPoints = profile.numSkeletonPoints; + this.innerWidth = profile.innerWidth; + this.outerWidth = profile.outerWidth; + this.uvScale = profile.uvScale; + + this.uvOffset = UnityEngine.Random.value; + } + + public override void Randomize() { + base.Randomize(); + uvOffset = UnityEngine.Random.value; // this way even two spirals that are exactly the same shape will look different (this changes the starting point of the handwriting texture) + } + + internal void updateMesh() { + skeleton = this.getSkeleton(numSkeletonPoints); + skeletonOutsidePoints = this.getSkeletonOutsidePoints(numSkeletonPoints); + + List vertsSide1 = skeleton.Select((skeletonPoint, index) => { + Vector3 normal = new Vector3(cos(skeletonPoint.z), 0, sin(skeletonPoint.z)); + float width = lerp(((float) index) / ((float) skeleton.Count()), outerWidth, innerWidth); + + return new Vector3(skeletonPoint.x, 0, skeletonPoint.y) + width * normal; + }).ToList(); + + List vertsSide2 = skeleton.Select((skeletonPoint, index) => { + Vector3 normal = new Vector3(cos(skeletonPoint.z), 0, sin(skeletonPoint.z)); + float width = lerp(((float) index) / ((float) skeleton.Count()), outerWidth, innerWidth); + + return new Vector3(skeletonPoint.x, 0, skeletonPoint.y) - width * normal; + }).ToList(); + + Vector3[] newVerts = vertsSide1.Zip(vertsSide2, (f, s) => new [] { + f, + s + }).SelectMany(f =>f).ToArray(); // interleave vertsSide1 and vertsSide2 + + List triangles = new List(); + for (int i = 0; i children; + + public float x; + public float y; + public float ang; + + public float startS = 42.87957f; // go all the way down to 0, all the way up to 50 + public float endS = 342.8796f; + + SpiralProfile profile; + + public MathematicalSpiral(SpiralProfile profile) { + this.profile = profile; + + this.Randomize(); + } + + public MathematicalSpiral(float startSOnParent = 0, bool mirror = false, float len = 300, float a = 0.5f, float b = 0.43f, float scale = 0.01f) { + this.mirror = mirror; + this.a = a; + this.b = b; + this.startSOnParent = startSOnParent; + this.scale = scale; + + this.children = new List(); + + this.x = 0; + this.y = 0; + this.ang = 0; + } + + public virtual void Randomize() { + this.a = UnityEngine.Random.Range(profile.a.x, profile.a.y); //0.5f; + this.b = UnityEngine.Random.Range(profile.b.x, profile.b.y); + this.startS = UnityEngine.Random.Range(profile.endS.x, profile.endS.y); + this.scale = UnityEngine.Random.Range(profile.skeletonScale.x, profile.skeletonScale.y); + if (profile.canMirror) this.mirror = UnityEngine.Random.value<0.5f; + } + + internal virtual void updateChild(MathematicalSpiral child) { + Vector3 pointAndNormal = getDrawnSpiralPointAndNormal(child.startSOnParent); + var cx = pointAndNormal.x; + var cy = pointAndNormal.y; + var cang = pointAndNormal.z; + child.x = cx; + child.y = cy; + child.ang = cang + (child.mirror ? Mathf.PI : 0); + } + + public virtual void addChild(MathematicalSpiral child) { + updateChild(child); + this.children.Add(child); + } + + public virtual void updateChildren() { + this.children.ForEach(child => { + updateChild(child); + child.updateChildren(); + }); + } + + // note: each Vector3 in this list is of form + public List getSkeleton(int numPoints) { + var skeleton = + WalkAlongSpiral(numPoints) + .Select(input => { + float inputS = input.y; + var skeletonPoint = getDrawnSpiralPointAndNormal(inputS); + return skeletonPoint; + }) + .Reverse() + .ToList(); + + return skeleton; + } + + public List getSkeletonOutsidePoints(int numPoints) { + var outsidePoints = + WalkAlongSpiral(numPoints) + .Select(input => { + float inputT = input.x; + float inputS = input.y; + + var skeletonPoint = getDrawnSpiralPointAndNormal(inputS); + + var deriv = spiralDerivative(inputT); + var outsidePoint = new Vector2(skeletonPoint.x, skeletonPoint.y) - (new Vector2(-deriv.y, deriv.x)).normalized * 0.1f; + return outsidePoint; + }) + .Reverse() + .ToList(); + + return outsidePoints; + } + + // generate a list of evenly distributed over the whole spiral. `numPoints` number of pairs are generated + public IEnumerable WalkAlongSpiral(int numPoints) { + var endT = tFromArcLen(endS); + var startT = tFromArcLen(startS); + var rangeT = endT - startT; + + for (int i = 0; i Date: Fri, 30 Dec 2022 19:21:46 -0500 Subject: [PATCH 02/58] fixed indentation to match the rest of the project and added namespace --- .../Props/NomaiText/NomaiTextArcArranger.cs | 480 ++++----- .../Props/NomaiText/NomaiTextArcBuilder.cs | 983 +++++++++--------- 2 files changed, 735 insertions(+), 728 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs index 590d61ad..4e1995e5 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs @@ -3,275 +3,279 @@ using System.Collections.Generic; using System.Linq; using UnityEngine; -[ExecuteInEditMode] -public class NomaiTextArcArranger : MonoBehaviour { - public List spirals = new List(); - private Dictionary sprialOverlapResolutionPriority = new Dictionary(); - private static int MAX_MOVE_DISTANCE = 2; - public float maxX = 4; - public float minX = -4; - public float maxY = 5f; - public float minY = -1f; +namespace NewHorizons.Builder.Props +{ + public class NomaiTextArcArranger : MonoBehaviour { + public List spirals = new List(); + private Dictionary sprialOverlapResolutionPriority = new Dictionary(); - public static SpiralManipulator Place(GameObject spiralMeshHolder = null) { - if (spiralMeshHolder == null) - { - spiralMeshHolder = new GameObject("spiral holder"); - spiralMeshHolder.AddComponent(); - } + private static int MAX_MOVE_DISTANCE = 2; - var rootArc = NomaiTextArcBuilder.BuildSpiralGameObject(NomaiTextArcBuilder.adultSpiralProfile); - rootArc.transform.parent = spiralMeshHolder.transform; - rootArc.transform.localEulerAngles = new Vector3(0, 0, Random.Range(-60, 60)); + public float maxX = 4; + public float minX = -4; + public float maxY = 5f; + public float minY = -1f; - var manip = rootArc.AddComponent(); - if (Random.value < 0.5) manip.transform.localScale = new Vector3(-1, 1, 1); // randomly mirror - spiralMeshHolder.GetComponent().spirals.Add(manip); + public static SpiralManipulator Place(GameObject spiralMeshHolder = null) { + if (spiralMeshHolder == null) + { + spiralMeshHolder = new GameObject("spiral holder"); + spiralMeshHolder.AddComponent(); + } - return manip; - } + var rootArc = NomaiTextArcBuilder.BuildSpiralGameObject(NomaiTextArcBuilder.adultSpiralProfile); + rootArc.transform.parent = spiralMeshHolder.transform; + rootArc.transform.localEulerAngles = new Vector3(0, 0, Random.Range(-60, 60)); - private void OnDrawGizmosSelected() - { - var topLeft = new Vector3(minX, maxY) + transform.position; - var topRight = new Vector3(maxX, maxY) + transform.position; - var bottomRight = new Vector3(maxX, minY) + transform.position; - var bottomLeft = new Vector3(minX, minY) + transform.position; - Debug.DrawLine(topLeft, topRight, Color.red); - Debug.DrawLine(topRight, bottomRight, Color.red); - Debug.DrawLine(bottomRight, bottomLeft, Color.red); - Debug.DrawLine(bottomLeft, topLeft, Color.red); - } + var manip = rootArc.AddComponent(); + if (Random.value < 0.5) manip.transform.localScale = new Vector3(-1, 1, 1); // randomly mirror + spiralMeshHolder.GetComponent().spirals.Add(manip); - public int AttemptOverlapResolution(Vector2Int overlappingSpirals) - { - if (!sprialOverlapResolutionPriority.ContainsKey(overlappingSpirals.x)) sprialOverlapResolutionPriority[overlappingSpirals.x] = 0; - if (!sprialOverlapResolutionPriority.ContainsKey(overlappingSpirals.y)) sprialOverlapResolutionPriority[overlappingSpirals.y] = 0; + return manip; + } - int mirrorIndex = overlappingSpirals.x; - if (sprialOverlapResolutionPriority[overlappingSpirals.y] > sprialOverlapResolutionPriority[overlappingSpirals.x]) mirrorIndex = overlappingSpirals.y; - - this.spirals[mirrorIndex].Mirror(); - sprialOverlapResolutionPriority[mirrorIndex]--; - - return mirrorIndex; - } - - public Vector2Int Overlap() - { - var index = -1; - foreach (var s1 in spirals) - { - index++; - if (s1.parent == null) continue; - - var jndex = -1; - foreach (var s2 in spirals) - { - jndex++; - if (s1 == s2) continue; - if (Vector3.Distance(s1.center, s2.center) > Mathf.Max(s1.NomaiTextLine.GetWorldRadius(), s2.NomaiTextLine.GetWorldRadius())) continue; // no overlap possible - too far away - - var s1Points = s1.NomaiTextLine.GetPoints().Select(p => s1.transform.TransformPoint(p)).ToList(); - var s2Points = s2.NomaiTextLine.GetPoints().Select(p => s2.transform.TransformPoint(p)).ToList(); - var s1ThresholdForOverlap = Vector3.Distance(s1Points[0], s1Points[1]); - var s2ThresholdForOverlap = Vector3.Distance(s2Points[0], s2Points[1]); - var thresholdForOverlap = Mathf.Pow(Mathf.Max(s1ThresholdForOverlap, s2ThresholdForOverlap), 2); // square to save on computation (we'll work in distance squared from here on) - - if (s1.parent == s2) s1Points.RemoveAt(0); // don't consider the base points so that we can check if children overlap their parents - if (s2.parent == s1) s2Points.RemoveAt(0); // (note: the base point of a child is always exactly overlapping with one of the parent's points) - - foreach(var p1 in s1Points) + private void OnDrawGizmosSelected() { - foreach(var p2 in s2Points) - { - if (Vector3.SqrMagnitude(p1-p2) <= thresholdForOverlap) return new Vector2Int(index, jndex); // s1 and s2 overlap - } + var topLeft = new Vector3(minX, maxY) + transform.position; + var topRight = new Vector3(maxX, maxY) + transform.position; + var bottomRight = new Vector3(maxX, minY) + transform.position; + var bottomLeft = new Vector3(minX, minY) + transform.position; + Debug.DrawLine(topLeft, topRight, Color.red); + Debug.DrawLine(topRight, bottomRight, Color.red); + Debug.DrawLine(bottomRight, bottomLeft, Color.red); + Debug.DrawLine(bottomLeft, topLeft, Color.red); } - } - } - return new Vector2Int(-1, -1); - } + public int AttemptOverlapResolution(Vector2Int overlappingSpirals) + { + if (!sprialOverlapResolutionPriority.ContainsKey(overlappingSpirals.x)) sprialOverlapResolutionPriority[overlappingSpirals.x] = 0; + if (!sprialOverlapResolutionPriority.ContainsKey(overlappingSpirals.y)) sprialOverlapResolutionPriority[overlappingSpirals.y] = 0; - public void Step() { - // TODO: after setting child position on parent in Step(), check to see if this spiral exits the bounds - if so, move it away until it no longer does - // this ensures that a spiral can never be outside the bounds, it makes them rigid + int mirrorIndex = overlappingSpirals.x; + if (sprialOverlapResolutionPriority[overlappingSpirals.y] > sprialOverlapResolutionPriority[overlappingSpirals.x]) mirrorIndex = overlappingSpirals.y; - // TODO: for integration with NH - before generating spirals, seed the RNG with the hash of the XML filename for this convo - // and add an option to specify the seed + this.spirals[mirrorIndex].Mirror(); + sprialOverlapResolutionPriority[mirrorIndex]--; - var index = -1; - foreach (var s1 in spirals) - { - index++; - if (s1.parent == null) continue; - - // - // Calculate the force s1 should experience - // - - Vector2 force = Vector2.zero; - foreach (var s2 in spirals) - { - if (s1 == s2) continue; - if (s1.parent == s2) continue; - if (s1 == s2.parent) continue; - - // push away from other spirals - var f = (s2.center - s1.center); - force -= f / Mathf.Pow(f.magnitude, 6); - - var f2 = (s2.localPosition - s1.localPosition); - force -= f2 / Mathf.Pow(f2.magnitude, 6); - } - - - // push away from the edges - if (s1.center.y < minY+s1.transform.parent.position.y) force += new Vector2(0, Mathf.Pow(10f*minY - 10f*s1.center.y, 6)); - if (s1.center.x < minX+s1.transform.parent.position.x) force += new Vector2(Mathf.Pow(10f*minX - 10f*s1.center.x, 6), 0); - if (s1.center.y > maxY+s1.transform.parent.position.y) force -= new Vector2(0, Mathf.Pow(10f*maxY - 10f*s1.center.y, 6)); - if (s1.center.x > maxX+s1.transform.parent.position.x) force -= new Vector2(Mathf.Pow(10f*maxX - 10f*s1.center.x, 6), 0); - - // - // renormalize the force magnitude (keeps force sizes reasonable, and improves stability in the case of small forces) - // - - var avg = 1; // the size of vector required to get a medium push - var scale = 0.75f; - force = force.normalized * scale * (1 / (1 + Mathf.Exp(avg-force.magnitude)) - 1 / (1 + Mathf.Exp(avg))); // apply a sigmoid-ish smoothing operation, so only giant forces actually move the spirals - - // - // apply the forces as we go to increase stability? - // - - var spiral = s1; - var parentPoints = spiral.parent.GetComponent().GetPoints(); - - // pick the parent point that's closest to center+force, and move to there - var idealPoint = spiral.position + force; - var bestPointIndex = 0; - var bestPointDistance = 99999999f; - for (var j = SpiralManipulator.MIN_PARENT_POINT; j < SpiralManipulator.MAX_PARENT_POINT; j++) - { - // skip this point if it's already occupied by ANOTHER spiral (if it's occupied by this spiral, DO count it) - if (j != spiral._parentPointIndex && spiral.parent.occupiedParentPoints.Contains(j)) continue; - - var point = parentPoints[j]; - point = spiral.parent.transform.TransformPoint(point); - - var dist = Vector2.Distance(point, idealPoint); - if (dist < bestPointDistance) { - bestPointDistance = dist; - bestPointIndex = j; + return mirrorIndex; } - } - - // - // limit the distance a spiral can move in a single step - // - bestPointIndex = spiral._parentPointIndex + Mathf.Min(MAX_MOVE_DISTANCE, Mathf.Max(-MAX_MOVE_DISTANCE, bestPointIndex - spiral._parentPointIndex)); // minimize step size to help stability - - // - // actually move the spiral - // + public Vector2Int Overlap() + { + var index = -1; + foreach (var s1 in spirals) + { + index++; + if (s1.parent == null) continue; - SpiralManipulator.PlaceChildOnParentPoint(spiral, spiral.parent, bestPointIndex); - } - } -} + var jndex = -1; + foreach (var s2 in spirals) + { + jndex++; + if (s1 == s2) continue; + if (Vector3.Distance(s1.center, s2.center) > Mathf.Max(s1.NomaiTextLine.GetWorldRadius(), s2.NomaiTextLine.GetWorldRadius())) continue; // no overlap possible - too far away -[ExecuteInEditMode] -public class SpiralManipulator : MonoBehaviour { - public SpiralManipulator parent; - public List children = new List(); + var s1Points = s1.NomaiTextLine.GetPoints().Select(p => s1.transform.TransformPoint(p)).ToList(); + var s2Points = s2.NomaiTextLine.GetPoints().Select(p => s2.transform.TransformPoint(p)).ToList(); + var s1ThresholdForOverlap = Vector3.Distance(s1Points[0], s1Points[1]); + var s2ThresholdForOverlap = Vector3.Distance(s2Points[0], s2Points[1]); + var thresholdForOverlap = Mathf.Pow(Mathf.Max(s1ThresholdForOverlap, s2ThresholdForOverlap), 2); // square to save on computation (we'll work in distance squared from here on) - public HashSet occupiedParentPoints = new HashSet(); - public int _parentPointIndex = -1; - - public static int MIN_PARENT_POINT = 3; - public static int MAX_PARENT_POINT = 26; - - - private NomaiTextLine _NomaiTextLine; - public NomaiTextLine NomaiTextLine - { - get - { - if (_NomaiTextLine == null) _NomaiTextLine = GetComponent(); - return _NomaiTextLine; - } - } + if (s1.parent == s2) s1Points.RemoveAt(0); // don't consider the base points so that we can check if children overlap their parents + if (s2.parent == s1) s2Points.RemoveAt(0); // (note: the base point of a child is always exactly overlapping with one of the parent's points) - public Vector2 center { - get { return NomaiTextLine.GetWorldCenter(); } - } + foreach(var p1 in s1Points) + { + foreach(var p2 in s2Points) + { + if (Vector3.SqrMagnitude(p1-p2) <= thresholdForOverlap) return new Vector2Int(index, jndex); // s1 and s2 overlap + } + } + } + } - public Vector2 localPosition { - get { return new Vector2(this.transform.localPosition.x, this.transform.localPosition.y); } - } - public Vector2 position { - get { return new Vector2(this.transform.position.x, this.transform.position.y); } - } + return new Vector2Int(-1, -1); + } - public SpiralManipulator AddChild() { - var index = Random.Range(MIN_PARENT_POINT, MAX_PARENT_POINT); - var child = NomaiTextArcArranger.Place(this.transform.parent.gameObject); - PlaceChildOnParentPoint(child, this, index); + public void Step() { + // TODO: after setting child position on parent in Step(), check to see if this spiral exits the bounds - if so, move it away until it no longer does + // this ensures that a spiral can never be outside the bounds, it makes them rigid - child.GetComponent().parent = this; - this.children.Add(child.GetComponent()); - return child.GetComponent(); - } + // TODO: for integration with NH - before generating spirals, seed the RNG with the hash of the XML filename for this convo + // and add an option to specify the seed - public void Mirror() - { - this.transform.localScale = new Vector3(-this.transform.localScale.x, 1, 1); - if (this.parent != null) SpiralManipulator.PlaceChildOnParentPoint(this, this.parent, this._parentPointIndex); - } - - public void UpdateChildren() - { - foreach(var child in this.children) - { - PlaceChildOnParentPoint(child, this, child._parentPointIndex); - } - } + var index = -1; + foreach (var s1 in spirals) + { + index++; + if (s1.parent == null) continue; - public static int PlaceChildOnParentPoint(SpiralManipulator child, SpiralManipulator parent, int parentPointIndex, bool updateChildren=true) - { - // track which points on the parent are being occupied - if (child._parentPointIndex != -1) parent.occupiedParentPoints.Remove(child._parentPointIndex); - child._parentPointIndex = parentPointIndex; // just in case this function was called without setting this value - parent.occupiedParentPoints.Add(parentPointIndex); + // + // Calculate the force s1 should experience + // - // get the parent's points and make parentPointIndex valid - var _points = parent.GetComponent().GetPoints(); - parentPointIndex = Mathf.Max(0, Mathf.Min(parentPointIndex, _points.Length-1)); + Vector2 force = Vector2.zero; + foreach (var s2 in spirals) + { + if (s1 == s2) continue; + if (s1.parent == s2) continue; + if (s1 == s2.parent) continue; + + // push away from other spirals + var f = (s2.center - s1.center); + force -= f / Mathf.Pow(f.magnitude, 6); - // calculate the normal at point by using the neighboring points to approximate the tangent (and account for mirroring, which means all points are actually at (-point.x, point.y) ) - var normal = _points[Mathf.Min(parentPointIndex+1, _points.Length-1)] - _points[Mathf.Max(parentPointIndex-1, 0)]; - if (parent.transform.localScale.x < 0) normal = new Vector3(-normal.x, normal.y, normal.z); - float rot = Mathf.Atan2(normal.y, normal.x) * Mathf.Rad2Deg; - if (parent.transform.localScale.x < 0) rot += 180; // account for mirroring again (without doing this, the normal points inward on mirrored spirals, instead of outward) + var f2 = (s2.localPosition - s1.localPosition); + force -= f2 / Mathf.Pow(f2.magnitude, 6); + } + + + // push away from the edges + if (s1.center.y < minY+s1.transform.parent.position.y) force += new Vector2(0, Mathf.Pow(10f*minY - 10f*s1.center.y, 6)); + if (s1.center.x < minX+s1.transform.parent.position.x) force += new Vector2(Mathf.Pow(10f*minX - 10f*s1.center.x, 6), 0); + if (s1.center.y > maxY+s1.transform.parent.position.y) force -= new Vector2(0, Mathf.Pow(10f*maxY - 10f*s1.center.y, 6)); + if (s1.center.x > maxX+s1.transform.parent.position.x) force -= new Vector2(Mathf.Pow(10f*maxX - 10f*s1.center.x, 6), 0); - // get the location the child spiral should be at (and yet again account for mirroring) - var point = _points[parentPointIndex]; - if (parent.transform.localScale.x < 0) point = new Vector3(-point.x, point.y, point.z); + // + // renormalize the force magnitude (keeps force sizes reasonable, and improves stability in the case of small forces) + // - // set the child's position and rotation according to calculations - child.transform.localPosition = Quaternion.Euler(0, 0, parent.transform.localEulerAngles.z) * point + parent.transform.localPosition; - child.transform.localEulerAngles = new Vector3(0, 0, rot + parent.transform.localEulerAngles.z); + var avg = 1; // the size of vector required to get a medium push + var scale = 0.75f; + force = force.normalized * scale * (1 / (1 + Mathf.Exp(avg-force.magnitude)) - 1 / (1 + Mathf.Exp(avg))); // apply a sigmoid-ish smoothing operation, so only giant forces actually move the spirals - // recursive update on all children so they move along with the parent - if (updateChildren) - { - child.UpdateChildren(); + // + // apply the forces as we go to increase stability? + // + + var spiral = s1; + var parentPoints = spiral.parent.GetComponent().GetPoints(); + + // pick the parent point that's closest to center+force, and move to there + var idealPoint = spiral.position + force; + var bestPointIndex = 0; + var bestPointDistance = 99999999f; + for (var j = SpiralManipulator.MIN_PARENT_POINT; j < SpiralManipulator.MAX_PARENT_POINT; j++) + { + // skip this point if it's already occupied by ANOTHER spiral (if it's occupied by this spiral, DO count it) + if (j != spiral._parentPointIndex && spiral.parent.occupiedParentPoints.Contains(j)) continue; + + var point = parentPoints[j]; + point = spiral.parent.transform.TransformPoint(point); + + var dist = Vector2.Distance(point, idealPoint); + if (dist < bestPointDistance) { + bestPointDistance = dist; + bestPointIndex = j; + } + } + + // + // limit the distance a spiral can move in a single step + // + + bestPointIndex = spiral._parentPointIndex + Mathf.Min(MAX_MOVE_DISTANCE, Mathf.Max(-MAX_MOVE_DISTANCE, bestPointIndex - spiral._parentPointIndex)); // minimize step size to help stability + + // + // actually move the spiral + // + + SpiralManipulator.PlaceChildOnParentPoint(spiral, spiral.parent, bestPointIndex); + } + } } - return parentPointIndex; - } + [ExecuteInEditMode] + public class SpiralManipulator : MonoBehaviour { + public SpiralManipulator parent; + public List children = new List(); + + public HashSet occupiedParentPoints = new HashSet(); + public int _parentPointIndex = -1; + + public static int MIN_PARENT_POINT = 3; + public static int MAX_PARENT_POINT = 26; + + + private NomaiTextLine _NomaiTextLine; + public NomaiTextLine NomaiTextLine + { + get + { + if (_NomaiTextLine == null) _NomaiTextLine = GetComponent(); + return _NomaiTextLine; + } + } + + public Vector2 center { + get { return NomaiTextLine.GetWorldCenter(); } + } + + public Vector2 localPosition { + get { return new Vector2(this.transform.localPosition.x, this.transform.localPosition.y); } + } + public Vector2 position { + get { return new Vector2(this.transform.position.x, this.transform.position.y); } + } + + public SpiralManipulator AddChild() { + var index = Random.Range(MIN_PARENT_POINT, MAX_PARENT_POINT); + var child = NomaiTextArcArranger.Place(this.transform.parent.gameObject); + PlaceChildOnParentPoint(child, this, index); + + child.GetComponent().parent = this; + this.children.Add(child.GetComponent()); + return child.GetComponent(); + } + + public void Mirror() + { + this.transform.localScale = new Vector3(-this.transform.localScale.x, 1, 1); + if (this.parent != null) SpiralManipulator.PlaceChildOnParentPoint(this, this.parent, this._parentPointIndex); + } + + public void UpdateChildren() + { + foreach(var child in this.children) + { + PlaceChildOnParentPoint(child, this, child._parentPointIndex); + } + } + + public static int PlaceChildOnParentPoint(SpiralManipulator child, SpiralManipulator parent, int parentPointIndex, bool updateChildren=true) + { + // track which points on the parent are being occupied + if (child._parentPointIndex != -1) parent.occupiedParentPoints.Remove(child._parentPointIndex); + child._parentPointIndex = parentPointIndex; // just in case this function was called without setting this value + parent.occupiedParentPoints.Add(parentPointIndex); + + // get the parent's points and make parentPointIndex valid + var _points = parent.GetComponent().GetPoints(); + parentPointIndex = Mathf.Max(0, Mathf.Min(parentPointIndex, _points.Length-1)); + + // calculate the normal at point by using the neighboring points to approximate the tangent (and account for mirroring, which means all points are actually at (-point.x, point.y) ) + var normal = _points[Mathf.Min(parentPointIndex+1, _points.Length-1)] - _points[Mathf.Max(parentPointIndex-1, 0)]; + if (parent.transform.localScale.x < 0) normal = new Vector3(-normal.x, normal.y, normal.z); + float rot = Mathf.Atan2(normal.y, normal.x) * Mathf.Rad2Deg; + if (parent.transform.localScale.x < 0) rot += 180; // account for mirroring again (without doing this, the normal points inward on mirrored spirals, instead of outward) + + // get the location the child spiral should be at (and yet again account for mirroring) + var point = _points[parentPointIndex]; + if (parent.transform.localScale.x < 0) point = new Vector3(-point.x, point.y, point.z); + + // set the child's position and rotation according to calculations + child.transform.localPosition = Quaternion.Euler(0, 0, parent.transform.localEulerAngles.z) * point + parent.transform.localPosition; + child.transform.localEulerAngles = new Vector3(0, 0, rot + parent.transform.localEulerAngles.z); + + // recursive update on all children so they move along with the parent + if (updateChildren) + { + child.UpdateChildren(); + } + + return parentPointIndex; + } + } } \ No newline at end of file diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs index 822a8823..bc12a600 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs @@ -5,504 +5,507 @@ using UnityEngine; using UnityEditor; using System.Reflection; -public static class NomaiTextArcBuilder { - public static int i = 0; +namespace NewHorizons.Builder.Props +{ + public static class NomaiTextArcBuilder { + public static int i = 0; - public static SpiralProfile spiralProfile; - public static bool removeBakedInRotationAndPosition = true; + public static SpiralProfile spiralProfile; + public static bool removeBakedInRotationAndPosition = true; - public static void PlaceAdult() - { - BuildSpiralGameObject(adultSpiralProfile, "Text Arc Prefab " + (i++)); - } - public static void PlaceChild() - { - BuildSpiralGameObject(childSpiralProfile, "Text Arc Prefab " + (i++)); - } + public static void PlaceAdult() + { + BuildSpiralGameObject(adultSpiralProfile, "Text Arc Prefab " + (i++)); + } + public static void PlaceChild() + { + BuildSpiralGameObject(childSpiralProfile, "Text Arc Prefab " + (i++)); + } - public static GameObject BuildSpiralGameObject(SpiralProfile profile, string goName="New Nomai Spiral") - { - var g = new GameObject(goName); - g.transform.localPosition = Vector3.zero; - g.transform.localEulerAngles = Vector3.zero; + public static GameObject BuildSpiralGameObject(SpiralProfile profile, string goName="New Nomai Spiral") + { + var g = new GameObject(goName); + g.transform.localPosition = Vector3.zero; + g.transform.localEulerAngles = Vector3.zero; - var m = new SpiralMesh(profile); - m.Randomize(); - m.updateMesh(); + var m = new SpiralMesh(profile); + m.Randomize(); + m.updateMesh(); - g.AddComponent().sharedMesh = m.mesh; - g.AddComponent().sharedMaterial = new Material(Shader.Find("Sprites/Default")); - g.GetComponent().sharedMaterial.color = Color.magenta; + g.AddComponent().sharedMesh = m.mesh; + g.AddComponent().sharedMaterial = new Material(Shader.Find("Sprites/Default")); + g.GetComponent().sharedMaterial.color = Color.magenta; - var owNomaiTextLine = g.AddComponent(); + var owNomaiTextLine = g.AddComponent(); - // - // rotate mesh to face up - // + // + // rotate mesh to face up + // + + var norm = m.skeleton[1] - m.skeleton[0]; + float r = Mathf.Atan2(-norm.y, norm.x) * Mathf.Rad2Deg; + if (m.mirror) r += 180; + var ang = m.mirror ? 90-r : -90-r; + + // using m.sharedMesh causes old meshes to disappear for some reason, idk why + var mesh = g.GetComponent().mesh; + if (removeBakedInRotationAndPosition) + { + var meshCopy = mesh; + var newVerts = meshCopy.vertices.Select(v => Quaternion.Euler(-90, 0, 0) * Quaternion.Euler(0, ang, 0) * v).ToArray(); + meshCopy.vertices = newVerts; + meshCopy.RecalculateBounds(); + } + + // TODO: caching? would this help with that? + //AssetDatabase.CreateAsset(mesh, "Assets/Spirals/"+(profile.profileName)+"spiral" + (NomaiTextArcBuilder.i) + ".asset"); + //g.GetComponent().sharedMesh = AssetDatabase.LoadAssetAtPath("Assets/Spirals/"+(profile.profileName)+"spiral" + (NomaiTextArcBuilder.i) + ".asset", typeof(Mesh)) as Mesh; + //NomaiTextArcBuilder.i++; + + // + // set up NomaiTextArc stuff + // + + var _points = m.skeleton + .Select((compiled) => + Quaternion.Euler(-90, 0, 0) * Quaternion.Euler(0, ang, 0) * (new Vector3(compiled.x, 0, compiled.y)) // decompile them, rotate them by ang, and then rotate them to be vertical, like the base game spirals are + ) + .ToList(); + + var _lengths = _points.Take(_points.Count()-1).Select((point, i) => Vector3.Distance(point, _points[i+1])).ToArray(); + var _totalLength = _lengths.Aggregate(0f, (acc, length) => acc + length); + var _state = NomaiTextLine.VisualState.UNREAD; + var _textLineLocation = NomaiText.Location.UNSPECIFIED; + var _center = _points.Aggregate(Vector3.zero, (acc, point) => acc + point) / (float)_points.Count(); + var _radius = _points.Aggregate(0f, (acc, point) => Mathf.Max(Vector3.Distance(_center, point), acc)); + var _active = true; + + (typeof (NomaiTextLine)).InvokeMember("_points", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _points.ToArray() }); + (typeof (NomaiTextLine)).InvokeMember("_lengths", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _lengths }); + (typeof (NomaiTextLine)).InvokeMember("_totalLength", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _totalLength }); + (typeof (NomaiTextLine)).InvokeMember("_state", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _state }); + (typeof (NomaiTextLine)).InvokeMember("_textLineLocation", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _textLineLocation }); + (typeof (NomaiTextLine)).InvokeMember("_center", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _center }); + (typeof (NomaiTextLine)).InvokeMember("_radius", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _radius }); + (typeof (NomaiTextLine)).InvokeMember("_active", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _active }); + + return g; + } + + // + // + // Handle the connection between game objects and spiral meshes + // + // + + public struct SpiralProfile { + // all of the Vector2 params here refer to a range of valid values + public string profileName; + public bool canMirror; + public Vector2 a; + public Vector2 b; + public Vector2 endS; + public Vector2 skeletonScale; + public int numSkeletonPoints; + public float uvScale; + public float innerWidth; // width at the tip + public float outerWidth; // width at the base + public Material material; + } - var norm = m.skeleton[1] - m.skeleton[0]; - float r = Mathf.Atan2(-norm.y, norm.x) * Mathf.Rad2Deg; - if (m.mirror) r += 180; - var ang = m.mirror ? 90-r : -90-r; + public static SpiralProfile adultSpiralProfile = new SpiralProfile() { + profileName="Adult", + canMirror = false, // we don't want to mirror the actual mesh itself anymore, we'll just mirror the game object using localScale.x + a = new Vector2(0.5f, 0.5f), + b = new Vector2(0.3f, 0.6f), + endS = new Vector2(0, 50f), + skeletonScale = new Vector2(0.01f, 0.01f), + numSkeletonPoints = 51, - // using m.sharedMesh causes old meshes to disappear for some reason, idk why - var mesh = g.GetComponent().mesh; - if (removeBakedInRotationAndPosition) - { - var meshCopy = mesh; - var newVerts = meshCopy.vertices.Select(v => Quaternion.Euler(-90, 0, 0) * Quaternion.Euler(0, ang, 0) * v).ToArray(); - meshCopy.vertices = newVerts; - meshCopy.RecalculateBounds(); + innerWidth = 0.001f, + outerWidth = 0.05f, + uvScale = 4.9f, + }; + + public static SpiralProfile childSpiralProfile = new SpiralProfile() { + profileName="Child", + canMirror = false, // we don't want to mirror the actual mesh itself anymore, we'll just mirror the game object using localScale.x + a = new Vector2(0.9f, 0.9f), + b = new Vector2(0.305f, 0.4f), + endS = new Vector2(16f, 60f), + skeletonScale = new Vector2(0.002f, 0.005f), + numSkeletonPoints = 51, + + innerWidth = 0.001f/10f, + outerWidth = 2f*0.05f, + uvScale = 4.9f/3.5f, + }; + + // + // + // Construct spiral meshes from the mathematical spirals generated below + // + // + + public class SpiralMesh: MathematicalSpiral { + public List skeleton; + public List skeletonOutsidePoints; + + public int numSkeletonPoints = 51; // seems to be Mobius' default + + public float innerWidth = 0.001f; // width at the tip + public float outerWidth = 0.05f; //0.107f; // width at the base + public float uvScale = 4.9f; //2.9f; + private float baseUVScale = 1f / 300f; + public float uvOffset = 0; + + public Mesh mesh; + + public SpiralMesh(SpiralProfile profile): base(profile) { + this.numSkeletonPoints = profile.numSkeletonPoints; + this.innerWidth = profile.innerWidth; + this.outerWidth = profile.outerWidth; + this.uvScale = profile.uvScale; + + this.uvOffset = UnityEngine.Random.value; + } + + public override void Randomize() { + base.Randomize(); + uvOffset = UnityEngine.Random.value; // this way even two spirals that are exactly the same shape will look different (this changes the starting point of the handwriting texture) + } + + internal void updateMesh() { + skeleton = this.getSkeleton(numSkeletonPoints); + skeletonOutsidePoints = this.getSkeletonOutsidePoints(numSkeletonPoints); + + List vertsSide1 = skeleton.Select((skeletonPoint, index) => { + Vector3 normal = new Vector3(cos(skeletonPoint.z), 0, sin(skeletonPoint.z)); + float width = lerp(((float) index) / ((float) skeleton.Count()), outerWidth, innerWidth); + + return new Vector3(skeletonPoint.x, 0, skeletonPoint.y) + width * normal; + }).ToList(); + + List vertsSide2 = skeleton.Select((skeletonPoint, index) => { + Vector3 normal = new Vector3(cos(skeletonPoint.z), 0, sin(skeletonPoint.z)); + float width = lerp(((float) index) / ((float) skeleton.Count()), outerWidth, innerWidth); + + return new Vector3(skeletonPoint.x, 0, skeletonPoint.y) - width * normal; + }).ToList(); + + Vector3[] newVerts = vertsSide1.Zip(vertsSide2, (f, s) => new [] { + f, + s + }).SelectMany(f =>f).ToArray(); // interleave vertsSide1 and vertsSide2 + + List triangles = new List(); + for (int i = 0; i children; + + public float x; + public float y; + public float ang; + + public float startS = 42.87957f; // go all the way down to 0, all the way up to 50 + public float endS = 342.8796f; + + SpiralProfile profile; + + public MathematicalSpiral(SpiralProfile profile) { + this.profile = profile; + + this.Randomize(); + } + + public MathematicalSpiral(float startSOnParent = 0, bool mirror = false, float len = 300, float a = 0.5f, float b = 0.43f, float scale = 0.01f) { + this.mirror = mirror; + this.a = a; + this.b = b; + this.startSOnParent = startSOnParent; + this.scale = scale; + + this.children = new List(); + + this.x = 0; + this.y = 0; + this.ang = 0; + } + + public virtual void Randomize() { + this.a = UnityEngine.Random.Range(profile.a.x, profile.a.y); //0.5f; + this.b = UnityEngine.Random.Range(profile.b.x, profile.b.y); + this.startS = UnityEngine.Random.Range(profile.endS.x, profile.endS.y); + this.scale = UnityEngine.Random.Range(profile.skeletonScale.x, profile.skeletonScale.y); + if (profile.canMirror) this.mirror = UnityEngine.Random.value<0.5f; + } + + internal virtual void updateChild(MathematicalSpiral child) { + Vector3 pointAndNormal = getDrawnSpiralPointAndNormal(child.startSOnParent); + var cx = pointAndNormal.x; + var cy = pointAndNormal.y; + var cang = pointAndNormal.z; + child.x = cx; + child.y = cy; + child.ang = cang + (child.mirror ? Mathf.PI : 0); + } + + public virtual void addChild(MathematicalSpiral child) { + updateChild(child); + this.children.Add(child); + } + + public virtual void updateChildren() { + this.children.ForEach(child => { + updateChild(child); + child.updateChildren(); + }); + } + + // note: each Vector3 in this list is of form + public List getSkeleton(int numPoints) { + var skeleton = + WalkAlongSpiral(numPoints) + .Select(input => { + float inputS = input.y; + var skeletonPoint = getDrawnSpiralPointAndNormal(inputS); + return skeletonPoint; + }) + .Reverse() + .ToList(); + + return skeleton; + } + + public List getSkeletonOutsidePoints(int numPoints) { + var outsidePoints = + WalkAlongSpiral(numPoints) + .Select(input => { + float inputT = input.x; + float inputS = input.y; + + var skeletonPoint = getDrawnSpiralPointAndNormal(inputS); + + var deriv = spiralDerivative(inputT); + var outsidePoint = new Vector2(skeletonPoint.x, skeletonPoint.y) - (new Vector2(-deriv.y, deriv.x)).normalized * 0.1f; + return outsidePoint; + }) + .Reverse() + .ToList(); + + return outsidePoints; + } + + // generate a list of evenly distributed over the whole spiral. `numPoints` number of pairs are generated + public IEnumerable WalkAlongSpiral(int numPoints) { + var endT = tFromArcLen(endS); + var startT = tFromArcLen(startS); + var rangeT = endT - startT; + + for (int i = 0; i().sharedMesh = AssetDatabase.LoadAssetAtPath("Assets/Spirals/"+(profile.profileName)+"spiral" + (NomaiTextArcBuilder.i) + ".asset", typeof(Mesh)) as Mesh; - NomaiTextArcBuilder.i++; - - // - // set up NomaiTextArc stuff - // - - var _points = m.skeleton - .Select((compiled) => - Quaternion.Euler(-90, 0, 0) * Quaternion.Euler(0, ang, 0) * (new Vector3(compiled.x, 0, compiled.y)) // decompile them, rotate them by ang, and then rotate them to be vertical, like the base game spirals are - ) - .ToList(); - - var _lengths = _points.Take(_points.Count()-1).Select((point, i) => Vector3.Distance(point, _points[i+1])).ToArray(); - var _totalLength = _lengths.Aggregate(0f, (acc, length) => acc + length); - var _state = NomaiTextLine.VisualState.UNREAD; - var _textLineLocation = NomaiText.Location.UNSPECIFIED; - var _center = _points.Aggregate(Vector3.zero, (acc, point) => acc + point) / (float)_points.Count(); - var _radius = _points.Aggregate(0f, (acc, point) => Mathf.Max(Vector3.Distance(_center, point), acc)); - var _active = true; - - (typeof (NomaiTextLine)).InvokeMember("_points", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _points.ToArray() }); - (typeof (NomaiTextLine)).InvokeMember("_lengths", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _lengths }); - (typeof (NomaiTextLine)).InvokeMember("_totalLength", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _totalLength }); - (typeof (NomaiTextLine)).InvokeMember("_state", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _state }); - (typeof (NomaiTextLine)).InvokeMember("_textLineLocation", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _textLineLocation }); - (typeof (NomaiTextLine)).InvokeMember("_center", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _center }); - (typeof (NomaiTextLine)).InvokeMember("_radius", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _radius }); - (typeof (NomaiTextLine)).InvokeMember("_active", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _active }); - - return g; - } - - // - // - // Handle the connection between game objects and spiral meshes - // - // - - public struct SpiralProfile { - // all of the Vector2 params here refer to a range of valid values - public string profileName; - public bool canMirror; - public Vector2 a; - public Vector2 b; - public Vector2 endS; - public Vector2 skeletonScale; - public int numSkeletonPoints; - public float uvScale; - public float innerWidth; // width at the tip - public float outerWidth; // width at the base - public Material material; - } - - public static SpiralProfile adultSpiralProfile = new SpiralProfile() { - profileName="Adult", - canMirror = false, // we don't want to mirror the actual mesh itself anymore, we'll just mirror the game object using localScale.x - a = new Vector2(0.5f, 0.5f), - b = new Vector2(0.3f, 0.6f), - endS = new Vector2(0, 50f), - skeletonScale = new Vector2(0.01f, 0.01f), - numSkeletonPoints = 51, - - innerWidth = 0.001f, - outerWidth = 0.05f, - uvScale = 4.9f, - }; - - public static SpiralProfile childSpiralProfile = new SpiralProfile() { - profileName="Child", - canMirror = false, // we don't want to mirror the actual mesh itself anymore, we'll just mirror the game object using localScale.x - a = new Vector2(0.9f, 0.9f), - b = new Vector2(0.305f, 0.4f), - endS = new Vector2(16f, 60f), - skeletonScale = new Vector2(0.002f, 0.005f), - numSkeletonPoints = 51, - - innerWidth = 0.001f/10f, - outerWidth = 2f*0.05f, - uvScale = 4.9f/3.5f, - }; - - // - // - // Construct spiral meshes from the mathematical spirals generated below - // - // - - public class SpiralMesh: MathematicalSpiral { - public List skeleton; - public List skeletonOutsidePoints; - - public int numSkeletonPoints = 51; // seems to be Mobius' default - - public float innerWidth = 0.001f; // width at the tip - public float outerWidth = 0.05f; //0.107f; // width at the base - public float uvScale = 4.9f; //2.9f; - private float baseUVScale = 1f / 300f; - public float uvOffset = 0; - - public Mesh mesh; - - public SpiralMesh(SpiralProfile profile): base(profile) { - this.numSkeletonPoints = profile.numSkeletonPoints; - this.innerWidth = profile.innerWidth; - this.outerWidth = profile.outerWidth; - this.uvScale = profile.uvScale; - - this.uvOffset = UnityEngine.Random.value; - } - - public override void Randomize() { - base.Randomize(); - uvOffset = UnityEngine.Random.value; // this way even two spirals that are exactly the same shape will look different (this changes the starting point of the handwriting texture) - } - - internal void updateMesh() { - skeleton = this.getSkeleton(numSkeletonPoints); - skeletonOutsidePoints = this.getSkeletonOutsidePoints(numSkeletonPoints); - - List vertsSide1 = skeleton.Select((skeletonPoint, index) => { - Vector3 normal = new Vector3(cos(skeletonPoint.z), 0, sin(skeletonPoint.z)); - float width = lerp(((float) index) / ((float) skeleton.Count()), outerWidth, innerWidth); - - return new Vector3(skeletonPoint.x, 0, skeletonPoint.y) + width * normal; - }).ToList(); - - List vertsSide2 = skeleton.Select((skeletonPoint, index) => { - Vector3 normal = new Vector3(cos(skeletonPoint.z), 0, sin(skeletonPoint.z)); - float width = lerp(((float) index) / ((float) skeleton.Count()), outerWidth, innerWidth); - - return new Vector3(skeletonPoint.x, 0, skeletonPoint.y) - width * normal; - }).ToList(); - - Vector3[] newVerts = vertsSide1.Zip(vertsSide2, (f, s) => new [] { - f, - s - }).SelectMany(f =>f).ToArray(); // interleave vertsSide1 and vertsSide2 - - List triangles = new List(); - for (int i = 0; i children; - - public float x; - public float y; - public float ang; - - public float startS = 42.87957f; // go all the way down to 0, all the way up to 50 - public float endS = 342.8796f; - - SpiralProfile profile; - - public MathematicalSpiral(SpiralProfile profile) { - this.profile = profile; - - this.Randomize(); - } - - public MathematicalSpiral(float startSOnParent = 0, bool mirror = false, float len = 300, float a = 0.5f, float b = 0.43f, float scale = 0.01f) { - this.mirror = mirror; - this.a = a; - this.b = b; - this.startSOnParent = startSOnParent; - this.scale = scale; - - this.children = new List(); - - this.x = 0; - this.y = 0; - this.ang = 0; - } - - public virtual void Randomize() { - this.a = UnityEngine.Random.Range(profile.a.x, profile.a.y); //0.5f; - this.b = UnityEngine.Random.Range(profile.b.x, profile.b.y); - this.startS = UnityEngine.Random.Range(profile.endS.x, profile.endS.y); - this.scale = UnityEngine.Random.Range(profile.skeletonScale.x, profile.skeletonScale.y); - if (profile.canMirror) this.mirror = UnityEngine.Random.value<0.5f; - } - - internal virtual void updateChild(MathematicalSpiral child) { - Vector3 pointAndNormal = getDrawnSpiralPointAndNormal(child.startSOnParent); - var cx = pointAndNormal.x; - var cy = pointAndNormal.y; - var cang = pointAndNormal.z; - child.x = cx; - child.y = cy; - child.ang = cang + (child.mirror ? Mathf.PI : 0); - } - - public virtual void addChild(MathematicalSpiral child) { - updateChild(child); - this.children.Add(child); - } - - public virtual void updateChildren() { - this.children.ForEach(child => { - updateChild(child); - child.updateChildren(); - }); - } - - // note: each Vector3 in this list is of form - public List getSkeleton(int numPoints) { - var skeleton = - WalkAlongSpiral(numPoints) - .Select(input => { - float inputS = input.y; - var skeletonPoint = getDrawnSpiralPointAndNormal(inputS); - return skeletonPoint; - }) - .Reverse() - .ToList(); - - return skeleton; - } - - public List getSkeletonOutsidePoints(int numPoints) { - var outsidePoints = - WalkAlongSpiral(numPoints) - .Select(input => { - float inputT = input.x; - float inputS = input.y; - - var skeletonPoint = getDrawnSpiralPointAndNormal(inputS); - - var deriv = spiralDerivative(inputT); - var outsidePoint = new Vector2(skeletonPoint.x, skeletonPoint.y) - (new Vector2(-deriv.y, deriv.x)).normalized * 0.1f; - return outsidePoint; - }) - .Reverse() - .ToList(); - - return outsidePoints; - } - - // generate a list of evenly distributed over the whole spiral. `numPoints` number of pairs are generated - public IEnumerable WalkAlongSpiral(int numPoints) { - var endT = tFromArcLen(endS); - var startT = tFromArcLen(startS); - var rangeT = endT - startT; - - for (int i = 0; i Date: Fri, 30 Dec 2022 19:22:03 -0500 Subject: [PATCH 03/58] removed some extra newlines --- NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs index 4e1995e5..87316417 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs @@ -3,8 +3,6 @@ using System.Collections.Generic; using System.Linq; using UnityEngine; - - namespace NewHorizons.Builder.Props { public class NomaiTextArcArranger : MonoBehaviour { From 58c4b4178c8706442efbf157d0ff63216c54a325 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Fri, 30 Dec 2022 19:25:45 -0500 Subject: [PATCH 04/58] removed reflectoin because we have that cool 'make everything public' utility --- .../Props/NomaiText/NomaiTextArcBuilder.cs | 25 ++++++------------- 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs index bc12a600..d198c37f 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs @@ -2,8 +2,6 @@ using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; -using UnityEditor; -using System.Reflection; namespace NewHorizons.Builder.Props { @@ -72,22 +70,13 @@ namespace NewHorizons.Builder.Props ) .ToList(); - var _lengths = _points.Take(_points.Count()-1).Select((point, i) => Vector3.Distance(point, _points[i+1])).ToArray(); - var _totalLength = _lengths.Aggregate(0f, (acc, length) => acc + length); - var _state = NomaiTextLine.VisualState.UNREAD; - var _textLineLocation = NomaiText.Location.UNSPECIFIED; - var _center = _points.Aggregate(Vector3.zero, (acc, point) => acc + point) / (float)_points.Count(); - var _radius = _points.Aggregate(0f, (acc, point) => Mathf.Max(Vector3.Distance(_center, point), acc)); - var _active = true; - - (typeof (NomaiTextLine)).InvokeMember("_points", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _points.ToArray() }); - (typeof (NomaiTextLine)).InvokeMember("_lengths", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _lengths }); - (typeof (NomaiTextLine)).InvokeMember("_totalLength", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _totalLength }); - (typeof (NomaiTextLine)).InvokeMember("_state", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _state }); - (typeof (NomaiTextLine)).InvokeMember("_textLineLocation", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _textLineLocation }); - (typeof (NomaiTextLine)).InvokeMember("_center", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _center }); - (typeof (NomaiTextLine)).InvokeMember("_radius", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _radius }); - (typeof (NomaiTextLine)).InvokeMember("_active", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic, null, owNomaiTextLine, new object[] { _active }); + owNomaiTextLine._lengths = _points.Take(_points.Count()-1).Select((point, i) => Vector3.Distance(point, _points[i+1])).ToArray(); + owNomaiTextLine._totalLength = owNomaiTextLine._lengths.Aggregate(0f, (acc, length) => acc + length); + owNomaiTextLine._state = NomaiTextLine.VisualState.UNREAD; + owNomaiTextLine._textLineLocation = NomaiText.Location.UNSPECIFIED; + owNomaiTextLine._center = _points.Aggregate(Vector3.zero, (acc, point) => acc + point) / (float)_points.Count(); + owNomaiTextLine._radius = _points.Aggregate(0f, (acc, point) => Mathf.Max(Vector3.Distance(owNomaiTextLine._center, point), acc)); + owNomaiTextLine._active = true; return g; } From 0b0aee5ba9a22da0eb5ac280a94818ddd31bf481 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Fri, 30 Dec 2022 19:31:23 -0500 Subject: [PATCH 05/58] added new function to support spawning and arranging spiral prefabs rather than just autogen spirals --- .../Props/NomaiText/NomaiTextArcArranger.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs index 87316417..97a64abd 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs @@ -217,15 +217,20 @@ namespace NewHorizons.Builder.Props public Vector2 position { get { return new Vector2(this.transform.position.x, this.transform.position.y); } } - + public SpiralManipulator AddChild() { + return AddChild(NomaiTextArcArranger.Place(this.transform.parent.gameObject).gameObject); + } + + public SpiralManipulator AddChild(GameObject prebuiltChild) { var index = Random.Range(MIN_PARENT_POINT, MAX_PARENT_POINT); - var child = NomaiTextArcArranger.Place(this.transform.parent.gameObject); + prebuiltChild.transform.parent = this.transform.parent; + var child = prebuiltChild.gameObject.GetAddComponent(); PlaceChildOnParentPoint(child, this, index); - child.GetComponent().parent = this; - this.children.Add(child.GetComponent()); - return child.GetComponent(); + child.parent = this; + this.children.Add(child); + return child; } public void Mirror() From 403047fcd0c188523049896b2c1a25ceefdc6039 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Sat, 31 Dec 2022 11:03:20 -0500 Subject: [PATCH 06/58] added stuff to generate stranger 'spirals' --- .../Props/NomaiText/NomaiTextArcBuilder.cs | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs index d198c37f..9868dee3 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs @@ -93,6 +93,7 @@ namespace NewHorizons.Builder.Props public bool canMirror; public Vector2 a; public Vector2 b; + public Vector2 startS; public Vector2 endS; public Vector2 skeletonScale; public int numSkeletonPoints; @@ -107,6 +108,7 @@ namespace NewHorizons.Builder.Props canMirror = false, // we don't want to mirror the actual mesh itself anymore, we'll just mirror the game object using localScale.x a = new Vector2(0.5f, 0.5f), b = new Vector2(0.3f, 0.6f), + startS = new Vector2(342.8796f, 342.8796f), endS = new Vector2(0, 50f), skeletonScale = new Vector2(0.01f, 0.01f), numSkeletonPoints = 51, @@ -121,6 +123,7 @@ namespace NewHorizons.Builder.Props canMirror = false, // we don't want to mirror the actual mesh itself anymore, we'll just mirror the game object using localScale.x a = new Vector2(0.9f, 0.9f), b = new Vector2(0.305f, 0.4f), + startS = new Vector2(342.8796f, 342.8796f), endS = new Vector2(16f, 60f), skeletonScale = new Vector2(0.002f, 0.005f), numSkeletonPoints = 51, @@ -129,6 +132,29 @@ namespace NewHorizons.Builder.Props outerWidth = 2f*0.05f, uvScale = 4.9f/3.5f, }; + + // location of example stranger writing: + // RingWorld_Body/Sector_RingInterior/Sector_Zone1/Interactables_Zone1/Props_IP_ZoneSign_1/Arc_TestAlienWriting/Arc 1 + // 17 points + // length of 1.8505 + // width of 1 + // _revealDuration of 0.5633 + // _targetColor of 1 1 1 0 + // I think this'll do it + public static SpiralProfile strangerSpiralProfile = new SpiralProfile() { + profileName="Stranger", + canMirror = false, + a = new Vector2(0.9f, 0.9f), // this value doesn't really matter for this + b = new Vector2(5f, 5f), + startS = new Vector2(1.8505f, 1.8505f), + endS = new Vector2(0, 0), + skeletonScale = new Vector2(1, 1), + numSkeletonPoints = 17, + + innerWidth = 1, + outerWidth = 1, + uvScale = 1f/1.8505f, + }; // // @@ -297,7 +323,8 @@ namespace NewHorizons.Builder.Props public virtual void Randomize() { this.a = UnityEngine.Random.Range(profile.a.x, profile.a.y); //0.5f; this.b = UnityEngine.Random.Range(profile.b.x, profile.b.y); - this.startS = UnityEngine.Random.Range(profile.endS.x, profile.endS.y); + this.startS = UnityEngine.Random.Range(profile.endS.x, profile.endS.y); // idk why I flipped these, please don't hate me + this.endS = UnityEngine.Random.Range(profile.startS.x, profile.startS.y); this.scale = UnityEngine.Random.Range(profile.skeletonScale.x, profile.skeletonScale.y); if (profile.canMirror) this.mirror = UnityEngine.Random.value<0.5f; } From 0860e93efba0147efda99fd4ab9f952f8646c783 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Sat, 31 Dec 2022 11:04:39 -0500 Subject: [PATCH 07/58] added the function for making a stranger spiral --- NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs index 9868dee3..c8b17789 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs @@ -7,8 +7,6 @@ namespace NewHorizons.Builder.Props { public static class NomaiTextArcBuilder { public static int i = 0; - - public static SpiralProfile spiralProfile; public static bool removeBakedInRotationAndPosition = true; public static void PlaceAdult() @@ -19,6 +17,10 @@ namespace NewHorizons.Builder.Props { BuildSpiralGameObject(childSpiralProfile, "Text Arc Prefab " + (i++)); } + public static void PlaceStranger() + { + BuildSpiralGameObject(strangerSpiralProfile, "Text Arc Prefab " + (i++)); + } public static GameObject BuildSpiralGameObject(SpiralProfile profile, string goName="New Nomai Spiral") { From e705c9bdc646bad3459fe0909519649359fa71ba Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Sat, 31 Dec 2022 11:16:08 -0500 Subject: [PATCH 08/58] integrated auto spirals with NomaiTextBuilder --- .../Props/NomaiText/NomaiTextArcArranger.cs | 14 +-- .../Props/NomaiText/NomaiTextBuilder.cs | 114 ++++++++---------- 2 files changed, 54 insertions(+), 74 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs index 97a64abd..326e086f 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs @@ -16,14 +16,8 @@ namespace NewHorizons.Builder.Props public float maxY = 5f; public float minY = -1f; - public static SpiralManipulator Place(GameObject spiralMeshHolder = null) { - if (spiralMeshHolder == null) - { - spiralMeshHolder = new GameObject("spiral holder"); - spiralMeshHolder.AddComponent(); - } - - var rootArc = NomaiTextArcBuilder.BuildSpiralGameObject(NomaiTextArcBuilder.adultSpiralProfile); + public static SpiralManipulator Place(NomaiTextArcBuilder.SpiralProfile profile, GameObject spiralMeshHolder) { + var rootArc = NomaiTextArcBuilder.BuildSpiralGameObject(profile); rootArc.transform.parent = spiralMeshHolder.transform; rootArc.transform.localEulerAngles = new Vector3(0, 0, Random.Range(-60, 60)); @@ -218,8 +212,8 @@ namespace NewHorizons.Builder.Props get { return new Vector2(this.transform.position.x, this.transform.position.y); } } - public SpiralManipulator AddChild() { - return AddChild(NomaiTextArcArranger.Place(this.transform.parent.gameObject).gameObject); + public SpiralManipulator AddChild(NomaiTextArcBuilder.SpiralProfile profile) { + return AddChild(NomaiTextArcArranger.Place(profile, this.transform.parent.gameObject).gameObject); } public SpiralManipulator AddChild(GameObject prebuiltChild) { diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs index a9b9e607..715249fb 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs @@ -16,8 +16,6 @@ namespace NewHorizons.Builder.Props { public static class NomaiTextBuilder { - private static List _arcPrefabs; - private static List _childArcPrefabs; private static List _ghostArcPrefabs; private static GameObject _scrollPrefab; private static GameObject _computerPrefab; @@ -43,9 +41,7 @@ namespace NewHorizons.Builder.Props if (!conversationInfoToCorrespondingSpawnedGameObject.ContainsKey(convo)) return null; return conversationInfoToCorrespondingSpawnedGameObject[convo]; } - - public static List GetArcPrefabs() { return _arcPrefabs; } - public static List GetChildArcPrefabs() { return _childArcPrefabs; } + public static List GetGhostArcPrefabs() { return _ghostArcPrefabs; } private static bool _isInit; @@ -56,29 +52,6 @@ namespace NewHorizons.Builder.Props _isInit = true; - if (_arcPrefabs == null || _childArcPrefabs == null) - { - // Just take every scroll and get the first arc - var existingArcs = GameObject.FindObjectsOfType() - .Select(x => x?._nomaiWallText?.gameObject?.transform?.Find("Arc 1")?.gameObject) - .Where(x => x != null) - .OrderBy(x => x.transform.GetPath()) // order by path so game updates dont break things - .ToArray(); - _arcPrefabs = new List(); - _childArcPrefabs = new List(); - foreach (var existingArc in existingArcs) - { - if (existingArc.GetComponent().material.name.Contains("Child")) - { - _childArcPrefabs.Add(existingArc.InstantiateInactive().Rename("Arc (Child)").DontDestroyOnLoad()); - } - else - { - _arcPrefabs.Add(existingArc.InstantiateInactive().Rename("Arc").DontDestroyOnLoad()); - } - } - } - if (_ghostArcPrefabs == null) { var existingGhostArcs = GameObject.FindObjectsOfType() @@ -644,6 +617,8 @@ namespace NewHorizons.Builder.Props return; } + var arranger = nomaiWallText.gameObject.AddComponent(); + var i = 0; foreach (var textData in dict.Values) { @@ -660,65 +635,76 @@ namespace NewHorizons.Builder.Props i++; } + + for (var k = 0; k < i*2; k++) + { + var overlap = arranger.Overlap(); + if (overlap.x < 0) return; + + arranger.AttemptOverlapResolution(overlap); + for(var a = 0; a < 10; a++) arranger.Step(); + } + + Logger.LogError("Overlap resolution failed!"); } internal static GameObject MakeArc(PropModule.NomaiTextArcInfo arcInfo, GameObject conversationZone, GameObject parent, int textEntryID) { GameObject arc; var type = arcInfo != null ? arcInfo.type : PropModule.NomaiTextArcInfo.NomaiTextArcType.Adult; - var variation = arcInfo != null ? arcInfo.variation : -1; + NomaiTextArcBuilder.SpiralProfile profile = new(); + Material mat = null; switch (type) { case PropModule.NomaiTextArcInfo.NomaiTextArcType.Child: - variation = variation < 0 - ? Random.Range(0, _childArcPrefabs.Count()) - : (variation % _childArcPrefabs.Count()); - arc = _childArcPrefabs[variation].InstantiateInactive(); + profile = NomaiTextArcBuilder.childSpiralProfile; + // TODO: set mat break; case PropModule.NomaiTextArcInfo.NomaiTextArcType.Stranger when _ghostArcPrefabs.Any(): - variation = variation < 0 - ? Random.Range(0, _ghostArcPrefabs.Count()) - : (variation % _ghostArcPrefabs.Count()); - arc = _ghostArcPrefabs[variation].InstantiateInactive(); + profile = NomaiTextArcBuilder.strangerSpiralProfile; + // TODO: set mat break; case PropModule.NomaiTextArcInfo.NomaiTextArcType.Adult: default: - variation = variation < 0 - ? Random.Range(0, _arcPrefabs.Count()) - : (variation % _arcPrefabs.Count()); - arc = _arcPrefabs[variation].InstantiateInactive(); + profile = NomaiTextArcBuilder.adultSpiralProfile; + // TODO: set mat break; } + + if (parent != null) arc = parent.GetComponent().AddChild(profile).gameObject; + else arc = NomaiTextArcArranger.Place(profile, conversationZone).gameObject; + + if (mat != null) arc.GetComponent().sharedMaterial = mat; arc.transform.parent = conversationZone.transform; arc.GetComponent()._prebuilt = false; - if (arcInfo != null) - { - arcInfo.variation = variation; - if (arcInfo.position == null) arc.transform.localPosition = Vector3.zero; - else arc.transform.localPosition = new Vector3(arcInfo.position.x, arcInfo.position.y, 0); + //if (arcInfo != null) + //{ + // arcInfo.variation = variation; + // if (arcInfo.position == null) arc.transform.localPosition = Vector3.zero; + // else arc.transform.localPosition = new Vector3(arcInfo.position.x, arcInfo.position.y, 0); - arc.transform.localRotation = Quaternion.Euler(0, 0, arcInfo.zRotation); + // arc.transform.localRotation = Quaternion.Euler(0, 0, arcInfo.zRotation); - if (arcInfo.mirror) arc.transform.localScale = new Vector3(-1, 1, 1); - } - // Try auto I guess - else - { - if (parent == null) - { - arc.transform.localPosition = Vector3.zero; - } - else - { - var points = parent.GetComponent().GetPoints(); - var point = points[points.Count() / 2]; + // if (arcInfo.mirror) arc.transform.localScale = new Vector3(-1, 1, 1); + //} + //// Try auto I guess + //else + //{ + // if (parent == null) + // { + // arc.transform.localPosition = Vector3.zero; + // } + // else + // { + // var points = parent.GetComponent().GetPoints(); + // var point = points[points.Count() / 2]; - arc.transform.localPosition = point; - arc.transform.localRotation = Quaternion.Euler(0, 0, Random.Range(0, 360)); - } - } + // arc.transform.localPosition = point; + // arc.transform.localRotation = Quaternion.Euler(0, 0, Random.Range(0, 360)); + // } + //} arc.GetComponent().SetEntryID(textEntryID); arc.GetComponent().enabled = false; From f33a65deaaa9c04044ab853a0c2385b15bf1d404 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Sat, 31 Dec 2022 23:13:21 -0500 Subject: [PATCH 09/58] various fixes; added material setting, and fixed some sizes --- .../Props/NomaiText/NomaiTextArcArranger.cs | 9 +++-- .../Props/NomaiText/NomaiTextArcBuilder.cs | 17 +++++---- .../Props/NomaiText/NomaiTextBuilder.cs | 37 +++++++++---------- 3 files changed, 33 insertions(+), 30 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs index 326e086f..09e1ecec 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs @@ -11,9 +11,9 @@ namespace NewHorizons.Builder.Props private static int MAX_MOVE_DISTANCE = 2; - public float maxX = 4; - public float minX = -4; - public float maxY = 5f; + public float maxX = 3; + public float minX = -3; + public float maxY = 2f; public float minY = -1f; public static SpiralManipulator Place(NomaiTextArcBuilder.SpiralProfile profile, GameObject spiralMeshHolder) { @@ -91,7 +91,8 @@ namespace NewHorizons.Builder.Props return new Vector2Int(-1, -1); } - public void Step() { + public void Step() + { // TODO: after setting child position on parent in Step(), check to see if this spiral exits the bounds - if so, move it away until it no longer does // this ensures that a spiral can never be outside the bounds, it makes them rigid diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs index c8b17789..57b38bc8 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs @@ -25,6 +25,7 @@ namespace NewHorizons.Builder.Props public static GameObject BuildSpiralGameObject(SpiralProfile profile, string goName="New Nomai Spiral") { var g = new GameObject(goName); + g.SetActive(false); g.transform.localPosition = Vector3.zero; g.transform.localEulerAngles = Vector3.zero; @@ -70,16 +71,18 @@ namespace NewHorizons.Builder.Props .Select((compiled) => Quaternion.Euler(-90, 0, 0) * Quaternion.Euler(0, ang, 0) * (new Vector3(compiled.x, 0, compiled.y)) // decompile them, rotate them by ang, and then rotate them to be vertical, like the base game spirals are ) - .ToList(); + .ToArray(); - owNomaiTextLine._lengths = _points.Take(_points.Count()-1).Select((point, i) => Vector3.Distance(point, _points[i+1])).ToArray(); - owNomaiTextLine._totalLength = owNomaiTextLine._lengths.Aggregate(0f, (acc, length) => acc + length); + owNomaiTextLine._points = _points; + //owNomaiTextLine._lengths = _points.Take(_points.Count()-1).Select((point, i) => Vector3.Distance(point, _points[i+1])).ToArray(); + //owNomaiTextLine._totalLength = owNomaiTextLine._lengths.Aggregate(0f, (acc, length) => acc + length); owNomaiTextLine._state = NomaiTextLine.VisualState.UNREAD; owNomaiTextLine._textLineLocation = NomaiText.Location.UNSPECIFIED; - owNomaiTextLine._center = _points.Aggregate(Vector3.zero, (acc, point) => acc + point) / (float)_points.Count(); - owNomaiTextLine._radius = _points.Aggregate(0f, (acc, point) => Mathf.Max(Vector3.Distance(owNomaiTextLine._center, point), acc)); + //owNomaiTextLine._center = _points.Aggregate(Vector3.zero, (acc, point) => acc + point) / (float)_points.Count(); + //owNomaiTextLine._radius = _points.Aggregate(0f, (acc, point) => Mathf.Max(Vector3.Distance(owNomaiTextLine._center, point), acc)); owNomaiTextLine._active = true; + g.SetActive(true); return g; } @@ -112,7 +115,7 @@ namespace NewHorizons.Builder.Props b = new Vector2(0.3f, 0.6f), startS = new Vector2(342.8796f, 342.8796f), endS = new Vector2(0, 50f), - skeletonScale = new Vector2(0.01f, 0.01f), + skeletonScale = 0.75f * new Vector2(0.01f, 0.01f), numSkeletonPoints = 51, innerWidth = 0.001f, @@ -127,7 +130,7 @@ namespace NewHorizons.Builder.Props b = new Vector2(0.305f, 0.4f), startS = new Vector2(342.8796f, 342.8796f), endS = new Vector2(16f, 60f), - skeletonScale = new Vector2(0.002f, 0.005f), + skeletonScale = 0.75f * new Vector2(0.002f, 0.005f), numSkeletonPoints = 51, innerWidth = 0.001f/10f, diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs index 715249fb..1d9c8b1d 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs @@ -16,7 +16,9 @@ namespace NewHorizons.Builder.Props { public static class NomaiTextBuilder { - private static List _ghostArcPrefabs; + private static Material _ghostArcMaterial; + private static Material _adultArcMaterial; + private static Material _childArcMaterial; private static GameObject _scrollPrefab; private static GameObject _computerPrefab; private static GameObject _preCrashComputerPrefab; @@ -41,8 +43,6 @@ namespace NewHorizons.Builder.Props if (!conversationInfoToCorrespondingSpawnedGameObject.ContainsKey(convo)) return null; return conversationInfoToCorrespondingSpawnedGameObject[convo]; } - - public static List GetGhostArcPrefabs() { return _ghostArcPrefabs; } private static bool _isInit; @@ -52,18 +52,18 @@ namespace NewHorizons.Builder.Props _isInit = true; - if (_ghostArcPrefabs == null) + if (_adultArcMaterial == null) { - var existingGhostArcs = GameObject.FindObjectsOfType() - .Select(x => x?._textLine?.gameObject) - .Where(x => x != null) - .OrderBy(x => x.transform.GetPath()) // order by path so game updates dont break things - .ToArray(); - _ghostArcPrefabs = new List(); - foreach (var existingArc in existingGhostArcs) - { - _ghostArcPrefabs.Add(existingArc.InstantiateInactive().Rename("Arc (Ghost)").DontDestroyOnLoad()); - } + _adultArcMaterial = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Sector_Crossroads/Interactables_Crossroads/Trailmarkers/Prefab_NOM_BH_Cairn_Arc (2)/Props_TH_ClutterSmall/Arc_Short/Arc") + .GetComponent() + .sharedMaterial; + } + + if (_ghostArcMaterial == null) + { + _ghostArcMaterial = SearchUtilities.Find("RingWorld_Body/Sector_RingInterior/Sector_Zone1/Interactables_Zone1/Props_IP_ZoneSign_1/Arc_TestAlienWriting/Arc 1") + .GetComponent() + .sharedMaterial; } if (_scrollPrefab == null) _scrollPrefab = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Sector_NorthHemisphere/Sector_NorthPole/Sector_HangingCity/Sector_HangingCity_District2/Interactables_HangingCity_District2/Prefab_NOM_Scroll").InstantiateInactive().Rename("Prefab_NOM_Scroll").DontDestroyOnLoad(); @@ -644,7 +644,6 @@ namespace NewHorizons.Builder.Props arranger.AttemptOverlapResolution(overlap); for(var a = 0; a < 10; a++) arranger.Step(); } - Logger.LogError("Overlap resolution failed!"); } @@ -658,16 +657,16 @@ namespace NewHorizons.Builder.Props { case PropModule.NomaiTextArcInfo.NomaiTextArcType.Child: profile = NomaiTextArcBuilder.childSpiralProfile; - // TODO: set mat + mat = _childArcMaterial; break; - case PropModule.NomaiTextArcInfo.NomaiTextArcType.Stranger when _ghostArcPrefabs.Any(): + case PropModule.NomaiTextArcInfo.NomaiTextArcType.Stranger when _ghostArcMaterial != null: profile = NomaiTextArcBuilder.strangerSpiralProfile; - // TODO: set mat + mat = _ghostArcMaterial; break; case PropModule.NomaiTextArcInfo.NomaiTextArcType.Adult: default: profile = NomaiTextArcBuilder.adultSpiralProfile; - // TODO: set mat + mat = _adultArcMaterial; break; } From 941f971b5a0ac1039646b5588ba7b1fb15767001 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Thu, 26 Jan 2023 22:28:01 -0500 Subject: [PATCH 10/58] updated the force directed graph code with the latest version from nh-unity --- .../Props/NomaiText/NomaiTextArcArranger.cs | 207 ++++++++++++++---- .../Props/NomaiText/NomaiTextBuilder.cs | 2 + 2 files changed, 167 insertions(+), 42 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs index 09e1ecec..28aff17b 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs @@ -7,28 +7,51 @@ namespace NewHorizons.Builder.Props { public class NomaiTextArcArranger : MonoBehaviour { public List spirals = new List(); + public List reverseToposortedSpirals = null; + public SpiralManipulator root { get; private set; } private Dictionary sprialOverlapResolutionPriority = new Dictionary(); private static int MAX_MOVE_DISTANCE = 2; - public float maxX = 3; - public float minX = -3; - public float maxY = 2f; - public float minY = -1f; - - public static SpiralManipulator Place(NomaiTextArcBuilder.SpiralProfile profile, GameObject spiralMeshHolder) { + public float maxX = 0.75f * 4; + public float minX = 0.75f * -4; + public float maxY = 0.75f * 5f; + public float minY = 0.75f * -1f; + + public void GenerateReverseToposort() + { + reverseToposortedSpirals = new List(); + Queue frontierQueue = new Queue(); + frontierQueue.Enqueue(root); + + while(frontierQueue.Count > 0) + { + var spiral = frontierQueue.Dequeue(); + reverseToposortedSpirals.Add(spiral); + + foreach(var child in spiral.children) frontierQueue.Enqueue(child); + } + + reverseToposortedSpirals.Reverse(); + } + public static SpiralManipulator Place(NomaiTextArcBuilder.SpiralProfile profile, GameObject spiralMeshHolder) + { var rootArc = NomaiTextArcBuilder.BuildSpiralGameObject(profile); rootArc.transform.parent = spiralMeshHolder.transform; rootArc.transform.localEulerAngles = new Vector3(0, 0, Random.Range(-60, 60)); var manip = rootArc.AddComponent(); if (Random.value < 0.5) manip.transform.localScale = new Vector3(-1, 1, 1); // randomly mirror - spiralMeshHolder.GetComponent().spirals.Add(manip); + + // add to arranger + var arranger = spiralMeshHolder.GetComponent(); + if (arranger.root == null) arranger.root = manip; + arranger.spirals.Add(manip); return manip; } - private void OnDrawGizmosSelected() + public void OnDrawGizmosSelected() { var topLeft = new Vector3(minX, maxY) + transform.position; var topRight = new Vector3(maxX, maxY) + transform.position; @@ -53,7 +76,7 @@ namespace NewHorizons.Builder.Props return mirrorIndex; } - + public Vector2Int Overlap() { var index = -1; @@ -66,55 +89,97 @@ namespace NewHorizons.Builder.Props foreach (var s2 in spirals) { jndex++; - if (s1 == s2) continue; - if (Vector3.Distance(s1.center, s2.center) > Mathf.Max(s1.NomaiTextLine.GetWorldRadius(), s2.NomaiTextLine.GetWorldRadius())) continue; // no overlap possible - too far away - - var s1Points = s1.NomaiTextLine.GetPoints().Select(p => s1.transform.TransformPoint(p)).ToList(); - var s2Points = s2.NomaiTextLine.GetPoints().Select(p => s2.transform.TransformPoint(p)).ToList(); - var s1ThresholdForOverlap = Vector3.Distance(s1Points[0], s1Points[1]); - var s2ThresholdForOverlap = Vector3.Distance(s2Points[0], s2Points[1]); - var thresholdForOverlap = Mathf.Pow(Mathf.Max(s1ThresholdForOverlap, s2ThresholdForOverlap), 2); // square to save on computation (we'll work in distance squared from here on) - - if (s1.parent == s2) s1Points.RemoveAt(0); // don't consider the base points so that we can check if children overlap their parents - if (s2.parent == s1) s2Points.RemoveAt(0); // (note: the base point of a child is always exactly overlapping with one of the parent's points) - - foreach(var p1 in s1Points) - { - foreach(var p2 in s2Points) - { - if (Vector3.SqrMagnitude(p1-p2) <= thresholdForOverlap) return new Vector2Int(index, jndex); // s1 and s2 overlap - } - } + if (Overlap(s1, s2)) return new Vector2Int(index, jndex);; } } return new Vector2Int(-1, -1); } + public bool Overlap(SpiralManipulator s1, SpiralManipulator s2) + { + if (s1 == s2) return false; + if (Vector3.Distance(s1.center, s2.center) > Mathf.Max(s1.NomaiTextLine.GetWorldRadius(), s2.NomaiTextLine.GetWorldRadius())) return false; // no overlap possible - too far away + + var s1Points = s1.NomaiTextLine.GetPoints().Select(p => s1.transform.TransformPoint(p)).ToList(); + var s2Points = s2.NomaiTextLine.GetPoints().Select(p => s2.transform.TransformPoint(p)).ToList(); + var s1ThresholdForOverlap = Vector3.Distance(s1Points[0], s1Points[1]); + var s2ThresholdForOverlap = Vector3.Distance(s2Points[0], s2Points[1]); + var thresholdForOverlap = Mathf.Pow(Mathf.Max(s1ThresholdForOverlap, s2ThresholdForOverlap), 2); // square to save on computation (we'll work in distance squared from here on) + + if (s1.parent == s2) s1Points.RemoveAt(0); // don't consider the base points so that we can check if children overlap their parents + if (s2.parent == s1) s2Points.RemoveAt(0); // (note: the base point of a child is always exactly overlapping with one of the parent's points) + + foreach(var p1 in s1Points) + { + foreach(var p2 in s2Points) + { + if (Vector3.SqrMagnitude(p1-p2) <= thresholdForOverlap) return true; // s1 and s2 overlap + } + } + + return false; + } + + + public bool OutsideBounds(SpiralManipulator spiral) + { + var points = spiral.NomaiTextLine.GetPoints() + .Select(p => spiral.transform.TransformPoint(p)) + .Select(p => spiral.transform.parent.InverseTransformPoint(p)) + .ToList(); + + foreach(var point in points) { + if (point.x < minX || point.x > maxX || + point.y < minY || point.y > maxY) + { + return true; + } + } + + return false; + } + + public void Step() { - // TODO: after setting child position on parent in Step(), check to see if this spiral exits the bounds - if so, move it away until it no longer does - // this ensures that a spiral can never be outside the bounds, it makes them rigid + if (reverseToposortedSpirals == null) GenerateReverseToposort(); // TODO: for integration with NH - before generating spirals, seed the RNG with the hash of the XML filename for this convo // and add an option to specify the seed + Dictionary childForces = new Dictionary(); + + //Debug.Log(reverseToposortedSpirals.Count); + //Debug.Log( string.Join(", ", reverseToposortedSpirals.Select(hmslnk => hmslnk.gameObject.name) ) ); + var index = -1; - foreach (var s1 in spirals) + foreach (var s1 in reverseToposortedSpirals) // treating the conversation like a tree datastructure, move "leaf" spirals first so that we can propogate their force up to the parents { index++; - if (s1.parent == null) continue; - - // - // Calculate the force s1 should experience - // Vector2 force = Vector2.zero; + + // + // Calculate the force s1 should experience from its children + // + + if (childForces.ContainsKey(s1)) + { + force += 0.9f * childForces[s1]; + } + + // + // Calculate the force s1 should experience from fellow spirals + // + foreach (var s2 in spirals) { if (s1 == s2) continue; if (s1.parent == s2) continue; if (s1 == s2.parent) continue; + + //if (!Overlap(s1, s2)) continue; // push away from other spirals var f = (s2.center - s1.center); @@ -124,13 +189,19 @@ namespace NewHorizons.Builder.Props force -= f2 / Mathf.Pow(f2.magnitude, 6); } - + // // push away from the edges - if (s1.center.y < minY+s1.transform.parent.position.y) force += new Vector2(0, Mathf.Pow(10f*minY - 10f*s1.center.y, 6)); - if (s1.center.x < minX+s1.transform.parent.position.x) force += new Vector2(Mathf.Pow(10f*minX - 10f*s1.center.x, 6), 0); - if (s1.center.y > maxY+s1.transform.parent.position.y) force -= new Vector2(0, Mathf.Pow(10f*maxY - 10f*s1.center.y, 6)); - if (s1.center.x > maxX+s1.transform.parent.position.x) force -= new Vector2(Mathf.Pow(10f*maxX - 10f*s1.center.x, 6), 0); + // + var MAX_EDGE_PUSH_FORCE = 1; + force += new Vector2(0, -1) * Mathf.Max(0, (s1.transform.localPosition.y + maxY)*(MAX_EDGE_PUSH_FORCE / maxY) - MAX_EDGE_PUSH_FORCE); + force += new Vector2(0, 1) * Mathf.Max(0, (s1.transform.localPosition.y + minY)*(MAX_EDGE_PUSH_FORCE / minY) - MAX_EDGE_PUSH_FORCE); + force += new Vector2(-1, 0) * Mathf.Max(0, (s1.transform.localPosition.x + maxX)*(MAX_EDGE_PUSH_FORCE / maxX) - MAX_EDGE_PUSH_FORCE); + force += new Vector2(1, 0) * Mathf.Max(0, (s1.transform.localPosition.x + minX)*(MAX_EDGE_PUSH_FORCE / minX) - MAX_EDGE_PUSH_FORCE); + + // push up just to make everything a little more pretty (this is not neccessary to get an arrangement that simply has no overlap/spirals exiting the bounds) + force += new Vector2(0, 1) * 1; + // // renormalize the force magnitude (keeps force sizes reasonable, and improves stability in the case of small forces) // @@ -139,8 +210,24 @@ namespace NewHorizons.Builder.Props var scale = 0.75f; force = force.normalized * scale * (1 / (1 + Mathf.Exp(avg-force.magnitude)) - 1 / (1 + Mathf.Exp(avg))); // apply a sigmoid-ish smoothing operation, so only giant forces actually move the spirals + // - // apply the forces as we go to increase stability? + // if this is the root spiral, then rotate it instead of trying to move it (what the rest of the code does) + // + + if (s1.parent == null) + { + // this is the root spiral, so rotate instead of moving + var finalAngle = Mathf.Atan2(force.y, force.x); // root spiral is always at 0, 0 + var currentAngle = Mathf.Atan2(s1.center.y, s1.center.x); // root spiral is always at 0, 0 + s1.transform.localEulerAngles = new Vector3(0, 0, finalAngle-currentAngle); + s1.UpdateChildren(); + + continue; + } + + // + // look for the point closest to where the forces want to push this spiral // var spiral = s1; @@ -176,6 +263,42 @@ namespace NewHorizons.Builder.Props // SpiralManipulator.PlaceChildOnParentPoint(spiral, spiral.parent, bestPointIndex); + + // + // Ensure the spiral has not moved out of bounds, and if it has, move it back in bounds + // + + if (OutsideBounds(s1)) + { + var start = s1._parentPointIndex; + var range = Mathf.Max(start-SpiralManipulator.MIN_PARENT_POINT, SpiralManipulator.MAX_PARENT_POINT-start); + var success = false; + for (var i = 1; i <= range; i++) + { + if (start-i >= SpiralManipulator.MIN_PARENT_POINT) + { + SpiralManipulator.PlaceChildOnParentPoint(s1, s1.parent, start-i); + if (!OutsideBounds(s1)) { success = true; break; } + } + + if (start+i <= SpiralManipulator.MAX_PARENT_POINT) + { + SpiralManipulator.PlaceChildOnParentPoint(s1, s1.parent, start+i); + if (!OutsideBounds(s1)) { success = true; break; } + } + } + + if (!success) + { + SpiralManipulator.PlaceChildOnParentPoint(s1, s1.parent, start); + Debug.LogWarning("Unable to place spiral " + s1.gameObject.name + " within bounds."); + } + } + + if (!childForces.ContainsKey(s1.parent)) childForces[s1.parent] = Vector2.zero; + childForces[s1.parent] += force; + + Debug.DrawRay(s1.transform.position, new Vector3(force.x, force.y, 0), Color.green); } } } diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs index 1d9c8b1d..13551441 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs @@ -636,6 +636,8 @@ namespace NewHorizons.Builder.Props i++; } + arranger.GenerateReverseToposort(); // Required before Step() is called + for (var k = 0; k < i*2; k++) { var overlap = arranger.Overlap(); From 275008e15e60b1fc3c488697b848a09550a6ae70 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Thu, 26 Jan 2023 23:33:20 -0500 Subject: [PATCH 11/58] prevented spirals from repeating the same mirrored status 3 in a row --- NewHorizons/Builder/Props/DetailBuilder.cs | 1 + .../Props/NomaiText/NomaiTextArcArranger.cs | 52 +++++++++++++------ .../Props/NomaiText/NomaiTextBuilder.cs | 4 ++ 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/NewHorizons/Builder/Props/DetailBuilder.cs b/NewHorizons/Builder/Props/DetailBuilder.cs index 09eb67a9..7cf79924 100644 --- a/NewHorizons/Builder/Props/DetailBuilder.cs +++ b/NewHorizons/Builder/Props/DetailBuilder.cs @@ -101,6 +101,7 @@ namespace NewHorizons.Builder.Props foreach (var component in prop.GetComponentsInChildren(true)) { + if (component == null) continue; if (component.gameObject == prop && component is OWItem) isItem = true; if (sector == null) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs index 28aff17b..d56892b2 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs @@ -1,3 +1,4 @@ +using NewHorizons.Utility; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -13,27 +14,47 @@ namespace NewHorizons.Builder.Props private static int MAX_MOVE_DISTANCE = 2; - public float maxX = 0.75f * 4; - public float minX = 0.75f * -4; - public float maxY = 0.75f * 5f; - public float minY = 0.75f * -1f; + public float maxX = 3; + public float minX = -3; + public float maxY = 2.75f; + public float minY = -1f; + + public void DrawBoundsWithDebugSpheres() + { + AddDebugShape.AddSphere(this.gameObject, 0.1f, Color.green).transform.localPosition = new Vector3(minX, minY, 0); + AddDebugShape.AddSphere(this.gameObject, 0.1f, Color.green).transform.localPosition = new Vector3(minX, maxY, 0); + AddDebugShape.AddSphere(this.gameObject, 0.1f, Color.green).transform.localPosition = new Vector3(maxX, maxY, 0); + AddDebugShape.AddSphere(this.gameObject, 0.1f, Color.green).transform.localPosition = new Vector3(maxX, minY, 0); + AddDebugShape.AddSphere(this.gameObject, 0.1f, Color.red).transform.localPosition = new Vector3(0, 0, 0); + } public void GenerateReverseToposort() { - reverseToposortedSpirals = new List(); - Queue frontierQueue = new Queue(); - frontierQueue.Enqueue(root); + reverseToposortedSpirals = new List(); + Queue frontierQueue = new Queue(); + frontierQueue.Enqueue(root); - while(frontierQueue.Count > 0) - { - var spiral = frontierQueue.Dequeue(); - reverseToposortedSpirals.Add(spiral); + while(frontierQueue.Count > 0) + { + var spiral = frontierQueue.Dequeue(); + reverseToposortedSpirals.Add(spiral); - foreach(var child in spiral.children) frontierQueue.Enqueue(child); - } + foreach(var child in spiral.children) frontierQueue.Enqueue(child); + } - reverseToposortedSpirals.Reverse(); + reverseToposortedSpirals.Reverse(); } + public void LimitRepeatedMirrors() + { + foreach(var spiral in reverseToposortedSpirals) + { + if (spiral.Mirrored == spiral.parent?.Mirrored && spiral.Mirrored == spiral.parent?.parent?.Mirrored) + { + spiral.parent.parent.Mirror(); // flipping the grandparent causes every 3rd spiral in a row to be flipped, meaning the maximum number of spirals in a row with the same mirror is 2 + } + } + } + public static SpiralManipulator Place(NomaiTextArcBuilder.SpiralProfile profile, GameObject spiralMeshHolder) { var rootArc = NomaiTextArcBuilder.BuildSpiralGameObject(profile); @@ -314,7 +335,8 @@ namespace NewHorizons.Builder.Props public static int MIN_PARENT_POINT = 3; public static int MAX_PARENT_POINT = 26; - + public bool Mirrored { get { return this.transform.localScale.x < 0; } } + private NomaiTextLine _NomaiTextLine; public NomaiTextLine NomaiTextLine { diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs index 13551441..d45e97a1 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs @@ -179,6 +179,9 @@ namespace NewHorizons.Builder.Props } } + nomaiWallTextObj.transform.localEulerAngles += new Vector3(0, 0, 90+45); + nomaiWallTextObj.GetComponent().DrawBoundsWithDebugSpheres(); + nomaiWallTextObj.SetActive(true); conversationInfoToCorrespondingSpawnedGameObject[info] = nomaiWallTextObj; @@ -637,6 +640,7 @@ namespace NewHorizons.Builder.Props } arranger.GenerateReverseToposort(); // Required before Step() is called + arranger.LimitRepeatedMirrors(); for (var k = 0; k < i*2; k++) { From 775554fbe475204ca307d7a36bb4b8a245f3d94f Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Thu, 26 Jan 2023 23:39:25 -0500 Subject: [PATCH 12/58] removed debugging hardcoded rotation --- NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs index d45e97a1..c4272be0 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs @@ -179,7 +179,6 @@ namespace NewHorizons.Builder.Props } } - nomaiWallTextObj.transform.localEulerAngles += new Vector3(0, 0, 90+45); nomaiWallTextObj.GetComponent().DrawBoundsWithDebugSpheres(); nomaiWallTextObj.SetActive(true); From 179b94424cd1ee44b1d3302570383a1d037734df Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Thu, 26 Jan 2023 23:45:37 -0500 Subject: [PATCH 13/58] shrunk the bounds a bit to account for whiteboards being crumbly around the edges --- .../Builder/Props/NomaiText/NomaiTextArcArranger.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs index d56892b2..898425f0 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs @@ -14,9 +14,9 @@ namespace NewHorizons.Builder.Props private static int MAX_MOVE_DISTANCE = 2; - public float maxX = 3; - public float minX = -3; - public float maxY = 2.75f; + public float maxX = 2.7f;//3; + public float minX = -2.7f;//-3; + public float maxY = 2.6f;//2.75f; public float minY = -1f; public void DrawBoundsWithDebugSpheres() @@ -46,6 +46,8 @@ namespace NewHorizons.Builder.Props } public void LimitRepeatedMirrors() { + if(true) return; // temp disable + foreach(var spiral in reverseToposortedSpirals) { if (spiral.Mirrored == spiral.parent?.Mirrored && spiral.Mirrored == spiral.parent?.parent?.Mirrored) From a13b79014526b7e9f08d40047becf7043331e149 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Thu, 26 Jan 2023 23:54:07 -0500 Subject: [PATCH 14/58] when trying to move a spiral back in bounds, if no solution is found, the spiral is mirrored and then attempted to be put in bounds again - this solves most cases of spirals being stuck out of bounds --- .../Props/NomaiText/NomaiTextArcArranger.cs | 44 +++++++++++++------ 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs index 898425f0..978b894a 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs @@ -294,25 +294,19 @@ namespace NewHorizons.Builder.Props if (OutsideBounds(s1)) { var start = s1._parentPointIndex; - var range = Mathf.Max(start-SpiralManipulator.MIN_PARENT_POINT, SpiralManipulator.MAX_PARENT_POINT-start); - var success = false; - for (var i = 1; i <= range; i++) + var originalMirror = s1.Mirrored; + + var success = AttemptToPushSpiralInBounds(s1, start); + if (!success) { - if (start-i >= SpiralManipulator.MIN_PARENT_POINT) - { - SpiralManipulator.PlaceChildOnParentPoint(s1, s1.parent, start-i); - if (!OutsideBounds(s1)) { success = true; break; } - } - - if (start+i <= SpiralManipulator.MAX_PARENT_POINT) - { - SpiralManipulator.PlaceChildOnParentPoint(s1, s1.parent, start+i); - if (!OutsideBounds(s1)) { success = true; break; } - } + s1.Mirror(); // try flipping it if nothing worked with original mirror + success = AttemptToPushSpiralInBounds(s1, start); } if (!success) { + // if we couldn't put it inside the bounds, put it back how we found it (this increases stability of the rest of the spirals) + if (s1.Mirrored != originalMirror) s1.Mirror(); SpiralManipulator.PlaceChildOnParentPoint(s1, s1.parent, start); Debug.LogWarning("Unable to place spiral " + s1.gameObject.name + " within bounds."); } @@ -324,6 +318,28 @@ namespace NewHorizons.Builder.Props Debug.DrawRay(s1.transform.position, new Vector3(force.x, force.y, 0), Color.green); } } + + private bool AttemptToPushSpiralInBounds(SpiralManipulator s1, int start) + { + var range = Mathf.Max(start-SpiralManipulator.MIN_PARENT_POINT, SpiralManipulator.MAX_PARENT_POINT-start); + + for (var i = 1; i <= range; i++) + { + if (start-i >= SpiralManipulator.MIN_PARENT_POINT) + { + SpiralManipulator.PlaceChildOnParentPoint(s1, s1.parent, start-i); + if (!OutsideBounds(s1)) return true; + } + + if (start+i <= SpiralManipulator.MAX_PARENT_POINT) + { + SpiralManipulator.PlaceChildOnParentPoint(s1, s1.parent, start+i); + if (!OutsideBounds(s1)) return true; + } + } + + return false; + } } [ExecuteInEditMode] From b62db562f06f72660ef11bc1f7c1aadcbf259226 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Fri, 27 Jan 2023 13:29:55 -0500 Subject: [PATCH 15/58] fixed bug with wall text not rotating around its normal properly --- NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs index c4272be0..8e8bf116 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs @@ -169,9 +169,12 @@ namespace NewHorizons.Builder.Props // In global coordinates (normal was in local coordinates) var up = (nomaiWallTextObj.transform.position - planetGO.transform.position).normalized; var forward = planetGO.transform.TransformDirection(info.normal).normalized; - - nomaiWallTextObj.transform.up = up; + nomaiWallTextObj.transform.forward = forward; + + var desiredUp = Vector3.ProjectOnPlane(up, forward); + var zRotation = Vector3.SignedAngle(nomaiWallTextObj.transform.up, desiredUp, forward); + nomaiWallTextObj.transform.RotateAround(nomaiWallTextObj.transform.position, forward, zRotation); } if (info.rotation != null) { From d5480d9781f9eb8dca9fe67eb17538ee1d576efb Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Fri, 27 Jan 2023 13:40:06 -0500 Subject: [PATCH 16/58] generated spirals now start off in the hidden state, allowing the reveal on translate stuff to work --- .../Builder/Props/NomaiText/NomaiTextArcArranger.cs | 2 ++ NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs | 7 ++----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs index 978b894a..26ffe171 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs @@ -171,6 +171,8 @@ namespace NewHorizons.Builder.Props // TODO: for integration with NH - before generating spirals, seed the RNG with the hash of the XML filename for this convo // and add an option to specify the seed + // TODO: make all spirals unrevealed at first except the root spiral + Dictionary childForces = new Dictionary(); //Debug.Log(reverseToposortedSpirals.Count); diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs index 57b38bc8..58aa8e1f 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs @@ -74,13 +74,10 @@ namespace NewHorizons.Builder.Props .ToArray(); owNomaiTextLine._points = _points; - //owNomaiTextLine._lengths = _points.Take(_points.Count()-1).Select((point, i) => Vector3.Distance(point, _points[i+1])).ToArray(); - //owNomaiTextLine._totalLength = owNomaiTextLine._lengths.Aggregate(0f, (acc, length) => acc + length); - owNomaiTextLine._state = NomaiTextLine.VisualState.UNREAD; + owNomaiTextLine._state = NomaiTextLine.VisualState.HIDDEN; owNomaiTextLine._textLineLocation = NomaiText.Location.UNSPECIFIED; - //owNomaiTextLine._center = _points.Aggregate(Vector3.zero, (acc, point) => acc + point) / (float)_points.Count(); - //owNomaiTextLine._radius = _points.Aggregate(0f, (acc, point) => Mathf.Max(Vector3.Distance(owNomaiTextLine._center, point), acc)); owNomaiTextLine._active = true; + owNomaiTextLine._prebuilt = false; g.SetActive(true); return g; From 24f7a9dfc860216850b8d52a8aedaa5b26448e03 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Fri, 27 Jan 2023 13:41:31 -0500 Subject: [PATCH 17/58] removed debug code --- NewHorizons/Builder/Props/DetailBuilder.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/NewHorizons/Builder/Props/DetailBuilder.cs b/NewHorizons/Builder/Props/DetailBuilder.cs index 7cf79924..09eb67a9 100644 --- a/NewHorizons/Builder/Props/DetailBuilder.cs +++ b/NewHorizons/Builder/Props/DetailBuilder.cs @@ -101,7 +101,6 @@ namespace NewHorizons.Builder.Props foreach (var component in prop.GetComponentsInChildren(true)) { - if (component == null) continue; if (component.gameObject == prop && component is OWItem) isItem = true; if (sector == null) From befd23018cdf7fd1864cc49205bf4269432631e9 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Fri, 27 Jan 2023 14:05:13 -0500 Subject: [PATCH 18/58] removed some commented code, implemented seed-by-file-hash, and readded support for manual spiral placements --- .../Props/NomaiText/NomaiTextArcArranger.cs | 5 +- .../Props/NomaiText/NomaiTextArcBuilder.cs | 5 - .../Props/NomaiText/NomaiTextBuilder.cs | 94 +++++++++++-------- 3 files changed, 56 insertions(+), 48 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs index 26ffe171..c4ded624 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs @@ -168,10 +168,7 @@ namespace NewHorizons.Builder.Props { if (reverseToposortedSpirals == null) GenerateReverseToposort(); - // TODO: for integration with NH - before generating spirals, seed the RNG with the hash of the XML filename for this convo - // and add an option to specify the seed - - // TODO: make all spirals unrevealed at first except the root spiral + // TODO: fix LocationB not making spiral orange Dictionary childForces = new Dictionary(); diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs index 58aa8e1f..037f0cd5 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs @@ -58,11 +58,6 @@ namespace NewHorizons.Builder.Props meshCopy.RecalculateBounds(); } - // TODO: caching? would this help with that? - //AssetDatabase.CreateAsset(mesh, "Assets/Spirals/"+(profile.profileName)+"spiral" + (NomaiTextArcBuilder.i) + ".asset"); - //g.GetComponent().sharedMesh = AssetDatabase.LoadAssetAtPath("Assets/Spirals/"+(profile.profileName)+"spiral" + (NomaiTextArcBuilder.i) + ".asset", typeof(Mesh)) as Mesh; - //NomaiTextArcBuilder.i++; - // // set up NomaiTextArc stuff // diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs index 8e8bf116..bf9ac8e9 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs @@ -51,14 +51,19 @@ namespace NewHorizons.Builder.Props if (_isInit) return; _isInit = true; - - if (_adultArcMaterial == null) + + if (_adultArcMaterial == null) { _adultArcMaterial = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Sector_Crossroads/Interactables_Crossroads/Trailmarkers/Prefab_NOM_BH_Cairn_Arc (2)/Props_TH_ClutterSmall/Arc_Short/Arc") .GetComponent() .sharedMaterial; } + if (_childArcMaterial == null) + { + // TODO: this + } + if (_ghostArcMaterial == null) { _ghostArcMaterial = SearchUtilities.Find("RingWorld_Body/Sector_RingInterior/Sector_Zone1/Interactables_Zone1/Props_IP_ZoneSign_1/Arc_TestAlienWriting/Arc 1") @@ -612,7 +617,7 @@ namespace NewHorizons.Builder.Props internal static void RefreshArcs(NomaiWallText nomaiWallText, GameObject conversationZone, PropModule.NomaiTextInfo info) { var dict = nomaiWallText._dictNomaiTextData; - Random.InitState(info.seed); + Random.InitState(info.seed == 0 ? info.xmlFile.GetHashCode() : info.seed); var arcsByID = new Dictionary(); @@ -624,6 +629,10 @@ namespace NewHorizons.Builder.Props var arranger = nomaiWallText.gameObject.AddComponent(); + // + // Generate spirals + // + var i = 0; foreach (var textData in dict.Values) { @@ -640,19 +649,53 @@ namespace NewHorizons.Builder.Props i++; } + + // + // place spirals + // - arranger.GenerateReverseToposort(); // Required before Step() is called - arranger.LimitRepeatedMirrors(); - - for (var k = 0; k < i*2; k++) + if (info.arcInfo?.Length > 0) { - var overlap = arranger.Overlap(); - if (overlap.x < 0) return; + // manual placement - arranger.AttemptOverlapResolution(overlap); - for(var a = 0; a < 10; a++) arranger.Step(); + if (info.arcInfo.Length != arranger.spirals.Count) + { + Logger.LogError($"Can't make NomaiWallText, arcInfo length [{info.arcInfo.Count()}] doesn't equal text entries [{dict.Values.Count()}]"); + } + + // TODO: test this + for (var j = 0; j < info.arcInfo.Length; j++) + { + var arcInfo = info.arcInfo[j]; + var arc = arranger.spirals[j]; + + if (arcInfo.position == null) arc.transform.localPosition = Vector3.zero; + else arc.transform.localPosition = new Vector3(arcInfo.position.x, arcInfo.position.y, 0); + + arc.transform.localRotation = Quaternion.Euler(0, 0, arcInfo.zRotation); + + if (arcInfo.mirror) arc.transform.localScale = new Vector3(-1, 1, 1); + else arc.transform.localScale = new Vector3( 1, 1, 1); + } + } + else + { + // auto placement + + arranger.GenerateReverseToposort(); // Required before Step() is called + arranger.LimitRepeatedMirrors(); + + for (var k = 0; k < arranger.spirals.Count*2; k++) + { + var overlappingSpiralIDs = arranger.Overlap(); + if (overlappingSpiralIDs.x < 0) return; + + arranger.AttemptOverlapResolution(overlappingSpiralIDs); + for(var a = 0; a < 10; a++) arranger.Step(); + } + + Logger.LogWarning("Overlap resolution failed!"); } - Logger.LogError("Overlap resolution failed!"); } internal static GameObject MakeArc(PropModule.NomaiTextArcInfo arcInfo, GameObject conversationZone, GameObject parent, int textEntryID) @@ -686,33 +729,6 @@ namespace NewHorizons.Builder.Props arc.transform.parent = conversationZone.transform; arc.GetComponent()._prebuilt = false; - //if (arcInfo != null) - //{ - // arcInfo.variation = variation; - // if (arcInfo.position == null) arc.transform.localPosition = Vector3.zero; - // else arc.transform.localPosition = new Vector3(arcInfo.position.x, arcInfo.position.y, 0); - - // arc.transform.localRotation = Quaternion.Euler(0, 0, arcInfo.zRotation); - - // if (arcInfo.mirror) arc.transform.localScale = new Vector3(-1, 1, 1); - //} - //// Try auto I guess - //else - //{ - // if (parent == null) - // { - // arc.transform.localPosition = Vector3.zero; - // } - // else - // { - // var points = parent.GetComponent().GetPoints(); - // var point = points[points.Count() / 2]; - - // arc.transform.localPosition = point; - // arc.transform.localRotation = Quaternion.Euler(0, 0, Random.Range(0, 360)); - // } - //} - arc.GetComponent().SetEntryID(textEntryID); arc.GetComponent().enabled = false; From 05e4e0a1fa69fe09b355e224e90ea475fb5a90b3 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Fri, 27 Jan 2023 14:11:28 -0500 Subject: [PATCH 19/58] added a property to ArcInfo to allow for easy modification of auto placement results --- .../Props/NomaiText/NomaiTextBuilder.cs | 54 ++++++++----------- 1 file changed, 23 insertions(+), 31 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs index bf9ac8e9..7d5f965d 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs @@ -654,47 +654,39 @@ namespace NewHorizons.Builder.Props // place spirals // - if (info.arcInfo?.Length > 0) + // auto placement + + arranger.GenerateReverseToposort(); // Required before Step() is called + arranger.LimitRepeatedMirrors(); + + for (var k = 0; k < arranger.spirals.Count*2; k++) { - // manual placement + var overlappingSpiralIDs = arranger.Overlap(); + if (overlappingSpiralIDs.x < 0) return; - if (info.arcInfo.Length != arranger.spirals.Count) - { - Logger.LogError($"Can't make NomaiWallText, arcInfo length [{info.arcInfo.Count()}] doesn't equal text entries [{dict.Values.Count()}]"); - } + arranger.AttemptOverlapResolution(overlappingSpiralIDs); + for(var a = 0; a < 10; a++) arranger.Step(); + } - // TODO: test this - for (var j = 0; j < info.arcInfo.Length; j++) - { - var arcInfo = info.arcInfo[j]; - var arc = arranger.spirals[j]; + Logger.LogWarning("Overlap resolution failed!"); - if (arcInfo.position == null) arc.transform.localPosition = Vector3.zero; - else arc.transform.localPosition = new Vector3(arcInfo.position.x, arcInfo.position.y, 0); + // manual placement - arc.transform.localRotation = Quaternion.Euler(0, 0, arcInfo.zRotation); - - if (arcInfo.mirror) arc.transform.localScale = new Vector3(-1, 1, 1); - else arc.transform.localScale = new Vector3( 1, 1, 1); - } - } - else + // TODO: test this + for (var j = 0; j < info.arcInfo.Length; j++) { - // auto placement + var arcInfo = info.arcInfo[j]; + var arc = arranger.spirals[j]; - arranger.GenerateReverseToposort(); // Required before Step() is called - arranger.LimitRepeatedMirrors(); + if (arcInfo.keepAutoPlacement) continue; - for (var k = 0; k < arranger.spirals.Count*2; k++) - { - var overlappingSpiralIDs = arranger.Overlap(); - if (overlappingSpiralIDs.x < 0) return; + if (arcInfo.position == null) arc.transform.localPosition = Vector3.zero; + else arc.transform.localPosition = new Vector3(arcInfo.position.x, arcInfo.position.y, 0); - arranger.AttemptOverlapResolution(overlappingSpiralIDs); - for(var a = 0; a < 10; a++) arranger.Step(); - } + arc.transform.localRotation = Quaternion.Euler(0, 0, arcInfo.zRotation); - Logger.LogWarning("Overlap resolution failed!"); + if (arcInfo.mirror) arc.transform.localScale = new Vector3(-1, 1, 1); + else arc.transform.localScale = new Vector3( 1, 1, 1); } } From 1f91e2e57ba00d2d0ce41af717c6e3e6c276ddd1 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Fri, 27 Jan 2023 14:20:46 -0500 Subject: [PATCH 20/58] forgot to commit the module change --- NewHorizons/External/Modules/PropModule.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/NewHorizons/External/Modules/PropModule.cs b/NewHorizons/External/Modules/PropModule.cs index 3fc44d0c..f75dff51 100644 --- a/NewHorizons/External/Modules/PropModule.cs +++ b/NewHorizons/External/Modules/PropModule.cs @@ -602,7 +602,7 @@ namespace NewHorizons.External.Modules /// Additional information about each arc in the text /// public NomaiTextArcInfo[] arcInfo; - + /// /// The normal vector for this object. Used for writing on walls and positioning computers. /// @@ -667,6 +667,11 @@ namespace NewHorizons.External.Modules [EnumMember(Value = @"stranger")] Stranger = 2 } + + /// + /// Whether to skip modifying this spiral's placement, and instead keep the automatically determined placement. + /// + public bool keepAutoPlacement; /// /// Whether to flip the spiral from left-curling to right-curling or vice versa. From d2cb4edc5cdf2edfcf5a2b4210c679562bdb2073 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Fri, 27 Jan 2023 21:50:58 -0500 Subject: [PATCH 21/58] removed temp disable and call to disabled function --- NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs | 2 -- NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs | 1 - 2 files changed, 3 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs index c4ded624..4cdcf09a 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs @@ -46,8 +46,6 @@ namespace NewHorizons.Builder.Props } public void LimitRepeatedMirrors() { - if(true) return; // temp disable - foreach(var spiral in reverseToposortedSpirals) { if (spiral.Mirrored == spiral.parent?.Mirrored && spiral.Mirrored == spiral.parent?.parent?.Mirrored) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs index 7d5f965d..46daf6c7 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs @@ -657,7 +657,6 @@ namespace NewHorizons.Builder.Props // auto placement arranger.GenerateReverseToposort(); // Required before Step() is called - arranger.LimitRepeatedMirrors(); for (var k = 0; k < arranger.spirals.Count*2; k++) { From 73a542cd21bb69e88ca2ee7bed854cf641fb6877 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Fri, 27 Jan 2023 22:26:13 -0500 Subject: [PATCH 22/58] found a child spiral to steal the material from and added that to the setup --- NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs index 46daf6c7..11757404 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs @@ -61,7 +61,9 @@ namespace NewHorizons.Builder.Props if (_childArcMaterial == null) { - // TODO: this + _childArcMaterial = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Sector_OldSettlement/Fragment OldSettlement 5/Core_OldSettlement 5/Interactables_Core_OldSettlement5/Arc_BH_OldSettlement_ChildrensRhyme/Arc 1") + .GetComponent() + .sharedMaterial; } if (_ghostArcMaterial == null) From 9b01f5b0536ddfc12ab9a81b96fc112718c5a35e Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Fri, 27 Jan 2023 23:15:39 -0500 Subject: [PATCH 23/58] fixed child spiral profile, so now generated child spirals look more like the original base game child spirals --- .../Builder/Props/NomaiText/NomaiTextArcBuilder.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs index 037f0cd5..5b5f8c24 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs @@ -115,19 +115,20 @@ namespace NewHorizons.Builder.Props uvScale = 4.9f, }; + // TODO: make uvScale proportionate to the arc's length, and make skeleton scale proportionate to b, maybe using this equation? (e^{-b\pi2}) public static SpiralProfile childSpiralProfile = new SpiralProfile() { profileName="Child", canMirror = false, // we don't want to mirror the actual mesh itself anymore, we'll just mirror the game object using localScale.x a = new Vector2(0.9f, 0.9f), - b = new Vector2(0.305f, 0.4f), + b = new Vector2(0.2f, 0.3f), //new Vector2(0.305f, 0.4f), startS = new Vector2(342.8796f, 342.8796f), - endS = new Vector2(16f, 60f), - skeletonScale = 0.75f * new Vector2(0.002f, 0.005f), + endS = new Vector2(7.8f, 16f), + skeletonScale = 0.75f * new Vector2(0.01f, 0.01f), numSkeletonPoints = 51, innerWidth = 0.001f/10f, outerWidth = 2f*0.05f, - uvScale = 4.9f/3.5f, + uvScale = 4.9f * 0.55f, }; // location of example stranger writing: From 5460929afc02b45e49d920b7c9522936c6c34ba3 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Sat, 28 Jan 2023 18:51:01 -0500 Subject: [PATCH 24/58] updated child spiral profile --- .../Props/NomaiText/NomaiTextArcBuilder.cs | 45 ++++++++++++++----- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs index 5b5f8c24..cf088038 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs @@ -69,8 +69,8 @@ namespace NewHorizons.Builder.Props .ToArray(); owNomaiTextLine._points = _points; - owNomaiTextLine._state = NomaiTextLine.VisualState.HIDDEN; - owNomaiTextLine._textLineLocation = NomaiText.Location.UNSPECIFIED; + //owNomaiTextLine._state = NomaiTextLine.VisualState.HIDDEN; + //owNomaiTextLine._textLineLocation = NomaiText.Location.UNSPECIFIED; owNomaiTextLine._active = true; owNomaiTextLine._prebuilt = false; @@ -98,6 +98,8 @@ namespace NewHorizons.Builder.Props public float innerWidth; // width at the tip public float outerWidth; // width at the base public Material material; + + public bool syncRandomRanges; } public static SpiralProfile adultSpiralProfile = new SpiralProfile() { @@ -115,20 +117,23 @@ namespace NewHorizons.Builder.Props uvScale = 4.9f, }; - // TODO: make uvScale proportionate to the arc's length, and make skeleton scale proportionate to b, maybe using this equation? (e^{-b\pi2}) + // TODO: make a property called something like "sync random ranges" that makes a, b, etc, all be sampled with the same random number, isntead of being independently sampled + // This will allow child spirals to be set up to be more authentic public static SpiralProfile childSpiralProfile = new SpiralProfile() { profileName="Child", canMirror = false, // we don't want to mirror the actual mesh itself anymore, we'll just mirror the game object using localScale.x a = new Vector2(0.9f, 0.9f), - b = new Vector2(0.2f, 0.3f), //new Vector2(0.305f, 0.4f), + b = new Vector2(0.15f, 0.3f), //new Vector2(0.305f, 0.4f), startS = new Vector2(342.8796f, 342.8796f), - endS = new Vector2(7.8f, 16f), - skeletonScale = 0.75f * new Vector2(0.01f, 0.01f), + endS = new Vector2(85f, 25f), + skeletonScale = 0.4f * new Vector2(0.02f, 0.01f), numSkeletonPoints = 51, innerWidth = 0.001f/10f, outerWidth = 2f*0.05f, uvScale = 4.9f * 0.55f, + + syncRandomRanges = true, }; // location of example stranger writing: @@ -152,6 +157,8 @@ namespace NewHorizons.Builder.Props innerWidth = 1, outerWidth = 1, uvScale = 1f/1.8505f, + + }; // @@ -319,11 +326,27 @@ namespace NewHorizons.Builder.Props } public virtual void Randomize() { - this.a = UnityEngine.Random.Range(profile.a.x, profile.a.y); //0.5f; - this.b = UnityEngine.Random.Range(profile.b.x, profile.b.y); - this.startS = UnityEngine.Random.Range(profile.endS.x, profile.endS.y); // idk why I flipped these, please don't hate me - this.endS = UnityEngine.Random.Range(profile.startS.x, profile.startS.y); - this.scale = UnityEngine.Random.Range(profile.skeletonScale.x, profile.skeletonScale.y); + if (profile.syncRandomRanges) + { + var rand = UnityEngine.Random.Range(0f, 1f); + + this.a = Mathf.Lerp(profile.a.x, profile.a.y, rand); + this.b = Mathf.Lerp(profile.b.x, profile.b.y, rand); + this.startS = Mathf.Lerp(profile.endS.x, profile.endS.y, rand); // idk why I flipped these, please don't hate me + this.endS = Mathf.Lerp(profile.startS.x, profile.startS.y, rand); + this.scale = Mathf.Lerp(profile.skeletonScale.x, profile.skeletonScale.y, rand); + + NewHorizons.Utility.Logger.Log($"rand {rand} a {a} b {b} startS {startS} endS {endS} scale {scale}"); + } + else + { + this.a = UnityEngine.Random.Range(profile.a.x, profile.a.y); + this.b = UnityEngine.Random.Range(profile.b.x, profile.b.y); + this.startS = UnityEngine.Random.Range(profile.endS.x, profile.endS.y); // idk why I flipped these, please don't hate me + this.endS = UnityEngine.Random.Range(profile.startS.x, profile.startS.y); + this.scale = UnityEngine.Random.Range(profile.skeletonScale.x, profile.skeletonScale.y); + } + if (profile.canMirror) this.mirror = UnityEngine.Random.value<0.5f; } From 8152e789036e617db6b7d472a590923f97546a8e Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Sat, 28 Jan 2023 19:01:28 -0500 Subject: [PATCH 25/58] improved the child spiral profile a lot --- .../Builder/Props/NomaiText/NomaiTextArcBuilder.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs index cf088038..682bc679 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs @@ -123,17 +123,17 @@ namespace NewHorizons.Builder.Props profileName="Child", canMirror = false, // we don't want to mirror the actual mesh itself anymore, we'll just mirror the game object using localScale.x a = new Vector2(0.9f, 0.9f), - b = new Vector2(0.15f, 0.3f), //new Vector2(0.305f, 0.4f), + b = new Vector2(0.17f, 0.4f), //new Vector2(0.305f, 0.4f), startS = new Vector2(342.8796f, 342.8796f), - endS = new Vector2(85f, 25f), - skeletonScale = 0.4f * new Vector2(0.02f, 0.01f), + endS = new Vector2(35f, 25f), + skeletonScale = 0.8f * new Vector2(0.01f, 0.01f), numSkeletonPoints = 51, innerWidth = 0.001f/10f, outerWidth = 2f*0.05f, uvScale = 4.9f * 0.55f, - syncRandomRanges = true, + syncRandomRanges = false, }; // location of example stranger writing: From 40eea158a69a9db5fc3a836ce352b4ec3f2be563 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Sat, 28 Jan 2023 19:07:55 -0500 Subject: [PATCH 26/58] removed now unused syncRandomRanges feature --- .../Props/NomaiText/NomaiTextArcBuilder.cs | 29 ++++--------------- 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs index 682bc679..68d21b3d 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs @@ -98,8 +98,6 @@ namespace NewHorizons.Builder.Props public float innerWidth; // width at the tip public float outerWidth; // width at the base public Material material; - - public bool syncRandomRanges; } public static SpiralProfile adultSpiralProfile = new SpiralProfile() { @@ -132,8 +130,6 @@ namespace NewHorizons.Builder.Props innerWidth = 0.001f/10f, outerWidth = 2f*0.05f, uvScale = 4.9f * 0.55f, - - syncRandomRanges = false, }; // location of example stranger writing: @@ -326,26 +322,11 @@ namespace NewHorizons.Builder.Props } public virtual void Randomize() { - if (profile.syncRandomRanges) - { - var rand = UnityEngine.Random.Range(0f, 1f); - - this.a = Mathf.Lerp(profile.a.x, profile.a.y, rand); - this.b = Mathf.Lerp(profile.b.x, profile.b.y, rand); - this.startS = Mathf.Lerp(profile.endS.x, profile.endS.y, rand); // idk why I flipped these, please don't hate me - this.endS = Mathf.Lerp(profile.startS.x, profile.startS.y, rand); - this.scale = Mathf.Lerp(profile.skeletonScale.x, profile.skeletonScale.y, rand); - - NewHorizons.Utility.Logger.Log($"rand {rand} a {a} b {b} startS {startS} endS {endS} scale {scale}"); - } - else - { - this.a = UnityEngine.Random.Range(profile.a.x, profile.a.y); - this.b = UnityEngine.Random.Range(profile.b.x, profile.b.y); - this.startS = UnityEngine.Random.Range(profile.endS.x, profile.endS.y); // idk why I flipped these, please don't hate me - this.endS = UnityEngine.Random.Range(profile.startS.x, profile.startS.y); - this.scale = UnityEngine.Random.Range(profile.skeletonScale.x, profile.skeletonScale.y); - } + this.a = UnityEngine.Random.Range(profile.a.x, profile.a.y); + this.b = UnityEngine.Random.Range(profile.b.x, profile.b.y); + this.startS = UnityEngine.Random.Range(profile.endS.x, profile.endS.y); // idk why I flipped these, please don't hate me + this.endS = UnityEngine.Random.Range(profile.startS.x, profile.startS.y); + this.scale = UnityEngine.Random.Range(profile.skeletonScale.x, profile.skeletonScale.y); if (profile.canMirror) this.mirror = UnityEngine.Random.value<0.5f; } From e9fb9f23f050c82995dc4b0e90c801cd3fea36d2 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Sat, 28 Jan 2023 19:14:43 -0500 Subject: [PATCH 27/58] removed more features that have been moved from MathematicalSpiral to SpiralManipulator --- .../Props/NomaiText/NomaiTextArcBuilder.cs | 35 +++---------------- 1 file changed, 5 insertions(+), 30 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs index 68d21b3d..79a5e034 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs @@ -45,8 +45,7 @@ namespace NewHorizons.Builder.Props var norm = m.skeleton[1] - m.skeleton[0]; float r = Mathf.Atan2(-norm.y, norm.x) * Mathf.Rad2Deg; - if (m.mirror) r += 180; - var ang = m.mirror ? 90-r : -90-r; + var ang = -90-r; // using m.sharedMesh causes old meshes to disappear for some reason, idk why var mesh = g.GetComponent().mesh; @@ -285,18 +284,16 @@ namespace NewHorizons.Builder.Props // public class MathematicalSpiral { - public bool mirror; public float a; - public float b; // 0.3-0.6 + public float b; public float startSOnParent; public float scale; - public List children; public float x; public float y; public float ang; - public float startS = 42.87957f; // go all the way down to 0, all the way up to 50 + public float startS = 42.87957f; public float endS = 342.8796f; SpiralProfile profile; @@ -307,15 +304,12 @@ namespace NewHorizons.Builder.Props this.Randomize(); } - public MathematicalSpiral(float startSOnParent = 0, bool mirror = false, float len = 300, float a = 0.5f, float b = 0.43f, float scale = 0.01f) { - this.mirror = mirror; + public MathematicalSpiral(float startSOnParent = 0, float len = 300, float a = 0.5f, float b = 0.43f, float scale = 0.01f) { this.a = a; this.b = b; this.startSOnParent = startSOnParent; this.scale = scale; - this.children = new List(); - this.x = 0; this.y = 0; this.ang = 0; @@ -327,8 +321,6 @@ namespace NewHorizons.Builder.Props this.startS = UnityEngine.Random.Range(profile.endS.x, profile.endS.y); // idk why I flipped these, please don't hate me this.endS = UnityEngine.Random.Range(profile.startS.x, profile.startS.y); this.scale = UnityEngine.Random.Range(profile.skeletonScale.x, profile.skeletonScale.y); - - if (profile.canMirror) this.mirror = UnityEngine.Random.value<0.5f; } internal virtual void updateChild(MathematicalSpiral child) { @@ -338,19 +330,7 @@ namespace NewHorizons.Builder.Props var cang = pointAndNormal.z; child.x = cx; child.y = cy; - child.ang = cang + (child.mirror ? Mathf.PI : 0); - } - - public virtual void addChild(MathematicalSpiral child) { - updateChild(child); - this.children.Add(child); - } - - public virtual void updateChildren() { - this.children.ForEach(child => { - updateChild(child); - child.updateChildren(); - }); + child.ang = cang; } // note: each Vector3 in this list is of form @@ -433,11 +413,6 @@ namespace NewHorizons.Builder.Props var y = point.y; var ang = normalAngle(t); - if (mirror) { - x = x + 2 * (startX - x); - ang = -ang + Mathf.PI; - } - // translate so that startPoint is at (0,0) // (also scale the spiral) var retX = scale * (x - startX); From 4ecfe3d5c9078230194b0e9f2ba404e2640c8a22 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Sat, 28 Jan 2023 19:19:59 -0500 Subject: [PATCH 28/58] removed commented out code and dead code --- .../Props/NomaiText/NomaiTextArcArranger.cs | 15 ++++----------- .../Props/NomaiText/NomaiTextArcBuilder.cs | 14 +++----------- 2 files changed, 7 insertions(+), 22 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs index 4cdcf09a..e14369e3 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs @@ -14,9 +14,9 @@ namespace NewHorizons.Builder.Props private static int MAX_MOVE_DISTANCE = 2; - public float maxX = 2.7f;//3; - public float minX = -2.7f;//-3; - public float maxY = 2.6f;//2.75f; + public float maxX = 2.7f; + public float minX = -2.7f; + public float maxY = 2.6f; public float minY = -1f; public void DrawBoundsWithDebugSpheres() @@ -166,13 +166,8 @@ namespace NewHorizons.Builder.Props { if (reverseToposortedSpirals == null) GenerateReverseToposort(); - // TODO: fix LocationB not making spiral orange - Dictionary childForces = new Dictionary(); - //Debug.Log(reverseToposortedSpirals.Count); - //Debug.Log( string.Join(", ", reverseToposortedSpirals.Select(hmslnk => hmslnk.gameObject.name) ) ); - var index = -1; foreach (var s1 in reverseToposortedSpirals) // treating the conversation like a tree datastructure, move "leaf" spirals first so that we can propogate their force up to the parents { @@ -198,8 +193,6 @@ namespace NewHorizons.Builder.Props if (s1 == s2) continue; if (s1.parent == s2) continue; if (s1 == s2.parent) continue; - - //if (!Overlap(s1, s2)) continue; // push away from other spirals var f = (s2.center - s1.center); @@ -259,7 +252,7 @@ namespace NewHorizons.Builder.Props var bestPointDistance = 99999999f; for (var j = SpiralManipulator.MIN_PARENT_POINT; j < SpiralManipulator.MAX_PARENT_POINT; j++) { - // skip this point if it's already occupied by ANOTHER spiral (if it's occupied by this spiral, DO count it) + // skip this point if it's already occupied by another spiral (if it's occupied by this spiral, don't skip it) if (j != spiral._parentPointIndex && spiral.parent.occupiedParentPoints.Contains(j)) continue; var point = parentPoints[j]; diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs index 79a5e034..7f07f26d 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs @@ -68,8 +68,6 @@ namespace NewHorizons.Builder.Props .ToArray(); owNomaiTextLine._points = _points; - //owNomaiTextLine._state = NomaiTextLine.VisualState.HIDDEN; - //owNomaiTextLine._textLineLocation = NomaiText.Location.UNSPECIFIED; owNomaiTextLine._active = true; owNomaiTextLine._prebuilt = false; @@ -86,7 +84,6 @@ namespace NewHorizons.Builder.Props public struct SpiralProfile { // all of the Vector2 params here refer to a range of valid values public string profileName; - public bool canMirror; public Vector2 a; public Vector2 b; public Vector2 startS; @@ -101,7 +98,6 @@ namespace NewHorizons.Builder.Props public static SpiralProfile adultSpiralProfile = new SpiralProfile() { profileName="Adult", - canMirror = false, // we don't want to mirror the actual mesh itself anymore, we'll just mirror the game object using localScale.x a = new Vector2(0.5f, 0.5f), b = new Vector2(0.3f, 0.6f), startS = new Vector2(342.8796f, 342.8796f), @@ -114,13 +110,10 @@ namespace NewHorizons.Builder.Props uvScale = 4.9f, }; - // TODO: make a property called something like "sync random ranges" that makes a, b, etc, all be sampled with the same random number, isntead of being independently sampled - // This will allow child spirals to be set up to be more authentic public static SpiralProfile childSpiralProfile = new SpiralProfile() { profileName="Child", - canMirror = false, // we don't want to mirror the actual mesh itself anymore, we'll just mirror the game object using localScale.x a = new Vector2(0.9f, 0.9f), - b = new Vector2(0.17f, 0.4f), //new Vector2(0.305f, 0.4f), + b = new Vector2(0.17f, 0.4f), startS = new Vector2(342.8796f, 342.8796f), endS = new Vector2(35f, 25f), skeletonScale = 0.8f * new Vector2(0.01f, 0.01f), @@ -141,7 +134,6 @@ namespace NewHorizons.Builder.Props // I think this'll do it public static SpiralProfile strangerSpiralProfile = new SpiralProfile() { profileName="Stranger", - canMirror = false, a = new Vector2(0.9f, 0.9f), // this value doesn't really matter for this b = new Vector2(5f, 5f), startS = new Vector2(1.8505f, 1.8505f), @@ -169,8 +161,8 @@ namespace NewHorizons.Builder.Props public int numSkeletonPoints = 51; // seems to be Mobius' default public float innerWidth = 0.001f; // width at the tip - public float outerWidth = 0.05f; //0.107f; // width at the base - public float uvScale = 4.9f; //2.9f; + public float outerWidth = 0.05f; // width at the base + public float uvScale = 4.9f; private float baseUVScale = 1f / 300f; public float uvOffset = 0; From d5770df27d1b1ab774cd355f5a18657de2691a4e Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Sat, 28 Jan 2023 22:43:44 -0500 Subject: [PATCH 29/58] replaced static PlaceChildOnParentPoint with non static PlaceOnParentPoint because that just makes so much more sense --- .../Props/NomaiText/NomaiTextArcArranger.cs | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs index e14369e3..ccb038aa 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs @@ -275,7 +275,7 @@ namespace NewHorizons.Builder.Props // actually move the spiral // - SpiralManipulator.PlaceChildOnParentPoint(spiral, spiral.parent, bestPointIndex); + spiral.PlaceOnParentPoint(bestPointIndex); // // Ensure the spiral has not moved out of bounds, and if it has, move it back in bounds @@ -297,7 +297,7 @@ namespace NewHorizons.Builder.Props { // if we couldn't put it inside the bounds, put it back how we found it (this increases stability of the rest of the spirals) if (s1.Mirrored != originalMirror) s1.Mirror(); - SpiralManipulator.PlaceChildOnParentPoint(s1, s1.parent, start); + s1.PlaceOnParentPoint(start); Debug.LogWarning("Unable to place spiral " + s1.gameObject.name + " within bounds."); } } @@ -317,13 +317,13 @@ namespace NewHorizons.Builder.Props { if (start-i >= SpiralManipulator.MIN_PARENT_POINT) { - SpiralManipulator.PlaceChildOnParentPoint(s1, s1.parent, start-i); + s1.PlaceOnParentPoint(start-i); if (!OutsideBounds(s1)) return true; } if (start+i <= SpiralManipulator.MAX_PARENT_POINT) { - SpiralManipulator.PlaceChildOnParentPoint(s1, s1.parent, start+i); + s1.PlaceOnParentPoint(start+i); if (!OutsideBounds(s1)) return true; } } @@ -374,9 +374,9 @@ namespace NewHorizons.Builder.Props var index = Random.Range(MIN_PARENT_POINT, MAX_PARENT_POINT); prebuiltChild.transform.parent = this.transform.parent; var child = prebuiltChild.gameObject.GetAddComponent(); - PlaceChildOnParentPoint(child, this, index); - child.parent = this; + child.PlaceOnParentPoint(index); + this.children.Add(child); return child; } @@ -384,22 +384,22 @@ namespace NewHorizons.Builder.Props public void Mirror() { this.transform.localScale = new Vector3(-this.transform.localScale.x, 1, 1); - if (this.parent != null) SpiralManipulator.PlaceChildOnParentPoint(this, this.parent, this._parentPointIndex); + if (this.parent != null) this.PlaceOnParentPoint(this._parentPointIndex); } public void UpdateChildren() { foreach(var child in this.children) { - PlaceChildOnParentPoint(child, this, child._parentPointIndex); + child.PlaceOnParentPoint(child._parentPointIndex); } } - public static int PlaceChildOnParentPoint(SpiralManipulator child, SpiralManipulator parent, int parentPointIndex, bool updateChildren=true) + public int PlaceOnParentPoint(int parentPointIndex, bool updateChildren=true) { // track which points on the parent are being occupied - if (child._parentPointIndex != -1) parent.occupiedParentPoints.Remove(child._parentPointIndex); - child._parentPointIndex = parentPointIndex; // just in case this function was called without setting this value + if (this._parentPointIndex != -1) parent.occupiedParentPoints.Remove(this._parentPointIndex); + this._parentPointIndex = parentPointIndex; // just in case this function was called without setting this value parent.occupiedParentPoints.Add(parentPointIndex); // get the parent's points and make parentPointIndex valid @@ -412,18 +412,18 @@ namespace NewHorizons.Builder.Props float rot = Mathf.Atan2(normal.y, normal.x) * Mathf.Rad2Deg; if (parent.transform.localScale.x < 0) rot += 180; // account for mirroring again (without doing this, the normal points inward on mirrored spirals, instead of outward) - // get the location the child spiral should be at (and yet again account for mirroring) + // get the location this spiral should be at (and yet again account for mirroring) var point = _points[parentPointIndex]; if (parent.transform.localScale.x < 0) point = new Vector3(-point.x, point.y, point.z); - // set the child's position and rotation according to calculations - child.transform.localPosition = Quaternion.Euler(0, 0, parent.transform.localEulerAngles.z) * point + parent.transform.localPosition; - child.transform.localEulerAngles = new Vector3(0, 0, rot + parent.transform.localEulerAngles.z); + // set the position and rotation according to calculations + this.transform.localPosition = Quaternion.Euler(0, 0, parent.transform.localEulerAngles.z) * point + parent.transform.localPosition; + this.transform.localEulerAngles = new Vector3(0, 0, rot + parent.transform.localEulerAngles.z); // recursive update on all children so they move along with the parent - if (updateChildren) + if (updateChildren) { - child.UpdateChildren(); + this.UpdateChildren(); } return parentPointIndex; From 3fa1e867fcfa79b732994c58ea90f4e24cca5d43 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Sat, 28 Jan 2023 22:44:17 -0500 Subject: [PATCH 30/58] organized code into regions and made BuildSpiralGameObject more readable --- .../Props/NomaiText/NomaiTextArcBuilder.cs | 74 +++++++------------ 1 file changed, 27 insertions(+), 47 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs index 7f07f26d..19d72dfd 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs @@ -5,22 +5,9 @@ using UnityEngine; namespace NewHorizons.Builder.Props { - public static class NomaiTextArcBuilder { - public static int i = 0; - public static bool removeBakedInRotationAndPosition = true; - - public static void PlaceAdult() - { - BuildSpiralGameObject(adultSpiralProfile, "Text Arc Prefab " + (i++)); - } - public static void PlaceChild() - { - BuildSpiralGameObject(childSpiralProfile, "Text Arc Prefab " + (i++)); - } - public static void PlaceStranger() - { - BuildSpiralGameObject(strangerSpiralProfile, "Text Arc Prefab " + (i++)); - } + public static class NomaiTextArcBuilder { + // TODO: stranger arcs + // TODO: swap endS with startS wherever it needs to be swapped public static GameObject BuildSpiralGameObject(SpiralProfile profile, string goName="New Nomai Spiral") { @@ -40,7 +27,7 @@ namespace NewHorizons.Builder.Props var owNomaiTextLine = g.AddComponent(); // - // rotate mesh to face up + // rotate mesh to point up // var norm = m.skeleton[1] - m.skeleton[0]; @@ -49,23 +36,20 @@ namespace NewHorizons.Builder.Props // using m.sharedMesh causes old meshes to disappear for some reason, idk why var mesh = g.GetComponent().mesh; - if (removeBakedInRotationAndPosition) - { - var meshCopy = mesh; - var newVerts = meshCopy.vertices.Select(v => Quaternion.Euler(-90, 0, 0) * Quaternion.Euler(0, ang, 0) * v).ToArray(); - meshCopy.vertices = newVerts; - meshCopy.RecalculateBounds(); - } + var newVerts = mesh.vertices.Select(v => Quaternion.Euler(-90, 0, 0) * Quaternion.Euler(0, ang, 0) * v).ToArray(); + mesh.vertices = newVerts; + mesh.RecalculateBounds(); + + // rotate the skeleton to point up, too + var _points = m.skeleton + .Select((point) => + Quaternion.Euler(-90, 0, 0) * Quaternion.Euler(0, ang, 0) * (new Vector3(point.x, 0, point.y)) + ) + .ToArray(); // // set up NomaiTextArc stuff // - - var _points = m.skeleton - .Select((compiled) => - Quaternion.Euler(-90, 0, 0) * Quaternion.Euler(0, ang, 0) * (new Vector3(compiled.x, 0, compiled.y)) // decompile them, rotate them by ang, and then rotate them to be vertical, like the base game spirals are - ) - .ToArray(); owNomaiTextLine._points = _points; owNomaiTextLine._active = true; @@ -74,12 +58,8 @@ namespace NewHorizons.Builder.Props g.SetActive(true); return g; } - - // - // - // Handle the connection between game objects and spiral meshes - // - // + + #region spiral shape definitions public struct SpiralProfile { // all of the Vector2 params here refer to a range of valid values @@ -147,12 +127,11 @@ namespace NewHorizons.Builder.Props }; - - // - // - // Construct spiral meshes from the mathematical spirals generated below - // - // + + + #endregion spiral shape definitions + + #region mesh generation public class SpiralMesh: MathematicalSpiral { public List skeleton; @@ -268,12 +247,10 @@ namespace NewHorizons.Builder.Props mesh.RecalculateBounds(); } } + + #endregion mesh generation - // - // - // Construct the mathematical spirals that Nomai arcs are built from - // - // + #region underlying math public class MathematicalSpiral { public float a; @@ -492,5 +469,8 @@ namespace NewHorizons.Builder.Props private static float ln(float t) { return Mathf.Log(t); } + + + #endregion underlying math } } \ No newline at end of file From ab966c5f6360f85bd832783c86a5670155e4aa58 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Sat, 28 Jan 2023 22:50:39 -0500 Subject: [PATCH 31/58] improved comments and slight refactoring on PlaceOnParentPoint for readability --- .../Props/NomaiText/NomaiTextArcArranger.cs | 38 +++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs index ccb038aa..6a30a6e7 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs @@ -253,7 +253,7 @@ namespace NewHorizons.Builder.Props for (var j = SpiralManipulator.MIN_PARENT_POINT; j < SpiralManipulator.MAX_PARENT_POINT; j++) { // skip this point if it's already occupied by another spiral (if it's occupied by this spiral, don't skip it) - if (j != spiral._parentPointIndex && spiral.parent.occupiedParentPoints.Contains(j)) continue; + if (j != spiral._parentPointIndex && spiral.parent.pointsOccupiedByChildren.Contains(j)) continue; var point = parentPoints[j]; point = spiral.parent.transform.TransformPoint(point); @@ -337,12 +337,14 @@ namespace NewHorizons.Builder.Props public SpiralManipulator parent; public List children = new List(); - public HashSet occupiedParentPoints = new HashSet(); + public HashSet pointsOccupiedByChildren = new HashSet(); public int _parentPointIndex = -1; public static int MIN_PARENT_POINT = 3; public static int MAX_PARENT_POINT = 26; - + + #region properties + public bool Mirrored { get { return this.transform.localScale.x < 0; } } private NomaiTextLine _NomaiTextLine; @@ -370,6 +372,8 @@ namespace NewHorizons.Builder.Props return AddChild(NomaiTextArcArranger.Place(profile, this.transform.parent.gameObject).gameObject); } + #endregion properties + public SpiralManipulator AddChild(GameObject prebuiltChild) { var index = Random.Range(MIN_PARENT_POINT, MAX_PARENT_POINT); prebuiltChild.transform.parent = this.transform.parent; @@ -397,34 +401,28 @@ namespace NewHorizons.Builder.Props public int PlaceOnParentPoint(int parentPointIndex, bool updateChildren=true) { - // track which points on the parent are being occupied - if (this._parentPointIndex != -1) parent.occupiedParentPoints.Remove(this._parentPointIndex); - this._parentPointIndex = parentPointIndex; // just in case this function was called without setting this value - parent.occupiedParentPoints.Add(parentPointIndex); - - // get the parent's points and make parentPointIndex valid + // validate var _points = parent.GetComponent().GetPoints(); parentPointIndex = Mathf.Max(0, Mathf.Min(parentPointIndex, _points.Length-1)); + + // track occupied points + if (this._parentPointIndex != -1) parent.pointsOccupiedByChildren.Remove(this._parentPointIndex); + this._parentPointIndex = parentPointIndex; + parent.pointsOccupiedByChildren.Add(parentPointIndex); - // calculate the normal at point by using the neighboring points to approximate the tangent (and account for mirroring, which means all points are actually at (-point.x, point.y) ) + // calculate the normal var normal = _points[Mathf.Min(parentPointIndex+1, _points.Length-1)] - _points[Mathf.Max(parentPointIndex-1, 0)]; - if (parent.transform.localScale.x < 0) normal = new Vector3(-normal.x, normal.y, normal.z); + if (parent.transform.localScale.x < 0) normal = new Vector3(normal.x, -normal.y, -normal.z); float rot = Mathf.Atan2(normal.y, normal.x) * Mathf.Rad2Deg; - if (parent.transform.localScale.x < 0) rot += 180; // account for mirroring again (without doing this, the normal points inward on mirrored spirals, instead of outward) - // get the location this spiral should be at (and yet again account for mirroring) + // get location of the point var point = _points[parentPointIndex]; if (parent.transform.localScale.x < 0) point = new Vector3(-point.x, point.y, point.z); - // set the position and rotation according to calculations + // finalize this.transform.localPosition = Quaternion.Euler(0, 0, parent.transform.localEulerAngles.z) * point + parent.transform.localPosition; this.transform.localEulerAngles = new Vector3(0, 0, rot + parent.transform.localEulerAngles.z); - - // recursive update on all children so they move along with the parent - if (updateChildren) - { - this.UpdateChildren(); - } + if (updateChildren) this.UpdateChildren(); return parentPointIndex; } From 8a1488a4ee0af6b0479bf9ffef1d4a8e2448c606 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Sat, 28 Jan 2023 22:53:42 -0500 Subject: [PATCH 32/58] consolidated AddChild functions --- .../Builder/Props/NomaiText/NomaiTextArcArranger.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs index 6a30a6e7..275725be 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; +using UnityEngine.Profiling; namespace NewHorizons.Builder.Props { @@ -368,16 +369,13 @@ namespace NewHorizons.Builder.Props get { return new Vector2(this.transform.position.x, this.transform.position.y); } } - public SpiralManipulator AddChild(NomaiTextArcBuilder.SpiralProfile profile) { - return AddChild(NomaiTextArcArranger.Place(profile, this.transform.parent.gameObject).gameObject); - } - #endregion properties - public SpiralManipulator AddChild(GameObject prebuiltChild) { + public SpiralManipulator AddChild(NomaiTextArcBuilder.SpiralProfile profile) { + var child = NomaiTextArcArranger.Place(profile, this.transform.parent.gameObject); + var index = Random.Range(MIN_PARENT_POINT, MAX_PARENT_POINT); - prebuiltChild.transform.parent = this.transform.parent; - var child = prebuiltChild.gameObject.GetAddComponent(); + child.transform.parent = this.transform.parent; child.parent = this; child.PlaceOnParentPoint(index); From e7534b76297548910ff5421ad34cb654e86b5971 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Sat, 28 Jan 2023 23:07:30 -0500 Subject: [PATCH 33/58] some refactoring in NomaiTextArcArranger --- .../Props/NomaiText/NomaiTextArcArranger.cs | 110 +++++------------- .../Props/NomaiText/NomaiTextBuilder.cs | 14 +-- 2 files changed, 36 insertions(+), 88 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs index 275725be..ba36a522 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.Profiling; +using Logger = NewHorizons.Utility.Logger; namespace NewHorizons.Builder.Props { @@ -45,18 +46,8 @@ namespace NewHorizons.Builder.Props reverseToposortedSpirals.Reverse(); } - public void LimitRepeatedMirrors() - { - foreach(var spiral in reverseToposortedSpirals) - { - if (spiral.Mirrored == spiral.parent?.Mirrored && spiral.Mirrored == spiral.parent?.parent?.Mirrored) - { - spiral.parent.parent.Mirror(); // flipping the grandparent causes every 3rd spiral in a row to be flipped, meaning the maximum number of spirals in a row with the same mirror is 2 - } - } - } - public static SpiralManipulator Place(NomaiTextArcBuilder.SpiralProfile profile, GameObject spiralMeshHolder) + public static SpiralManipulator CreateSpiral(NomaiTextArcBuilder.SpiralProfile profile, GameObject spiralMeshHolder) { var rootArc = NomaiTextArcBuilder.BuildSpiralGameObject(profile); rootArc.transform.parent = spiralMeshHolder.transform; @@ -72,21 +63,13 @@ namespace NewHorizons.Builder.Props return manip; } - - public void OnDrawGizmosSelected() + + // returns whether there was overlap or not + public bool AttemptOverlapResolution() { - var topLeft = new Vector3(minX, maxY) + transform.position; - var topRight = new Vector3(maxX, maxY) + transform.position; - var bottomRight = new Vector3(maxX, minY) + transform.position; - var bottomLeft = new Vector3(minX, minY) + transform.position; - Debug.DrawLine(topLeft, topRight, Color.red); - Debug.DrawLine(topRight, bottomRight, Color.red); - Debug.DrawLine(bottomRight, bottomLeft, Color.red); - Debug.DrawLine(bottomLeft, topLeft, Color.red); - } + var overlappingSpirals = FindOverlap(); + if (overlappingSpirals.x < 0) return false; - public int AttemptOverlapResolution(Vector2Int overlappingSpirals) - { if (!sprialOverlapResolutionPriority.ContainsKey(overlappingSpirals.x)) sprialOverlapResolutionPriority[overlappingSpirals.x] = 0; if (!sprialOverlapResolutionPriority.ContainsKey(overlappingSpirals.y)) sprialOverlapResolutionPriority[overlappingSpirals.y] = 0; @@ -96,10 +79,10 @@ namespace NewHorizons.Builder.Props this.spirals[mirrorIndex].Mirror(); sprialOverlapResolutionPriority[mirrorIndex]--; - return mirrorIndex; + return true; } - public Vector2Int Overlap() + public Vector2Int FindOverlap() { var index = -1; foreach (var s1 in spirals) @@ -111,14 +94,14 @@ namespace NewHorizons.Builder.Props foreach (var s2 in spirals) { jndex++; - if (Overlap(s1, s2)) return new Vector2Int(index, jndex);; + if (SpiralsOverlap(s1, s2)) return new Vector2Int(index, jndex);; } } return new Vector2Int(-1, -1); } - public bool Overlap(SpiralManipulator s1, SpiralManipulator s2) + public bool SpiralsOverlap(SpiralManipulator s1, SpiralManipulator s2) { if (s1 == s2) return false; if (Vector3.Distance(s1.center, s2.center) > Mathf.Max(s1.NomaiTextLine.GetWorldRadius(), s2.NomaiTextLine.GetWorldRadius())) return false; // no overlap possible - too far away @@ -142,7 +125,6 @@ namespace NewHorizons.Builder.Props return false; } - public bool OutsideBounds(SpiralManipulator spiral) { @@ -161,41 +143,30 @@ namespace NewHorizons.Builder.Props return false; } - - public void Step() + public void FDGSimulationStep() { if (reverseToposortedSpirals == null) GenerateReverseToposort(); Dictionary childForces = new Dictionary(); - var index = -1; foreach (var s1 in reverseToposortedSpirals) // treating the conversation like a tree datastructure, move "leaf" spirals first so that we can propogate their force up to the parents { - index++; - Vector2 force = Vector2.zero; - // - // Calculate the force s1 should experience from its children - // - + // accumulate the force the children feel if (childForces.ContainsKey(s1)) { force += 0.9f * childForces[s1]; } - // - // Calculate the force s1 should experience from fellow spirals - // - + // push away from fellow spirals foreach (var s2 in spirals) { if (s1 == s2) continue; if (s1.parent == s2) continue; if (s1 == s2.parent) continue; - - // push away from other spirals + var f = (s2.center - s1.center); force -= f / Mathf.Pow(f.magnitude, 6); @@ -203,10 +174,7 @@ namespace NewHorizons.Builder.Props force -= f2 / Mathf.Pow(f2.magnitude, 6); } - // // push away from the edges - // - var MAX_EDGE_PUSH_FORCE = 1; force += new Vector2(0, -1) * Mathf.Max(0, (s1.transform.localPosition.y + maxY)*(MAX_EDGE_PUSH_FORCE / maxY) - MAX_EDGE_PUSH_FORCE); force += new Vector2(0, 1) * Mathf.Max(0, (s1.transform.localPosition.y + minY)*(MAX_EDGE_PUSH_FORCE / minY) - MAX_EDGE_PUSH_FORCE); @@ -216,19 +184,12 @@ namespace NewHorizons.Builder.Props // push up just to make everything a little more pretty (this is not neccessary to get an arrangement that simply has no overlap/spirals exiting the bounds) force += new Vector2(0, 1) * 1; - // // renormalize the force magnitude (keeps force sizes reasonable, and improves stability in the case of small forces) - // - var avg = 1; // the size of vector required to get a medium push var scale = 0.75f; force = force.normalized * scale * (1 / (1 + Mathf.Exp(avg-force.magnitude)) - 1 / (1 + Mathf.Exp(avg))); // apply a sigmoid-ish smoothing operation, so only giant forces actually move the spirals - - // - // if this is the root spiral, then rotate it instead of trying to move it (what the rest of the code does) - // - + // if this is the root spiral, then rotate it instead of trying to move it if (s1.parent == null) { // this is the root spiral, so rotate instead of moving @@ -240,20 +201,16 @@ namespace NewHorizons.Builder.Props continue; } - // - // look for the point closest to where the forces want to push this spiral - // - + // pick the parent point that's closest to center+force, and move to there var spiral = s1; var parentPoints = spiral.parent.GetComponent().GetPoints(); - // pick the parent point that's closest to center+force, and move to there var idealPoint = spiral.position + force; var bestPointIndex = 0; var bestPointDistance = 99999999f; for (var j = SpiralManipulator.MIN_PARENT_POINT; j < SpiralManipulator.MAX_PARENT_POINT; j++) { - // skip this point if it's already occupied by another spiral (if it's occupied by this spiral, don't skip it) + // don't put this spiral on a point already occupied by a sibling if (j != spiral._parentPointIndex && spiral.parent.pointsOccupiedByChildren.Contains(j)) continue; var point = parentPoints[j]; @@ -266,22 +223,13 @@ namespace NewHorizons.Builder.Props } } - // // limit the distance a spiral can move in a single step - // - bestPointIndex = spiral._parentPointIndex + Mathf.Min(MAX_MOVE_DISTANCE, Mathf.Max(-MAX_MOVE_DISTANCE, bestPointIndex - spiral._parentPointIndex)); // minimize step size to help stability - // // actually move the spiral - // - spiral.PlaceOnParentPoint(bestPointIndex); - // - // Ensure the spiral has not moved out of bounds, and if it has, move it back in bounds - // - + // Enforce bounds if (OutsideBounds(s1)) { var start = s1._parentPointIndex; @@ -290,7 +238,8 @@ namespace NewHorizons.Builder.Props var success = AttemptToPushSpiralInBounds(s1, start); if (!success) { - s1.Mirror(); // try flipping it if nothing worked with original mirror + // try flipping it if nothing worked with original mirror state + s1.Mirror(); success = AttemptToPushSpiralInBounds(s1, start); } @@ -299,14 +248,13 @@ namespace NewHorizons.Builder.Props // if we couldn't put it inside the bounds, put it back how we found it (this increases stability of the rest of the spirals) if (s1.Mirrored != originalMirror) s1.Mirror(); s1.PlaceOnParentPoint(start); - Debug.LogWarning("Unable to place spiral " + s1.gameObject.name + " within bounds."); + Logger.LogVerbose("Unable to place spiral " + s1.gameObject.name + " within bounds."); } } + // record force for parents if (!childForces.ContainsKey(s1.parent)) childForces[s1.parent] = Vector2.zero; childForces[s1.parent] += force; - - Debug.DrawRay(s1.transform.position, new Vector3(force.x, force.y, 0), Color.green); } } @@ -333,7 +281,6 @@ namespace NewHorizons.Builder.Props } } - [ExecuteInEditMode] public class SpiralManipulator : MonoBehaviour { public SpiralManipulator parent; public List children = new List(); @@ -358,21 +305,24 @@ namespace NewHorizons.Builder.Props } } - public Vector2 center { + public Vector2 center + { get { return NomaiTextLine.GetWorldCenter(); } } - public Vector2 localPosition { + public Vector2 localPosition + { get { return new Vector2(this.transform.localPosition.x, this.transform.localPosition.y); } } - public Vector2 position { + public Vector2 position + { get { return new Vector2(this.transform.position.x, this.transform.position.y); } } #endregion properties public SpiralManipulator AddChild(NomaiTextArcBuilder.SpiralProfile profile) { - var child = NomaiTextArcArranger.Place(profile, this.transform.parent.gameObject); + var child = NomaiTextArcArranger.CreateSpiral(profile, this.transform.parent.gameObject); var index = Random.Range(MIN_PARENT_POINT, MAX_PARENT_POINT); child.transform.parent = this.transform.parent; diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs index 11757404..1685e532 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs @@ -660,20 +660,18 @@ namespace NewHorizons.Builder.Props arranger.GenerateReverseToposort(); // Required before Step() is called + var overlapFound = true; for (var k = 0; k < arranger.spirals.Count*2; k++) { - var overlappingSpiralIDs = arranger.Overlap(); - if (overlappingSpiralIDs.x < 0) return; - - arranger.AttemptOverlapResolution(overlappingSpiralIDs); - for(var a = 0; a < 10; a++) arranger.Step(); + var overlapFound = arranger.AttemptOverlapResolution(); + if (!overlapFound) break; + for(var a = 0; a < 10; a++) arranger.FDGSimulationStep(); } - Logger.LogWarning("Overlap resolution failed!"); + if (overlapFound) Logger.LogWarning("Overlap resolution failed!"); // manual placement - // TODO: test this for (var j = 0; j < info.arcInfo.Length; j++) { var arcInfo = info.arcInfo[j]; @@ -715,7 +713,7 @@ namespace NewHorizons.Builder.Props } if (parent != null) arc = parent.GetComponent().AddChild(profile).gameObject; - else arc = NomaiTextArcArranger.Place(profile, conversationZone).gameObject; + else arc = NomaiTextArcArranger.CreateSpiral(profile, conversationZone).gameObject; if (mat != null) arc.GetComponent().sharedMaterial = mat; From d5e771709f61f128897c0e6f3b82b8c5678262c7 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Sat, 28 Jan 2023 23:14:19 -0500 Subject: [PATCH 34/58] more refactoring in NomaiTextArcArranger --- .../Props/NomaiText/NomaiTextArcArranger.cs | 235 ++++++++++-------- .../Props/NomaiText/NomaiTextBuilder.cs | 4 +- 2 files changed, 126 insertions(+), 113 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs index ba36a522..4fad0fda 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs @@ -9,44 +9,20 @@ using Logger = NewHorizons.Utility.Logger; namespace NewHorizons.Builder.Props { public class NomaiTextArcArranger : MonoBehaviour { + private static int MAX_MOVE_DISTANCE = 2; + public List spirals = new List(); public List reverseToposortedSpirals = null; - public SpiralManipulator root { get; private set; } + private bool updateToposortOnNextStep = true; private Dictionary sprialOverlapResolutionPriority = new Dictionary(); - private static int MAX_MOVE_DISTANCE = 2; + public SpiralManipulator root { get; private set; } public float maxX = 2.7f; public float minX = -2.7f; public float maxY = 2.6f; public float minY = -1f; - public void DrawBoundsWithDebugSpheres() - { - AddDebugShape.AddSphere(this.gameObject, 0.1f, Color.green).transform.localPosition = new Vector3(minX, minY, 0); - AddDebugShape.AddSphere(this.gameObject, 0.1f, Color.green).transform.localPosition = new Vector3(minX, maxY, 0); - AddDebugShape.AddSphere(this.gameObject, 0.1f, Color.green).transform.localPosition = new Vector3(maxX, maxY, 0); - AddDebugShape.AddSphere(this.gameObject, 0.1f, Color.green).transform.localPosition = new Vector3(maxX, minY, 0); - AddDebugShape.AddSphere(this.gameObject, 0.1f, Color.red).transform.localPosition = new Vector3(0, 0, 0); - } - - public void GenerateReverseToposort() - { - reverseToposortedSpirals = new List(); - Queue frontierQueue = new Queue(); - frontierQueue.Enqueue(root); - - while(frontierQueue.Count > 0) - { - var spiral = frontierQueue.Dequeue(); - reverseToposortedSpirals.Add(spiral); - - foreach(var child in spiral.children) frontierQueue.Enqueue(child); - } - - reverseToposortedSpirals.Reverse(); - } - public static SpiralManipulator CreateSpiral(NomaiTextArcBuilder.SpiralProfile profile, GameObject spiralMeshHolder) { var rootArc = NomaiTextArcBuilder.BuildSpiralGameObject(profile); @@ -57,96 +33,21 @@ namespace NewHorizons.Builder.Props if (Random.value < 0.5) manip.transform.localScale = new Vector3(-1, 1, 1); // randomly mirror // add to arranger - var arranger = spiralMeshHolder.GetComponent(); + var arranger = spiralMeshHolder.GetAddComponent(); if (arranger.root == null) arranger.root = manip; arranger.spirals.Add(manip); + arranger.updateToposortOnNextStep = true; return manip; } - // returns whether there was overlap or not - public bool AttemptOverlapResolution() - { - var overlappingSpirals = FindOverlap(); - if (overlappingSpirals.x < 0) return false; - - if (!sprialOverlapResolutionPriority.ContainsKey(overlappingSpirals.x)) sprialOverlapResolutionPriority[overlappingSpirals.x] = 0; - if (!sprialOverlapResolutionPriority.ContainsKey(overlappingSpirals.y)) sprialOverlapResolutionPriority[overlappingSpirals.y] = 0; - - int mirrorIndex = overlappingSpirals.x; - if (sprialOverlapResolutionPriority[overlappingSpirals.y] > sprialOverlapResolutionPriority[overlappingSpirals.x]) mirrorIndex = overlappingSpirals.y; - - this.spirals[mirrorIndex].Mirror(); - sprialOverlapResolutionPriority[mirrorIndex]--; - - return true; - } - - public Vector2Int FindOverlap() - { - var index = -1; - foreach (var s1 in spirals) - { - index++; - if (s1.parent == null) continue; - - var jndex = -1; - foreach (var s2 in spirals) - { - jndex++; - if (SpiralsOverlap(s1, s2)) return new Vector2Int(index, jndex);; - } - } - - return new Vector2Int(-1, -1); - } - - public bool SpiralsOverlap(SpiralManipulator s1, SpiralManipulator s2) - { - if (s1 == s2) return false; - if (Vector3.Distance(s1.center, s2.center) > Mathf.Max(s1.NomaiTextLine.GetWorldRadius(), s2.NomaiTextLine.GetWorldRadius())) return false; // no overlap possible - too far away - - var s1Points = s1.NomaiTextLine.GetPoints().Select(p => s1.transform.TransformPoint(p)).ToList(); - var s2Points = s2.NomaiTextLine.GetPoints().Select(p => s2.transform.TransformPoint(p)).ToList(); - var s1ThresholdForOverlap = Vector3.Distance(s1Points[0], s1Points[1]); - var s2ThresholdForOverlap = Vector3.Distance(s2Points[0], s2Points[1]); - var thresholdForOverlap = Mathf.Pow(Mathf.Max(s1ThresholdForOverlap, s2ThresholdForOverlap), 2); // square to save on computation (we'll work in distance squared from here on) - - if (s1.parent == s2) s1Points.RemoveAt(0); // don't consider the base points so that we can check if children overlap their parents - if (s2.parent == s1) s2Points.RemoveAt(0); // (note: the base point of a child is always exactly overlapping with one of the parent's points) - - foreach(var p1 in s1Points) - { - foreach(var p2 in s2Points) - { - if (Vector3.SqrMagnitude(p1-p2) <= thresholdForOverlap) return true; // s1 and s2 overlap - } - } - - return false; - } - - public bool OutsideBounds(SpiralManipulator spiral) - { - var points = spiral.NomaiTextLine.GetPoints() - .Select(p => spiral.transform.TransformPoint(p)) - .Select(p => spiral.transform.parent.InverseTransformPoint(p)) - .ToList(); - - foreach(var point in points) { - if (point.x < minX || point.x > maxX || - point.y < minY || point.y > maxY) - { - return true; - } - } - - return false; - } - public void FDGSimulationStep() { - if (reverseToposortedSpirals == null) GenerateReverseToposort(); + if (updateToposortOnNextStep) + { + updateToposortOnNextStep = false; + GenerateReverseToposort(); + } Dictionary childForces = new Dictionary(); @@ -258,6 +159,109 @@ namespace NewHorizons.Builder.Props } } + public void GenerateReverseToposort() + { + reverseToposortedSpirals = new List(); + Queue frontierQueue = new Queue(); + frontierQueue.Enqueue(root); + + while(frontierQueue.Count > 0) + { + var spiral = frontierQueue.Dequeue(); + reverseToposortedSpirals.Add(spiral); + + foreach(var child in spiral.children) frontierQueue.Enqueue(child); + } + + reverseToposortedSpirals.Reverse(); + } + + #region overlap handling + + // returns whether there was overlap or not + public bool AttemptOverlapResolution() + { + var overlappingSpirals = FindOverlap(); + if (overlappingSpirals.x < 0) return false; + + if (!sprialOverlapResolutionPriority.ContainsKey(overlappingSpirals.x)) sprialOverlapResolutionPriority[overlappingSpirals.x] = 0; + if (!sprialOverlapResolutionPriority.ContainsKey(overlappingSpirals.y)) sprialOverlapResolutionPriority[overlappingSpirals.y] = 0; + + int mirrorIndex = overlappingSpirals.x; + if (sprialOverlapResolutionPriority[overlappingSpirals.y] > sprialOverlapResolutionPriority[overlappingSpirals.x]) mirrorIndex = overlappingSpirals.y; + + this.spirals[mirrorIndex].Mirror(); + sprialOverlapResolutionPriority[mirrorIndex]--; + + return true; + } + + public Vector2Int FindOverlap() + { + var index = -1; + foreach (var s1 in spirals) + { + index++; + if (s1.parent == null) continue; + + var jndex = -1; + foreach (var s2 in spirals) + { + jndex++; + if (SpiralsOverlap(s1, s2)) return new Vector2Int(index, jndex);; + } + } + + return new Vector2Int(-1, -1); + } + + public bool SpiralsOverlap(SpiralManipulator s1, SpiralManipulator s2) + { + if (s1 == s2) return false; + if (Vector3.Distance(s1.center, s2.center) > Mathf.Max(s1.NomaiTextLine.GetWorldRadius(), s2.NomaiTextLine.GetWorldRadius())) return false; // no overlap possible - too far away + + var s1Points = s1.NomaiTextLine.GetPoints().Select(p => s1.transform.TransformPoint(p)).ToList(); + var s2Points = s2.NomaiTextLine.GetPoints().Select(p => s2.transform.TransformPoint(p)).ToList(); + var s1ThresholdForOverlap = Vector3.Distance(s1Points[0], s1Points[1]); + var s2ThresholdForOverlap = Vector3.Distance(s2Points[0], s2Points[1]); + var thresholdForOverlap = Mathf.Pow(Mathf.Max(s1ThresholdForOverlap, s2ThresholdForOverlap), 2); // square to save on computation (we'll work in distance squared from here on) + + if (s1.parent == s2) s1Points.RemoveAt(0); // don't consider the base points so that we can check if children overlap their parents + if (s2.parent == s1) s2Points.RemoveAt(0); // (note: the base point of a child is always exactly overlapping with one of the parent's points) + + foreach(var p1 in s1Points) + { + foreach(var p2 in s2Points) + { + if (Vector3.SqrMagnitude(p1-p2) <= thresholdForOverlap) return true; // s1 and s2 overlap + } + } + + return false; + } + + #endregion overlap handling + + #region bounds handling + + public bool OutsideBounds(SpiralManipulator spiral) + { + var points = spiral.NomaiTextLine.GetPoints() + .Select(p => spiral.transform.TransformPoint(p)) + .Select(p => spiral.transform.parent.InverseTransformPoint(p)) + .ToList(); + + foreach(var point in points) { + if (point.x < minX || point.x > maxX || + point.y < minY || point.y > maxY) + { + return true; + } + } + + return false; + } + private bool AttemptToPushSpiralInBounds(SpiralManipulator s1, int start) { var range = Mathf.Max(start-SpiralManipulator.MIN_PARENT_POINT, SpiralManipulator.MAX_PARENT_POINT-start); @@ -279,6 +283,17 @@ namespace NewHorizons.Builder.Props return false; } + + public void DrawBoundsWithDebugSpheres() + { + AddDebugShape.AddSphere(this.gameObject, 0.1f, Color.green).transform.localPosition = new Vector3(minX, minY, 0); + AddDebugShape.AddSphere(this.gameObject, 0.1f, Color.green).transform.localPosition = new Vector3(minX, maxY, 0); + AddDebugShape.AddSphere(this.gameObject, 0.1f, Color.green).transform.localPosition = new Vector3(maxX, maxY, 0); + AddDebugShape.AddSphere(this.gameObject, 0.1f, Color.green).transform.localPosition = new Vector3(maxX, minY, 0); + AddDebugShape.AddSphere(this.gameObject, 0.1f, Color.red).transform.localPosition = new Vector3(0, 0, 0); + } + + #endregion bounds handling } public class SpiralManipulator : MonoBehaviour { diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs index 1685e532..e43b0c75 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs @@ -658,12 +658,10 @@ namespace NewHorizons.Builder.Props // auto placement - arranger.GenerateReverseToposort(); // Required before Step() is called - var overlapFound = true; for (var k = 0; k < arranger.spirals.Count*2; k++) { - var overlapFound = arranger.AttemptOverlapResolution(); + overlapFound = arranger.AttemptOverlapResolution(); if (!overlapFound) break; for(var a = 0; a < 10; a++) arranger.FDGSimulationStep(); } From 932b0bf3e8b98077b425a86a9e00819194dca4c2 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Sat, 28 Jan 2023 23:19:42 -0500 Subject: [PATCH 35/58] light refactoring --- .../Props/NomaiText/NomaiTextBuilder.cs | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs index e43b0c75..437b98fb 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs @@ -631,9 +631,7 @@ namespace NewHorizons.Builder.Props var arranger = nomaiWallText.gameObject.AddComponent(); - // - // Generate spirals - // + // Generate spiral meshes/GOs var i = 0; foreach (var textData in dict.Values) @@ -645,16 +643,12 @@ namespace NewHorizons.Builder.Props var parent = parentID == -1 ? null : arcsByID[parentID]; GameObject arc = MakeArc(arcInfo, conversationZone, parent, textEntryID); - arc.name = $"Arc {i} - Child of {parentID}"; + arc.name = $"Arc {textEntryID} - Child of {parentID}"; arcsByID.Add(textEntryID, arc); i++; } - - // - // place spirals - // // auto placement @@ -666,7 +660,7 @@ namespace NewHorizons.Builder.Props for(var a = 0; a < 10; a++) arranger.FDGSimulationStep(); } - if (overlapFound) Logger.LogWarning("Overlap resolution failed!"); + if (overlapFound) Logger.LogVerbose("Overlap resolution failed!"); // manual placement @@ -683,7 +677,7 @@ namespace NewHorizons.Builder.Props arc.transform.localRotation = Quaternion.Euler(0, 0, arcInfo.zRotation); if (arcInfo.mirror) arc.transform.localScale = new Vector3(-1, 1, 1); - else arc.transform.localScale = new Vector3( 1, 1, 1); + else arc.transform.localScale = new Vector3( 1, 1, 1); } } @@ -691,8 +685,8 @@ namespace NewHorizons.Builder.Props { GameObject arc; var type = arcInfo != null ? arcInfo.type : PropModule.NomaiTextArcInfo.NomaiTextArcType.Adult; - NomaiTextArcBuilder.SpiralProfile profile = new(); - Material mat = null; + NomaiTextArcBuilder.SpiralProfile profile; + Material mat; switch (type) { case PropModule.NomaiTextArcInfo.NomaiTextArcType.Child: @@ -711,7 +705,7 @@ namespace NewHorizons.Builder.Props } if (parent != null) arc = parent.GetComponent().AddChild(profile).gameObject; - else arc = NomaiTextArcArranger.CreateSpiral(profile, conversationZone).gameObject; + else arc = NomaiTextArcArranger.CreateSpiral(profile, conversationZone).gameObject; if (mat != null) arc.GetComponent().sharedMaterial = mat; From 155b7869bb135459e8880d386de3c419e5a0baa7 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Sat, 28 Jan 2023 23:30:19 -0500 Subject: [PATCH 36/58] fixed oob error with stranger arcs --- NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs index 4fad0fda..d05bda58 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs @@ -109,7 +109,7 @@ namespace NewHorizons.Builder.Props var idealPoint = spiral.position + force; var bestPointIndex = 0; var bestPointDistance = 99999999f; - for (var j = SpiralManipulator.MIN_PARENT_POINT; j < SpiralManipulator.MAX_PARENT_POINT; j++) + for (var j = SpiralManipulator.MIN_PARENT_POINT; j < SpiralManipulator.MAX_PARENT_POINT && j < parentPoints.Length; j++) { // don't put this spiral on a point already occupied by a sibling if (j != spiral._parentPointIndex && spiral.parent.pointsOccupiedByChildren.Contains(j)) continue; From cca1afe0344c9750cb44c4fcc189ff78062a21c4 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Sun, 29 Jan 2023 10:45:17 -0500 Subject: [PATCH 37/58] finally switched startS and endS so they're consistent --- .../Props/NomaiText/NomaiTextArcBuilder.cs | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs index 19d72dfd..5867189d 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs @@ -205,11 +205,11 @@ namespace NewHorizons.Builder.Props triangles.Add(i + 1); } - var startT = tFromArcLen(startS); - var endT = tFromArcLen(endS); + var startT = tFromArcLen(endS); + var endT = tFromArcLen(startS); var rangeT = endT - startT; - var rangeS = endS - startS; + var rangeS = startS - endS; Vector2[] uvs = new Vector2[newVerts.Length]; Vector2[] uv2s = new Vector2[newVerts.Length]; @@ -222,8 +222,8 @@ namespace NewHorizons.Builder.Props // will cluster points in areas of higher detail. this is the way Mobius does it, so it is the way we also will do it float inputT = startT + rangeT * fraction; float inputS = tToArcLen(inputT); - float sFraction = (inputS - startS) / rangeS; - float absoluteS = (inputS - startS); + float sFraction = (inputS - endS) / rangeS; + float absoluteS = (inputS - endS); float u = absoluteS * uvScale * baseUVScale + uvOffset; uvs[i * 2] = new Vector2(u, 0); @@ -252,6 +252,14 @@ namespace NewHorizons.Builder.Props #region underlying math + // NOTE: startS is greater than endS because the math equation traces the spiral outward - it starts at the center + // and winds its way out. However, since we want to think of the least curly part as the start, that means we + // start at a higher S and end at a lower S + // + // note: t refers to theta, and s refers to arc length + // + // All this math is based off this Desmos graph I made. Play around with it if something doesn't make sense :) + // https://www.desmos.com/calculator/9gdfgyuzf6 public class MathematicalSpiral { public float a; public float b; @@ -262,8 +270,8 @@ namespace NewHorizons.Builder.Props public float y; public float ang; - public float startS = 42.87957f; - public float endS = 342.8796f; + public float endS = 42.87957f; + public float startS = 342.8796f; SpiralProfile profile; @@ -287,8 +295,8 @@ namespace NewHorizons.Builder.Props public virtual void Randomize() { this.a = UnityEngine.Random.Range(profile.a.x, profile.a.y); this.b = UnityEngine.Random.Range(profile.b.x, profile.b.y); - this.startS = UnityEngine.Random.Range(profile.endS.x, profile.endS.y); // idk why I flipped these, please don't hate me - this.endS = UnityEngine.Random.Range(profile.startS.x, profile.startS.y); + this.endS = UnityEngine.Random.Range(profile.endS.x, profile.endS.y); + this.startS = UnityEngine.Random.Range(profile.startS.x, profile.startS.y); this.scale = UnityEngine.Random.Range(profile.skeletonScale.x, profile.skeletonScale.y); } @@ -338,8 +346,8 @@ namespace NewHorizons.Builder.Props // generate a list of evenly distributed over the whole spiral. `numPoints` number of pairs are generated public IEnumerable WalkAlongSpiral(int numPoints) { - var endT = tFromArcLen(endS); - var startT = tFromArcLen(startS); + var endT = tFromArcLen(startS); + var startT = tFromArcLen(endS); var rangeT = endT - startT; for (int i = 0; i Date: Mon, 30 Jan 2023 19:00:57 -0500 Subject: [PATCH 38/58] updated some comments --- NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs index 5867189d..751a9332 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs @@ -7,7 +7,9 @@ namespace NewHorizons.Builder.Props { public static class NomaiTextArcBuilder { // TODO: stranger arcs - // TODO: swap endS with startS wherever it needs to be swapped + // Note: building a wall text (making meshes and arranging) takes 0.1s for an example with 10 spirals + // TODO: caching - maybe make a "cachable" annotaion! if cache does not contain results of function, run function, write results to cache file. otherwise return results from cache file. + // cache file should be shipped with release but doesn't need to be. if debug mode is enabled, always regen cache, if click regen configs, reload cache public static GameObject BuildSpiralGameObject(SpiralProfile profile, string goName="New Nomai Spiral") { From c9e46050d28d21115cd6516fda02f1d7e6c4913a Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Mon, 30 Jan 2023 22:51:04 -0500 Subject: [PATCH 39/58] added a cache to new horizons bodies that get loaded on body creation and then released --- NewHorizons/Handlers/PlanetCreationHandler.cs | 14 +++++++-- NewHorizons/Utility/Cache.cs | 30 +++++++++++++++++++ NewHorizons/Utility/NewHorizonBody.cs | 14 +++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 NewHorizons/Utility/Cache.cs diff --git a/NewHorizons/Handlers/PlanetCreationHandler.cs b/NewHorizons/Handlers/PlanetCreationHandler.cs index e0912246..db5d7430 100644 --- a/NewHorizons/Handlers/PlanetCreationHandler.cs +++ b/NewHorizons/Handlers/PlanetCreationHandler.cs @@ -136,6 +136,8 @@ namespace NewHorizons.Handlers public static bool LoadBody(NewHorizonsBody body, bool defaultPrimaryToSun = false) { + body.LoadCache(); + // I don't remember doing this why is it exceptions what am I doing GameObject existingPlanet = null; try @@ -202,6 +204,7 @@ namespace NewHorizons.Handlers catch (Exception ex) { Logger.LogError($"Couldn't make quantum state for [{body.Config.name}]:\n{ex}"); + body.UnloadCache(); return false; } } @@ -217,6 +220,7 @@ namespace NewHorizons.Handlers catch (Exception e) { Logger.LogError($"Couldn't update body {body.Config?.name}:\n{e}"); + body.UnloadCache(); return false; } } @@ -237,8 +241,12 @@ namespace NewHorizons.Handlers { Logger.Log($"Creating [{body.Config.name}]"); var planetObject = GenerateBody(body, defaultPrimaryToSun); - if (planetObject == null) return false; - planetObject.SetActive(true); + planetObject?.SetActive(true); + if (planetObject == null) + { + body.UnloadCache(); + return false; + } var ao = planetObject.GetComponent(); @@ -250,6 +258,7 @@ namespace NewHorizons.Handlers catch (Exception e) { Logger.LogError($"Couldn't generate body {body.Config?.name}:\n{e}"); + body.UnloadCache(); return false; } } @@ -264,6 +273,7 @@ namespace NewHorizons.Handlers Logger.LogError($"Error in event handler for OnPlanetLoaded on body {body.Config.name}: {e}"); } + body.UnloadCache(); return true; } diff --git a/NewHorizons/Utility/Cache.cs b/NewHorizons/Utility/Cache.cs new file mode 100644 index 00000000..8113575e --- /dev/null +++ b/NewHorizons/Utility/Cache.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; + +namespace NewHorizons.Utility +{ + public class Cache : Dictionary + { + [NonSerialized] string filepath; + + public Cache(string cacheFilePath) + { + filepath = cacheFilePath; + var existingEntries = NewHorizons.Main.Instance.ModHelper.Storage.Load>(filepath); + + if (existingEntries == null) return; + + foreach(var entry in existingEntries) + { + this[entry.Key] = entry.Value; + } + } + + public void WriteToFile() + { + NewHorizons.Main.Instance.ModHelper.Storage.Save>(this, filepath); + } + } +} diff --git a/NewHorizons/Utility/NewHorizonBody.cs b/NewHorizons/Utility/NewHorizonBody.cs index fc9f53bd..074ccf71 100644 --- a/NewHorizons/Utility/NewHorizonBody.cs +++ b/NewHorizons/Utility/NewHorizonBody.cs @@ -17,10 +17,23 @@ namespace NewHorizons.Utility public PlanetConfig Config; public IModBehaviour Mod; + public Cache Cache; public string RelativePath; public GameObject Object; + #region Cache + public void LoadCache() + { + if (RelativePath != null) Cache = new Cache(RelativePath+".nhcache"); + } + + public void UnloadCache() + { + Cache = null; // garbage collection will take care of it + } + #endregion Cache + #region Migration private static readonly string[] _keepLoadedModsList = new string[] { @@ -45,6 +58,7 @@ namespace NewHorizons.Utility } } } + #endregion } } From c7f12c967b4f8060e90d04ab793207a24a3c040a Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Mon, 30 Jan 2023 22:52:43 -0500 Subject: [PATCH 40/58] cache now gets written to file when body building is successful --- NewHorizons/Handlers/PlanetCreationHandler.cs | 2 +- NewHorizons/Utility/NewHorizonBody.cs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/NewHorizons/Handlers/PlanetCreationHandler.cs b/NewHorizons/Handlers/PlanetCreationHandler.cs index db5d7430..6c36fdec 100644 --- a/NewHorizons/Handlers/PlanetCreationHandler.cs +++ b/NewHorizons/Handlers/PlanetCreationHandler.cs @@ -273,7 +273,7 @@ namespace NewHorizons.Handlers Logger.LogError($"Error in event handler for OnPlanetLoaded on body {body.Config.name}: {e}"); } - body.UnloadCache(); + body.UnloadCache(true); return true; } diff --git a/NewHorizons/Utility/NewHorizonBody.cs b/NewHorizons/Utility/NewHorizonBody.cs index 074ccf71..8eff0b9a 100644 --- a/NewHorizons/Utility/NewHorizonBody.cs +++ b/NewHorizons/Utility/NewHorizonBody.cs @@ -28,8 +28,10 @@ namespace NewHorizons.Utility if (RelativePath != null) Cache = new Cache(RelativePath+".nhcache"); } - public void UnloadCache() + public void UnloadCache(bool writeBeforeUnload=false) { + if (writeBeforeUnload) Cache.WriteToFile(); + Cache = null; // garbage collection will take care of it } #endregion Cache From a99054f0d415a7a8f945d9a44c28f81594f5e673 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Tue, 31 Jan 2023 15:00:31 -0500 Subject: [PATCH 41/58] passing a reference to the cache to the text builder --- .../Props/NomaiText/NomaiTextBuilder.cs | 25 +++++++++++++------ .../Utility/DebugMenu/DebugMenuNomaiText.cs | 16 +----------- NewHorizons/Utility/NewHorizonBody.cs | 2 +- 3 files changed, 19 insertions(+), 24 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs index 437b98fb..ccf664db 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs @@ -118,7 +118,7 @@ namespace NewHorizons.Builder.Props } } - public static GameObject Make(GameObject planetGO, Sector sector, PropModule.NomaiTextInfo info, IModBehaviour mod) + public static GameObject Make(GameObject planetGO, Sector sector, PropModule.NomaiTextInfo info, NewHorizonsBody body, IModBehaviour mod) { InitPrefabs(); @@ -128,7 +128,7 @@ namespace NewHorizons.Builder.Props { case PropModule.NomaiTextInfo.NomaiTextType.Wall: { - var nomaiWallTextObj = MakeWallText(planetGO, sector, info, xmlPath).gameObject; + var nomaiWallTextObj = MakeWallText(planetGO, sector, info, xmlPath, body).gameObject; if (!string.IsNullOrEmpty(info.rename)) { @@ -209,7 +209,7 @@ namespace NewHorizons.Builder.Props customScroll.name = _scrollPrefab.name; } - var nomaiWallText = MakeWallText(planetGO, sector, info, xmlPath); + var nomaiWallText = MakeWallText(planetGO, sector, info, xmlPath, body); nomaiWallText.transform.parent = customScroll.transform; nomaiWallText.transform.localPosition = Vector3.zero; nomaiWallText.transform.localRotation = Quaternion.identity; @@ -570,7 +570,7 @@ namespace NewHorizons.Builder.Props } } - private static NomaiWallText MakeWallText(GameObject go, Sector sector, PropModule.NomaiTextInfo info, string xmlPath) + private static NomaiWallText MakeWallText(GameObject go, Sector sector, PropModule.NomaiTextInfo info, string xmlPath, NewHorizonsBody body) { GameObject nomaiWallTextObj = new GameObject("NomaiWallText"); nomaiWallTextObj.SetActive(false); @@ -592,7 +592,7 @@ namespace NewHorizons.Builder.Props // Text assets need a name to be used with VoiceMod text.name = Path.GetFileNameWithoutExtension(info.xmlFile); - BuildArcs(xmlPath, nomaiWallText, nomaiWallTextObj, info); + BuildArcs(xmlPath, nomaiWallText, nomaiWallTextObj, info, body); AddTranslation(xmlPath); nomaiWallText._nomaiTextAsset = text; @@ -607,16 +607,16 @@ namespace NewHorizons.Builder.Props return nomaiWallText; } - internal static void BuildArcs(string xml, NomaiWallText nomaiWallText, GameObject conversationZone, PropModule.NomaiTextInfo info) + internal static void BuildArcs(string xml, NomaiWallText nomaiWallText, GameObject conversationZone, PropModule.NomaiTextInfo info, NewHorizonsBody body) { var dict = MakeNomaiTextDict(xml); nomaiWallText._dictNomaiTextData = dict; - RefreshArcs(nomaiWallText, conversationZone, info); + RefreshArcs(nomaiWallText, conversationZone, info, body); } - internal static void RefreshArcs(NomaiWallText nomaiWallText, GameObject conversationZone, PropModule.NomaiTextInfo info) + internal static void RefreshArcs(NomaiWallText nomaiWallText, GameObject conversationZone, PropModule.NomaiTextInfo info, NewHorizonsBody body) { var dict = nomaiWallText._dictNomaiTextData; Random.InitState(info.seed == 0 ? info.xmlFile.GetHashCode() : info.seed); @@ -633,6 +633,13 @@ namespace NewHorizons.Builder.Props // Generate spiral meshes/GOs + var cacheKey = ""; // info.tojson.stringhashcode + "-" + xmlContents.stringhashcode + if (body.Cache?[cacheKey] != null) + { + // TODO: use cache data + // return; + } + var i = 0; foreach (var textData in dict.Values) { @@ -679,6 +686,8 @@ namespace NewHorizons.Builder.Props if (arcInfo.mirror) arc.transform.localScale = new Vector3(-1, 1, 1); else arc.transform.localScale = new Vector3( 1, 1, 1); } + + // TODO: body.Cache?[cacheKey] = cacheData; } internal static GameObject MakeArc(PropModule.NomaiTextArcInfo arcInfo, GameObject conversationZone, GameObject parent, int textEntryID) diff --git a/NewHorizons/Utility/DebugMenu/DebugMenuNomaiText.cs b/NewHorizons/Utility/DebugMenu/DebugMenuNomaiText.cs index 99843836..2ebcc329 100644 --- a/NewHorizons/Utility/DebugMenu/DebugMenuNomaiText.cs +++ b/NewHorizons/Utility/DebugMenu/DebugMenuNomaiText.cs @@ -266,7 +266,7 @@ namespace NewHorizons.Utility.DebugMenu GUILayout.Label("Variation"); GUILayout.BeginHorizontal(); - var varietyCount = GetVarietyCountForType(spiralMeta.spiral.type); + var varietyCount = 1; //var newVariation = int.Parse(GUILayout.TextField(spiralMeta.spiral.variation+"")); //newVariation = Mathf.Min(Mathf.Max(0, newVariation), varietyCount); //if (newVariation != spiralMeta.spiral.variation) changed = true; @@ -469,20 +469,6 @@ namespace NewHorizons.Utility.DebugMenu }); } - private int GetVarietyCountForType(NomaiTextArcInfo.NomaiTextArcType type) - { - switch(type) - { - case NomaiTextArcInfo.NomaiTextArcType.Stranger: - return NomaiTextBuilder.GetGhostArcPrefabs().Count(); - case NomaiTextArcInfo.NomaiTextArcType.Child: - return NomaiTextBuilder.GetChildArcPrefabs().Count(); - default: - case NomaiTextArcInfo.NomaiTextArcType.Adult: - return NomaiTextBuilder.GetArcPrefabs().Count(); - } - } - void UpdateConversationTransform(ConversationMetadata conversationMetadata, GameObject sectorParent) { var nomaiWallTextObj = conversationMetadata.conversationGo; diff --git a/NewHorizons/Utility/NewHorizonBody.cs b/NewHorizons/Utility/NewHorizonBody.cs index 8eff0b9a..9bc88027 100644 --- a/NewHorizons/Utility/NewHorizonBody.cs +++ b/NewHorizons/Utility/NewHorizonBody.cs @@ -30,7 +30,7 @@ namespace NewHorizons.Utility public void UnloadCache(bool writeBeforeUnload=false) { - if (writeBeforeUnload) Cache.WriteToFile(); + if (writeBeforeUnload) Cache?.WriteToFile(); Cache = null; // garbage collection will take care of it } From b3accf459d6a84d8b1daf133973f1e63161570d2 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Tue, 31 Jan 2023 15:08:03 -0500 Subject: [PATCH 42/58] fixed file extension --- NewHorizons/Utility/NewHorizonBody.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/NewHorizons/Utility/NewHorizonBody.cs b/NewHorizons/Utility/NewHorizonBody.cs index 8eff0b9a..f178e50e 100644 --- a/NewHorizons/Utility/NewHorizonBody.cs +++ b/NewHorizons/Utility/NewHorizonBody.cs @@ -25,12 +25,19 @@ namespace NewHorizons.Utility #region Cache public void LoadCache() { - if (RelativePath != null) Cache = new Cache(RelativePath+".nhcache"); + if (RelativePath == null) + { + Logger.LogWarning("Cannot load cache! RelativePath is null!"); + return; + } + + var pathWithoutExtension = RelativePath.Substring(0, RelativePath.LastIndexOf('.')); + Cache = new Cache(pathWithoutExtension+".nhcache"); } public void UnloadCache(bool writeBeforeUnload=false) { - if (writeBeforeUnload) Cache.WriteToFile(); + if (writeBeforeUnload) Cache?.WriteToFile(); Cache = null; // garbage collection will take care of it } From f99de094c1d4af269cddc8e863f12af5a8edfcc1 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Tue, 31 Jan 2023 21:13:31 -0500 Subject: [PATCH 43/58] spiral meshse and positions are now being cached --- .../Props/NomaiText/NomaiTextBuilder.cs | 55 +++++++++++++------ NewHorizons/Builder/Props/PropBuildManager.cs | 10 +++- NewHorizons/Builder/Props/RemoteBuilder.cs | 9 +-- NewHorizons/Handlers/PlanetCreationHandler.cs | 2 +- NewHorizons/Utility/Cache.cs | 4 +- NewHorizons/Utility/MMesh.cs | 51 +++++++++++++++++ 6 files changed, 105 insertions(+), 26 deletions(-) create mode 100644 NewHorizons/Utility/MMesh.cs diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs index ccf664db..f9fb00f3 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs @@ -11,6 +11,8 @@ using Enum = System.Enum; using Logger = NewHorizons.Utility.Logger; using Random = UnityEngine.Random; using OWML.Utils; +using Newtonsoft.Json; +using System; namespace NewHorizons.Builder.Props { @@ -118,17 +120,17 @@ namespace NewHorizons.Builder.Props } } - public static GameObject Make(GameObject planetGO, Sector sector, PropModule.NomaiTextInfo info, NewHorizonsBody body, IModBehaviour mod) + public static GameObject Make(GameObject planetGO, Sector sector, PropModule.NomaiTextInfo info, NewHorizonsBody nhBody) { InitPrefabs(); - var xmlPath = File.ReadAllText(Path.Combine(mod.ModHelper.Manifest.ModFolderPath, info.xmlFile)); + var xmlPath = File.ReadAllText(Path.Combine(nhBody.Mod.ModHelper.Manifest.ModFolderPath, info.xmlFile)); switch (info.type) { case PropModule.NomaiTextInfo.NomaiTextType.Wall: { - var nomaiWallTextObj = MakeWallText(planetGO, sector, info, xmlPath, body).gameObject; + var nomaiWallTextObj = MakeWallText(planetGO, sector, info, xmlPath, nhBody).gameObject; if (!string.IsNullOrEmpty(info.rename)) { @@ -209,7 +211,7 @@ namespace NewHorizons.Builder.Props customScroll.name = _scrollPrefab.name; } - var nomaiWallText = MakeWallText(planetGO, sector, info, xmlPath, body); + var nomaiWallText = MakeWallText(planetGO, sector, info, xmlPath, nhBody); nomaiWallText.transform.parent = customScroll.transform; nomaiWallText.transform.localPosition = Vector3.zero; nomaiWallText.transform.localRotation = Quaternion.identity; @@ -570,7 +572,7 @@ namespace NewHorizons.Builder.Props } } - private static NomaiWallText MakeWallText(GameObject go, Sector sector, PropModule.NomaiTextInfo info, string xmlPath, NewHorizonsBody body) + private static NomaiWallText MakeWallText(GameObject go, Sector sector, PropModule.NomaiTextInfo info, string xmlPath, NewHorizonsBody nhBody) { GameObject nomaiWallTextObj = new GameObject("NomaiWallText"); nomaiWallTextObj.SetActive(false); @@ -592,7 +594,7 @@ namespace NewHorizons.Builder.Props // Text assets need a name to be used with VoiceMod text.name = Path.GetFileNameWithoutExtension(info.xmlFile); - BuildArcs(xmlPath, nomaiWallText, nomaiWallTextObj, info, body); + BuildArcs(xmlPath, nomaiWallText, nomaiWallTextObj, info, nhBody); AddTranslation(xmlPath); nomaiWallText._nomaiTextAsset = text; @@ -607,16 +609,26 @@ namespace NewHorizons.Builder.Props return nomaiWallText; } - internal static void BuildArcs(string xml, NomaiWallText nomaiWallText, GameObject conversationZone, PropModule.NomaiTextInfo info, NewHorizonsBody body) + internal static void BuildArcs(string xml, NomaiWallText nomaiWallText, GameObject conversationZone, PropModule.NomaiTextInfo info, NewHorizonsBody nhBody) { var dict = MakeNomaiTextDict(xml); nomaiWallText._dictNomaiTextData = dict; - RefreshArcs(nomaiWallText, conversationZone, info, body); + var cacheKey = xml.GetHashCode() + " " + JsonConvert.SerializeObject(info).GetHashCode(); + RefreshArcs(nomaiWallText, conversationZone, info, nhBody, cacheKey); } - internal static void RefreshArcs(NomaiWallText nomaiWallText, GameObject conversationZone, PropModule.NomaiTextInfo info, NewHorizonsBody body) + [Serializable] + private struct ArcCacheData + { + public MMesh mesh; + public MVector3[] skeletonPoints; + public MVector3 position; + public float zRotation; + } + + internal static void RefreshArcs(NomaiWallText nomaiWallText, GameObject conversationZone, PropModule.NomaiTextInfo info, NewHorizonsBody nhBody, string cacheKey) { var dict = nomaiWallText._dictNomaiTextData; Random.InitState(info.seed == 0 ? info.xmlFile.GetHashCode() : info.seed); @@ -629,17 +641,16 @@ namespace NewHorizons.Builder.Props return; } - var arranger = nomaiWallText.gameObject.AddComponent(); - - // Generate spiral meshes/GOs - - var cacheKey = ""; // info.tojson.stringhashcode + "-" + xmlContents.stringhashcode - if (body.Cache?[cacheKey] != null) + if (nhBody.Cache?.ContainsKey(cacheKey) ?? false) { // TODO: use cache data // return; } + var arranger = nomaiWallText.gameObject.AddComponent(); + + // Generate spiral meshes/GOs + var i = 0; foreach (var textData in dict.Values) { @@ -687,7 +698,19 @@ namespace NewHorizons.Builder.Props else arc.transform.localScale = new Vector3( 1, 1, 1); } - // TODO: body.Cache?[cacheKey] = cacheData; + // cache data + if (nhBody.Cache != null) + { + var cacheData = arranger.spirals.Select(spiralManipulator => new ArcCacheData() + { + mesh = spiralManipulator.GetComponent().sharedMesh, // TODO: create a serializable version of Mesh and pass this: spiralManipulator.GetComponent().mesh + skeletonPoints = spiralManipulator.NomaiTextLine._points.Select(v => (MVector3)v).ToArray(), + position = spiralManipulator.transform.localPosition, + zRotation = spiralManipulator.transform.localEulerAngles.z + }).ToArray(); + + nhBody.Cache[cacheKey] = cacheData; + } } internal static GameObject MakeArc(PropModule.NomaiTextArcInfo arcInfo, GameObject conversationZone, GameObject parent, int textEntryID) diff --git a/NewHorizons/Builder/Props/PropBuildManager.cs b/NewHorizons/Builder/Props/PropBuildManager.cs index d3dcbf4f..b1018554 100644 --- a/NewHorizons/Builder/Props/PropBuildManager.cs +++ b/NewHorizons/Builder/Props/PropBuildManager.cs @@ -1,6 +1,7 @@ using NewHorizons.Builder.Body; using NewHorizons.Builder.ShipLog; using NewHorizons.External.Configs; +using NewHorizons.Utility; using OWML.Common; using System; using System.Collections.Generic; @@ -10,8 +11,11 @@ namespace NewHorizons.Builder.Props { public static class PropBuildManager { - public static void Make(GameObject go, Sector sector, OWRigidbody planetBody, PlanetConfig config, IModBehaviour mod) + public static void Make(GameObject go, Sector sector, OWRigidbody planetBody, NewHorizonsBody nhBody) { + PlanetConfig config = nhBody.Config; + IModBehaviour mod = nhBody.Mod; + if (config.Props.scatter != null) { try @@ -128,7 +132,7 @@ namespace NewHorizons.Builder.Props { try { - NomaiTextBuilder.Make(go, sector, nomaiTextInfo, mod); + NomaiTextBuilder.Make(go, sector, nomaiTextInfo, nhBody); } catch (Exception ex) { @@ -205,7 +209,7 @@ namespace NewHorizons.Builder.Props { try { - RemoteBuilder.Make(go, sector, remoteInfo, mod); + RemoteBuilder.Make(go, sector, remoteInfo, nhBody); } catch (Exception ex) { diff --git a/NewHorizons/Builder/Props/RemoteBuilder.cs b/NewHorizons/Builder/Props/RemoteBuilder.cs index c8cd3334..f179cdd9 100644 --- a/NewHorizons/Builder/Props/RemoteBuilder.cs +++ b/NewHorizons/Builder/Props/RemoteBuilder.cs @@ -116,10 +116,11 @@ namespace NewHorizons.Builder.Props } } - public static void Make(GameObject go, Sector sector, PropModule.RemoteInfo info, IModBehaviour mod) + public static void Make(GameObject go, Sector sector, PropModule.RemoteInfo info, NewHorizonsBody nhBody) { InitPrefabs(); + var mod = nhBody.Mod; var id = RemoteHandler.GetPlatformID(info.id); Texture2D decal = Texture2D.whiteTexture; @@ -142,7 +143,7 @@ namespace NewHorizons.Builder.Props { try { - RemoteBuilder.MakeWhiteboard(go, sector, id, decal, info.whiteboard, mod); + RemoteBuilder.MakeWhiteboard(go, sector, id, decal, info.whiteboard, nhBody); } catch (Exception ex) { @@ -166,7 +167,7 @@ namespace NewHorizons.Builder.Props } } - public static void MakeWhiteboard(GameObject go, Sector sector, NomaiRemoteCameraPlatform.ID id, Texture2D decal, PropModule.RemoteInfo.WhiteboardInfo info, IModBehaviour mod) + public static void MakeWhiteboard(GameObject go, Sector sector, NomaiRemoteCameraPlatform.ID id, Texture2D decal, PropModule.RemoteInfo.WhiteboardInfo info, NewHorizonsBody nhBody) { var detailInfo = new PropModule.DetailInfo() { @@ -204,7 +205,7 @@ namespace NewHorizons.Builder.Props seed = textInfo.seed, type = PropModule.NomaiTextInfo.NomaiTextType.Wall, xmlFile = textInfo.xmlFile - }, mod).GetComponent(); + }, nhBody).GetComponent(); wallText._showTextOnStart = false; component._nomaiTexts[i] = wallText; } diff --git a/NewHorizons/Handlers/PlanetCreationHandler.cs b/NewHorizons/Handlers/PlanetCreationHandler.cs index 6c36fdec..ee7cbba5 100644 --- a/NewHorizons/Handlers/PlanetCreationHandler.cs +++ b/NewHorizons/Handlers/PlanetCreationHandler.cs @@ -638,7 +638,7 @@ namespace NewHorizons.Handlers if (body.Config.Props != null) { - PropBuildManager.Make(go, sector, rb, body.Config, body.Mod); + PropBuildManager.Make(go, sector, rb, body); } if (body.Config.Volumes != null) diff --git a/NewHorizons/Utility/Cache.cs b/NewHorizons/Utility/Cache.cs index 8113575e..26ef3535 100644 --- a/NewHorizons/Utility/Cache.cs +++ b/NewHorizons/Utility/Cache.cs @@ -5,7 +5,7 @@ using System.Runtime.Serialization; namespace NewHorizons.Utility { - public class Cache : Dictionary + public class Cache : Dictionary { [NonSerialized] string filepath; @@ -24,7 +24,7 @@ namespace NewHorizons.Utility public void WriteToFile() { - NewHorizons.Main.Instance.ModHelper.Storage.Save>(this, filepath); + NewHorizons.Main.Instance.ModHelper.Storage.Save>(this, filepath); } } } diff --git a/NewHorizons/Utility/MMesh.cs b/NewHorizons/Utility/MMesh.cs new file mode 100644 index 00000000..6ab39722 --- /dev/null +++ b/NewHorizons/Utility/MMesh.cs @@ -0,0 +1,51 @@ +using System.ComponentModel; +using System.Linq; +using Newtonsoft.Json; +using UnityEngine; +namespace NewHorizons.Utility +{ + [JsonObject] + public class MMesh + { + public MMesh(MVector3[] vertices, int[] triangles, MVector3[] normals, MVector2[] uv, MVector2[] uv2) + { + this.vertices = vertices; + this.triangles = triangles; + this.normals = normals; + this.uv = uv; + this.uv2 = uv2; + } + + public MVector3[] vertices; + public int[] triangles; + public MVector3[] normals; + public MVector2[] uv; + public MVector2[] uv2; + + public static implicit operator MMesh(Mesh mesh) + { + return new MMesh + ( + mesh.vertices.Select(v => (MVector3)v).ToArray(), + mesh.triangles, + mesh.normals.Select(v => (MVector3)v).ToArray(), + mesh.uv.Select(v => (MVector2)v).ToArray(), + mesh.uv2.Select(v => (MVector2)v).ToArray() + ); + } + + public static implicit operator Mesh(MMesh mmesh) + { + var mesh = new Mesh(); + + mesh.vertices = mmesh.vertices.Select(mv => (Vector3)mv).ToArray(); + mesh.triangles = mmesh.triangles; + mesh.normals = mmesh.normals.Select(mv => (Vector3)mv).ToArray(); + mesh.uv = mmesh.uv.Select(mv => (Vector2)mv).ToArray(); + mesh.uv2 = mmesh.uv2.Select(mv => (Vector2)mv).ToArray(); + mesh.RecalculateBounds(); + + return mesh; + } + } +} From 6acbd8f836de3e77e97a562a0bc4e524f935fbbb Mon Sep 17 00:00:00 2001 From: Ben C Date: Wed, 1 Feb 2023 02:18:20 +0000 Subject: [PATCH 44/58] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index 97d49113..3b7d3fc8 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -1310,6 +1310,10 @@ "type": "object", "additionalProperties": false, "properties": { + "keepAutoPlacement": { + "type": "boolean", + "description": "Whether to skip modifying this spiral's placement, and instead keep the automatically determined placement." + }, "mirror": { "type": "boolean", "description": "Whether to flip the spiral from left-curling to right-curling or vice versa." From a1b7a4ab8607b938f542fcf5f1ed11a16e481ce9 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Tue, 31 Jan 2023 22:47:07 -0500 Subject: [PATCH 45/58] added stuff to read nomai wall text data from the cache --- .../Props/NomaiText/NomaiTextArcBuilder.cs | 32 ++--- .../Props/NomaiText/NomaiTextBuilder.cs | 109 +++++++++++------- NewHorizons/Utility/Cache.cs | 10 +- NewHorizons/Utility/NewHorizonBody.cs | 2 + 4 files changed, 95 insertions(+), 58 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs index 751a9332..b88cf7ab 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs @@ -13,21 +13,10 @@ namespace NewHorizons.Builder.Props public static GameObject BuildSpiralGameObject(SpiralProfile profile, string goName="New Nomai Spiral") { - var g = new GameObject(goName); - g.SetActive(false); - g.transform.localPosition = Vector3.zero; - g.transform.localEulerAngles = Vector3.zero; - var m = new SpiralMesh(profile); m.Randomize(); m.updateMesh(); - g.AddComponent().sharedMesh = m.mesh; - g.AddComponent().sharedMaterial = new Material(Shader.Find("Sprites/Default")); - g.GetComponent().sharedMaterial.color = Color.magenta; - - var owNomaiTextLine = g.AddComponent(); - // // rotate mesh to point up // @@ -37,7 +26,7 @@ namespace NewHorizons.Builder.Props var ang = -90-r; // using m.sharedMesh causes old meshes to disappear for some reason, idk why - var mesh = g.GetComponent().mesh; + var mesh = m.mesh; var newVerts = mesh.vertices.Select(v => Quaternion.Euler(-90, 0, 0) * Quaternion.Euler(0, ang, 0) * v).ToArray(); mesh.vertices = newVerts; mesh.RecalculateBounds(); @@ -49,9 +38,22 @@ namespace NewHorizons.Builder.Props ) .ToArray(); - // - // set up NomaiTextArc stuff - // + + return BuildSpiralGameObject(_points, mesh, goName); + } + + public static GameObject BuildSpiralGameObject(Vector3[] _points, Mesh mesh, string goName="New Nomai Spiral") + { + var g = new GameObject(goName); + g.SetActive(false); + g.transform.localPosition = Vector3.zero; + g.transform.localEulerAngles = Vector3.zero; + + g.AddComponent().sharedMesh = mesh; + g.AddComponent().sharedMaterial = new Material(Shader.Find("Sprites/Default")); + g.GetComponent().sharedMaterial.color = Color.magenta; + + var owNomaiTextLine = g.AddComponent(); owNomaiTextLine._points = _points; owNomaiTextLine._active = true; diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs index f9fb00f3..c7de8d03 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs @@ -641,11 +641,14 @@ namespace NewHorizons.Builder.Props return; } + ArcCacheData[] cachedData = null; if (nhBody.Cache?.ContainsKey(cacheKey) ?? false) - { - // TODO: use cache data - // return; - } + cachedData = (ArcCacheData[])nhBody.Cache[cacheKey]; + + Logger.LogWarning("CACHE DEBUG: cache key *" + cacheKey+"*"); + Logger.LogWarning("CACHE DEBUG: cache is null? " + (nhBody.Cache == null)); + Logger.LogWarning("CACHE DEBUG: cache contains key? " + (nhBody.Cache?.ContainsKey(cacheKey) ?? false)); + Logger.LogWarning("CACHE DEBUG: cache keys: " + String.Join(",", nhBody.Cache?.Keys)); var arranger = nomaiWallText.gameObject.AddComponent(); @@ -660,7 +663,19 @@ namespace NewHorizons.Builder.Props var parent = parentID == -1 ? null : arcsByID[parentID]; - GameObject arc = MakeArc(arcInfo, conversationZone, parent, textEntryID); + GameObject arcReadFromCache = null; + if (cachedData != null) + { + Logger.LogWarning("CACHE DEBUG: cache was not null, loading meshes and positions"); + + var skeletonPoints = cachedData[i].skeletonPoints.Select(mv => (Vector3)mv).ToArray(); + arcReadFromCache = NomaiTextArcBuilder.BuildSpiralGameObject(skeletonPoints, cachedData[i].mesh); + arcReadFromCache.transform.parent = arranger.transform; + arcReadFromCache.transform.localPosition = cachedData[i].position; + arcReadFromCache.transform.localEulerAngles = new Vector3(0, 0, cachedData[i].zRotation); + } + + GameObject arc = MakeArc(arcInfo, conversationZone, parent, textEntryID, arcReadFromCache); arc.name = $"Arc {textEntryID} - Child of {parentID}"; arcsByID.Add(textEntryID, arc); @@ -668,52 +683,59 @@ namespace NewHorizons.Builder.Props i++; } - // auto placement + // no need to arrange if the cache exists + if (cachedData == null) + { + Logger.LogWarning("CACHE DEBUG: cache was null, proceding with arrangment"); - var overlapFound = true; - for (var k = 0; k < arranger.spirals.Count*2; k++) - { - overlapFound = arranger.AttemptOverlapResolution(); - if (!overlapFound) break; - for(var a = 0; a < 10; a++) arranger.FDGSimulationStep(); - } + // auto placement - if (overlapFound) Logger.LogVerbose("Overlap resolution failed!"); + var overlapFound = true; + for (var k = 0; k < arranger.spirals.Count*2; k++) + { + overlapFound = arranger.AttemptOverlapResolution(); + if (!overlapFound) break; + for(var a = 0; a < 10; a++) arranger.FDGSimulationStep(); + } - // manual placement + if (overlapFound) Logger.LogVerbose("Overlap resolution failed!"); - for (var j = 0; j < info.arcInfo.Length; j++) - { - var arcInfo = info.arcInfo[j]; - var arc = arranger.spirals[j]; + // manual placement - if (arcInfo.keepAutoPlacement) continue; + for (var j = 0; j < info.arcInfo.Length; j++) + { + var arcInfo = info.arcInfo[j]; + var arc = arranger.spirals[j]; - if (arcInfo.position == null) arc.transform.localPosition = Vector3.zero; - else arc.transform.localPosition = new Vector3(arcInfo.position.x, arcInfo.position.y, 0); + if (arcInfo.keepAutoPlacement) continue; - arc.transform.localRotation = Quaternion.Euler(0, 0, arcInfo.zRotation); + if (arcInfo.position == null) arc.transform.localPosition = Vector3.zero; + else arc.transform.localPosition = new Vector3(arcInfo.position.x, arcInfo.position.y, 0); - if (arcInfo.mirror) arc.transform.localScale = new Vector3(-1, 1, 1); - else arc.transform.localScale = new Vector3( 1, 1, 1); - } + arc.transform.localRotation = Quaternion.Euler(0, 0, arcInfo.zRotation); - // cache data - if (nhBody.Cache != null) - { - var cacheData = arranger.spirals.Select(spiralManipulator => new ArcCacheData() - { - mesh = spiralManipulator.GetComponent().sharedMesh, // TODO: create a serializable version of Mesh and pass this: spiralManipulator.GetComponent().mesh - skeletonPoints = spiralManipulator.NomaiTextLine._points.Select(v => (MVector3)v).ToArray(), - position = spiralManipulator.transform.localPosition, - zRotation = spiralManipulator.transform.localEulerAngles.z - }).ToArray(); + if (arcInfo.mirror) arc.transform.localScale = new Vector3(-1, 1, 1); + else arc.transform.localScale = new Vector3( 1, 1, 1); + } - nhBody.Cache[cacheKey] = cacheData; + // make an entry in the cache for all these spirals + + if (nhBody.Cache != null) + { + var cacheData = arranger.spirals.Select(spiralManipulator => new ArcCacheData() + { + mesh = spiralManipulator.GetComponent().sharedMesh, // TODO: create a serializable version of Mesh and pass this: spiralManipulator.GetComponent().mesh + skeletonPoints = spiralManipulator.NomaiTextLine._points.Select(v => (MVector3)v).ToArray(), + position = spiralManipulator.transform.localPosition, + zRotation = spiralManipulator.transform.localEulerAngles.z + }).ToArray(); + + nhBody.Cache[cacheKey] = cacheData; + } } } - internal static GameObject MakeArc(PropModule.NomaiTextArcInfo arcInfo, GameObject conversationZone, GameObject parent, int textEntryID) + internal static GameObject MakeArc(PropModule.NomaiTextArcInfo arcInfo, GameObject conversationZone, GameObject parent, int textEntryID, GameObject prebuiltArc = null) { GameObject arc; var type = arcInfo != null ? arcInfo.type : PropModule.NomaiTextArcInfo.NomaiTextArcType.Adult; @@ -736,8 +758,15 @@ namespace NewHorizons.Builder.Props break; } - if (parent != null) arc = parent.GetComponent().AddChild(profile).gameObject; - else arc = NomaiTextArcArranger.CreateSpiral(profile, conversationZone).gameObject; + if (prebuiltArc != null) + { + arc = prebuiltArc; + } + else + { + if (parent != null) arc = parent.GetComponent().AddChild(profile).gameObject; + else arc = NomaiTextArcArranger.CreateSpiral(profile, conversationZone).gameObject; + } if (mat != null) arc.GetComponent().sharedMaterial = mat; diff --git a/NewHorizons/Utility/Cache.cs b/NewHorizons/Utility/Cache.cs index 26ef3535..f4767a50 100644 --- a/NewHorizons/Utility/Cache.cs +++ b/NewHorizons/Utility/Cache.cs @@ -5,14 +5,18 @@ using System.Runtime.Serialization; namespace NewHorizons.Utility { - public class Cache : Dictionary + public class Cache : Dictionary { [NonSerialized] string filepath; public Cache(string cacheFilePath) { filepath = cacheFilePath; - var existingEntries = NewHorizons.Main.Instance.ModHelper.Storage.Load>(filepath); + var existingEntries = NewHorizons.Main.Instance.ModHelper.Storage.Load>(filepath); + + Logger.LogWarning("CACHE DEBUG: Cache path: " + cacheFilePath); + Logger.LogWarning("CACHE DEBUG: Loaded cache == null? " + (existingEntries == null)); + Logger.LogWarning("CACHE DEBUG: Loaded cache keys: " + String.Join(",", existingEntries?.Keys)); if (existingEntries == null) return; @@ -24,7 +28,7 @@ namespace NewHorizons.Utility public void WriteToFile() { - NewHorizons.Main.Instance.ModHelper.Storage.Save>(this, filepath); + NewHorizons.Main.Instance.ModHelper.Storage.Save>(this, filepath); } } } diff --git a/NewHorizons/Utility/NewHorizonBody.cs b/NewHorizons/Utility/NewHorizonBody.cs index f178e50e..e75bb562 100644 --- a/NewHorizons/Utility/NewHorizonBody.cs +++ b/NewHorizons/Utility/NewHorizonBody.cs @@ -12,6 +12,8 @@ namespace NewHorizons.Utility Mod = mod; RelativePath = relativePath; + Logger.LogWarning("CACHE DEBUG: NHBODY CONSTRUCTOR: " + config.name + " - *" + relativePath+"*"); + Migrate(); } From 7061a23e1f1e0d804d86b9369e6a72373ea45ae0 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Wed, 1 Feb 2023 11:30:55 -0500 Subject: [PATCH 46/58] fixed cache reading --- NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs index c7de8d03..42f15192 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs @@ -643,7 +643,10 @@ namespace NewHorizons.Builder.Props ArcCacheData[] cachedData = null; if (nhBody.Cache?.ContainsKey(cacheKey) ?? false) - cachedData = (ArcCacheData[])nhBody.Cache[cacheKey]; + { + var json = JsonConvert.SerializeObject(nhBody.Cache[cacheKey]); + cachedData = JsonConvert.DeserializeObject(json); + } Logger.LogWarning("CACHE DEBUG: cache key *" + cacheKey+"*"); Logger.LogWarning("CACHE DEBUG: cache is null? " + (nhBody.Cache == null)); From c03938a1ab328e4ab92bae76262313d3fce1a564 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Wed, 1 Feb 2023 12:40:13 -0500 Subject: [PATCH 47/58] fixed the Cache class not being able to handle loading arbitrary data --- .../Props/NomaiText/NomaiTextBuilder.cs | 9 ++-- NewHorizons/Utility/Cache.cs | 54 ++++++++++++++----- NewHorizons/Utility/NewHorizonBody.cs | 2 +- 3 files changed, 44 insertions(+), 21 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs index 42f15192..e14c261b 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs @@ -643,15 +643,12 @@ namespace NewHorizons.Builder.Props ArcCacheData[] cachedData = null; if (nhBody.Cache?.ContainsKey(cacheKey) ?? false) - { - var json = JsonConvert.SerializeObject(nhBody.Cache[cacheKey]); - cachedData = JsonConvert.DeserializeObject(json); - } + cachedData = nhBody.Cache.Get(cacheKey); Logger.LogWarning("CACHE DEBUG: cache key *" + cacheKey+"*"); Logger.LogWarning("CACHE DEBUG: cache is null? " + (nhBody.Cache == null)); Logger.LogWarning("CACHE DEBUG: cache contains key? " + (nhBody.Cache?.ContainsKey(cacheKey) ?? false)); - Logger.LogWarning("CACHE DEBUG: cache keys: " + String.Join(",", nhBody.Cache?.Keys)); + //Logger.LogWarning("CACHE DEBUG: cache keys: " + String.Join(",", nhBody.Cache?.Keys)); var arranger = nomaiWallText.gameObject.AddComponent(); @@ -733,7 +730,7 @@ namespace NewHorizons.Builder.Props zRotation = spiralManipulator.transform.localEulerAngles.z }).ToArray(); - nhBody.Cache[cacheKey] = cacheData; + nhBody.Cache.Set(cacheKey, cacheData); } } } diff --git a/NewHorizons/Utility/Cache.cs b/NewHorizons/Utility/Cache.cs index f4767a50..5860cc9e 100644 --- a/NewHorizons/Utility/Cache.cs +++ b/NewHorizons/Utility/Cache.cs @@ -1,34 +1,60 @@ +using Newtonsoft.Json; +using OWML.Common; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Runtime.Serialization; namespace NewHorizons.Utility { - public class Cache : Dictionary + public class Cache { - [NonSerialized] string filepath; + string filepath; + IModBehaviour mod; + Dictionary data = new Dictionary(); - public Cache(string cacheFilePath) + public Cache(IModBehaviour mod, string cacheFilePath) { + this.mod = mod; + filepath = cacheFilePath; - var existingEntries = NewHorizons.Main.Instance.ModHelper.Storage.Load>(filepath); + + var json = File.ReadAllText(mod.ModHelper.Manifest.ModFolderPath + cacheFilePath); + data = JsonConvert.DeserializeObject>(json); + // the above is exactly the same thing that the below does, but the below for some reason always returns null. no clue why + // data = mod.ModHelper.Storage.Load>(filepath); + + if (data == null) + { + Logger.LogWarning("Failed to load cache! Cache path: " + cacheFilePath); + data = new Dictionary(); + } Logger.LogWarning("CACHE DEBUG: Cache path: " + cacheFilePath); - Logger.LogWarning("CACHE DEBUG: Loaded cache == null? " + (existingEntries == null)); - Logger.LogWarning("CACHE DEBUG: Loaded cache keys: " + String.Join(",", existingEntries?.Keys)); - - if (existingEntries == null) return; - - foreach(var entry in existingEntries) - { - this[entry.Key] = entry.Value; - } + Logger.LogWarning("CACHE DEBUG: Loaded cache == null? " + (data == null)); + Logger.LogWarning("CACHE DEBUG: Loaded cache keys: " + String.Join(",", data?.Keys ?? new Dictionary().Keys)); } public void WriteToFile() { - NewHorizons.Main.Instance.ModHelper.Storage.Save>(this, filepath); + mod.ModHelper.Storage.Save>(data, filepath); + } + + public bool ContainsKey(string key) + { + return data.ContainsKey(key); + } + + public T Get(string key) + { + var json = data[key]; + return JsonConvert.DeserializeObject(json); + } + + public void Set(string key, T value) + { + data[key] = JsonConvert.SerializeObject(value); } } } diff --git a/NewHorizons/Utility/NewHorizonBody.cs b/NewHorizons/Utility/NewHorizonBody.cs index e75bb562..2bd4370d 100644 --- a/NewHorizons/Utility/NewHorizonBody.cs +++ b/NewHorizons/Utility/NewHorizonBody.cs @@ -34,7 +34,7 @@ namespace NewHorizons.Utility } var pathWithoutExtension = RelativePath.Substring(0, RelativePath.LastIndexOf('.')); - Cache = new Cache(pathWithoutExtension+".nhcache"); + Cache = new Cache(Mod, pathWithoutExtension+".nhcache"); } public void UnloadCache(bool writeBeforeUnload=false) From bf2043a80b0a063dba703976079e412dba856f38 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Wed, 1 Feb 2023 13:02:21 -0500 Subject: [PATCH 48/58] added mirror status to spiral cache and fixed bugs with loading cache --- .../Props/NomaiText/NomaiTextBuilder.cs | 5 ++++- NewHorizons/Utility/Cache.cs | 21 ++++++++----------- NewHorizons/Utility/NewHorizonBody.cs | 13 ++++++++++-- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs index e14c261b..efa7aa7b 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs @@ -626,6 +626,7 @@ namespace NewHorizons.Builder.Props public MVector3[] skeletonPoints; public MVector3 position; public float zRotation; + public bool mirrored; } internal static void RefreshArcs(NomaiWallText nomaiWallText, GameObject conversationZone, PropModule.NomaiTextInfo info, NewHorizonsBody nhBody, string cacheKey) @@ -671,6 +672,7 @@ namespace NewHorizons.Builder.Props var skeletonPoints = cachedData[i].skeletonPoints.Select(mv => (Vector3)mv).ToArray(); arcReadFromCache = NomaiTextArcBuilder.BuildSpiralGameObject(skeletonPoints, cachedData[i].mesh); arcReadFromCache.transform.parent = arranger.transform; + arcReadFromCache.transform.localScale = new Vector3(cachedData[i].mirrored? -1 : 1, 1, 1); arcReadFromCache.transform.localPosition = cachedData[i].position; arcReadFromCache.transform.localEulerAngles = new Vector3(0, 0, cachedData[i].zRotation); } @@ -727,7 +729,8 @@ namespace NewHorizons.Builder.Props mesh = spiralManipulator.GetComponent().sharedMesh, // TODO: create a serializable version of Mesh and pass this: spiralManipulator.GetComponent().mesh skeletonPoints = spiralManipulator.NomaiTextLine._points.Select(v => (MVector3)v).ToArray(), position = spiralManipulator.transform.localPosition, - zRotation = spiralManipulator.transform.localEulerAngles.z + zRotation = spiralManipulator.transform.localEulerAngles.z, + mirrored = spiralManipulator.transform.localScale.x < 0 }).ToArray(); nhBody.Cache.Set(cacheKey, cacheData); diff --git a/NewHorizons/Utility/Cache.cs b/NewHorizons/Utility/Cache.cs index 5860cc9e..2b382d80 100644 --- a/NewHorizons/Utility/Cache.cs +++ b/NewHorizons/Utility/Cache.cs @@ -17,23 +17,20 @@ namespace NewHorizons.Utility public Cache(IModBehaviour mod, string cacheFilePath) { this.mod = mod; - - filepath = cacheFilePath; + this.filepath = cacheFilePath; + var fullPath = mod.ModHelper.Manifest.ModFolderPath + cacheFilePath; - var json = File.ReadAllText(mod.ModHelper.Manifest.ModFolderPath + cacheFilePath); - data = JsonConvert.DeserializeObject>(json); - // the above is exactly the same thing that the below does, but the below for some reason always returns null. no clue why - // data = mod.ModHelper.Storage.Load>(filepath); - - if (data == null) + if (!File.Exists(fullPath)) { - Logger.LogWarning("Failed to load cache! Cache path: " + cacheFilePath); + Logger.LogWarning("Cache file not found! Cache path: " + cacheFilePath); data = new Dictionary(); + return; } - Logger.LogWarning("CACHE DEBUG: Cache path: " + cacheFilePath); - Logger.LogWarning("CACHE DEBUG: Loaded cache == null? " + (data == null)); - Logger.LogWarning("CACHE DEBUG: Loaded cache keys: " + String.Join(",", data?.Keys ?? new Dictionary().Keys)); + var json = File.ReadAllText(fullPath); + data = JsonConvert.DeserializeObject>(json); + // the code above does exactly the same thing that the code below does, but the below for some reason always returns null. no clue why + // data = mod.ModHelper.Storage.Load>(filepath); } public void WriteToFile() diff --git a/NewHorizons/Utility/NewHorizonBody.cs b/NewHorizons/Utility/NewHorizonBody.cs index 2bd4370d..f96fe4b0 100644 --- a/NewHorizons/Utility/NewHorizonBody.cs +++ b/NewHorizons/Utility/NewHorizonBody.cs @@ -1,5 +1,6 @@ using NewHorizons.External.Configs; using OWML.Common; +using System; using System.Linq; using UnityEngine; namespace NewHorizons.Utility @@ -33,8 +34,16 @@ namespace NewHorizons.Utility return; } - var pathWithoutExtension = RelativePath.Substring(0, RelativePath.LastIndexOf('.')); - Cache = new Cache(Mod, pathWithoutExtension+".nhcache"); + try + { + var pathWithoutExtension = RelativePath.Substring(0, RelativePath.LastIndexOf('.')); + Cache = new Cache(Mod, pathWithoutExtension+".nhcache"); + } + catch (Exception e) + { + Logger.LogError("Cache failed to load: " + e.Message); + Cache = null; + } } public void UnloadCache(bool writeBeforeUnload=false) From 104ff288d66b3ed3676e44a8ff6b58a3b1a16f5b Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Wed, 1 Feb 2023 22:34:44 -0500 Subject: [PATCH 49/58] fixed stranger arcs and removed debug logs --- .../Props/NomaiText/NomaiTextArcBuilder.cs | 4 +- .../Props/NomaiText/NomaiTextBuilder.cs | 37 +++++++++++++++---- NewHorizons/Utility/NewHorizonBody.cs | 2 - 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs index b88cf7ab..039f97a7 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs @@ -125,8 +125,8 @@ namespace NewHorizons.Builder.Props skeletonScale = new Vector2(1, 1), numSkeletonPoints = 17, - innerWidth = 1, - outerWidth = 1, + innerWidth = 0.75f, + outerWidth = 0.75f, uvScale = 1f/1.8505f, diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs index efa7aa7b..e3f80dca 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs @@ -646,11 +646,6 @@ namespace NewHorizons.Builder.Props if (nhBody.Cache?.ContainsKey(cacheKey) ?? false) cachedData = nhBody.Cache.Get(cacheKey); - Logger.LogWarning("CACHE DEBUG: cache key *" + cacheKey+"*"); - Logger.LogWarning("CACHE DEBUG: cache is null? " + (nhBody.Cache == null)); - Logger.LogWarning("CACHE DEBUG: cache contains key? " + (nhBody.Cache?.ContainsKey(cacheKey) ?? false)); - //Logger.LogWarning("CACHE DEBUG: cache keys: " + String.Join(",", nhBody.Cache?.Keys)); - var arranger = nomaiWallText.gameObject.AddComponent(); // Generate spiral meshes/GOs @@ -667,8 +662,6 @@ namespace NewHorizons.Builder.Props GameObject arcReadFromCache = null; if (cachedData != null) { - Logger.LogWarning("CACHE DEBUG: cache was not null, loading meshes and positions"); - var skeletonPoints = cachedData[i].skeletonPoints.Select(mv => (Vector3)mv).ToArray(); arcReadFromCache = NomaiTextArcBuilder.BuildSpiralGameObject(skeletonPoints, cachedData[i].mesh); arcReadFromCache.transform.parent = arranger.transform; @@ -688,7 +681,7 @@ namespace NewHorizons.Builder.Props // no need to arrange if the cache exists if (cachedData == null) { - Logger.LogWarning("CACHE DEBUG: cache was null, proceding with arrangment"); + Logger.LogVerbose("Cache and/or cache entry was null, proceding with wall text arc arrangment."); // auto placement @@ -779,6 +772,34 @@ namespace NewHorizons.Builder.Props arc.GetComponent().SetEntryID(textEntryID); arc.GetComponent().enabled = false; + if (type == PropModule.NomaiTextArcInfo.NomaiTextArcType.Stranger && _ghostArcMaterial != null) + { + // curse you jank!! + var meshFilter = arc.GetComponent(); + Vector3 rectangularRadius = meshFilter.sharedMesh.bounds.extents; + MVector3[] verts = new MVector3[] { + new Vector3(-rectangularRadius.x, 0, 0), + new Vector3( rectangularRadius.x, 0, 0), + new Vector3(-rectangularRadius.x, 2*rectangularRadius.y, 0), + new Vector3( rectangularRadius.x, 2*rectangularRadius.y, 0), + }; + int[] triangles = new int[] { + 0, 1, 2, + 1, 3, 2, + }; + MVector3[] normals = new MVector3[verts.Length]; + for (int i = 0; i().sharedMesh = new MMesh(verts, triangles, normals, uv, uv2); + } + arc.SetActive(true); if (arcInfo != null) arcInfoToCorrespondingSpawnedGameObject[arcInfo] = arc; diff --git a/NewHorizons/Utility/NewHorizonBody.cs b/NewHorizons/Utility/NewHorizonBody.cs index f96fe4b0..f3382ec7 100644 --- a/NewHorizons/Utility/NewHorizonBody.cs +++ b/NewHorizons/Utility/NewHorizonBody.cs @@ -13,8 +13,6 @@ namespace NewHorizons.Utility Mod = mod; RelativePath = relativePath; - Logger.LogWarning("CACHE DEBUG: NHBODY CONSTRUCTOR: " + config.name + " - *" + relativePath+"*"); - Migrate(); } From 022dd0ac8af28040c67e43cdec08b2028ebf52dc Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Wed, 1 Feb 2023 22:46:00 -0500 Subject: [PATCH 50/58] framework for removing stranger arc jank --- .../Props/NomaiText/NomaiTextBuilder.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs index e3f80dca..00a83003 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs @@ -737,6 +737,8 @@ namespace NewHorizons.Builder.Props var type = arcInfo != null ? arcInfo.type : PropModule.NomaiTextArcInfo.NomaiTextArcType.Adult; NomaiTextArcBuilder.SpiralProfile profile; Material mat; + Mesh overrideMesh = null; + MColor overrideColor = null; switch (type) { case PropModule.NomaiTextArcInfo.NomaiTextArcType.Child: @@ -746,6 +748,8 @@ namespace NewHorizons.Builder.Props case PropModule.NomaiTextArcInfo.NomaiTextArcType.Stranger when _ghostArcMaterial != null: profile = NomaiTextArcBuilder.strangerSpiralProfile; mat = _ghostArcMaterial; + //overrideMesh = RectangleMeshFromCorners(Mesh verts logged below); + //overrideColor = some kinda green break; case PropModule.NomaiTextArcInfo.NomaiTextArcType.Adult: default: @@ -772,9 +776,18 @@ namespace NewHorizons.Builder.Props arc.GetComponent().SetEntryID(textEntryID); arc.GetComponent().enabled = false; + if (overrideMesh != null) + arc.GetComponent().sharedMesh = overrideMesh; + + if (overrideColor != null) + arc.GetComponent()._targetColor = overrideColor.ToColor(); + + // TODO: once I have the right size for the stranger arcs and have the results of the "Mesh verts" log, + // remove whole below if statement (move most of it to a new RectangleMeshFromCorners function located + // somewhere in the utils folder probably + // and then, uncomment the stuff in the stranger case of the switch statement if (type == PropModule.NomaiTextArcInfo.NomaiTextArcType.Stranger && _ghostArcMaterial != null) { - // curse you jank!! var meshFilter = arc.GetComponent(); Vector3 rectangularRadius = meshFilter.sharedMesh.bounds.extents; MVector3[] verts = new MVector3[] { @@ -783,6 +796,9 @@ namespace NewHorizons.Builder.Props new Vector3(-rectangularRadius.x, 2*rectangularRadius.y, 0), new Vector3( rectangularRadius.x, 2*rectangularRadius.y, 0), }; + + Logger.LogWarning("Mesh verts: " + String.Join(" v ", verts.Select(mv => mv.ToString()))); + int[] triangles = new int[] { 0, 1, 2, 1, 3, 2, From 9ba8472988b1ffe3ff50ea02c4134c794c238ca6 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Thu, 2 Feb 2023 22:51:55 -0500 Subject: [PATCH 51/58] found stranger color and made rectangular mesh generating function --- .../Props/NomaiText/NomaiTextArcBuilder.cs | 10 +---- .../Props/NomaiText/NomaiTextBuilder.cs | 42 ++----------------- NewHorizons/Utility/MeshUtilities.cs | 17 ++++++++ 3 files changed, 22 insertions(+), 47 deletions(-) create mode 100644 NewHorizons/Utility/MeshUtilities.cs diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs index 039f97a7..cb7343fa 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs @@ -108,21 +108,13 @@ namespace NewHorizons.Builder.Props uvScale = 4.9f * 0.55f, }; - // location of example stranger writing: - // RingWorld_Body/Sector_RingInterior/Sector_Zone1/Interactables_Zone1/Props_IP_ZoneSign_1/Arc_TestAlienWriting/Arc 1 - // 17 points - // length of 1.8505 - // width of 1 - // _revealDuration of 0.5633 - // _targetColor of 1 1 1 0 - // I think this'll do it public static SpiralProfile strangerSpiralProfile = new SpiralProfile() { profileName="Stranger", a = new Vector2(0.9f, 0.9f), // this value doesn't really matter for this b = new Vector2(5f, 5f), startS = new Vector2(1.8505f, 1.8505f), endS = new Vector2(0, 0), - skeletonScale = new Vector2(1, 1), + skeletonScale = new Vector2(0.6f, 0.6f), numSkeletonPoints = 17, innerWidth = 0.75f, diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs index 00a83003..a93f1096 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs @@ -738,7 +738,7 @@ namespace NewHorizons.Builder.Props NomaiTextArcBuilder.SpiralProfile profile; Material mat; Mesh overrideMesh = null; - MColor overrideColor = null; + Color? overrideColor = null; switch (type) { case PropModule.NomaiTextArcInfo.NomaiTextArcType.Child: @@ -748,8 +748,8 @@ namespace NewHorizons.Builder.Props case PropModule.NomaiTextArcInfo.NomaiTextArcType.Stranger when _ghostArcMaterial != null: profile = NomaiTextArcBuilder.strangerSpiralProfile; mat = _ghostArcMaterial; - //overrideMesh = RectangleMeshFromCorners(Mesh verts logged below); - //overrideColor = some kinda green + overrideMesh = MeshUtilities.RectangleMeshFromCorners(new Vector3[]{ new Vector3(-0.9f, 0.0f, 0.0f), new Vector3(0.9f, 0.0f, 0.0f), new Vector3(-0.9f, 2.0f, 0.0f), new Vector3(0.9f, 2.0f, 0.0f) }); + overrideColor = new Color(0.2f, 1.1f, 0.8f, 1f); break; case PropModule.NomaiTextArcInfo.NomaiTextArcType.Adult: default: @@ -780,41 +780,7 @@ namespace NewHorizons.Builder.Props arc.GetComponent().sharedMesh = overrideMesh; if (overrideColor != null) - arc.GetComponent()._targetColor = overrideColor.ToColor(); - - // TODO: once I have the right size for the stranger arcs and have the results of the "Mesh verts" log, - // remove whole below if statement (move most of it to a new RectangleMeshFromCorners function located - // somewhere in the utils folder probably - // and then, uncomment the stuff in the stranger case of the switch statement - if (type == PropModule.NomaiTextArcInfo.NomaiTextArcType.Stranger && _ghostArcMaterial != null) - { - var meshFilter = arc.GetComponent(); - Vector3 rectangularRadius = meshFilter.sharedMesh.bounds.extents; - MVector3[] verts = new MVector3[] { - new Vector3(-rectangularRadius.x, 0, 0), - new Vector3( rectangularRadius.x, 0, 0), - new Vector3(-rectangularRadius.x, 2*rectangularRadius.y, 0), - new Vector3( rectangularRadius.x, 2*rectangularRadius.y, 0), - }; - - Logger.LogWarning("Mesh verts: " + String.Join(" v ", verts.Select(mv => mv.ToString()))); - - int[] triangles = new int[] { - 0, 1, 2, - 1, 3, 2, - }; - MVector3[] normals = new MVector3[verts.Length]; - for (int i = 0; i().sharedMesh = new MMesh(verts, triangles, normals, uv, uv2); - } + arc.GetComponent()._targetColor = (Color)overrideColor; arc.SetActive(true); diff --git a/NewHorizons/Utility/MeshUtilities.cs b/NewHorizons/Utility/MeshUtilities.cs new file mode 100644 index 00000000..d547195d --- /dev/null +++ b/NewHorizons/Utility/MeshUtilities.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace NewHorizons.Utility +{ + public class MeshUtilities + { + public static Mesh RectangleMeshFromCorners(Vector3[] corners) + { + + } + } +} From 5727493fde093c14a325b79a91882559af6aab63 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Thu, 2 Feb 2023 23:04:04 -0500 Subject: [PATCH 52/58] updated stranger arc color --- NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs index a93f1096..1fa69e53 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs @@ -749,7 +749,7 @@ namespace NewHorizons.Builder.Props profile = NomaiTextArcBuilder.strangerSpiralProfile; mat = _ghostArcMaterial; overrideMesh = MeshUtilities.RectangleMeshFromCorners(new Vector3[]{ new Vector3(-0.9f, 0.0f, 0.0f), new Vector3(0.9f, 0.0f, 0.0f), new Vector3(-0.9f, 2.0f, 0.0f), new Vector3(0.9f, 2.0f, 0.0f) }); - overrideColor = new Color(0.2f, 1.1f, 0.8f, 1f); + overrideColor = new Color(0.0158f, 1.0f, 0.5601f, 1f); break; case PropModule.NomaiTextArcInfo.NomaiTextArcType.Adult: default: From 5d409d7be88b7cccd24795a48527ba822d932101 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Thu, 2 Feb 2023 23:27:40 -0500 Subject: [PATCH 53/58] added missing code --- NewHorizons/Utility/MeshUtilities.cs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/NewHorizons/Utility/MeshUtilities.cs b/NewHorizons/Utility/MeshUtilities.cs index d547195d..b9f2d320 100644 --- a/NewHorizons/Utility/MeshUtilities.cs +++ b/NewHorizons/Utility/MeshUtilities.cs @@ -11,7 +11,27 @@ namespace NewHorizons.Utility { public static Mesh RectangleMeshFromCorners(Vector3[] corners) { - + MVector3[] verts = corners.Select(v => (MVector3)v).ToArray(); + + int[] triangles = new int[] { + 0, 1, 2, + 1, 3, 2, + }; + + MVector3[] normals = new MVector3[verts.Length]; + for (int i = 0; i Date: Fri, 3 Feb 2023 11:39:35 -0500 Subject: [PATCH 54/58] fixed null reference error and removed debug sphere --- NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs index 1fa69e53..d8f8e4ce 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs @@ -191,7 +191,7 @@ namespace NewHorizons.Builder.Props } } - nomaiWallTextObj.GetComponent().DrawBoundsWithDebugSpheres(); + // nomaiWallTextObj.GetComponent().DrawBoundsWithDebugSpheres(); nomaiWallTextObj.SetActive(true); conversationInfoToCorrespondingSpawnedGameObject[info] = nomaiWallTextObj; @@ -697,7 +697,7 @@ namespace NewHorizons.Builder.Props // manual placement - for (var j = 0; j < info.arcInfo.Length; j++) + for (var j = 0; j < info.arcInfo?.Length; j++) { var arcInfo = info.arcInfo[j]; var arc = arranger.spirals[j]; From 5e3690efc83621204617ed75436cff53d730e84b Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Fri, 3 Feb 2023 15:13:36 -0500 Subject: [PATCH 55/58] cache now clears unused keys before writing to disk, this should mean that addon devs don't need to manually clear their caches before release --- NewHorizons/Utility/Cache.cs | 13 +++++++++++++ NewHorizons/Utility/NewHorizonBody.cs | 6 +++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/NewHorizons/Utility/Cache.cs b/NewHorizons/Utility/Cache.cs index 2b382d80..ba062551 100644 --- a/NewHorizons/Utility/Cache.cs +++ b/NewHorizons/Utility/Cache.cs @@ -13,6 +13,7 @@ namespace NewHorizons.Utility string filepath; IModBehaviour mod; Dictionary data = new Dictionary(); + HashSet accessedKeys = new HashSet(); public Cache(IModBehaviour mod, string cacheFilePath) { @@ -45,13 +46,25 @@ namespace NewHorizons.Utility public T Get(string key) { + accessedKeys.Add(key); var json = data[key]; return JsonConvert.DeserializeObject(json); } public void Set(string key, T value) { + accessedKeys.Add(key); data[key] = JsonConvert.SerializeObject(value); } + + public void ClearUnaccessed() + { + var keys = data.Keys.ToList(); + foreach(var key in keys) + { + if (accessedKeys.Contains(key)) continue; + data.Remove(key); + } + } } } diff --git a/NewHorizons/Utility/NewHorizonBody.cs b/NewHorizons/Utility/NewHorizonBody.cs index f3382ec7..f64e625a 100644 --- a/NewHorizons/Utility/NewHorizonBody.cs +++ b/NewHorizons/Utility/NewHorizonBody.cs @@ -46,7 +46,11 @@ namespace NewHorizons.Utility public void UnloadCache(bool writeBeforeUnload=false) { - if (writeBeforeUnload) Cache?.WriteToFile(); + if (writeBeforeUnload) + { + Cache?.ClearUnaccessed(); + Cache?.WriteToFile(); + } Cache = null; // garbage collection will take care of it } From 8c1032e67a14885e0d2236206baf54c2d6dd9919 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Sat, 4 Feb 2023 14:47:58 -0500 Subject: [PATCH 56/58] reintroduced legacy NomaiTextBuilder and put new code under the name (and module) of TranslatorTextBuilder --- .../Props/{NomaiText => }/NomaiTextBuilder.cs | 252 ++---- NewHorizons/Builder/Props/PropBuildManager.cs | 17 +- NewHorizons/Builder/Props/RemoteBuilder.cs | 2 +- .../NomaiTextArcArranger.cs | 0 .../NomaiTextArcBuilder.cs | 0 .../TranslatorText/TranslatorTextBuilder.cs | 845 ++++++++++++++++++ NewHorizons/External/Modules/PropModule.cs | 8 +- NewHorizons/Main.cs | 1 + .../Utility/DebugMenu/DebugMenuNomaiText.cs | 6 +- 9 files changed, 971 insertions(+), 160 deletions(-) rename NewHorizons/Builder/Props/{NomaiText => }/NomaiTextBuilder.cs (82%) rename NewHorizons/Builder/Props/{NomaiText => TranslatorText}/NomaiTextArcArranger.cs (100%) rename NewHorizons/Builder/Props/{NomaiText => TranslatorText}/NomaiTextArcBuilder.cs (100%) create mode 100644 NewHorizons/Builder/Props/TranslatorText/TranslatorTextBuilder.cs diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs b/NewHorizons/Builder/Props/NomaiTextBuilder.cs similarity index 82% rename from NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs rename to NewHorizons/Builder/Props/NomaiTextBuilder.cs index d8f8e4ce..b76b04ce 100644 --- a/NewHorizons/Builder/Props/NomaiText/NomaiTextBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiTextBuilder.cs @@ -11,16 +11,17 @@ using Enum = System.Enum; using Logger = NewHorizons.Utility.Logger; using Random = UnityEngine.Random; using OWML.Utils; -using Newtonsoft.Json; -using System; namespace NewHorizons.Builder.Props { + /// + /// Legacy - this class is used with the deprecated "nomaiText" module (deprecated on release of autospirals) + /// public static class NomaiTextBuilder { - private static Material _ghostArcMaterial; - private static Material _adultArcMaterial; - private static Material _childArcMaterial; + private static List _arcPrefabs; + private static List _childArcPrefabs; + private static List _ghostArcPrefabs; private static GameObject _scrollPrefab; private static GameObject _computerPrefab; private static GameObject _preCrashComputerPrefab; @@ -46,6 +47,10 @@ namespace NewHorizons.Builder.Props return conversationInfoToCorrespondingSpawnedGameObject[convo]; } + public static List GetArcPrefabs() { return _arcPrefabs; } + public static List GetChildArcPrefabs() { return _childArcPrefabs; } + public static List GetGhostArcPrefabs() { return _ghostArcPrefabs; } + private static bool _isInit; internal static void InitPrefabs() @@ -53,26 +58,42 @@ namespace NewHorizons.Builder.Props if (_isInit) return; _isInit = true; - - if (_adultArcMaterial == null) + + if (_arcPrefabs == null || _childArcPrefabs == null) { - _adultArcMaterial = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Sector_Crossroads/Interactables_Crossroads/Trailmarkers/Prefab_NOM_BH_Cairn_Arc (2)/Props_TH_ClutterSmall/Arc_Short/Arc") - .GetComponent() - .sharedMaterial; + // Just take every scroll and get the first arc + var existingArcs = GameObject.FindObjectsOfType() + .Select(x => x?._nomaiWallText?.gameObject?.transform?.Find("Arc 1")?.gameObject) + .Where(x => x != null) + .OrderBy(x => x.transform.GetPath()) // order by path so game updates dont break things + .ToArray(); + _arcPrefabs = new List(); + _childArcPrefabs = new List(); + foreach (var existingArc in existingArcs) + { + if (existingArc.GetComponent().material.name.Contains("Child")) + { + _childArcPrefabs.Add(existingArc.InstantiateInactive().Rename("Arc (Child)").DontDestroyOnLoad()); + } + else + { + _arcPrefabs.Add(existingArc.InstantiateInactive().Rename("Arc").DontDestroyOnLoad()); + } + } } - if (_childArcMaterial == null) + if (_ghostArcPrefabs == null) { - _childArcMaterial = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Sector_OldSettlement/Fragment OldSettlement 5/Core_OldSettlement 5/Interactables_Core_OldSettlement5/Arc_BH_OldSettlement_ChildrensRhyme/Arc 1") - .GetComponent() - .sharedMaterial; - } - - if (_ghostArcMaterial == null) - { - _ghostArcMaterial = SearchUtilities.Find("RingWorld_Body/Sector_RingInterior/Sector_Zone1/Interactables_Zone1/Props_IP_ZoneSign_1/Arc_TestAlienWriting/Arc 1") - .GetComponent() - .sharedMaterial; + var existingGhostArcs = GameObject.FindObjectsOfType() + .Select(x => x?._textLine?.gameObject) + .Where(x => x != null) + .OrderBy(x => x.transform.GetPath()) // order by path so game updates dont break things + .ToArray(); + _ghostArcPrefabs = new List(); + foreach (var existingArc in existingGhostArcs) + { + _ghostArcPrefabs.Add(existingArc.InstantiateInactive().Rename("Arc (Ghost)").DontDestroyOnLoad()); + } } if (_scrollPrefab == null) _scrollPrefab = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Sector_NorthHemisphere/Sector_NorthPole/Sector_HangingCity/Sector_HangingCity_District2/Interactables_HangingCity_District2/Prefab_NOM_Scroll").InstantiateInactive().Rename("Prefab_NOM_Scroll").DontDestroyOnLoad(); @@ -120,17 +141,17 @@ namespace NewHorizons.Builder.Props } } - public static GameObject Make(GameObject planetGO, Sector sector, PropModule.NomaiTextInfo info, NewHorizonsBody nhBody) + public static GameObject Make(GameObject planetGO, Sector sector, PropModule.NomaiTextInfo info, IModBehaviour mod) { InitPrefabs(); - var xmlPath = File.ReadAllText(Path.Combine(nhBody.Mod.ModHelper.Manifest.ModFolderPath, info.xmlFile)); + var xmlPath = File.ReadAllText(Path.Combine(mod.ModHelper.Manifest.ModFolderPath, info.xmlFile)); switch (info.type) { case PropModule.NomaiTextInfo.NomaiTextType.Wall: { - var nomaiWallTextObj = MakeWallText(planetGO, sector, info, xmlPath, nhBody).gameObject; + var nomaiWallTextObj = MakeWallText(planetGO, sector, info, xmlPath).gameObject; if (!string.IsNullOrEmpty(info.rename)) { @@ -178,12 +199,9 @@ namespace NewHorizons.Builder.Props // In global coordinates (normal was in local coordinates) var up = (nomaiWallTextObj.transform.position - planetGO.transform.position).normalized; var forward = planetGO.transform.TransformDirection(info.normal).normalized; - - nomaiWallTextObj.transform.forward = forward; - var desiredUp = Vector3.ProjectOnPlane(up, forward); - var zRotation = Vector3.SignedAngle(nomaiWallTextObj.transform.up, desiredUp, forward); - nomaiWallTextObj.transform.RotateAround(nomaiWallTextObj.transform.position, forward, zRotation); + nomaiWallTextObj.transform.up = up; + nomaiWallTextObj.transform.forward = forward; } if (info.rotation != null) { @@ -191,8 +209,6 @@ namespace NewHorizons.Builder.Props } } - // nomaiWallTextObj.GetComponent().DrawBoundsWithDebugSpheres(); - nomaiWallTextObj.SetActive(true); conversationInfoToCorrespondingSpawnedGameObject[info] = nomaiWallTextObj; @@ -211,7 +227,7 @@ namespace NewHorizons.Builder.Props customScroll.name = _scrollPrefab.name; } - var nomaiWallText = MakeWallText(planetGO, sector, info, xmlPath, nhBody); + var nomaiWallText = MakeWallText(planetGO, sector, info, xmlPath); nomaiWallText.transform.parent = customScroll.transform; nomaiWallText.transform.localPosition = Vector3.zero; nomaiWallText.transform.localRotation = Quaternion.identity; @@ -572,7 +588,7 @@ namespace NewHorizons.Builder.Props } } - private static NomaiWallText MakeWallText(GameObject go, Sector sector, PropModule.NomaiTextInfo info, string xmlPath, NewHorizonsBody nhBody) + private static NomaiWallText MakeWallText(GameObject go, Sector sector, PropModule.NomaiTextInfo info, string xmlPath) { GameObject nomaiWallTextObj = new GameObject("NomaiWallText"); nomaiWallTextObj.SetActive(false); @@ -594,7 +610,7 @@ namespace NewHorizons.Builder.Props // Text assets need a name to be used with VoiceMod text.name = Path.GetFileNameWithoutExtension(info.xmlFile); - BuildArcs(xmlPath, nomaiWallText, nomaiWallTextObj, info, nhBody); + BuildArcs(xmlPath, nomaiWallText, nomaiWallTextObj, info); AddTranslation(xmlPath); nomaiWallText._nomaiTextAsset = text; @@ -609,30 +625,19 @@ namespace NewHorizons.Builder.Props return nomaiWallText; } - internal static void BuildArcs(string xml, NomaiWallText nomaiWallText, GameObject conversationZone, PropModule.NomaiTextInfo info, NewHorizonsBody nhBody) + internal static void BuildArcs(string xml, NomaiWallText nomaiWallText, GameObject conversationZone, PropModule.NomaiTextInfo info) { var dict = MakeNomaiTextDict(xml); nomaiWallText._dictNomaiTextData = dict; - var cacheKey = xml.GetHashCode() + " " + JsonConvert.SerializeObject(info).GetHashCode(); - RefreshArcs(nomaiWallText, conversationZone, info, nhBody, cacheKey); + RefreshArcs(nomaiWallText, conversationZone, info); } - [Serializable] - private struct ArcCacheData - { - public MMesh mesh; - public MVector3[] skeletonPoints; - public MVector3 position; - public float zRotation; - public bool mirrored; - } - - internal static void RefreshArcs(NomaiWallText nomaiWallText, GameObject conversationZone, PropModule.NomaiTextInfo info, NewHorizonsBody nhBody, string cacheKey) + internal static void RefreshArcs(NomaiWallText nomaiWallText, GameObject conversationZone, PropModule.NomaiTextInfo info) { var dict = nomaiWallText._dictNomaiTextData; - Random.InitState(info.seed == 0 ? info.xmlFile.GetHashCode() : info.seed); + Random.InitState(info.seed); var arcsByID = new Dictionary(); @@ -642,14 +647,6 @@ namespace NewHorizons.Builder.Props return; } - ArcCacheData[] cachedData = null; - if (nhBody.Cache?.ContainsKey(cacheKey) ?? false) - cachedData = nhBody.Cache.Get(cacheKey); - - var arranger = nomaiWallText.gameObject.AddComponent(); - - // Generate spiral meshes/GOs - var i = 0; foreach (var textData in dict.Values) { @@ -659,129 +656,76 @@ namespace NewHorizons.Builder.Props var parent = parentID == -1 ? null : arcsByID[parentID]; - GameObject arcReadFromCache = null; - if (cachedData != null) - { - var skeletonPoints = cachedData[i].skeletonPoints.Select(mv => (Vector3)mv).ToArray(); - arcReadFromCache = NomaiTextArcBuilder.BuildSpiralGameObject(skeletonPoints, cachedData[i].mesh); - arcReadFromCache.transform.parent = arranger.transform; - arcReadFromCache.transform.localScale = new Vector3(cachedData[i].mirrored? -1 : 1, 1, 1); - arcReadFromCache.transform.localPosition = cachedData[i].position; - arcReadFromCache.transform.localEulerAngles = new Vector3(0, 0, cachedData[i].zRotation); - } - - GameObject arc = MakeArc(arcInfo, conversationZone, parent, textEntryID, arcReadFromCache); - arc.name = $"Arc {textEntryID} - Child of {parentID}"; + GameObject arc = MakeArc(arcInfo, conversationZone, parent, textEntryID); + arc.name = $"Arc {i} - Child of {parentID}"; arcsByID.Add(textEntryID, arc); i++; } - - // no need to arrange if the cache exists - if (cachedData == null) - { - Logger.LogVerbose("Cache and/or cache entry was null, proceding with wall text arc arrangment."); - - // auto placement - - var overlapFound = true; - for (var k = 0; k < arranger.spirals.Count*2; k++) - { - overlapFound = arranger.AttemptOverlapResolution(); - if (!overlapFound) break; - for(var a = 0; a < 10; a++) arranger.FDGSimulationStep(); - } - - if (overlapFound) Logger.LogVerbose("Overlap resolution failed!"); - - // manual placement - - for (var j = 0; j < info.arcInfo?.Length; j++) - { - var arcInfo = info.arcInfo[j]; - var arc = arranger.spirals[j]; - - if (arcInfo.keepAutoPlacement) continue; - - if (arcInfo.position == null) arc.transform.localPosition = Vector3.zero; - else arc.transform.localPosition = new Vector3(arcInfo.position.x, arcInfo.position.y, 0); - - arc.transform.localRotation = Quaternion.Euler(0, 0, arcInfo.zRotation); - - if (arcInfo.mirror) arc.transform.localScale = new Vector3(-1, 1, 1); - else arc.transform.localScale = new Vector3( 1, 1, 1); - } - - // make an entry in the cache for all these spirals - - if (nhBody.Cache != null) - { - var cacheData = arranger.spirals.Select(spiralManipulator => new ArcCacheData() - { - mesh = spiralManipulator.GetComponent().sharedMesh, // TODO: create a serializable version of Mesh and pass this: spiralManipulator.GetComponent().mesh - skeletonPoints = spiralManipulator.NomaiTextLine._points.Select(v => (MVector3)v).ToArray(), - position = spiralManipulator.transform.localPosition, - zRotation = spiralManipulator.transform.localEulerAngles.z, - mirrored = spiralManipulator.transform.localScale.x < 0 - }).ToArray(); - - nhBody.Cache.Set(cacheKey, cacheData); - } - } } - internal static GameObject MakeArc(PropModule.NomaiTextArcInfo arcInfo, GameObject conversationZone, GameObject parent, int textEntryID, GameObject prebuiltArc = null) + internal static GameObject MakeArc(PropModule.NomaiTextArcInfo arcInfo, GameObject conversationZone, GameObject parent, int textEntryID) { GameObject arc; var type = arcInfo != null ? arcInfo.type : PropModule.NomaiTextArcInfo.NomaiTextArcType.Adult; - NomaiTextArcBuilder.SpiralProfile profile; - Material mat; - Mesh overrideMesh = null; - Color? overrideColor = null; + var variation = arcInfo != null ? arcInfo.variation : -1; switch (type) { case PropModule.NomaiTextArcInfo.NomaiTextArcType.Child: - profile = NomaiTextArcBuilder.childSpiralProfile; - mat = _childArcMaterial; + variation = variation < 0 + ? Random.Range(0, _childArcPrefabs.Count()) + : (variation % _childArcPrefabs.Count()); + arc = _childArcPrefabs[variation].InstantiateInactive(); break; - case PropModule.NomaiTextArcInfo.NomaiTextArcType.Stranger when _ghostArcMaterial != null: - profile = NomaiTextArcBuilder.strangerSpiralProfile; - mat = _ghostArcMaterial; - overrideMesh = MeshUtilities.RectangleMeshFromCorners(new Vector3[]{ new Vector3(-0.9f, 0.0f, 0.0f), new Vector3(0.9f, 0.0f, 0.0f), new Vector3(-0.9f, 2.0f, 0.0f), new Vector3(0.9f, 2.0f, 0.0f) }); - overrideColor = new Color(0.0158f, 1.0f, 0.5601f, 1f); + case PropModule.NomaiTextArcInfo.NomaiTextArcType.Stranger when _ghostArcPrefabs.Any(): + variation = variation < 0 + ? Random.Range(0, _ghostArcPrefabs.Count()) + : (variation % _ghostArcPrefabs.Count()); + arc = _ghostArcPrefabs[variation].InstantiateInactive(); break; case PropModule.NomaiTextArcInfo.NomaiTextArcType.Adult: default: - profile = NomaiTextArcBuilder.adultSpiralProfile; - mat = _adultArcMaterial; + variation = variation < 0 + ? Random.Range(0, _arcPrefabs.Count()) + : (variation % _arcPrefabs.Count()); + arc = _arcPrefabs[variation].InstantiateInactive(); break; } - - if (prebuiltArc != null) - { - arc = prebuiltArc; - } - else - { - if (parent != null) arc = parent.GetComponent().AddChild(profile).gameObject; - else arc = NomaiTextArcArranger.CreateSpiral(profile, conversationZone).gameObject; - } - - if (mat != null) arc.GetComponent().sharedMaterial = mat; arc.transform.parent = conversationZone.transform; arc.GetComponent()._prebuilt = false; + if (arcInfo != null) + { + arcInfo.variation = variation; + if (arcInfo.position == null) arc.transform.localPosition = Vector3.zero; + else arc.transform.localPosition = new Vector3(arcInfo.position.x, arcInfo.position.y, 0); + + arc.transform.localRotation = Quaternion.Euler(0, 0, arcInfo.zRotation); + + if (arcInfo.mirror) arc.transform.localScale = new Vector3(-1, 1, 1); + } + // Try auto I guess + else + { + if (parent == null) + { + arc.transform.localPosition = Vector3.zero; + } + else + { + var points = parent.GetComponent().GetPoints(); + var point = points[points.Count() / 2]; + + arc.transform.localPosition = point; + arc.transform.localRotation = Quaternion.Euler(0, 0, Random.Range(0, 360)); + } + } + arc.GetComponent().SetEntryID(textEntryID); arc.GetComponent().enabled = false; - if (overrideMesh != null) - arc.GetComponent().sharedMesh = overrideMesh; - - if (overrideColor != null) - arc.GetComponent()._targetColor = (Color)overrideColor; - arc.SetActive(true); if (arcInfo != null) arcInfoToCorrespondingSpawnedGameObject[arcInfo] = arc; diff --git a/NewHorizons/Builder/Props/PropBuildManager.cs b/NewHorizons/Builder/Props/PropBuildManager.cs index b1018554..10536aaa 100644 --- a/NewHorizons/Builder/Props/PropBuildManager.cs +++ b/NewHorizons/Builder/Props/PropBuildManager.cs @@ -132,7 +132,22 @@ namespace NewHorizons.Builder.Props { try { - NomaiTextBuilder.Make(go, sector, nomaiTextInfo, nhBody); + NomaiTextBuilder.Make(go, sector, nomaiTextInfo, nhBody.Mod); + } + catch (Exception ex) + { + Logger.LogError($"Couldn't make text [{nomaiTextInfo.xmlFile}] for [{go.name}]:\n{ex}"); + } + + } + } + if (config.Props.translatorText != null) + { + foreach (var nomaiTextInfo in config.Props.translatorText) + { + try + { + TranslatorTextBuilder.Make(go, sector, nomaiTextInfo, nhBody); } catch (Exception ex) { diff --git a/NewHorizons/Builder/Props/RemoteBuilder.cs b/NewHorizons/Builder/Props/RemoteBuilder.cs index f179cdd9..4fa179f3 100644 --- a/NewHorizons/Builder/Props/RemoteBuilder.cs +++ b/NewHorizons/Builder/Props/RemoteBuilder.cs @@ -194,7 +194,7 @@ namespace NewHorizons.Builder.Props { var textInfo = info.nomaiText[i]; component._remoteIDs[i] = RemoteHandler.GetPlatformID(textInfo.id); - var wallText = NomaiTextBuilder.Make(whiteboard, sector, new PropModule.NomaiTextInfo + var wallText = TranslatorTextBuilder.Make(whiteboard, sector, new PropModule.NomaiTextInfo { arcInfo = textInfo.arcInfo, location = textInfo.location, diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs b/NewHorizons/Builder/Props/TranslatorText/NomaiTextArcArranger.cs similarity index 100% rename from NewHorizons/Builder/Props/NomaiText/NomaiTextArcArranger.cs rename to NewHorizons/Builder/Props/TranslatorText/NomaiTextArcArranger.cs diff --git a/NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs b/NewHorizons/Builder/Props/TranslatorText/NomaiTextArcBuilder.cs similarity index 100% rename from NewHorizons/Builder/Props/NomaiText/NomaiTextArcBuilder.cs rename to NewHorizons/Builder/Props/TranslatorText/NomaiTextArcBuilder.cs diff --git a/NewHorizons/Builder/Props/TranslatorText/TranslatorTextBuilder.cs b/NewHorizons/Builder/Props/TranslatorText/TranslatorTextBuilder.cs new file mode 100644 index 00000000..f864361c --- /dev/null +++ b/NewHorizons/Builder/Props/TranslatorText/TranslatorTextBuilder.cs @@ -0,0 +1,845 @@ +using NewHorizons.External.Modules; +using NewHorizons.Handlers; +using NewHorizons.Utility; +using OWML.Common; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml; +using UnityEngine; +using Enum = System.Enum; +using Logger = NewHorizons.Utility.Logger; +using Random = UnityEngine.Random; +using OWML.Utils; +using Newtonsoft.Json; +using System; + +namespace NewHorizons.Builder.Props +{ + public static class TranslatorTextBuilder + { + private static Material _ghostArcMaterial; + private static Material _adultArcMaterial; + private static Material _childArcMaterial; + private static GameObject _scrollPrefab; + private static GameObject _computerPrefab; + private static GameObject _preCrashComputerPrefab; + private static GameObject _cairnPrefab; + private static GameObject _cairnVariantPrefab; + private static GameObject _recorderPrefab; + private static GameObject _preCrashRecorderPrefab; + private static GameObject _trailmarkerPrefab; + + private static Dictionary arcInfoToCorrespondingSpawnedGameObject = new Dictionary(); + public static GameObject GetSpawnedGameObjectByNomaiTextArcInfo(PropModule.NomaiTextArcInfo arc) + { + if (!arcInfoToCorrespondingSpawnedGameObject.ContainsKey(arc)) return null; + return arcInfoToCorrespondingSpawnedGameObject[arc]; + } + + private static Dictionary conversationInfoToCorrespondingSpawnedGameObject = new Dictionary(); + + public static GameObject GetSpawnedGameObjectByNomaiTextInfo(PropModule.NomaiTextInfo convo) + { + Logger.LogVerbose("Retrieving wall text obj for " + convo); + if (!conversationInfoToCorrespondingSpawnedGameObject.ContainsKey(convo)) return null; + return conversationInfoToCorrespondingSpawnedGameObject[convo]; + } + + private static bool _isInit; + + internal static void InitPrefabs() + { + if (_isInit) return; + + _isInit = true; + + if (_adultArcMaterial == null) + { + _adultArcMaterial = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Sector_Crossroads/Interactables_Crossroads/Trailmarkers/Prefab_NOM_BH_Cairn_Arc (2)/Props_TH_ClutterSmall/Arc_Short/Arc") + .GetComponent() + .sharedMaterial; + } + + if (_childArcMaterial == null) + { + _childArcMaterial = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Sector_OldSettlement/Fragment OldSettlement 5/Core_OldSettlement 5/Interactables_Core_OldSettlement5/Arc_BH_OldSettlement_ChildrensRhyme/Arc 1") + .GetComponent() + .sharedMaterial; + } + + if (_ghostArcMaterial == null) + { + _ghostArcMaterial = SearchUtilities.Find("RingWorld_Body/Sector_RingInterior/Sector_Zone1/Interactables_Zone1/Props_IP_ZoneSign_1/Arc_TestAlienWriting/Arc 1") + .GetComponent() + .sharedMaterial; + } + + if (_scrollPrefab == null) _scrollPrefab = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Sector_NorthHemisphere/Sector_NorthPole/Sector_HangingCity/Sector_HangingCity_District2/Interactables_HangingCity_District2/Prefab_NOM_Scroll").InstantiateInactive().Rename("Prefab_NOM_Scroll").DontDestroyOnLoad(); + + if (_computerPrefab == null) + { + _computerPrefab = SearchUtilities.Find("VolcanicMoon_Body/Sector_VM/Interactables_VM/Prefab_NOM_Computer").InstantiateInactive().Rename("Prefab_NOM_Computer").DontDestroyOnLoad(); + _computerPrefab.transform.rotation = Quaternion.identity; + } + + if (_preCrashComputerPrefab == null) + { + _preCrashComputerPrefab = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Sector_EscapePodCrashSite/Sector_CrashFragment/EscapePod_Socket/Interactibles_EscapePod/Prefab_NOM_Vessel_Computer").InstantiateInactive().Rename("Prefab_NOM_Vessel_Computer").DontDestroyOnLoad(); + _preCrashComputerPrefab.transform.rotation = Quaternion.identity; + } + + if (_cairnPrefab == null) + { + _cairnPrefab = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Sector_Crossroads/Interactables_Crossroads/Trailmarkers/Prefab_NOM_BH_Cairn_Arc (1)").InstantiateInactive().Rename("Prefab_NOM_Cairn").DontDestroyOnLoad(); + _cairnPrefab.transform.rotation = Quaternion.identity; + } + + if (_cairnVariantPrefab == null) + { + _cairnVariantPrefab = SearchUtilities.Find("TimberHearth_Body/Sector_TH/Sector_NomaiMines/Interactables_NomaiMines/Prefab_NOM_TH_Cairn_Arc").InstantiateInactive().Rename("Prefab_NOM_Cairn").DontDestroyOnLoad(); + _cairnVariantPrefab.transform.rotation = Quaternion.identity; + } + + if (_recorderPrefab == null) + { + _recorderPrefab = SearchUtilities.Find("Comet_Body/Prefab_NOM_Shuttle/Sector_NomaiShuttleInterior/Interactibles_NomaiShuttleInterior/Prefab_NOM_Recorder").InstantiateInactive().Rename("Prefab_NOM_Recorder").DontDestroyOnLoad(); + _recorderPrefab.transform.rotation = Quaternion.identity; + } + + if (_preCrashRecorderPrefab == null) + { + _preCrashRecorderPrefab = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Sector_EscapePodCrashSite/Sector_CrashFragment/Interactables_CrashFragment/Prefab_NOM_Recorder").InstantiateInactive().Rename("Prefab_NOM_Recorder_Vessel").DontDestroyOnLoad(); + _preCrashRecorderPrefab.transform.rotation = Quaternion.identity; + } + + if (_trailmarkerPrefab == null) + { + _trailmarkerPrefab = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Sector_NorthHemisphere/Sector_NorthPole/Sector_HangingCity/Sector_HangingCity_District2/Interactables_HangingCity_District2/Prefab_NOM_Sign").InstantiateInactive().Rename("Prefab_NOM_Trailmarker").DontDestroyOnLoad(); + _trailmarkerPrefab.transform.rotation = Quaternion.identity; + } + } + + public static GameObject Make(GameObject planetGO, Sector sector, PropModule.NomaiTextInfo info, NewHorizonsBody nhBody) + { + InitPrefabs(); + + var xmlPath = File.ReadAllText(Path.Combine(nhBody.Mod.ModHelper.Manifest.ModFolderPath, info.xmlFile)); + + switch (info.type) + { + case PropModule.NomaiTextInfo.NomaiTextType.Wall: + { + var nomaiWallTextObj = MakeWallText(planetGO, sector, info, xmlPath, nhBody).gameObject; + + if (!string.IsNullOrEmpty(info.rename)) + { + nomaiWallTextObj.name = info.rename; + } + + nomaiWallTextObj.transform.parent = sector?.transform ?? planetGO.transform; + + if (!string.IsNullOrEmpty(info.parentPath)) + { + var newParent = planetGO.transform.Find(info.parentPath); + if (newParent != null) + { + nomaiWallTextObj.transform.parent = newParent; + } + else + { + Logger.LogError($"Cannot find parent object at path: {planetGO.name}/{info.parentPath}"); + } + } + + var pos = (Vector3)(info.position ?? Vector3.zero); + if (info.isRelativeToParent) + { + nomaiWallTextObj.transform.localPosition = pos; + if (info.normal != null) + { + // In global coordinates (normal was in local coordinates) + var up = (nomaiWallTextObj.transform.position - planetGO.transform.position).normalized; + var forward = planetGO.transform.TransformDirection(info.normal).normalized; + + nomaiWallTextObj.transform.up = up; + nomaiWallTextObj.transform.forward = forward; + } + if (info.rotation != null) + { + nomaiWallTextObj.transform.localRotation = Quaternion.Euler(info.rotation); + } + } + else + { + nomaiWallTextObj.transform.position = planetGO.transform.TransformPoint(pos); + if (info.normal != null) + { + // In global coordinates (normal was in local coordinates) + var up = (nomaiWallTextObj.transform.position - planetGO.transform.position).normalized; + var forward = planetGO.transform.TransformDirection(info.normal).normalized; + + nomaiWallTextObj.transform.forward = forward; + + var desiredUp = Vector3.ProjectOnPlane(up, forward); + var zRotation = Vector3.SignedAngle(nomaiWallTextObj.transform.up, desiredUp, forward); + nomaiWallTextObj.transform.RotateAround(nomaiWallTextObj.transform.position, forward, zRotation); + } + if (info.rotation != null) + { + nomaiWallTextObj.transform.rotation = planetGO.transform.TransformRotation(Quaternion.Euler(info.rotation)); + } + } + + // nomaiWallTextObj.GetComponent().DrawBoundsWithDebugSpheres(); + + nomaiWallTextObj.SetActive(true); + conversationInfoToCorrespondingSpawnedGameObject[info] = nomaiWallTextObj; + + return nomaiWallTextObj; + } + case PropModule.NomaiTextInfo.NomaiTextType.Scroll: + { + var customScroll = _scrollPrefab.InstantiateInactive(); + + if (!string.IsNullOrEmpty(info.rename)) + { + customScroll.name = info.rename; + } + else + { + customScroll.name = _scrollPrefab.name; + } + + var nomaiWallText = MakeWallText(planetGO, sector, info, xmlPath, nhBody); + nomaiWallText.transform.parent = customScroll.transform; + nomaiWallText.transform.localPosition = Vector3.zero; + nomaiWallText.transform.localRotation = Quaternion.identity; + + nomaiWallText._showTextOnStart = false; + + // Don't want to be able to translate until its in a socket + nomaiWallText.GetComponent().enabled = false; + + nomaiWallText.gameObject.SetActive(true); + + var scrollItem = customScroll.GetComponent(); + + // Idk why this thing is always around + GameObject.Destroy(customScroll.transform.Find("Arc_BH_City_Forum_2").gameObject); + + // This variable is the bane of my existence i dont get it + scrollItem._nomaiWallText = nomaiWallText; + + // Because the scroll was already awake it does weird shit in Awake and makes some of the entries in this array be null + scrollItem._colliders = new OWCollider[] { scrollItem.GetComponent() }; + + // Else when you put them down you can't pick them back up + customScroll.GetComponent()._physicsRemoved = false; + + // Place scroll + customScroll.transform.parent = sector?.transform ?? planetGO.transform; + + if (!string.IsNullOrEmpty(info.parentPath)) + { + var newParent = planetGO.transform.Find(info.parentPath); + if (newParent != null) + { + customScroll.transform.parent = newParent; + } + else + { + Logger.LogError($"Cannot find parent object at path: {planetGO.name}/{info.parentPath}"); + } + } + + var pos = (Vector3)(info.position ?? Vector3.zero); + if (info.isRelativeToParent) customScroll.transform.localPosition = pos; + else customScroll.transform.position = planetGO.transform.TransformPoint(pos); + + var up = planetGO.transform.InverseTransformPoint(customScroll.transform.position).normalized; + if (info.rotation != null) + { + customScroll.transform.rotation = planetGO.transform.TransformRotation(Quaternion.Euler(info.rotation)); + } + else + { + customScroll.transform.rotation = Quaternion.FromToRotation(customScroll.transform.up, up) * customScroll.transform.rotation; + } + + customScroll.SetActive(true); + + // Enable the collider and renderer + Delay.RunWhen( + () => Main.IsSystemReady, + () => + { + Logger.LogVerbose("Fixing scroll!"); + scrollItem._nomaiWallText = nomaiWallText; + scrollItem.SetSector(sector); + customScroll.transform.Find("Props_NOM_Scroll/Props_NOM_Scroll_Geo").GetComponent().enabled = true; + customScroll.transform.Find("Props_NOM_Scroll/Props_NOM_Scroll_Collider").gameObject.SetActive(true); + nomaiWallText.gameObject.GetComponent().enabled = false; + customScroll.GetComponent().enabled = true; + } + ); + conversationInfoToCorrespondingSpawnedGameObject[info] = customScroll; + + return customScroll; + } + case PropModule.NomaiTextInfo.NomaiTextType.Computer: + { + var computerObject = _computerPrefab.InstantiateInactive(); + + if (!string.IsNullOrEmpty(info.rename)) + { + computerObject.name = info.rename; + } + else + { + computerObject.name = _computerPrefab.name; + } + + computerObject.transform.parent = sector?.transform ?? planetGO.transform; + + if (!string.IsNullOrEmpty(info.parentPath)) + { + var newParent = planetGO.transform.Find(info.parentPath); + if (newParent != null) + { + computerObject.transform.parent = newParent; + } + else + { + Logger.LogError($"Cannot find parent object at path: {planetGO.name}/{info.parentPath}"); + } + } + + var pos = (Vector3)(info.position ?? Vector3.zero); + if (info.isRelativeToParent) computerObject.transform.localPosition = pos; + else computerObject.transform.position = planetGO.transform.TransformPoint(pos); + + var up = computerObject.transform.position - planetGO.transform.position; + if (info.normal != null) up = planetGO.transform.TransformDirection(info.normal); + computerObject.transform.rotation = Quaternion.FromToRotation(Vector3.up, up) * computerObject.transform.rotation; + + var computer = computerObject.GetComponent(); + computer.SetSector(sector); + + computer._location = EnumUtils.Parse(info.location.ToString()); + computer._dictNomaiTextData = MakeNomaiTextDict(xmlPath); + computer._nomaiTextAsset = new TextAsset(xmlPath); + computer._nomaiTextAsset.name = Path.GetFileNameWithoutExtension(info.xmlFile); + AddTranslation(xmlPath); + + // Make sure the computer model is loaded + StreamingHandler.SetUpStreaming(computerObject, sector); + + computerObject.SetActive(true); + conversationInfoToCorrespondingSpawnedGameObject[info] = computerObject; + + return computerObject; + } + case PropModule.NomaiTextInfo.NomaiTextType.PreCrashComputer: + { + var detailInfo = new PropModule.DetailInfo() + { + position = info.position, + parentPath = info.parentPath, + isRelativeToParent = info.isRelativeToParent, + rename = info.rename + }; + var computerObject = DetailBuilder.Make(planetGO, sector, _preCrashComputerPrefab, detailInfo); + computerObject.SetActive(false); + + var up = computerObject.transform.position - planetGO.transform.position; + if (info.normal != null) up = planetGO.transform.TransformDirection(info.normal); + computerObject.transform.rotation = Quaternion.FromToRotation(Vector3.up, up) * computerObject.transform.rotation; + + var computer = computerObject.GetComponent(); + computer.SetSector(sector); + + computer._location = EnumUtils.Parse(info.location.ToString()); + computer._dictNomaiTextData = MakeNomaiTextDict(xmlPath); + computer._nomaiTextAsset = new TextAsset(xmlPath); + computer._nomaiTextAsset.name = Path.GetFileNameWithoutExtension(info.xmlFile); + AddTranslation(xmlPath); + + // Make fifth ring work + var fifthRingObject = computerObject.FindChild("Props_NOM_Vessel_Computer 1/Props_NOM_Vessel_Computer_Effects (4)"); + fifthRingObject.SetActive(true); + var fifthRing = fifthRingObject.GetComponent(); + //fifthRing._baseProjectorColor = new Color(1.4118, 1.5367, 4, 1); + //fifthRing._baseTextColor = new Color(0.8824, 0.9604, 2.5, 1); + //fifthRing._baseTextShadowColor = new Color(0.3529, 0.3843, 1, 0.25); + fifthRing._computer = computer; + + computerObject.SetActive(true); + + // All rings are rendered by detail builder so dont do that (have to wait for entries to be set) + Delay.FireOnNextUpdate(() => + { + for (var i = computer.GetNumTextBlocks(); i < 5; i++) + { + var ring = computer._computerRings[i]; + ring.gameObject.SetActive(false); + } + }); + + conversationInfoToCorrespondingSpawnedGameObject[info] = computerObject; + + return computerObject; + } + case PropModule.NomaiTextInfo.NomaiTextType.Cairn: + case PropModule.NomaiTextInfo.NomaiTextType.CairnVariant: + { + var cairnObject = (info.type == PropModule.NomaiTextInfo.NomaiTextType.CairnVariant ? _cairnVariantPrefab : _cairnPrefab).InstantiateInactive(); + + if (!string.IsNullOrEmpty(info.rename)) + { + cairnObject.name = info.rename; + } + else + { + cairnObject.name = _cairnPrefab.name; + } + + cairnObject.transform.parent = sector?.transform ?? planetGO.transform; + + if (!string.IsNullOrEmpty(info.parentPath)) + { + var newParent = planetGO.transform.Find(info.parentPath); + if (newParent != null) + { + cairnObject.transform.parent = newParent; + } + else + { + Logger.LogError($"Cannot find parent object at path: {planetGO.name}/{info.parentPath}"); + } + } + + var pos = (Vector3)(info.position ?? Vector3.zero); + if (info.isRelativeToParent) cairnObject.transform.localPosition = pos; + else cairnObject.transform.position = planetGO.transform.TransformPoint(pos); + + if (info.rotation != null) + { + var rot = Quaternion.Euler(info.rotation); + if (info.isRelativeToParent) cairnObject.transform.localRotation = rot; + else cairnObject.transform.rotation = planetGO.transform.TransformRotation(rot); + } + else + { + // By default align it to normal + var up = (cairnObject.transform.position - planetGO.transform.position).normalized; + cairnObject.transform.rotation = Quaternion.FromToRotation(Vector3.up, up) * cairnObject.transform.rotation; + } + + // Idk do we have to set it active before finding things? + cairnObject.SetActive(true); + + // Make it do the thing when it finishes being knocked over + foreach (var rock in cairnObject.GetComponent()._rocks) + { + rock._returning = false; + rock._owCollider.SetActivation(true); + rock.enabled = false; + } + + // So we can actually knock it over + cairnObject.GetComponent().enabled = true; + + var nomaiWallText = cairnObject.transform.Find("Props_TH_ClutterSmall/Arc_Short").GetComponent(); + nomaiWallText.SetSector(sector); + + nomaiWallText._location = EnumUtils.Parse(info.location.ToString()); + nomaiWallText._dictNomaiTextData = MakeNomaiTextDict(xmlPath); + nomaiWallText._nomaiTextAsset = new TextAsset(xmlPath); + nomaiWallText._nomaiTextAsset.name = Path.GetFileNameWithoutExtension(info.xmlFile); + AddTranslation(xmlPath); + + // Make sure the computer model is loaded + StreamingHandler.SetUpStreaming(cairnObject, sector); + conversationInfoToCorrespondingSpawnedGameObject[info] = cairnObject; + + return cairnObject; + } + case PropModule.NomaiTextInfo.NomaiTextType.PreCrashRecorder: + case PropModule.NomaiTextInfo.NomaiTextType.Recorder: + { + var prefab = (info.type == PropModule.NomaiTextInfo.NomaiTextType.PreCrashRecorder ? _preCrashRecorderPrefab : _recorderPrefab); + var detailInfo = new PropModule.DetailInfo { + parentPath = info.parentPath, + rotation = info.rotation, + position = info.position, + isRelativeToParent = info.isRelativeToParent, + rename = info.rename + }; + var recorderObject = DetailBuilder.Make(planetGO, sector, prefab, detailInfo); + recorderObject.SetActive(false); + + if (info.rotation == null) + { + var up = recorderObject.transform.position - planetGO.transform.position; + recorderObject.transform.rotation = Quaternion.FromToRotation(Vector3.up, up) * recorderObject.transform.rotation; + } + + var nomaiText = recorderObject.GetComponentInChildren(); + nomaiText.SetSector(sector); + + nomaiText._location = EnumUtils.Parse(info.location.ToString()); + nomaiText._dictNomaiTextData = MakeNomaiTextDict(xmlPath); + nomaiText._nomaiTextAsset = new TextAsset(xmlPath); + nomaiText._nomaiTextAsset.name = Path.GetFileNameWithoutExtension(info.xmlFile); + AddTranslation(xmlPath); + + recorderObject.SetActive(true); + + recorderObject.transform.Find("InteractSphere").gameObject.GetComponent().enabled = true; + conversationInfoToCorrespondingSpawnedGameObject[info] = recorderObject; + return recorderObject; + } + case PropModule.NomaiTextInfo.NomaiTextType.Trailmarker: + { + var trailmarkerObject = _trailmarkerPrefab.InstantiateInactive(); + + if (!string.IsNullOrEmpty(info.rename)) + { + trailmarkerObject.name = info.rename; + } + else + { + trailmarkerObject.name = _trailmarkerPrefab.name; + } + + trailmarkerObject.transform.parent = sector?.transform ?? planetGO.transform; + + if (!string.IsNullOrEmpty(info.parentPath)) + { + var newParent = planetGO.transform.Find(info.parentPath); + if (newParent != null) + { + trailmarkerObject.transform.parent = newParent; + } + else + { + Logger.LogError($"Cannot find parent object at path: {planetGO.name}/{info.parentPath}"); + } + } + + var pos = (Vector3)(info.position ?? Vector3.zero); + if (info.isRelativeToParent) trailmarkerObject.transform.localPosition = pos; + else trailmarkerObject.transform.position = planetGO.transform.TransformPoint(pos); + + // shrink because that is what mobius does on all trailmarkers or else they are the size of the player + trailmarkerObject.transform.localScale = Vector3.one * 0.75f; + + if (info.rotation != null) + { + var rot = Quaternion.Euler(info.rotation); + if (info.isRelativeToParent) trailmarkerObject.transform.localRotation = rot; + else trailmarkerObject.transform.rotation = planetGO.transform.TransformRotation(rot); + } + else + { + // By default align it to normal + var up = (trailmarkerObject.transform.position - planetGO.transform.position).normalized; + trailmarkerObject.transform.rotation = Quaternion.FromToRotation(Vector3.up, up) * trailmarkerObject.transform.rotation; + } + + // Idk do we have to set it active before finding things? + trailmarkerObject.SetActive(true); + + var nomaiWallText = trailmarkerObject.transform.Find("Arc_Short").GetComponent(); + nomaiWallText.SetSector(sector); + + nomaiWallText._location = EnumUtils.Parse(info.location.ToString()); + nomaiWallText._dictNomaiTextData = MakeNomaiTextDict(xmlPath); + nomaiWallText._nomaiTextAsset = new TextAsset(xmlPath); + nomaiWallText._nomaiTextAsset.name = Path.GetFileNameWithoutExtension(info.xmlFile); + AddTranslation(xmlPath); + + // Make sure the model is loaded + StreamingHandler.SetUpStreaming(trailmarkerObject, sector); + conversationInfoToCorrespondingSpawnedGameObject[info] = trailmarkerObject; + + return trailmarkerObject; + } + default: + Logger.LogError($"Unsupported NomaiText type {info.type}"); + return null; + } + } + + private static NomaiWallText MakeWallText(GameObject go, Sector sector, PropModule.NomaiTextInfo info, string xmlPath, NewHorizonsBody nhBody) + { + GameObject nomaiWallTextObj = new GameObject("NomaiWallText"); + nomaiWallTextObj.SetActive(false); + + var box = nomaiWallTextObj.AddComponent(); + box.center = new Vector3(-0.0643f, 1.1254f, 0f); + box.size = new Vector3(6.1424f, 5.2508f, 0.5f); + + box.isTrigger = true; + + nomaiWallTextObj.AddComponent(); + + var nomaiWallText = nomaiWallTextObj.AddComponent(); + + nomaiWallText._location = EnumUtils.Parse(info.location.ToString()); + + var text = new TextAsset(xmlPath); + + // Text assets need a name to be used with VoiceMod + text.name = Path.GetFileNameWithoutExtension(info.xmlFile); + + BuildArcs(xmlPath, nomaiWallText, nomaiWallTextObj, info, nhBody); + AddTranslation(xmlPath); + nomaiWallText._nomaiTextAsset = text; + + nomaiWallText.SetTextAsset(text); + + // #433 fuzzy stranger text + if (info.arcInfo != null && info.arcInfo.Any(x => x.type == PropModule.NomaiTextArcInfo.NomaiTextArcType.Stranger)) + { + StreamingHandler.SetUpStreaming(AstroObject.Name.RingWorld, sector); + } + + return nomaiWallText; + } + + internal static void BuildArcs(string xml, NomaiWallText nomaiWallText, GameObject conversationZone, PropModule.NomaiTextInfo info, NewHorizonsBody nhBody) + { + var dict = MakeNomaiTextDict(xml); + + nomaiWallText._dictNomaiTextData = dict; + + var cacheKey = xml.GetHashCode() + " " + JsonConvert.SerializeObject(info).GetHashCode(); + RefreshArcs(nomaiWallText, conversationZone, info, nhBody, cacheKey); + } + + [Serializable] + private struct ArcCacheData + { + public MMesh mesh; + public MVector3[] skeletonPoints; + public MVector3 position; + public float zRotation; + public bool mirrored; + } + + internal static void RefreshArcs(NomaiWallText nomaiWallText, GameObject conversationZone, PropModule.NomaiTextInfo info, NewHorizonsBody nhBody, string cacheKey) + { + var dict = nomaiWallText._dictNomaiTextData; + Random.InitState(info.seed == 0 ? info.xmlFile.GetHashCode() : info.seed); + + var arcsByID = new Dictionary(); + + if (info.arcInfo != null && info.arcInfo.Count() != dict.Values.Count()) + { + Logger.LogError($"Can't make NomaiWallText, arcInfo length [{info.arcInfo.Count()}] doesn't equal text entries [{dict.Values.Count()}]"); + return; + } + + ArcCacheData[] cachedData = null; + if (nhBody.Cache?.ContainsKey(cacheKey) ?? false) + cachedData = nhBody.Cache.Get(cacheKey); + + var arranger = nomaiWallText.gameObject.AddComponent(); + + // Generate spiral meshes/GOs + + var i = 0; + foreach (var textData in dict.Values) + { + var arcInfo = info.arcInfo?.Length > i ? info.arcInfo[i] : null; + var textEntryID = textData.ID; + var parentID = textData.ParentID; + + var parent = parentID == -1 ? null : arcsByID[parentID]; + + GameObject arcReadFromCache = null; + if (cachedData != null) + { + var skeletonPoints = cachedData[i].skeletonPoints.Select(mv => (Vector3)mv).ToArray(); + arcReadFromCache = NomaiTextArcBuilder.BuildSpiralGameObject(skeletonPoints, cachedData[i].mesh); + arcReadFromCache.transform.parent = arranger.transform; + arcReadFromCache.transform.localScale = new Vector3(cachedData[i].mirrored? -1 : 1, 1, 1); + arcReadFromCache.transform.localPosition = cachedData[i].position; + arcReadFromCache.transform.localEulerAngles = new Vector3(0, 0, cachedData[i].zRotation); + } + + GameObject arc = MakeArc(arcInfo, conversationZone, parent, textEntryID, arcReadFromCache); + arc.name = $"Arc {textEntryID} - Child of {parentID}"; + + arcsByID.Add(textEntryID, arc); + + i++; + } + + // no need to arrange if the cache exists + if (cachedData == null) + { + Logger.LogVerbose("Cache and/or cache entry was null, proceding with wall text arc arrangment."); + + // auto placement + + var overlapFound = true; + for (var k = 0; k < arranger.spirals.Count*2; k++) + { + overlapFound = arranger.AttemptOverlapResolution(); + if (!overlapFound) break; + for(var a = 0; a < 10; a++) arranger.FDGSimulationStep(); + } + + if (overlapFound) Logger.LogVerbose("Overlap resolution failed!"); + + // manual placement + + for (var j = 0; j < info.arcInfo?.Length; j++) + { + var arcInfo = info.arcInfo[j]; + var arc = arranger.spirals[j]; + + if (arcInfo.keepAutoPlacement) continue; + + if (arcInfo.position == null) arc.transform.localPosition = Vector3.zero; + else arc.transform.localPosition = new Vector3(arcInfo.position.x, arcInfo.position.y, 0); + + arc.transform.localRotation = Quaternion.Euler(0, 0, arcInfo.zRotation); + + if (arcInfo.mirror) arc.transform.localScale = new Vector3(-1, 1, 1); + else arc.transform.localScale = new Vector3( 1, 1, 1); + } + + // make an entry in the cache for all these spirals + + if (nhBody.Cache != null) + { + var cacheData = arranger.spirals.Select(spiralManipulator => new ArcCacheData() + { + mesh = spiralManipulator.GetComponent().sharedMesh, // TODO: create a serializable version of Mesh and pass this: spiralManipulator.GetComponent().mesh + skeletonPoints = spiralManipulator.NomaiTextLine._points.Select(v => (MVector3)v).ToArray(), + position = spiralManipulator.transform.localPosition, + zRotation = spiralManipulator.transform.localEulerAngles.z, + mirrored = spiralManipulator.transform.localScale.x < 0 + }).ToArray(); + + nhBody.Cache.Set(cacheKey, cacheData); + } + } + } + + internal static GameObject MakeArc(PropModule.NomaiTextArcInfo arcInfo, GameObject conversationZone, GameObject parent, int textEntryID, GameObject prebuiltArc = null) + { + GameObject arc; + var type = arcInfo != null ? arcInfo.type : PropModule.NomaiTextArcInfo.NomaiTextArcType.Adult; + NomaiTextArcBuilder.SpiralProfile profile; + Material mat; + Mesh overrideMesh = null; + Color? overrideColor = null; + switch (type) + { + case PropModule.NomaiTextArcInfo.NomaiTextArcType.Child: + profile = NomaiTextArcBuilder.childSpiralProfile; + mat = _childArcMaterial; + break; + case PropModule.NomaiTextArcInfo.NomaiTextArcType.Stranger when _ghostArcMaterial != null: + profile = NomaiTextArcBuilder.strangerSpiralProfile; + mat = _ghostArcMaterial; + overrideMesh = MeshUtilities.RectangleMeshFromCorners(new Vector3[]{ new Vector3(-0.9f, 0.0f, 0.0f), new Vector3(0.9f, 0.0f, 0.0f), new Vector3(-0.9f, 2.0f, 0.0f), new Vector3(0.9f, 2.0f, 0.0f) }); + overrideColor = new Color(0.0158f, 1.0f, 0.5601f, 1f); + break; + case PropModule.NomaiTextArcInfo.NomaiTextArcType.Adult: + default: + profile = NomaiTextArcBuilder.adultSpiralProfile; + mat = _adultArcMaterial; + break; + } + + if (prebuiltArc != null) + { + arc = prebuiltArc; + } + else + { + if (parent != null) arc = parent.GetComponent().AddChild(profile).gameObject; + else arc = NomaiTextArcArranger.CreateSpiral(profile, conversationZone).gameObject; + } + + if (mat != null) arc.GetComponent().sharedMaterial = mat; + + arc.transform.parent = conversationZone.transform; + arc.GetComponent()._prebuilt = false; + + arc.GetComponent().SetEntryID(textEntryID); + arc.GetComponent().enabled = false; + + if (overrideMesh != null) + arc.GetComponent().sharedMesh = overrideMesh; + + if (overrideColor != null) + arc.GetComponent()._targetColor = (Color)overrideColor; + + arc.SetActive(true); + + if (arcInfo != null) arcInfoToCorrespondingSpawnedGameObject[arcInfo] = arc; + + return arc; + } + + private static Dictionary MakeNomaiTextDict(string xmlPath) + { + var dict = new Dictionary(); + + XmlDocument xmlDocument = new XmlDocument(); + xmlDocument.LoadXml(xmlPath); + XmlNode rootNode = xmlDocument.SelectSingleNode("NomaiObject"); + + foreach (object obj in rootNode.SelectNodes("TextBlock")) + { + XmlNode xmlNode = (XmlNode)obj; + + int textEntryID = -1; + int parentID = -1; + + XmlNode textNode = xmlNode.SelectSingleNode("Text"); + XmlNode entryIDNode = xmlNode.SelectSingleNode("ID"); + XmlNode parentIDNode = xmlNode.SelectSingleNode("ParentID"); + + if (entryIDNode != null && !int.TryParse(entryIDNode.InnerText, out textEntryID)) + { + Logger.LogError($"Couldn't parse int ID in [{entryIDNode?.InnerText}] for [{xmlPath}]"); + textEntryID = -1; + } + + if (parentIDNode != null && !int.TryParse(parentIDNode.InnerText, out parentID)) + { + Logger.LogError($"Couldn't parse int ParentID in [{parentIDNode?.InnerText}] for [{xmlPath}]"); + parentID = -1; + } + + NomaiText.NomaiTextData value = new NomaiText.NomaiTextData(textEntryID, parentID, textNode, false, NomaiText.Location.UNSPECIFIED); + dict.Add(textEntryID, value); + } + return dict; + } + + private static void AddTranslation(string xmlPath) + { + XmlDocument xmlDocument = new XmlDocument(); + xmlDocument.LoadXml(xmlPath); + + XmlNode xmlNode = xmlDocument.SelectSingleNode("NomaiObject"); + XmlNodeList xmlNodeList = xmlNode.SelectNodes("TextBlock"); + + foreach (object obj in xmlNodeList) + { + XmlNode xmlNode2 = (XmlNode)obj; + var text = xmlNode2.SelectSingleNode("Text").InnerText; + TranslationHandler.AddDialogue(text); + } + } + } +} diff --git a/NewHorizons/External/Modules/PropModule.cs b/NewHorizons/External/Modules/PropModule.cs index 9cc829e6..a2d16a1e 100644 --- a/NewHorizons/External/Modules/PropModule.cs +++ b/NewHorizons/External/Modules/PropModule.cs @@ -33,10 +33,16 @@ namespace NewHorizons.External.Modules /// public GeyserInfo[] geysers; + /// + /// Add translatable text to this planet. (LEGACY - for use with pre-autospirals configs) + /// + [Obsolete("nomaiText is deprecated as of the release of auto spirals, instead please use translatorText with new configs.")] + public NomaiTextInfo[] nomaiText; + /// /// Add translatable text to this planet /// - public NomaiTextInfo[] nomaiText; + public NomaiTextInfo[] translatorText; /// /// Details which will be shown from 50km away. Meant to be lower resolution. diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index 93e4ed9c..eff0ff25 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -275,6 +275,7 @@ namespace NewHorizons GeyserBuilder.InitPrefab(); LavaBuilder.InitPrefabs(); NomaiTextBuilder.InitPrefabs(); + TranslatorTextBuilder.InitPrefabs(); RemoteBuilder.InitPrefabs(); SandBuilder.InitPrefabs(); SingularityBuilder.InitPrefabs(); diff --git a/NewHorizons/Utility/DebugMenu/DebugMenuNomaiText.cs b/NewHorizons/Utility/DebugMenu/DebugMenuNomaiText.cs index 2ebcc329..07d192d8 100644 --- a/NewHorizons/Utility/DebugMenu/DebugMenuNomaiText.cs +++ b/NewHorizons/Utility/DebugMenu/DebugMenuNomaiText.cs @@ -100,7 +100,7 @@ namespace NewHorizons.Utility.DebugMenu ConversationMetadata conversationMetadata = new ConversationMetadata() { conversation = conversation, - conversationGo = NomaiTextBuilder.GetSpawnedGameObjectByNomaiTextInfo(conversation), + conversationGo = TranslatorTextBuilder.GetSpawnedGameObjectByNomaiTextInfo(conversation), planetConfig = config, spirals = new List(), collapsed = true @@ -120,7 +120,7 @@ namespace NewHorizons.Utility.DebugMenu SpiralMetadata metadata = new SpiralMetadata() { spiral = arcInfo, - spiralGo = NomaiTextBuilder.GetSpawnedGameObjectByNomaiTextArcInfo(arcInfo), + spiralGo = TranslatorTextBuilder.GetSpawnedGameObjectByNomaiTextArcInfo(arcInfo), conversation = conversation, planetConfig = config, planetName = config.name, @@ -365,7 +365,7 @@ namespace NewHorizons.Utility.DebugMenu for (indexInParent = 0; indexInParent < wallTextComponent._textLines.Length; indexInParent++) if (oldTextLineComponent == wallTextComponent._textLines[indexInParent]) break; var textEntryId = oldTextLineComponent._entryID; GameObject.Destroy(spiralMeta.spiralGo); - spiralMeta.spiralGo = NomaiTextBuilder.MakeArc(spiralMeta.spiral, conversationZone, null, textEntryId); + spiralMeta.spiralGo = TranslatorTextBuilder.MakeArc(spiralMeta.spiral, conversationZone, null, textEntryId); wallTextComponent._textLines[indexInParent] = spiralMeta.spiralGo.GetComponent(); spiralMeta.spiralGo.name = "Brandnewspiral"; From d080be35f3fc1f168c9b474f8ca96b58313a829a Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Tue, 21 Feb 2023 17:24:44 -0500 Subject: [PATCH 57/58] removed uneeded comment --- .../Builder/Props/TranslatorText/TranslatorTextBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/Builder/Props/TranslatorText/TranslatorTextBuilder.cs b/NewHorizons/Builder/Props/TranslatorText/TranslatorTextBuilder.cs index f864361c..ce06a4b1 100644 --- a/NewHorizons/Builder/Props/TranslatorText/TranslatorTextBuilder.cs +++ b/NewHorizons/Builder/Props/TranslatorText/TranslatorTextBuilder.cs @@ -719,7 +719,7 @@ namespace NewHorizons.Builder.Props { var cacheData = arranger.spirals.Select(spiralManipulator => new ArcCacheData() { - mesh = spiralManipulator.GetComponent().sharedMesh, // TODO: create a serializable version of Mesh and pass this: spiralManipulator.GetComponent().mesh + mesh = spiralManipulator.GetComponent().sharedMesh, skeletonPoints = spiralManipulator.NomaiTextLine._points.Select(v => (MVector3)v).ToArray(), position = spiralManipulator.transform.localPosition, zRotation = spiralManipulator.transform.localEulerAngles.z, From 9da397cdff340af6cf12ec418d5e9c97734a0068 Mon Sep 17 00:00:00 2001 From: Ben C Date: Tue, 21 Feb 2023 22:27:20 +0000 Subject: [PATCH 58/58] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index 3b7d3fc8..3ef66e6a 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -947,7 +947,7 @@ "$ref": "#/definitions/GeyserInfo" } }, - "nomaiText": { + "translatorText": { "type": "array", "description": "Add translatable text to this planet", "items": {