using System.Runtime.InteropServices; using UnityEngine; /// /// The renderer's lowering of the zone's potential (aetheria-gravity, cut grid-lowering). Zone.GetHeight owns the drawn /// height: this hands its inputs (each term's position, strength, softening, band edges and reference, and the /// exaggeration) to Assets/Shaders/Gravity.cginc, which sums them per texel, and makes the one quad that carries the sum into every /// camera that culls layer 9. Nothing here decides a height. /// public static class GravityLowering { /// The layer the gravity cameras cull; nothing but the run-time field sits on it. public const int FieldLayer = 9; /// The quad's side in u: larger than any zone (10000) plus any view (4096); texels outside a camera cost nothing. public const float FieldExtent = 100000f; // Mirrors the term record of Gravity.cginc: float2 position, strength, softening, inner, outer, reference. [StructLayout(LayoutKind.Sequential)] struct TermRecord { public Vector2 Position; public float Strength; public float Softening; public float Inner; public float Outer; public float Reference; } static readonly int Stride = Marshal.SizeOf(); static readonly int WellsId = Shader.PropertyToID("_GravityWells"); static readonly int CountId = Shader.PropertyToID("_GravityWellCount"); static readonly int ExaggerationId = Shader.PropertyToID("_HeightExaggeration"); static TermRecord[] _records = new TermRecord[0]; /// /// Fills one record per term of the zone's WellTree (a null zone has none) and sets the shader globals. The buffer is /// the caller's, (re)allocated to the term count (at least one, so the global is always bound). Terms are lowered at /// their node's rail at the drawn time (DrawAhead.Time), the same T the planets and a ship's ride height use. /// public static void Lower(Zone zone, float lead, ref ComputeBuffer wells) { var count = zone?.Wells.Terms.Count ?? 0; var capacity = Mathf.Max(count, 1); if (wells == null || wells.count != capacity) { wells?.Release(); wells = new ComputeBuffer(capacity, Stride); } if (_records.Length != capacity) _records = new TermRecord[capacity]; for (var t = 0; t < count; t++) { var term = zone.Wells.Terms[t]; var at = term.Node.IsSet() ? DrawAhead.Orbit(zone, term.Node, lead) : zone.Wells.Centre; // an unset node is the zone node, stationary at Centre _records[t] = new TermRecord { Position = new Vector2(at.x, at.y), Strength = term.Strength, Softening = term.Softening, Inner = term.Inner, Outer = term.Outer, Reference = term.Reference }; } wells.SetData(_records); Shader.SetGlobalBuffer(WellsId, wells); Shader.SetGlobalInt(CountId, count); Shader.SetGlobalFloat(ExaggerationId, zone?.Settings.HeightExaggeration ?? 1f); } /// The one quad of the field: flat in xz at y 0 on layer 9, drawn with the Gravity Field material. public static GameObject CreateField(Material material) { var field = new GameObject("Gravity Field") { layer = FieldLayer }; field.transform.rotation = Quaternion.Euler(90f, 0f, 0f); field.transform.localScale = FieldExtent * Vector3.one; field.AddComponent().sharedMesh = Resources.GetBuiltinResource("Quad.fbx"); var renderer = field.AddComponent(); renderer.sharedMaterial = material; renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; renderer.receiveShadows = false; return field; } }