From c977cb53335cafa81d81e499a27ceb48453113da Mon Sep 17 00:00:00 2001 From: "Nick J. Connors" Date: Sun, 19 Dec 2021 02:37:52 -0500 Subject: [PATCH] Decluttered logs and added asteroid belts Also includes some basic proc gen --- NewHorizons/AssetBundle/shader | Bin 0 -> 18768 bytes .../{assets => AssetBundle}/shader.manifest | 5 +- NewHorizons/Atmosphere/AirBuilder.cs | 1 - NewHorizons/Atmosphere/AtmosphereBuilder.cs | 1 - NewHorizons/Atmosphere/CloudsBuilder.cs | 4 +- NewHorizons/Atmosphere/EffectsBuilder.cs | 2 - NewHorizons/Atmosphere/SunOverrideBuilder.cs | 1 - NewHorizons/Atmosphere/VolumesBuilder.cs | 1 - NewHorizons/Body/AsteroidBeltBuilder.cs | 64 ++++ NewHorizons/Body/Geometry/CubeSphere.cs | 12 - NewHorizons/Body/Geometry/Icosphere.cs | 15 +- NewHorizons/Body/Geometry/Perlin.cs | 176 +++++++++ NewHorizons/Body/GeometryBuilder.cs | 1 - NewHorizons/Body/HeightMapBuilder.cs | 10 +- NewHorizons/Body/ProcGenBuilder.cs | 34 ++ NewHorizons/Body/RingBuilder.cs | 11 +- NewHorizons/Body/SunBuilder.cs | 16 + NewHorizons/Body/WaterBuilder.cs | 2 - NewHorizons/External/AsteroidBeltModule.cs | 16 + NewHorizons/External/BaseModule.cs | 1 + NewHorizons/External/IPlanetConfig.cs | 2 + NewHorizons/External/Module.cs | 6 +- NewHorizons/External/OrbitModule.cs | 1 + NewHorizons/External/PlanetConfig.cs | 42 +-- NewHorizons/External/ProcGenModule.cs | 15 + NewHorizons/General/AmbientLightBuilder.cs | 2 - NewHorizons/General/BaseBuilder.cs | 13 - NewHorizons/General/DetectorBuilder.cs | 9 +- NewHorizons/General/GravityBuilder.cs | 2 - NewHorizons/General/InitialMotionBuilder.cs | 54 +++ NewHorizons/General/MarkerBuilder.cs | 1 - NewHorizons/General/OrbitlineBuilder.cs | 21 +- NewHorizons/General/PlanetDestroyer.cs | 118 ++++++ NewHorizons/General/RFVolumeBuilder.cs | 19 +- NewHorizons/General/SectorBuilder.cs | 1 - NewHorizons/Main.cs | 345 ++++++------------ NewHorizons/NewHorizons.csproj | 14 +- NewHorizons/Physics/OrbitalHelper.cs | 99 +++++ NewHorizons/Physics/ParameterizedOrbitLine.cs | 125 +++++++ NewHorizons/Utility/AddToUITable.cs | 8 +- NewHorizons/Utility/AstroObjectLocator.cs | 15 +- NewHorizons/assets/shader | Bin 18191 -> 0 bytes 42 files changed, 929 insertions(+), 356 deletions(-) create mode 100644 NewHorizons/AssetBundle/shader rename NewHorizons/{assets => AssetBundle}/shader.manifest (75%) create mode 100644 NewHorizons/Body/AsteroidBeltBuilder.cs create mode 100644 NewHorizons/Body/Geometry/Perlin.cs create mode 100644 NewHorizons/Body/ProcGenBuilder.cs create mode 100644 NewHorizons/Body/SunBuilder.cs create mode 100644 NewHorizons/External/AsteroidBeltModule.cs create mode 100644 NewHorizons/External/ProcGenModule.cs create mode 100644 NewHorizons/General/InitialMotionBuilder.cs create mode 100644 NewHorizons/General/PlanetDestroyer.cs create mode 100644 NewHorizons/Physics/OrbitalHelper.cs create mode 100644 NewHorizons/Physics/ParameterizedOrbitLine.cs delete mode 100644 NewHorizons/assets/shader diff --git a/NewHorizons/AssetBundle/shader b/NewHorizons/AssetBundle/shader new file mode 100644 index 0000000000000000000000000000000000000000..a6cf02462fe92a16a8dbf3ca8465ece8becc9894 GIT binary patch literal 18768 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 L#An<8<8)_w7z|CT literal 0 HcmV?d00001 diff --git a/NewHorizons/assets/shader.manifest b/NewHorizons/AssetBundle/shader.manifest similarity index 75% rename from NewHorizons/assets/shader.manifest rename to NewHorizons/AssetBundle/shader.manifest index b5c4697c..d8a544b8 100644 --- a/NewHorizons/assets/shader.manifest +++ b/NewHorizons/AssetBundle/shader.manifest @@ -1,9 +1,9 @@ ManifestFileVersion: 0 -CRC: 3655445632 +CRC: 3840241390 Hashes: AssetFileHash: serializedVersion: 2 - Hash: 160438a2f12bb2f4bd67ddfa0e6f2f3f + Hash: 1c2c90716d1ed7c9cbf5f785e41eace6 TypeTreeHash: serializedVersion: 2 Hash: 6370d3f9de9eca57f523bce048404a46 @@ -13,4 +13,5 @@ ClassTypes: Script: {instanceID: 0} Assets: - Assets/SphereTextureWrapper.shader +- Assets/UnlitTransparent.shader Dependencies: [] diff --git a/NewHorizons/Atmosphere/AirBuilder.cs b/NewHorizons/Atmosphere/AirBuilder.cs index 0c2b0900..414862f7 100644 --- a/NewHorizons/Atmosphere/AirBuilder.cs +++ b/NewHorizons/Atmosphere/AirBuilder.cs @@ -60,7 +60,6 @@ namespace NewHorizons.Atmosphere } airGO.SetActive(true); - Logger.Log("Finished building air.", Logger.LogType.Log); } } } diff --git a/NewHorizons/Atmosphere/AtmosphereBuilder.cs b/NewHorizons/Atmosphere/AtmosphereBuilder.cs index 522d4572..a5c878d8 100644 --- a/NewHorizons/Atmosphere/AtmosphereBuilder.cs +++ b/NewHorizons/Atmosphere/AtmosphereBuilder.cs @@ -77,7 +77,6 @@ namespace NewHorizons.Atmosphere } atmoGO.SetActive(true); - Logger.Log("Finished building atmosphere.", Logger.LogType.Log); } } } diff --git a/NewHorizons/Atmosphere/CloudsBuilder.cs b/NewHorizons/Atmosphere/CloudsBuilder.cs index 5296c087..4a5953c1 100644 --- a/NewHorizons/Atmosphere/CloudsBuilder.cs +++ b/NewHorizons/Atmosphere/CloudsBuilder.cs @@ -110,11 +110,13 @@ namespace NewHorizons.Atmosphere fluidCLFV.SetValue("_allowShipAutoroll", true); fluidCLFV.SetValue("_disableOnStart", false); + // Fix the rotations once the rest is done + cloudsMainGO.transform.localRotation = Quaternion.Euler(0, 0, 0); + cloudsTopGO.SetActive(true); cloudsBottomGO.SetActive(true); cloudsFluidGO.SetActive(true); cloudsMainGO.SetActive(true); - Logger.Log("Finished building clouds.", Logger.LogType.Log); } } } diff --git a/NewHorizons/Atmosphere/EffectsBuilder.cs b/NewHorizons/Atmosphere/EffectsBuilder.cs index 73a08868..36a6563e 100644 --- a/NewHorizons/Atmosphere/EffectsBuilder.cs +++ b/NewHorizons/Atmosphere/EffectsBuilder.cs @@ -51,8 +51,6 @@ namespace NewHorizons.Atmosphere } effectsGO.SetActive(true); - - Logger.Log("Finished building effects", Logger.LogType.Log); } } } diff --git a/NewHorizons/Atmosphere/SunOverrideBuilder.cs b/NewHorizons/Atmosphere/SunOverrideBuilder.cs index ce890886..073c609f 100644 --- a/NewHorizons/Atmosphere/SunOverrideBuilder.cs +++ b/NewHorizons/Atmosphere/SunOverrideBuilder.cs @@ -21,7 +21,6 @@ namespace NewHorizons.Atmosphere GDSOV.SetValue("_waterInnerRadius", 0f); overrideGO.SetActive(true); - Logger.Log("Finished building sun override.", Logger.LogType.Log); } } } diff --git a/NewHorizons/Atmosphere/VolumesBuilder.cs b/NewHorizons/Atmosphere/VolumesBuilder.cs index c6496345..96cda341 100644 --- a/NewHorizons/Atmosphere/VolumesBuilder.cs +++ b/NewHorizons/Atmosphere/VolumesBuilder.cs @@ -38,7 +38,6 @@ namespace NewHorizons.Atmosphere rulesetGO.SetActive(true); volumesGO.SetActive(true); - Logger.Log("Finished building volumes", Logger.LogType.Log); } } } diff --git a/NewHorizons/Body/AsteroidBeltBuilder.cs b/NewHorizons/Body/AsteroidBeltBuilder.cs new file mode 100644 index 00000000..d2392b2e --- /dev/null +++ b/NewHorizons/Body/AsteroidBeltBuilder.cs @@ -0,0 +1,64 @@ +using NewHorizons.External; +using NewHorizons.Utility; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using Logger = NewHorizons.Utility.Logger; +using Random = UnityEngine.Random; + +namespace NewHorizons.Body +{ + static class AsteroidBeltBuilder + { + public static void Make(string bodyName, AsteroidBeltModule belt) + { + var minSize = 20; + var maxSize = 50; + int count = (int)(2f * Mathf.PI * belt.InnerRadius / (10f * maxSize)); + if (count > 400) count = 400; + + Logger.Log($"Making {count} asteroids around {bodyName}"); + + for (int i = 0; i < count; i++) + { + var size = Random.Range(minSize, maxSize); + var config = new Dictionary() + { + {"Name", $"{bodyName} Asteroid {i}"}, + {"Base", new Dictionary() + { + {"HasMapMarker", false }, + {"SurfaceGravity", 1 }, + {"SurfaceSize", size }, + {"HideOrbitLine", true } + } + }, + {"Orbit", new Dictionary() + { + {"IsMoon", true }, + {"Inclination", belt.Inclination + Random.Range(-2f, 2f) }, + {"LongitudeOfAscendingNode", belt.LongitudeOfAscendingNode }, + {"TrueAnomaly", 360f * (i + Random.Range(-0.2f, 0.2f)) / (float)count }, + {"PrimaryBody", bodyName }, + {"SemiMajorAxis", maxSize * Random.Range(belt.InnerRadius, belt.OuterRadius) } + } + }, + {"ProcGen", new Dictionary() + { + {"Scale", size }, + {"Color", new MColor32(126, 94, 73, 255) } + } + } + }; + + Logger.Log($"{config}"); + + var asteroid = new NewHorizonsBody(new PlanetConfig(config)); + Main.AdditionalBodies.Add(asteroid); + } + } + } +} diff --git a/NewHorizons/Body/Geometry/CubeSphere.cs b/NewHorizons/Body/Geometry/CubeSphere.cs index 05962a78..01653ee6 100644 --- a/NewHorizons/Body/Geometry/CubeSphere.cs +++ b/NewHorizons/Body/Geometry/CubeSphere.cs @@ -48,10 +48,6 @@ namespace NewHorizons.Body for (int x = 0; x <= resolution; x++) { SetVertex(vertices, normals, uvs, v++, x, y, 0, resolution, heightMap, minHeight, maxHeight); - if (x == resolution / 2 && y < resolution / 2) - { - Logger.Log($"{uvs[v - 1]}"); - } } for (int z = 1; z <= resolution; z++) { @@ -60,10 +56,6 @@ namespace NewHorizons.Body for (int x = resolution - 1; x >= 0; x--) { SetVertex(vertices, normals, uvs, v++, x, y, resolution, resolution, heightMap, minHeight, maxHeight); - if (x == resolution / 2 && y < resolution / 2) - { - Logger.Log($"{uvs[v - 1]}"); - } } for (int z = resolution - 1; z > 0; z--) { @@ -83,10 +75,6 @@ namespace NewHorizons.Body for (int x = 1; x < resolution; x++) { SetVertex(vertices, normals, uvs, v++, x, 0, z, resolution, heightMap, minHeight, maxHeight); - if (x == resolution / 2) - { - Logger.Log($"{uvs[v - 1]}"); - } } } diff --git a/NewHorizons/Body/Geometry/Icosphere.cs b/NewHorizons/Body/Geometry/Icosphere.cs index 5ea5cc2f..3020005a 100644 --- a/NewHorizons/Body/Geometry/Icosphere.cs +++ b/NewHorizons/Body/Geometry/Icosphere.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using UnityEngine; +using Random = UnityEngine.Random; namespace NewHorizons.Body.Geometry { @@ -59,7 +60,7 @@ namespace NewHorizons.Body.Geometry } }; - public static Mesh Build(int subdivisions, Texture2D heightMap, float minHeight, float maxHeight) + public static Mesh Build(int subdivisions, float minHeight, float maxHeight) { Mesh mesh = new Mesh(); @@ -72,7 +73,7 @@ namespace NewHorizons.Body.Geometry Vector3[] normals = new Vector3[verticesToCopy.Length]; Vector2[] uvs = new Vector2[verticesToCopy.Length]; - Dictionary seamVertices = new Dictionary(); + var randomOffset = new Vector3(Random.Range(0, 10f), Random.Range(0, 10f), Random.Range(0, 10f)); for(int i = 0; i < verticesToCopy.Length; i++) { @@ -80,11 +81,8 @@ namespace NewHorizons.Body.Geometry float latitude = Mathf.Repeat(Mathf.Rad2Deg * Mathf.Acos(v.z / Mathf.Sqrt(v.x * v.x + v.y * v.y + v.z * v.z)), 180f); float longitude = Mathf.Repeat(Mathf.Rad2Deg * (v.x > 0 ? Mathf.Atan(v.y / v.x) : Mathf.Atan(v.y / v.x) + Mathf.PI) + 90f, 360f); - - float sampleX = heightMap.width * longitude / 360f; - float sampleY = heightMap.height * latitude / 180f; - - float height = heightMap.GetPixel((int)sampleX, (int)sampleY).r * (maxHeight - minHeight) + minHeight; + + float height = Perlin.Noise(v + randomOffset) * (maxHeight - minHeight) + minHeight; newVertices[i] = verticesToCopy[i] * height; normals[i] = v.normalized; @@ -92,9 +90,6 @@ namespace NewHorizons.Body.Geometry var x = longitude / 360f; var y = latitude / 180f; - if (x == 0) seamVertices.Add(y, i); - - uvs[i] = new Vector2(x, y); } diff --git a/NewHorizons/Body/Geometry/Perlin.cs b/NewHorizons/Body/Geometry/Perlin.cs new file mode 100644 index 00000000..d3f22604 --- /dev/null +++ b/NewHorizons/Body/Geometry/Perlin.cs @@ -0,0 +1,176 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace NewHorizons.Body.Geometry +{ + // Perlin noise generator for Unity + // Keijiro Takahashi, 2013, 2015 + // https://github.com/keijiro/PerlinNoise + public static class Perlin + { + #region Noise functions + + public static float Noise(float x) + { + var X = Mathf.FloorToInt(x) & 0xff; + x -= Mathf.Floor(x); + var u = Fade(x); + return Lerp(u, Grad(perm[X], x), Grad(perm[X + 1], x - 1)) * 2; + } + + public static float Noise(float x, float y) + { + var X = Mathf.FloorToInt(x) & 0xff; + var Y = Mathf.FloorToInt(y) & 0xff; + x -= Mathf.Floor(x); + y -= Mathf.Floor(y); + var u = Fade(x); + var v = Fade(y); + var A = (perm[X] + Y) & 0xff; + var B = (perm[X + 1] + Y) & 0xff; + return Lerp(v, Lerp(u, Grad(perm[A], x, y), Grad(perm[B], x - 1, y)), + Lerp(u, Grad(perm[A + 1], x, y - 1), Grad(perm[B + 1], x - 1, y - 1))); + } + + public static float Noise(Vector2 coord) + { + return Noise(coord.x, coord.y); + } + + public static float Noise(float x, float y, float z) + { + var X = Mathf.FloorToInt(x) & 0xff; + var Y = Mathf.FloorToInt(y) & 0xff; + var Z = Mathf.FloorToInt(z) & 0xff; + x -= Mathf.Floor(x); + y -= Mathf.Floor(y); + z -= Mathf.Floor(z); + var u = Fade(x); + var v = Fade(y); + var w = Fade(z); + var A = (perm[X] + Y) & 0xff; + var B = (perm[X + 1] + Y) & 0xff; + var AA = (perm[A] + Z) & 0xff; + var BA = (perm[B] + Z) & 0xff; + var AB = (perm[A + 1] + Z) & 0xff; + var BB = (perm[B + 1] + Z) & 0xff; + return Lerp(w, Lerp(v, Lerp(u, Grad(perm[AA], x, y, z), Grad(perm[BA], x - 1, y, z)), + Lerp(u, Grad(perm[AB], x, y - 1, z), Grad(perm[BB], x - 1, y - 1, z))), + Lerp(v, Lerp(u, Grad(perm[AA + 1], x, y, z - 1), Grad(perm[BA + 1], x - 1, y, z - 1)), + Lerp(u, Grad(perm[AB + 1], x, y - 1, z - 1), Grad(perm[BB + 1], x - 1, y - 1, z - 1)))); + } + + public static float Noise(Vector3 coord) + { + return Noise(coord.x, coord.y, coord.z); + } + + #endregion + + #region fBm functions + + public static float Fbm(float x, int octave) + { + var f = 0.0f; + var w = 0.5f; + for (var i = 0; i < octave; i++) + { + f += w * Noise(x); + x *= 2.0f; + w *= 0.5f; + } + return f; + } + + public static float Fbm(Vector2 coord, int octave) + { + var f = 0.0f; + var w = 0.5f; + for (var i = 0; i < octave; i++) + { + f += w * Noise(coord); + coord *= 2.0f; + w *= 0.5f; + } + return f; + } + + public static float Fbm(float x, float y, int octave) + { + return Fbm(new Vector2(x, y), octave); + } + + public static float Fbm(Vector3 coord, int octave) + { + var f = 0.0f; + var w = 0.5f; + for (var i = 0; i < octave; i++) + { + f += w * Noise(coord); + coord *= 2.0f; + w *= 0.5f; + } + return f; + } + + public static float Fbm(float x, float y, float z, int octave) + { + return Fbm(new Vector3(x, y, z), octave); + } + + #endregion + + #region Private functions + + static float Fade(float t) + { + return t * t * t * (t * (t * 6 - 15) + 10); + } + + static float Lerp(float t, float a, float b) + { + return a + t * (b - a); + } + + static float Grad(int hash, float x) + { + return (hash & 1) == 0 ? x : -x; + } + + static float Grad(int hash, float x, float y) + { + return ((hash & 1) == 0 ? x : -x) + ((hash & 2) == 0 ? y : -y); + } + + static float Grad(int hash, float x, float y, float z) + { + var h = hash & 15; + var u = h < 8 ? x : y; + var v = h < 4 ? y : (h == 12 || h == 14 ? x : z); + return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); + } + + static int[] perm = { + 151,160,137,91,90,15, + 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, + 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, + 88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, + 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, + 102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, + 135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, + 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, + 223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, + 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, + 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, + 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, + 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180, + 151 + }; + + #endregion + } +} diff --git a/NewHorizons/Body/GeometryBuilder.cs b/NewHorizons/Body/GeometryBuilder.cs index 3ceaaff4..a5c38ad9 100644 --- a/NewHorizons/Body/GeometryBuilder.cs +++ b/NewHorizons/Body/GeometryBuilder.cs @@ -49,7 +49,6 @@ namespace NewHorizons.Body geo.SetActive(true); */ - Logger.Log("Finished building geometry", Logger.LogType.Log); } } } diff --git a/NewHorizons/Body/HeightMapBuilder.cs b/NewHorizons/Body/HeightMapBuilder.cs index 246a1e42..5832df48 100644 --- a/NewHorizons/Body/HeightMapBuilder.cs +++ b/NewHorizons/Body/HeightMapBuilder.cs @@ -12,7 +12,6 @@ namespace NewHorizons.Body { static class HeightMapBuilder { - public static AssetBundle ShaderBundle; public static Shader PlanetShader; public static void Make(GameObject go, HeightMapModule module) @@ -29,7 +28,6 @@ namespace NewHorizons.Body return; } - GameObject cubeSphere = new GameObject("CubeSphere"); cubeSphere.transform.parent = go.transform; cubeSphere.transform.rotation = Quaternion.Euler(90, 0, 0); @@ -39,8 +37,7 @@ namespace NewHorizons.Body cubeSphere.AddComponent(); cubeSphere.GetComponent().mesh = mesh; - if(ShaderBundle == null) ShaderBundle = Main.Instance.ModHelper.Assets.LoadBundle("assets/shader"); - if(PlanetShader == null) PlanetShader = ShaderBundle.LoadAsset("Assets/SphereTextureWrapper.shader"); + if(PlanetShader == null) PlanetShader = Main.ShaderBundle.LoadAsset("Assets/SphereTextureWrapper.shader"); var cubeSphereMR = cubeSphere.AddComponent(); cubeSphereMR.material = new Material(PlanetShader); @@ -48,7 +45,9 @@ namespace NewHorizons.Body var cubeSphereMC = cubeSphere.AddComponent(); cubeSphereMC.sharedMesh = mesh; - + + // Fix rotation in the end + cubeSphere.transform.localRotation = Quaternion.Euler(90, 0, 0); /* GameObject icosphere = new GameObject("Icosphere"); @@ -66,6 +65,7 @@ namespace NewHorizons.Body var cubeSphereMC = icosphere.AddComponent(); cubeSphereMC.sharedMesh = mesh; + icosphere.transform.localRotation = Quaternion.Euler(90, 0, 0); */ } } diff --git a/NewHorizons/Body/ProcGenBuilder.cs b/NewHorizons/Body/ProcGenBuilder.cs new file mode 100644 index 00000000..3feb6aba --- /dev/null +++ b/NewHorizons/Body/ProcGenBuilder.cs @@ -0,0 +1,34 @@ +using NewHorizons.Body.Geometry; +using NewHorizons.External; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace NewHorizons.Body +{ + static class ProcGenBuilder + { + public static void Make(GameObject go, ProcGenModule module) + { + GameObject icosphere = new GameObject("Icosphere"); + icosphere.transform.parent = go.transform; + icosphere.transform.rotation = Quaternion.Euler(90, 0, 0); + + Mesh mesh = Icosphere.Build(3, module.Scale, module.Scale * 1.2f); + + icosphere.AddComponent(); + icosphere.GetComponent().mesh = mesh; + + var cubeSphereMR = icosphere.AddComponent(); + cubeSphereMR.material = new Material(Shader.Find("Standard")); + cubeSphereMR.material.color = module.Color.ToColor32(); + + var cubeSphereMC = icosphere.AddComponent(); + cubeSphereMC.sharedMesh = mesh; + icosphere.transform.localRotation = Quaternion.Euler(90, 0, 0); + } + } +} diff --git a/NewHorizons/Body/RingBuilder.cs b/NewHorizons/Body/RingBuilder.cs index e47dd4f1..02b39a70 100644 --- a/NewHorizons/Body/RingBuilder.cs +++ b/NewHorizons/Body/RingBuilder.cs @@ -11,7 +11,9 @@ namespace NewHorizons.General { static class RingBuilder { - public static void Make(GameObject body, RingModule ring) + public static Shader RingShader; + + public static void Make(GameObject body, RingModule ring) { Texture2D ringTexture; try @@ -27,6 +29,7 @@ namespace NewHorizons.General var ringGO = new GameObject("Ring"); ringGO.transform.parent = body.transform; ringGO.transform.localPosition = Vector3.zero; + ringGO.transform.localRotation = Quaternion.Euler(0, 0, 0); ringGO.transform.Rotate(ringGO.transform.TransformDirection(Vector3.up), ring.LongitudeOfAscendingNode); ringGO.transform.Rotate(ringGO.transform.TransformDirection(Vector3.right), ring.Inclination); @@ -35,7 +38,9 @@ namespace NewHorizons.General var ringMR = ringGO.AddComponent(); var texture = ringTexture; - var mat = new Material(Shader.Find("Legacy Shaders/Particles/Alpha Blended Premultiply")); + if (RingShader == null) RingShader = Main.ShaderBundle.LoadAsset("Assets/UnlitTransparent.shader"); + + var mat = new Material(RingShader); mat.mainTexture = texture; mat.renderQueue = 3000; ringMR.material = mat; @@ -43,8 +48,6 @@ namespace NewHorizons.General // Make mesh var segments = (int)Math.Max(20, ring.OuterRadius); BuildRingMesh(ringMesh, segments, ring.InnerRadius, ring.OuterRadius); - - Logger.Log("Finished building rings", Logger.LogType.Log); } // Thank you https://github.com/boardtobits/planet-ring-mesh/blob/master/PlanetRing.cs diff --git a/NewHorizons/Body/SunBuilder.cs b/NewHorizons/Body/SunBuilder.cs new file mode 100644 index 00000000..79dab1bc --- /dev/null +++ b/NewHorizons/Body/SunBuilder.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace NewHorizons.Body +{ + static class SunBuilder + { + public static void Make(GameObject body, Sector sector) + { + } + } +} diff --git a/NewHorizons/Body/WaterBuilder.cs b/NewHorizons/Body/WaterBuilder.cs index ade661de..b41d7fe1 100644 --- a/NewHorizons/Body/WaterBuilder.cs +++ b/NewHorizons/Body/WaterBuilder.cs @@ -80,8 +80,6 @@ namespace NewHorizons.Body */ waterGO.SetActive(true); - - Logger.Log("Finished building water", Logger.LogType.Log); } } } diff --git a/NewHorizons/External/AsteroidBeltModule.cs b/NewHorizons/External/AsteroidBeltModule.cs new file mode 100644 index 00000000..29db0f74 --- /dev/null +++ b/NewHorizons/External/AsteroidBeltModule.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NewHorizons.External +{ + public class AsteroidBeltModule : Module + { + public float InnerRadius { get; set; } + public float OuterRadius { get; set; } + public float Inclination { get; set; } + public float LongitudeOfAscendingNode { get; set; } + } +} diff --git a/NewHorizons/External/BaseModule.cs b/NewHorizons/External/BaseModule.cs index ae6d9f84..bd00e905 100644 --- a/NewHorizons/External/BaseModule.cs +++ b/NewHorizons/External/BaseModule.cs @@ -17,5 +17,6 @@ namespace NewHorizons.External public float GroundSize { get; set; } public float BlackHoleSize { get; set; } public float LavaSize { get; set; } + public bool HideOrbitLine { get; set; } } } diff --git a/NewHorizons/External/IPlanetConfig.cs b/NewHorizons/External/IPlanetConfig.cs index f94b78de..e16215a6 100644 --- a/NewHorizons/External/IPlanetConfig.cs +++ b/NewHorizons/External/IPlanetConfig.cs @@ -11,6 +11,8 @@ namespace NewHorizons.External OrbitModule Orbit { get; } RingModule Ring { get; } HeightMapModule HeightMap { get; } + ProcGenModule ProcGen { get; } + AsteroidBeltModule AsteroidBelt { get; } SpawnModule Spawn { get; } } } diff --git a/NewHorizons/External/Module.cs b/NewHorizons/External/Module.cs index 67de2cae..922ffcd7 100644 --- a/NewHorizons/External/Module.cs +++ b/NewHorizons/External/Module.cs @@ -17,10 +17,8 @@ namespace NewHorizons.External } foreach (var item in dict) { - Logger.Log($"{item.Key} : {item.Value}", Logger.LogType.Log); - - var field = GetType().GetField(item.Key, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); - field.SetValue(this, Convert.ChangeType(item.Value, field.FieldType)); + var property = GetType().GetProperty(item.Key, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); + property.SetValue(this, Convert.ChangeType(item.Value, property.PropertyType)); } } } diff --git a/NewHorizons/External/OrbitModule.cs b/NewHorizons/External/OrbitModule.cs index 3f4ab9fe..b4ef5ccb 100644 --- a/NewHorizons/External/OrbitModule.cs +++ b/NewHorizons/External/OrbitModule.cs @@ -15,6 +15,7 @@ namespace NewHorizons.External public float LongitudeOfAscendingNode { get; set; } public float Eccentricity { get; set; } public float ArgumentOfPeriapsis { get; set; } + public float TrueAnomaly { get; set; } public float AxialTilt { get; set; } public float SiderealPeriod { get; set; } public bool IsTidallyLocked { get; set; } diff --git a/NewHorizons/External/PlanetConfig.cs b/NewHorizons/External/PlanetConfig.cs index c8f28dfe..2ce82ac9 100644 --- a/NewHorizons/External/PlanetConfig.cs +++ b/NewHorizons/External/PlanetConfig.cs @@ -14,33 +14,32 @@ namespace NewHorizons.External public OrbitModule Orbit { get; set; } public RingModule Ring { get; set; } public HeightMapModule HeightMap { get; set; } + public ProcGenModule ProcGen { get; set; } + public AsteroidBeltModule AsteroidBelt { get; set; } public SpawnModule Spawn { get; set; } public PlanetConfig(Dictionary dict) { - // Always have to have a base module + // Always have to have a base module and orbit module Base = new BaseModule(); - if (dict == null) - { - return; - } + Orbit = new OrbitModule(); + + if (dict == null) return; + foreach (var item in dict) { - Logger.Log($"{item.Key} : {item.Value}", Logger.LogType.Log); - switch(item.Key) { case "Base": Base.Build(item.Value as Dictionary); break; + case "Orbit": + Orbit.Build(item.Value as Dictionary); + break; case "Atmosphere": Atmosphere = new AtmosphereModule(); Atmosphere.Build(item.Value as Dictionary); break; - case "Orbit": - Orbit = new OrbitModule(); - Orbit.Build(item.Value as Dictionary); - break; case "Ring": Ring = new RingModule(); Ring.Build(item.Value as Dictionary); @@ -49,23 +48,24 @@ namespace NewHorizons.External HeightMap = new HeightMapModule(); HeightMap.Build(item.Value as Dictionary); break; + case "ProcGen": + ProcGen = new ProcGenModule(); + ProcGen.Build(item.Value as Dictionary); + break; + case "AsteroidBelt": + AsteroidBelt = new AsteroidBeltModule(); + AsteroidBelt.Build(item.Value as Dictionary); + break; case "Spawn": Spawn = new SpawnModule(); Spawn.Build(item.Value as Dictionary); break; default: - var field = GetType().GetField(item.Key, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); - if (field != null) - field.SetValue(this, Convert.ChangeType(item.Value, field.FieldType)); - else - Logger.LogError($"{item.Key} is not valid. Is your config file formatted correctly?"); + var property = typeof(PlanetConfig).GetProperty(item.Key, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); + if (property != null) property.SetValue(this, Convert.ChangeType(item.Value, property.PropertyType)); + else Logger.LogError($"{item.Key} {item.Value} is not valid. Is your config formatted correctly?"); break; } - - /* - var field = GetType().GetField(item.Key, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); - field.SetValue(this, Convert.ChangeType(item.Value, field.FieldType)); - */ } } } diff --git a/NewHorizons/External/ProcGenModule.cs b/NewHorizons/External/ProcGenModule.cs new file mode 100644 index 00000000..e4e738e5 --- /dev/null +++ b/NewHorizons/External/ProcGenModule.cs @@ -0,0 +1,15 @@ +using NewHorizons.Utility; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NewHorizons.External +{ + public class ProcGenModule : Module + { + public float Scale { get; set; } + public MColor32 Color { get; set; } + } +} diff --git a/NewHorizons/General/AmbientLightBuilder.cs b/NewHorizons/General/AmbientLightBuilder.cs index 0965800b..572c9c9e 100644 --- a/NewHorizons/General/AmbientLightBuilder.cs +++ b/NewHorizons/General/AmbientLightBuilder.cs @@ -19,8 +19,6 @@ namespace NewHorizons.General light.color = new Color(0.5f, 1f, 1f, 0.0225f); light.range = scale; light.intensity = 0.5f; - - Logger.Log("Finished building ambient light", Logger.LogType.Log); } } } diff --git a/NewHorizons/General/BaseBuilder.cs b/NewHorizons/General/BaseBuilder.cs index 75717f95..a6fb2fc1 100644 --- a/NewHorizons/General/BaseBuilder.cs +++ b/NewHorizons/General/BaseBuilder.cs @@ -26,18 +26,6 @@ namespace NewHorizons.General OWRB.SetValue("_maintainOriginalCenterOfMass", true); OWRB.SetValue("_rigidbody", RB); - InitialMotion IM = body.AddComponent(); - IM.SetPrimaryBody(primaryBody.GetAttachedOWRigidbody()); - IM.SetValue("_orbitAngle", config.Orbit.Inclination); - IM.SetValue("_isGlobalAxis", false); - IM.SetValue("_initAngularSpeed", config.Orbit.SiderealPeriod == 0 ? 0.02f : 1.0f / config.Orbit.SiderealPeriod); - var orbitVector = positionVector.normalized; - var rotationAxis = Quaternion.AngleAxis(config.Orbit.AxialTilt + config.Orbit.Inclination, orbitVector) * Vector3.up; - IM.SetValue("_rotationAxis", rotationAxis); - IM.SetValue("_initLinearSpeed", 0f); - - body.transform.rotation = Quaternion.FromToRotation(Vector3.up, rotationAxis); - DetectorBuilder.Make(body, primaryBody); AstroObject AO = body.AddComponent(); @@ -55,7 +43,6 @@ namespace NewHorizons.General */ } - Logger.Log("Finished building base", Logger.LogType.Log); return new MTuple(AO, OWRB); } } diff --git a/NewHorizons/General/DetectorBuilder.cs b/NewHorizons/General/DetectorBuilder.cs index 0b0bd7a8..52186f5f 100644 --- a/NewHorizons/General/DetectorBuilder.cs +++ b/NewHorizons/General/DetectorBuilder.cs @@ -1,4 +1,5 @@ using OWML.Utils; +using System; using UnityEngine; using Logger = NewHorizons.Utility.Logger; @@ -14,13 +15,13 @@ namespace NewHorizons.General detectorGO.layer = 20; ConstantForceDetector CFD = detectorGO.AddComponent(); - ForceVolume[] temp = new ForceVolume[1]; - temp[0] = primaryBody.GetAttachedOWRigidbody().GetAttachedGravityVolume(); - CFD.SetValue("_detectableFields", temp); + + GravityVolume parentGravityVolume = primaryBody.GetAttachedOWRigidbody().GetAttachedGravityVolume(); + + CFD.SetValue("_detectableFields", new ForceVolume[] { parentGravityVolume }); CFD.SetValue("_inheritElement0", true); detectorGO.SetActive(true); - Logger.Log("Finished building detector", Logger.LogType.Log); } } } diff --git a/NewHorizons/General/GravityBuilder.cs b/NewHorizons/General/GravityBuilder.cs index d00a5039..8f9a485f 100644 --- a/NewHorizons/General/GravityBuilder.cs +++ b/NewHorizons/General/GravityBuilder.cs @@ -40,8 +40,6 @@ namespace NewHorizons.General gravityGO.SetActive(true); ao.SetValue("_gravityVolume", GV); - - Logger.Log("Finished building gravity", Logger.LogType.Log); } } } diff --git a/NewHorizons/General/InitialMotionBuilder.cs b/NewHorizons/General/InitialMotionBuilder.cs new file mode 100644 index 00000000..8b0b755f --- /dev/null +++ b/NewHorizons/General/InitialMotionBuilder.cs @@ -0,0 +1,54 @@ +using NewHorizons.External; +using OWML.Utils; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using Logger = NewHorizons.Utility.Logger; + +namespace NewHorizons.General +{ + static class InitialMotionBuilder + { + public static InitialMotion Make(GameObject body, AstroObject primaryBody, OWRigidbody OWRB, Vector3 positionVector, OrbitModule orbit) + { + InitialMotion IM = body.AddComponent(); + IM.SetPrimaryBody(primaryBody.GetAttachedOWRigidbody()); + IM.SetValue("_orbitAngle", orbit.Inclination); + IM.SetValue("_isGlobalAxis", false); + IM.SetValue("_initAngularSpeed", orbit.SiderealPeriod == 0 ? 0.02f : 1.0f / orbit.SiderealPeriod); + + // Initial velocity + var distance = positionVector - primaryBody.transform.position; + var speed = Kepler.OrbitalHelper.VisViva(primaryBody.GetGravityVolume().GetStandardGravitationalParameter(), distance, orbit.SemiMajorAxis); + var direction = Kepler.OrbitalHelper.EllipseTangent(orbit.Eccentricity, orbit.SemiMajorAxis, orbit.Inclination, orbit.LongitudeOfAscendingNode, orbit.ArgumentOfPeriapsis, orbit.TrueAnomaly); + var velocity = speed * direction; + + // Cancel out what the game does + if(orbit.Eccentricity != 0) + { + var oldVelocity = Kepler.OrbitalHelper.CalculateOrbitVelocity(primaryBody.GetAttachedOWRigidbody(), distance, orbit.Inclination); + if (!float.IsNaN(oldVelocity.magnitude)) velocity -= oldVelocity; + else Logger.LogError($"The original orbital velocity for {body.name} was calculated to be NaN?"); + + var trueSpeed = velocity.magnitude; + var trueDirection = velocity.normalized; + + Logger.Log($"Setting initial motion {velocity.magnitude} in direction {velocity.normalized}"); + if (!float.IsNaN(trueSpeed) && trueDirection != Vector3.zero) + { + IM.SetValue("_initLinearDirection", velocity.normalized); + IM.SetValue("_initLinearSpeed", velocity.magnitude); + } + else Logger.LogError($"Could not set velocity ({speed}, {direction}) -> ({trueSpeed}, {trueDirection}) for {body.name}"); + } + + var rotationAxis = Quaternion.AngleAxis(orbit.AxialTilt + orbit.Inclination, Vector3.right) * Vector3.up; + body.transform.rotation = Quaternion.FromToRotation(Vector3.up, rotationAxis); + + return IM; + } + } +} diff --git a/NewHorizons/General/MarkerBuilder.cs b/NewHorizons/General/MarkerBuilder.cs index 7943c7e7..8a5dbb00 100644 --- a/NewHorizons/General/MarkerBuilder.cs +++ b/NewHorizons/General/MarkerBuilder.cs @@ -21,7 +21,6 @@ namespace NewHorizons.General { MM.SetValue("_markerType", MM.GetType().GetNestedType("MarkerType", BindingFlags.NonPublic).GetField("Planet").GetValue(MM)); } - Logger.Log("Finished building map marker", Logger.LogType.Log); } } } diff --git a/NewHorizons/General/OrbitlineBuilder.cs b/NewHorizons/General/OrbitlineBuilder.cs index 47b63716..1e901654 100644 --- a/NewHorizons/General/OrbitlineBuilder.cs +++ b/NewHorizons/General/OrbitlineBuilder.cs @@ -1,4 +1,6 @@ -using OWML.Utils; +using NewHorizons.External; +using NewHorizons.Utility; +using OWML.Utils; using UnityEngine; using Logger = NewHorizons.Utility.Logger; @@ -6,28 +8,25 @@ namespace NewHorizons.General { static class OrbitlineBuilder { - public static void Make(GameObject body, AstroObject astroobject, bool isMoon) + public static void Make(GameObject body, AstroObject astroobject, bool isMoon, OrbitModule orbit) { - GameObject orbit = new GameObject("Orbit"); - orbit.transform.parent = body.transform; + GameObject orbitGO = new GameObject("Orbit"); + orbitGO.transform.parent = body.transform; - var LR = orbit.AddComponent(); + var LR = orbitGO.AddComponent(); - var thLR = GameObject.Find("OrbitLine_TH").GetComponent(); + var thLR = GameObject.Find("OrbitLine_CO").GetComponent(); LR.material = thLR.material; LR.useWorldSpace = false; LR.loop = false; - Logger.Log("AO primary body is " + astroobject.GetPrimaryBody().name, Logger.LogType.Log); - - var ol = orbit.AddComponent(); + OrbitLine ol = orbit.Eccentricity != 0 ? orbitGO.AddComponent() : orbitGO.AddComponent(); ol.SetValue("_astroObject", astroobject); ol.SetValue("_fade", isMoon); ol.SetValue("_lineWidth", 0.5f); + //ol.SetOrbitalParameters(orbit.Eccentricity, orbit.SemiMajorAxis, orbit.Inclination, orbit.LongitudeOfAscendingNode, orbit.ArgumentOfPeriapsis, orbit.TrueAnomaly); typeof(OrbitLine).GetMethod("InitializeLineRenderer", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).Invoke(ol, new object[] { }); - - Logger.Log("Finished building orbit line", Logger.LogType.Log); } } } diff --git a/NewHorizons/General/PlanetDestroyer.cs b/NewHorizons/General/PlanetDestroyer.cs new file mode 100644 index 00000000..7a520b9f --- /dev/null +++ b/NewHorizons/General/PlanetDestroyer.cs @@ -0,0 +1,118 @@ +using NewHorizons.Utility; +using OWML.Utils; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using Logger = NewHorizons.Utility.Logger; + +namespace NewHorizons.General +{ + static class PlanetDestroyer + { + public static void RemoveBody(AstroObject ao, List toDestroy = null) + { + Logger.Log($"Removing {ao.name}"); + + if (ao.gameObject == null || !ao.gameObject.activeInHierarchy) return; + + if (toDestroy == null) toDestroy = new List(); + + if (toDestroy.Contains(ao)) + { + Logger.LogError($"Possible infinite recursion in RemoveBody: {ao.name} might be it's own primary body?"); + return; + } + + toDestroy.Add(ao); + + if (ao.GetAstroObjectName() == AstroObject.Name.BrittleHollow) + RemoveBody(AstroObjectLocator.GetAstroObject(AstroObject.Name.WhiteHole), toDestroy); + + // Check if any other objects depend on it and remove them too + var aoArray = AstroObjectLocator.GetAllAstroObjects(); + foreach (AstroObject obj in aoArray) + { + if (obj?.gameObject == null || !obj.gameObject.activeInHierarchy) + { + AstroObjectLocator.RemoveAstroObject(obj); + continue; + } + if (ao.Equals(obj.GetPrimaryBody())) + { + AstroObjectLocator.RemoveAstroObject(obj); + RemoveBody(obj, toDestroy); + } + } + + if (ao.GetAstroObjectName() == AstroObject.Name.CaveTwin || ao.GetAstroObjectName() == AstroObject.Name.TowerTwin) + { + var focalBody = GameObject.Find("FocalBody"); + if (focalBody != null) focalBody.SetActive(false); + } + if (ao.GetAstroObjectName() == AstroObject.Name.MapSatellite) + { + var msb = GameObject.Find("MapSatellite_Body"); + if (msb != null) msb.SetActive(false); + } + if (ao.GetAstroObjectName() == AstroObject.Name.TowerTwin) + GameObject.Find("TimeLoopRing_Body").SetActive(false); + if (ao.GetAstroObjectName() == AstroObject.Name.ProbeCannon) + { + GameObject.Find("NomaiProbe_Body").SetActive(false); + GameObject.Find("CannonMuzzle_Body").SetActive(false); + GameObject.Find("FakeCannonMuzzle_Body (1)").SetActive(false); + GameObject.Find("CannonBarrel_Body").SetActive(false); + GameObject.Find("FakeCannonBarrel_Body (1)").SetActive(false); + GameObject.Find("Debris_Body (1)").SetActive(false); + } + if (ao.GetAstroObjectName() == AstroObject.Name.SunStation) + { + GameObject.Find("SS_Debris_Body").SetActive(false); + } + if (ao.GetAstroObjectName() == AstroObject.Name.GiantsDeep) + { + GameObject.Find("BrambleIsland_Body").SetActive(false); + GameObject.Find("GabbroIsland_Body").SetActive(false); + GameObject.Find("QuantumIsland_Body").SetActive(false); + GameObject.Find("StatueIsland_Body").SetActive(false); + GameObject.Find("ConstructionYardIsland_Body").SetActive(false); + } + if (ao.GetAstroObjectName() == AstroObject.Name.WhiteHole) + { + GameObject.Find("WhiteholeStation_Body").SetActive(false); + GameObject.Find("WhiteholeStationSuperstructure_Body").SetActive(false); + } + if (ao.GetAstroObjectName() == AstroObject.Name.TimberHearth) + { + GameObject.Find("MiningRig_Body").SetActive(false); + } + + // Deal with proxies + foreach (var p in GameObject.FindObjectsOfType()) + { + if (p.GetValue("_originalBody") == ao.gameObject) + { + p.gameObject.SetActive(false); + break; + } + } + Main.Instance.ModHelper.Events.Unity.FireOnNextUpdate(() => RemoveProxy(ao.name.Replace("_Body", ""))); + + ao.transform.root.gameObject.SetActive(false); + } + + private static void RemoveProxy(string name) + { + if (name.Equals("TowerTwin")) name = "AshTwin"; + if (name.Equals("CaveTwin")) name = "EmberTwin"; + var distantProxy = GameObject.Find(name + "_DistantProxy"); + var distantProxyClone = GameObject.Find(name + "_DistantProxy(Clone)"); + + if (distantProxy != null) GameObject.Destroy(distantProxy.gameObject); + if (distantProxyClone != null) GameObject.Destroy(distantProxyClone.gameObject); + } + } +} diff --git a/NewHorizons/General/RFVolumeBuilder.cs b/NewHorizons/General/RFVolumeBuilder.cs index 659f334b..f90545b2 100644 --- a/NewHorizons/General/RFVolumeBuilder.cs +++ b/NewHorizons/General/RFVolumeBuilder.cs @@ -7,7 +7,7 @@ namespace NewHorizons.General { static class RFVolumeBuilder { - public static void Make(GameObject body, OWRigidbody rigidbody, float atmoEndSize) + public static void Make(GameObject body, OWRigidbody rigidbody, float sphereOfInfluence) { GameObject rfGO = new GameObject("RFVolume"); rfGO.transform.parent = body.transform; @@ -16,30 +16,29 @@ namespace NewHorizons.General SphereCollider SC = rfGO.AddComponent(); SC.isTrigger = true; - SC.radius = atmoEndSize * 2; + SC.radius = sphereOfInfluence * 2; ReferenceFrameVolume RFV = rfGO.AddComponent(); ReferenceFrame RV = new ReferenceFrame(rigidbody); - RV.SetValue("_minSuitTargetDistance", 300); + RV.SetValue("_minSuitTargetDistance", sphereOfInfluence); RV.SetValue("_maxTargetDistance", 0); - RV.SetValue("_autopilotArrivalDistance", atmoEndSize * 2f); - RV.SetValue("_autoAlignmentDistance", atmoEndSize * 1.5f); - //Utility.AddDebugShape.AddSphere(rfGO, 1000, new Color32(0, 255, 0, 128)); + RV.SetValue("_autopilotArrivalDistance", sphereOfInfluence * 2f); + RV.SetValue("_autoAlignmentDistance", sphereOfInfluence * 1.5f); + RV.SetValue("_hideLandingModePrompt", false); RV.SetValue("_matchAngularVelocity", true); RV.SetValue("_minMatchAngularVelocityDistance", 70); RV.SetValue("_maxMatchAngularVelocityDistance", 400); - RV.SetValue("_bracketsRadius", 300); + RV.SetValue("_bracketsRadius", sphereOfInfluence); RFV.SetValue("_referenceFrame", RV); - RFV.SetValue("_minColliderRadius", 300); - RFV.SetValue("_maxColliderRadius", atmoEndSize * 2f); + RFV.SetValue("_minColliderRadius", sphereOfInfluence); + RFV.SetValue("_maxColliderRadius", sphereOfInfluence * 2f); RFV.SetValue("_isPrimaryVolume", true); RFV.SetValue("_isCloseRangeVolume", false); rfGO.SetActive(true); - Logger.Log("Finished building rfvolume", Logger.LogType.Log); } } } diff --git a/NewHorizons/General/SectorBuilder.cs b/NewHorizons/General/SectorBuilder.cs index e1be5c3c..714e89f3 100644 --- a/NewHorizons/General/SectorBuilder.cs +++ b/NewHorizons/General/SectorBuilder.cs @@ -31,7 +31,6 @@ namespace NewHorizons.Body sectorGO.SetActive(true); - Logger.Log("Finished building sector", Logger.LogType.Log); return S; } } diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index b3872d45..7b777ee9 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -18,9 +18,12 @@ namespace NewHorizons { public class Main : ModBehaviour { + + public static AssetBundle ShaderBundle; public static Main Instance { get; private set; } public static List BodyList = new List(); + public static List AdditionalBodies = new List(); public IModAssets CurrentAssets { get; private set; } @@ -33,6 +36,7 @@ namespace NewHorizons { SceneManager.sceneLoaded += OnSceneLoaded; Instance = this; + ShaderBundle = Main.Instance.ModHelper.Assets.LoadBundle("AssetBundle/shader"); Utility.Patches.Apply(); @@ -51,10 +55,7 @@ namespace NewHorizons void OnSceneLoaded(Scene scene, LoadSceneMode mode) { - if (scene.name != "SolarSystem") - { - return; - } + if (scene.name != "SolarSystem") return; Instance.ModHelper.Events.Unity.FireOnNextUpdate(() => Locator.GetPlayerBody().gameObject.AddComponent()); @@ -64,60 +65,73 @@ namespace NewHorizons AstroObjectLocator.AddAstroObject(ao); } - foreach (var body in BodyList) - { - var stringID = body.Config.Name.ToUpper().Replace(" ", "_").Replace("'", ""); - if (stringID.Equals("ATTLEROCK")) stringID = "TIMBER_MOON"; - if (stringID.Equals("HOLLOWS_LANTERN")) stringID = "VOLCANIC_MOON"; - if (stringID.Equals("ASH_TWIN")) stringID = "TOWER_TWIN"; - if (stringID.Equals("EMBER_TWIN")) stringID = "CAVE_TWIN"; - if (stringID.Equals("INTERLOPER")) stringID = "COMET"; + // Should make moons come after planets + BodyList = BodyList.OrderBy(b => (b.Config?.Orbit?.IsMoon)).ToList(); - Logger.Log($"Checking if [{stringID}] already exists"); - AstroObject existingPlanet = null; + while(BodyList.Count != 0) + { + foreach (var body in BodyList) + { + LoadBody(body); + } + BodyList = AdditionalBodies; + AdditionalBodies = new List(); + } + } + + private void LoadBody(NewHorizonsBody body) + { + var stringID = body.Config.Name.ToUpper().Replace(" ", "_").Replace("'", ""); + if (stringID.Equals("ATTLEROCK")) stringID = "TIMBER_MOON"; + if (stringID.Equals("HOLLOWS_LANTERN")) stringID = "VOLCANIC_MOON"; + if (stringID.Equals("ASH_TWIN")) stringID = "TOWER_TWIN"; + if (stringID.Equals("EMBER_TWIN")) stringID = "CAVE_TWIN"; + if (stringID.Equals("INTERLOPER")) stringID = "COMET"; + + AstroObject existingPlanet = null; + try + { + existingPlanet = AstroObjectLocator.GetAstroObject(stringID); + if (existingPlanet == null) + existingPlanet = existingPlanet = AstroObjectLocator.GetAstroObject(body.Config.Name.Replace(" ", "")); + } + catch (Exception e) + { + Logger.LogWarning($"Error when looking for {body.Config.Name}: {e.Message}, {e.StackTrace}"); + } + + + if (existingPlanet != null) + { try { - existingPlanet = AstroObjectLocator.GetAstroObject(stringID); - if (existingPlanet == null) - existingPlanet = existingPlanet = AstroObjectLocator.GetAstroObject(body.Config.Name.Replace(" ", "")); + if (body.Config.Destroy) + { + Instance.ModHelper.Events.Unity.FireInNUpdates(() => PlanetDestroyer.RemoveBody(existingPlanet), 2); + } + else UpdateBody(body, existingPlanet); } - catch(Exception e) + catch (Exception e) { - Logger.LogWarning($"Error when looking for {body.Config.Name}: {e.Message}, {e.StackTrace}"); + Logger.LogError($"Couldn't update body {body.Config?.Name}: {e.Message}, {e.StackTrace}"); } - - - if (existingPlanet != null) + } + else + { + try { - try - { - if (body.Config.Destroy) - { - Instance.ModHelper.Events.Unity.FireInNUpdates(() => RemoveBody(existingPlanet), 2); - } - else UpdateBody(body, existingPlanet); - } - catch(Exception e) - { - Logger.LogError($"Couldn't update body {body.Config?.Name}: {e.Message}, {e.StackTrace}"); - } + GameObject planetObject; + planetObject = GenerateBody(body); + planetObject.SetActive(true); } - else + catch (Exception e) { - try - { - GameObject planetObject; - planetObject = GenerateBody(body); - planetObject.SetActive(true); - } - catch(Exception e) - { - Logger.LogError($"Couldn't generate body {body.Config?.Name}: {e.Message}, {e.StackTrace}"); - } + Logger.LogError($"Couldn't generate body {body.Config?.Name}: {e.Message}, {e.StackTrace}"); } } } + public void LoadConfigs(IModBehaviour mod) { CurrentAssets = mod.ModHelper.Assets; @@ -146,144 +160,8 @@ namespace NewHorizons var sector = go.GetComponentInChildren(); var rb = go.GetAttachedOWRigidbody(); - if (body.Config.Ring != null) - { - RingBuilder.Make(go, body.Config.Ring); - } - if (body.Config.Base.LavaSize != 0) - { - LavaBuilder.Make(go, sector, rb, body.Config.Base.LavaSize); - } - if (body.Config.Base.WaterSize != 0) - { - WaterBuilder.Make(go, sector, rb, body.Config.Base.WaterSize); - } - if(body.Config.Atmosphere != null) - { - AirBuilder.Make(go, body.Config.Atmosphere.Size, body.Config.Atmosphere.HasRain, body.Config.Atmosphere.HasOxygen); - - if (body.Config.Atmosphere.Cloud != null) - { - CloudsBuilder.Make(go, sector, body.Config.Atmosphere); - SunOverrideBuilder.Make(go, sector, body.Config.Base.SurfaceSize, body.Config.Atmosphere); - } - - if (body.Config.Atmosphere.HasRain || body.Config.Atmosphere.HasSnow) - EffectsBuilder.Make(go, sector, body.Config.Base.SurfaceSize, body.Config.Atmosphere.Size / 2f, body.Config.Atmosphere.HasRain, body.Config.Atmosphere.HasSnow); - - if (body.Config.Atmosphere.FogSize != 0) - FogBuilder.Make(go, sector, body.Config.Atmosphere); - - AtmosphereBuilder.Make(go, body.Config.Atmosphere); - } - - return go; - } - - private static void RemoveBody(AstroObject ao, List toDestroy = null) - { - Logger.Log($"Removing {ao.name}"); - - if (ao.gameObject == null || !ao.gameObject.activeInHierarchy) return; - - if (toDestroy == null) toDestroy = new List(); - - if(toDestroy.Contains(ao)) - { - Logger.LogError($"Possible infinite recursion in RemoveBody: {ao.name} might be it's own primary body?"); - return; - } - - toDestroy.Add(ao); - - if (ao.GetAstroObjectName() == AstroObject.Name.BrittleHollow) - RemoveBody(AstroObjectLocator.GetAstroObject(AstroObject.Name.WhiteHole), toDestroy); - - // Check if any other objects depend on it and remove them too - var aoArray = AstroObjectLocator.GetAllAstroObjects(); - foreach(AstroObject obj in aoArray) - { - if (obj?.gameObject == null || !obj.gameObject.activeInHierarchy) - { - AstroObjectLocator.RemoveAstroObject(obj); - continue; - } - if (ao.Equals(obj.GetPrimaryBody())) - { - AstroObjectLocator.RemoveAstroObject(obj); - RemoveBody(obj, toDestroy); - } - } - - if (ao.GetAstroObjectName() == AstroObject.Name.CaveTwin || ao.GetAstroObjectName() == AstroObject.Name.TowerTwin) - { - var focalBody = GameObject.Find("FocalBody"); - if(focalBody != null) focalBody.SetActive(false); - } - if (ao.GetAstroObjectName() == AstroObject.Name.MapSatellite) - { - var msb = GameObject.Find("MapSatellite_Body"); - if (msb != null) msb.SetActive(false); - } - if (ao.GetAstroObjectName() == AstroObject.Name.TowerTwin) - GameObject.Find("TimeLoopRing_Body").SetActive(false); - if (ao.GetAstroObjectName() == AstroObject.Name.ProbeCannon) - { - GameObject.Find("NomaiProbe_Body").SetActive(false); - GameObject.Find("CannonMuzzle_Body").SetActive(false); - GameObject.Find("FakeCannonMuzzle_Body (1)").SetActive(false); - GameObject.Find("CannonBarrel_Body").SetActive(false); - GameObject.Find("FakeCannonBarrel_Body (1)").SetActive(false); - GameObject.Find("Debris_Body (1)").SetActive(false); - } - if(ao.GetAstroObjectName() == AstroObject.Name.SunStation) - { - GameObject.Find("SS_Debris_Body").SetActive(false); - } - if(ao.GetAstroObjectName() == AstroObject.Name.GiantsDeep) - { - GameObject.Find("BrambleIsland_Body").SetActive(false); - GameObject.Find("GabbroIsland_Body").SetActive(false); - GameObject.Find("QuantumIsland_Body").SetActive(false); - GameObject.Find("StatueIsland_Body").SetActive(false); - GameObject.Find("ConstructionYardIsland_Body").SetActive(false); - } - if(ao.GetAstroObjectName() == AstroObject.Name.WhiteHole) - { - GameObject.Find("WhiteholeStation_Body").SetActive(false); - GameObject.Find("WhiteholeStationSuperstructure_Body").SetActive(false); - } - if(ao.GetAstroObjectName() == AstroObject.Name.TimberHearth) - { - GameObject.Find("MiningRig_Body").SetActive(false); - } - - // Deal with proxies - foreach(var p in GameObject.FindObjectsOfType()) - { - if(p.GetValue("_originalBody") == ao.gameObject) - { - p.gameObject.SetActive(false); - break; - } - } - Instance.ModHelper.Events.Unity.FireOnNextUpdate(() => RemoveProxy(ao.name.Replace("_Body", ""))); - - ao.transform.root.gameObject.SetActive(false); - } - - private static void RemoveProxy(string name) - { - if (name.Equals("TowerTwin")) name = "AshTwin"; - if (name.Equals("CaveTwin")) name = "EmberTwin"; - var distantProxy = GameObject.Find(name + "_DistantProxy"); - var distantProxyClone = GameObject.Find(name + "_DistantProxy(Clone)"); - - - if (distantProxy != null) Destroy(distantProxy.gameObject); - else Logger.LogWarning($"Couldn't find distant proxy {name + "_DistantProxy"}"); - if (distantProxyClone != null) Destroy(distantProxyClone.gameObject); - else Logger.LogWarning($"Couldn't find distant proxy {name + "_DistantProxy(Clone)"}"); + // Do stuff that's shared between generating new planets and updating old ones + return SharedGenerateBody(body, go, sector, rb); } public static GameObject GenerateBody(NewHorizonsBody body) @@ -295,23 +173,22 @@ namespace NewHorizons if(body.Config.Base.GroundSize != 0) GeometryBuilder.Make(go, body.Config.Base.GroundSize); - AstroObject primaryBody = AstroObjectLocator.GetAstroObject(AstroObject.Name.Sun); - try - { - primaryBody = AstroObjectLocator.GetAstroObject(body.Config.Orbit.PrimaryBody); - } - catch(Exception) + AstroObject primaryBody = AstroObjectLocator.GetAstroObject(body.Config.Orbit.PrimaryBody); + if(primaryBody == null) { Logger.LogError($"Could not find AstroObject {body.Config.Orbit.PrimaryBody}, defaulting to SUN"); - } + primaryBody = AstroObjectLocator.GetAstroObject(AstroObject.Name.Sun); + } var atmoSize = body.Config.Atmosphere != null ? body.Config.Atmosphere.Size : 0f; float sphereOfInfluence = Mathf.Max(atmoSize, body.Config.Base.SurfaceSize * 2f); // Get initial position but set it at the end - var a = body.Config.Orbit.SemiMajorAxis; - var omega = Mathf.Deg2Rad * body.Config.Orbit.LongitudeOfAscendingNode; - var positionVector = primaryBody.gameObject.transform.position + new Vector3(a * Mathf.Sin(omega), 0, a * Mathf.Cos(omega)); + //var a = body.Config.Orbit.SemiMajorAxis; + //var omega = Mathf.Deg2Rad * body.Config.Orbit.LongitudeOfAscendingNode; + //var positionVector = primaryBody.gameObject.transform.position + new Vector3(a * Mathf.Sin(omega), 0, a * Mathf.Cos(omega)); + var positionVector = Kepler.OrbitalHelper.CartesianFromOrbitalElements(body.Config.Orbit.Eccentricity, body.Config.Orbit.SemiMajorAxis, body.Config.Orbit.Inclination, + body.Config.Orbit.LongitudeOfAscendingNode, body.Config.Orbit.ArgumentOfPeriapsis, body.Config.Orbit.TrueAnomaly); var outputTuple = BaseBuilder.Make(go, primaryBody, positionVector, body.Config); var ao = (AstroObject)outputTuple.Items[0]; @@ -333,7 +210,53 @@ namespace NewHorizons if (body.Config.HeightMap != null) HeightMapBuilder.Make(go, body.Config.HeightMap); - // These can be shared between creating new planets and updating planets + if (body.Config.ProcGen != null) + ProcGenBuilder.Make(go, body.Config.ProcGen); + + InitialMotionBuilder.Make(go, primaryBody, rb, positionVector, body.Config.Orbit); + + // Do stuff that's shared between generating new planets and updating old ones + go = SharedGenerateBody(body, go, sector, rb); + + body.Object = go; + + // Some things have to be done the second tick + if(body.Config.Orbit != null && !body.Config.Base.HideOrbitLine) + Instance.ModHelper.Events.Unity.FireOnNextUpdate(() => OrbitlineBuilder.Make(body.Object, ao, body.Config.Orbit.IsMoon, body.Config.Orbit)); + + // Now that we're done move the planet into place + go.transform.parent = Locator.GetRootTransform(); + go.transform.position = positionVector + primaryBody.transform.position; + + // Spawning on other planets is a bit hacky so we do it last + if (body.Config.Spawn != null) + { + SpawnPointBuilder.Make(go, body.Config.Spawn, rb); + } + + if (ao.GetAstroObjectName() == AstroObject.Name.CustomString) AstroObjectLocator.RegisterCustomAstroObject(ao); + + Logger.Log("Generation of [" + body.Config.Name + "] completed.", Logger.LogType.Log); + + return go; + } + + private static GameObject SharedGenerateBody(NewHorizonsBody body, GameObject go, Sector sector, OWRigidbody rb) + { + if (body.Config.Ring != null) + RingBuilder.Make(go, body.Config.Ring); + + if (body.Config.AsteroidBelt != null) + AsteroidBeltBuilder.Make(body.Config.Name, body.Config.AsteroidBelt); + + if(body.Config.Base != null) + { + if (body.Config.Base.LavaSize != 0) + LavaBuilder.Make(go, sector, rb, body.Config.Base.LavaSize); + if (body.Config.Base.WaterSize != 0) + WaterBuilder.Make(go, sector, rb, body.Config.Base.WaterSize); + } + if (body.Config.Atmosphere != null) { AirBuilder.Make(go, body.Config.Atmosphere.Size, body.Config.Atmosphere.HasRain, body.Config.Atmosphere.HasOxygen); @@ -344,7 +267,7 @@ namespace NewHorizons SunOverrideBuilder.Make(go, sector, body.Config.Base.SurfaceSize, body.Config.Atmosphere); } - if(body.Config.Atmosphere.HasRain || body.Config.Atmosphere.HasSnow) + if (body.Config.Atmosphere.HasRain || body.Config.Atmosphere.HasSnow) EffectsBuilder.Make(go, sector, body.Config.Base.SurfaceSize, body.Config.Atmosphere.Size / 2f, body.Config.Atmosphere.HasRain, body.Config.Atmosphere.HasSnow); if (body.Config.Atmosphere.FogSize != 0) @@ -353,38 +276,6 @@ namespace NewHorizons AtmosphereBuilder.Make(go, body.Config.Atmosphere); } - if (body.Config.Ring != null) - RingBuilder.Make(go, body.Config.Ring); - - if (body.Config.Base.BlackHoleSize != 0) - BlackHoleBuilder.Make(go); - - if (body.Config.Base.LavaSize != 0) - LavaBuilder.Make(go, sector, rb, body.Config.Base.LavaSize); - - if (body.Config.Base.WaterSize != 0) - WaterBuilder.Make(go, sector, rb, body.Config.Base.WaterSize); - - if (body.Config.Base.HasAmbientLight) - AmbientLightBuilder.Make(go, sphereOfInfluence); - - Logger.Log("Generation of [" + body.Config.Name + "] completed.", Logger.LogType.Log); - - body.Object = go; - - // Some things have to be done the second tick - Instance.ModHelper.Events.Unity.FireOnNextUpdate(() => OrbitlineBuilder.Make(body.Object, ao, body.Config.Orbit.IsMoon)); - - go.transform.parent = Locator.GetRootTransform(); - go.transform.localPosition = positionVector; - - if (body.Config.Spawn != null) - { - SpawnPointBuilder.Make(go, body.Config.Spawn, rb); - } - - if (ao.GetAstroObjectName() == AstroObject.Name.CustomString) AstroObjectLocator.RegisterCustomAstroObject(ao); - return go; } } diff --git a/NewHorizons/NewHorizons.csproj b/NewHorizons/NewHorizons.csproj index 80431b61..65b42e33 100644 --- a/NewHorizons/NewHorizons.csproj +++ b/NewHorizons/NewHorizons.csproj @@ -230,14 +230,20 @@ + + + + + + @@ -245,12 +251,15 @@ + + + @@ -269,6 +278,7 @@ + @@ -290,14 +300,14 @@ md "$(OwmlDir)\Mods\$(ModUniqueName)" md "$(OwmlDir)\Mods\$(ModUniqueName)\planets" md "$(OwmlDir)\Mods\$(ModUniqueName)\planets\assets" - md "$(OwmlDir)\Mods\$(ModUniqueName)\assets" + md "$(OwmlDir)\Mods\$(ModUniqueName)\AssetBundle" copy /y "$(TargetPath)" "$(OwmlDir)\Mods\$(ModUniqueName)" copy /y "$(ProjectDir)\default-config.json" "$(OwmlDir)\Mods\$(ModUniqueName)" copy /y "$(ProjectDir)\manifest.json" "$(OwmlDir)\Mods\$(ModUniqueName)" copy /y "$(ProjectDir)planets\" "$(OwmlDir)\Mods\$(ModUniqueName)\planets" copy /y "$(ProjectDir)planets\assets" "$(OwmlDir)\Mods\$(ModUniqueName)\planets\assets" - copy /y "$(ProjectDir)assets\" "$(OwmlDir)\Mods\$(ModUniqueName)\assets" + copy /y "$(ProjectDir)AssetBundle\" "$(OwmlDir)\Mods\$(ModUniqueName)\AssetBundle" \ No newline at end of file diff --git a/NewHorizons/Physics/OrbitalHelper.cs b/NewHorizons/Physics/OrbitalHelper.cs new file mode 100644 index 00000000..3643b259 --- /dev/null +++ b/NewHorizons/Physics/OrbitalHelper.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace NewHorizons.Kepler +{ + public static class OrbitalHelper + { + public static Vector3 CartesianFromOrbitalElements(float e, float a, float inclination, float ascending, float periapsis, float angle) + { + float b = a * Mathf.Sqrt(1f - (e * e)); + + // Get point on ellipse + // Angle starts at apoapsis, shift by 90 degrees to get to periapsis and add true anomaly + float ellipseAngle = Mathf.Repeat(Mathf.PI / 4f + Mathf.Deg2Rad * angle, 2 * Mathf.PI); + + float tAngle = Mathf.Tan(ellipseAngle); + float x = (a * b) / Mathf.Sqrt(b * b + a * a * tAngle * tAngle); + if (ellipseAngle > Mathf.PI / 2f && ellipseAngle < 3f * Mathf.PI / 2f) x *= -1; + float y = x * tAngle; + + // Fix limits + if (float.IsNaN(x)) { + x = 0; + if (angle < 180) y = b; + else y = -b; + } + + var position = new Vector3(x, 0, y); + + return RotateToOrbitalPlane(position, ascending, periapsis, inclination); + } + + public static float VisViva(float standardGravitationalParameter, Vector3 relativePosition, float semiMajorAxis) + { + return Mathf.Sqrt(standardGravitationalParameter * (2f / relativePosition.magnitude - 1f / semiMajorAxis)); + } + + public static Vector3 EllipseTangent(float e, float a, float inclination, float ascending, float periapsis, float angle) + { + float b = a * Mathf.Sqrt(1f - (e * e)); + float ellipseAngle = Mathf.Repeat(Mathf.PI / 4f + Mathf.Deg2Rad * angle, 2 * Mathf.PI); + + var tan = Mathf.Tan(ellipseAngle); + + float x = (a * b) / Mathf.Sqrt(b * b + a * a * tan * tan); + + var sec2 = 1f / (tan * tan); + float dxdt = -a * a * a * b * sec2 * tan / Mathf.Pow(a * a * tan * tan + b * b, 3f / 2f); + + if (ellipseAngle > Mathf.PI / 2f && ellipseAngle < 3f * Mathf.PI / 2f) + { + dxdt *= -1; + x *= -1; + } + + // Product rule + var dydt = sec2 * x + dxdt * tan; + + // Fix limits + if(float.IsNaN(dxdt)) + { + dydt = 0; + if (angle == Mathf.PI / 2f) dxdt = -1; + else dxdt = 1; + } + + var vector = new Vector3(dxdt, 0, dydt).normalized; + return RotateToOrbitalPlane(vector, ascending, periapsis, inclination); + } + + private static Vector3 RotateToOrbitalPlane(Vector3 vector, float ascending, float periapsis, float inclination) + { + // Periapsis is at 90 degrees + vector = Quaternion.AngleAxis(Mathf.Deg2Rad * (ascending + periapsis) + Mathf.PI / 2f, Vector3.up) * vector; + + var inclinationAxis = Quaternion.AngleAxis(Mathf.Repeat(Mathf.Deg2Rad * ascending, 2f * Mathf.PI), Vector3.up) * new Vector3(1, 0, 0); + vector = Quaternion.AngleAxis(Mathf.Repeat(Mathf.Deg2Rad * inclination, 2f * Mathf.PI), inclinationAxis) * vector; + + return vector; + } + + public static Vector3 CalculateOrbitVelocity(OWRigidbody primaryBody, Vector3 relativePosition, float inclination = 0f) + { + GravityVolume attachedGravityVolume = primaryBody.GetAttachedGravityVolume(); + if (attachedGravityVolume == null) + { + return Vector3.zero; + } + Vector3 vector2 = Vector3.Cross(relativePosition, Vector3.up).normalized; + vector2 = Quaternion.AngleAxis(inclination, relativePosition) * vector2; + float d = Mathf.Sqrt(attachedGravityVolume.CalculateForceAccelerationAtPoint(relativePosition + primaryBody.transform.position).magnitude * relativePosition.magnitude); + return vector2 * d; + } + } +} diff --git a/NewHorizons/Physics/ParameterizedOrbitLine.cs b/NewHorizons/Physics/ParameterizedOrbitLine.cs new file mode 100644 index 00000000..4df18206 --- /dev/null +++ b/NewHorizons/Physics/ParameterizedOrbitLine.cs @@ -0,0 +1,125 @@ +using NewHorizons.Kepler; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace NewHorizons.Utility +{ + public class ParameterizedOrbitLine : OrbitLine + { + protected override void InitializeLineRenderer() + { + base.GetComponent().positionCount = this._numVerts; + } + + protected override void OnValidate() + { + if (this._numVerts < 0 || this._numVerts > 4096) + { + this._numVerts = Mathf.Clamp(this._numVerts, 0, 4096); + } + if (base.GetComponent().positionCount != this._numVerts) + { + this.InitializeLineRenderer(); + } + } + + protected override void Start() + { + base.Start(); + this._verts = new Vector3[this._numVerts]; + base.enabled = false; + } + + public void SetOrbitalParameters(float eccentricity, float semiMajorAxis, float inclination, float longitudeOfAscendingNode, float argumentOfPeriapsis, float trueAnomaly) + { + _eccentricity = eccentricity; + _semiMajorAxis = semiMajorAxis; + _inclination = inclination; + _longitudeOfAscendingNode = longitudeOfAscendingNode; + _argumentOfPeriapsis = argumentOfPeriapsis; + _trueAnomaly = trueAnomaly; + + _initialMotion = (this._astroObject != null) ? this._astroObject.GetComponent() : null; + _primary = (this._astroObject != null) ? this._astroObject.GetPrimaryBody() : null; + + if (_initialMotion && _primary) + { + var periapsisAngle = longitudeOfAscendingNode + argumentOfPeriapsis; + var apoapsisAngle = periapsisAngle + 90f; + + _vSemiMinorAxis = OrbitalHelper.CartesianFromOrbitalElements(_eccentricity, _semiMajorAxis, _inclination, _longitudeOfAscendingNode, _argumentOfPeriapsis, periapsisAngle); + _vSemiMajorAxis = OrbitalHelper.CartesianFromOrbitalElements(_eccentricity, _semiMajorAxis, _inclination, _longitudeOfAscendingNode, _argumentOfPeriapsis, apoapsisAngle); + + Vector3 rhs = this._astroObject.transform.position - _primary.transform.position; + Vector3 initVelocity = _initialMotion.GetInitVelocity(); + Vector3 vector = Vector3.Cross(initVelocity, rhs); + this._upAxisDir = vector.normalized; + var semiMinorAxis = semiMajorAxis * Mathf.Sqrt(1 - (eccentricity * eccentricity)); + + _fociDistance = Mathf.Sqrt((semiMajorAxis * semiMajorAxis) - (semiMinorAxis * semiMinorAxis)); + + base.enabled = true; + } + else + { + Logger.LogError($"Couldn't set values for KeplerOrbitLine. InitialMotion = {_initialMotion}, AstroObject = {_primary}"); + } + } + + protected override void Update() + { + Vector3 vector = _primary.transform.position + _vSemiMajorAxis * _fociDistance; + float num = CalcProjectedAngleToCenter(vector, _vSemiMajorAxis, _vSemiMinorAxis, _astroObject.transform.position); + for (int i = 0; i < _numVerts; i++) + { + var angle = ((float)i / (float)(_numVerts - 1)) * 360f - Mathf.Rad2Deg * num; + _verts[i] = OrbitalHelper.CartesianFromOrbitalElements(_eccentricity, _semiMajorAxis, _inclination, _longitudeOfAscendingNode, _argumentOfPeriapsis, angle); + } + _lineRenderer.SetPositions(_verts); + + // From EllipticalOrbitLine + base.transform.position = vector; + base.transform.rotation = Quaternion.LookRotation(Quaternion.AngleAxis(-48f, Vector3.up) * _vSemiMajorAxis, -_upAxisDir); + float num2 = DistanceToEllipticalOrbitLine(vector, _vSemiMajorAxis, _vSemiMinorAxis, _upAxisDir, Locator.GetActiveCamera().transform.position); + float widthMultiplier = Mathf.Min(num2 * (_lineWidth / 1000f), _maxLineWidth); + float num3 = _fade ? (1f - Mathf.Clamp01((num2 - _fadeStartDist) / (_fadeEndDist - _fadeStartDist))) : 1f; + _lineRenderer.widthMultiplier = widthMultiplier; + _lineRenderer.startColor = new Color(_color.r, _color.g, _color.b, num3 * num3); + } + + private float CalcProjectedAngleToCenter(Vector3 foci, Vector3 semiMajorAxis, Vector3 semiMinorAxis, Vector3 point) + { + Vector3 lhs = point - foci; + Vector3 vector = new Vector3(Vector3.Dot(lhs, semiMajorAxis.normalized), 0f, Vector3.Dot(lhs, semiMinorAxis.normalized)); + vector.x *= semiMinorAxis.magnitude / semiMajorAxis.magnitude; + return Mathf.Atan2(vector.z, vector.x); + } + + private float DistanceToEllipticalOrbitLine(Vector3 foci, Vector3 semiMajorAxis, Vector3 semiMinorAxis, Vector3 upAxis, Vector3 point) + { + float f = this.CalcProjectedAngleToCenter(foci, semiMajorAxis, semiMinorAxis, point); + Vector3 b = foci + this._vSemiMajorAxis * Mathf.Cos(f) + this._vSemiMinorAxis * Mathf.Sin(f); + return Vector3.Distance(point, b); + } + + private Vector3 _vSemiMajorAxis; + private Vector3 _vSemiMinorAxis; + private Vector3 _upAxisDir; + private float _fociDistance; + + private float _eccentricity; + private float _semiMajorAxis; + private float _inclination; + private float _longitudeOfAscendingNode; + private float _argumentOfPeriapsis; + private float _trueAnomaly; + + private Vector3[] _verts; + private InitialMotion _initialMotion; + private AstroObject _primary; + } +} diff --git a/NewHorizons/Utility/AddToUITable.cs b/NewHorizons/Utility/AddToUITable.cs index 4d6f62bd..954b3d06 100644 --- a/NewHorizons/Utility/AddToUITable.cs +++ b/NewHorizons/Utility/AddToUITable.cs @@ -15,16 +15,10 @@ namespace NewHorizons.Utility try { KeyValuePair pair = instance.theUITable.First(x => x.Value.Equals(text)); - if (pair.Equals(default(KeyValuePair))) - { - Logger.Log($"UI table already contains [{text}] with key [{pair.Key}]"); - return pair.Key; - } + if (pair.Equals(default(KeyValuePair))) return pair.Key; } catch (Exception) { } - instance.Insert_UI(instance.theUITable.Keys.Max() + 1, text); - Logger.Log($"Added [{text}] to UI table with key [{instance.theUITable.Keys.Max()}]"); return instance.theUITable.Keys.Max(); } } diff --git a/NewHorizons/Utility/AstroObjectLocator.cs b/NewHorizons/Utility/AstroObjectLocator.cs index 2d5b5b88..349abe25 100644 --- a/NewHorizons/Utility/AstroObjectLocator.cs +++ b/NewHorizons/Utility/AstroObjectLocator.cs @@ -22,6 +22,7 @@ namespace NewHorizons.Utility if (name.Equals("MAP_SATELLITE")) return GetAstroObject(AstroObject.Name.MapSatellite); var aoName = AstroObject.StringIDToAstroObjectName(name); + if(aoName == AstroObject.Name.None) aoName = AstroObject.StringIDToAstroObjectName(name.ToUpper().Replace(" ", "_")); if (aoName != AstroObject.Name.None && aoName != AstroObject.Name.CustomString) return GetAstroObject(aoName); if (_customAstroObjectDictionary.ContainsKey(name)) @@ -75,23 +76,21 @@ namespace NewHorizons.Utility } var name = ao.GetCustomName(); - if (_customAstroObjectDictionary.Keys.Contains(name)) - Logger.Log($"Custom astro object dictionary already contains {name}. Replacing it.", Logger.LogType.Warning); - _customAstroObjectDictionary.Add(name, ao); + if (_customAstroObjectDictionary.Keys.Contains(name)) + _customAstroObjectDictionary[name] = ao; + else + _customAstroObjectDictionary.Add(name, ao); } public static void DeregisterCustomAstroObject(AstroObject ao) { - if (ao.GetAstroObjectName() != AstroObject.Name.CustomString) - { - Logger.Log($"Can't deregister {ao.name} as it's AstroObject.Name isn't CustomString."); - return; - } + if (ao.GetAstroObjectName() != AstroObject.Name.CustomString) return; _customAstroObjectDictionary.Remove(ao.GetCustomName()); } public static void RefreshList() { + _customAstroObjectDictionary = new Dictionary(); _list = new List(); } diff --git a/NewHorizons/assets/shader b/NewHorizons/assets/shader deleted file mode 100644 index 2340d65c8bcab0cd56639b8fcca6a12773a61492..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18191 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