using NewHorizons.Utility.OWML; using System.Collections.Generic; using System.Linq; using UnityEngine; namespace NewHorizons.Builder.Props.TranslatorText { public static class NomaiTextArcBuilder { // TODO: stranger arcs // 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") { var m = new SpiralMesh(profile); m.Randomize(); m.updateMesh(); // // rotate mesh to point up // var norm = m.skeleton[1] - m.skeleton[0]; float r = Mathf.Atan2(-norm.y, norm.x) * Mathf.Rad2Deg; var ang = -90-r; // using m.sharedMesh causes old meshes to disappear for some reason, idk why 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(); // 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(); 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; owNomaiTextLine._prebuilt = false; g.SetActive(true); return g; } #region spiral shape definitions public struct SpiralProfile { // all of the Vector2 params here refer to a range of valid values public string profileName; /// /// What is this /// public Vector2 a; /// /// What is this /// public Vector2 b; /// /// What is this /// public Vector2 startS; /// /// What is this /// public Vector2 endS; /// /// What is this /// public Vector2 skeletonScale; /// /// What is this /// public int numSkeletonPoints; /// /// What is this /// public float uvScale; /// /// Width at tip /// public float innerWidth; /// /// Width at base /// public float outerWidth; public Material material; } public static SpiralProfile adultSpiralProfile = new SpiralProfile() { profileName="Adult", 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 = 0.75f * 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", a = new Vector2(0.9f, 0.9f), 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), numSkeletonPoints = 51, innerWidth = 0.001f/10f, outerWidth = 2f*0.05f, uvScale = 4.9f * 0.55f, }; 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(0.6f, 0.6f), numSkeletonPoints = 17, innerWidth = 0.75f, outerWidth = 0.75f, uvScale = 1f/1.8505f, }; #endregion spiral shape definitions #region mesh generation 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; // width at the base public float uvScale = 4.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 = Random.value; } public override void Randomize() { base.Randomize(); uvOffset = 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 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(startS); var startT = tFromArcLen(endS); var rangeT = endT - startT; for (int i = 0; i