diff --git a/NewHorizons/Assets/textures/Ice.png b/NewHorizons/Assets/textures/Ice.png new file mode 100644 index 00000000..c4d945db Binary files /dev/null and b/NewHorizons/Assets/textures/Ice.png differ diff --git a/NewHorizons/Assets/textures/Quantum.png b/NewHorizons/Assets/textures/Quantum.png new file mode 100644 index 00000000..606b7870 Binary files /dev/null and b/NewHorizons/Assets/textures/Quantum.png differ diff --git a/NewHorizons/Assets/textures/Rocks.png b/NewHorizons/Assets/textures/Rocks.png new file mode 100644 index 00000000..1a2ccf91 Binary files /dev/null and b/NewHorizons/Assets/textures/Rocks.png differ diff --git a/NewHorizons/Builder/Body/AsteroidBeltBuilder.cs b/NewHorizons/Builder/Body/AsteroidBeltBuilder.cs index fdc893c7..263fb0ce 100644 --- a/NewHorizons/Builder/Body/AsteroidBeltBuilder.cs +++ b/NewHorizons/Builder/Body/AsteroidBeltBuilder.cs @@ -39,7 +39,8 @@ namespace NewHorizons.Builder.Body { surfaceGravity = belt.gravity, surfaceSize = size, - gravityFallOff = GravityFallOff.InverseSquared + gravityFallOff = GravityFallOff.InverseSquared, + hasFluidDetector = false }; config.Orbit = new OrbitModule() @@ -95,6 +96,7 @@ namespace NewHorizons.Builder.Body color = new MColor(126, 94, 73) }; } + config.AmbientLights = new[] { new AmbientLightModule() { outerRadius = size * 1.2f, intensity = 0.1f } }; var asteroid = new NewHorizonsBody(config, mod); PlanetCreationHandler.GenerateBody(asteroid); diff --git a/NewHorizons/Builder/Body/Geometry/Icosphere.cs b/NewHorizons/Builder/Body/Geometry/Icosphere.cs index 71233c18..a5da82e6 100644 --- a/NewHorizons/Builder/Body/Geometry/Icosphere.cs +++ b/NewHorizons/Builder/Body/Geometry/Icosphere.cs @@ -100,6 +100,11 @@ namespace NewHorizons.Builder.Body.Geometry mesh.normals = normals; mesh.uv = uvs; + mesh.RecalculateBounds(); + // Unity recalculate normals does not support smooth normals + //mesh.RecalculateNormals(); + mesh.RecalculateTangents(); + return mesh; } diff --git a/NewHorizons/Builder/Body/HeightMapBuilder.cs b/NewHorizons/Builder/Body/HeightMapBuilder.cs index 48e15193..f816ca80 100644 --- a/NewHorizons/Builder/Body/HeightMapBuilder.cs +++ b/NewHorizons/Builder/Body/HeightMapBuilder.cs @@ -11,7 +11,7 @@ namespace NewHorizons.Builder.Body { public static class HeightMapBuilder { - public static Shader PlanetShader; + private static Shader _planetShader; // I hate nested functions okay private static IModBehaviour _currentMod; @@ -62,7 +62,7 @@ namespace NewHorizons.Builder.Body cubeSphere.SetActive(false); cubeSphere.transform.SetParent(sector?.transform ?? planetGO.transform, false); - if (PlanetShader == null) PlanetShader = AssetBundleUtilities.NHAssetBundle.LoadAsset("Assets/Shaders/SphereTextureWrapperTriplanar.shader"); + if (_planetShader == null) _planetShader = AssetBundleUtilities.NHAssetBundle.LoadAsset("Assets/Shaders/SphereTextureWrapperTriplanar.shader"); var stretch = module.stretch != null ? (Vector3)module.stretch : Vector3.one; @@ -115,7 +115,7 @@ namespace NewHorizons.Builder.Body LODCubeSphere.AddComponent().mesh = CubeSphere.Build(resolution, heightMap, module.minHeight, module.maxHeight, stretch); var cubeSphereMR = LODCubeSphere.AddComponent(); - var material = new Material(PlanetShader); + var material = new Material(_planetShader); cubeSphereMR.material = material; material.name = textureMap.name; diff --git a/NewHorizons/Builder/Body/ProcGenBuilder.cs b/NewHorizons/Builder/Body/ProcGenBuilder.cs index e168c678..913ea72c 100644 --- a/NewHorizons/Builder/Body/ProcGenBuilder.cs +++ b/NewHorizons/Builder/Body/ProcGenBuilder.cs @@ -1,21 +1,50 @@ using NewHorizons.Builder.Body.Geometry; using NewHorizons.External.Modules; using NewHorizons.Utility; +using NewHorizons.Utility.Files; +using OWML.Common; +using System.Collections.Generic; using UnityEngine; + namespace NewHorizons.Builder.Body { public static class ProcGenBuilder { - private static Material quantumMaterial; - private static Material iceMaterial; + private static Material _material; + private static Shader _planetShader; - public static GameObject Make(GameObject planetGO, Sector sector, ProcGenModule module) + private static Dictionary _materialCache = new(); + + public static void ClearCache() { - if (quantumMaterial == null) quantumMaterial = SearchUtilities.FindResourceOfTypeAndName("Rock_QM_EyeRock_mat"); - if (iceMaterial == null) iceMaterial = SearchUtilities.FindResourceOfTypeAndName("Rock_BH_IceSpike_mat"); + foreach (var material in _materialCache.Values) + { + Object.Destroy(material); + } + _materialCache.Clear(); + } + private static Material MakeMaterial() + { + var material = new Material(_planetShader); - GameObject icosphere = new GameObject("Icosphere"); + var keyword = "BASE_TILE"; + var prefix = "_BaseTile"; + + material.SetFloat(prefix, 1); + material.EnableKeyword(keyword); + + material.SetTexture("_BlendMap", ImageUtilities.MakeSolidColorTexture(1, 1, Color.white)); + + return material; + } + + public static GameObject Make(IModBehaviour mod, GameObject planetGO, Sector sector, ProcGenModule module) + { + if (_planetShader == null) _planetShader = AssetBundleUtilities.NHAssetBundle.LoadAsset("Assets/Shaders/SphereTextureWrapperTriplanar.shader"); + if (_material == null) _material = MakeMaterial(); + + var icosphere = new GameObject("Icosphere"); icosphere.SetActive(false); icosphere.transform.parent = sector?.transform ?? planetGO.transform; icosphere.transform.rotation = Quaternion.Euler(90, 0, 0); @@ -26,8 +55,61 @@ namespace NewHorizons.Builder.Body icosphere.AddComponent().mesh = mesh; var cubeSphereMR = icosphere.AddComponent(); - cubeSphereMR.material = new Material(Shader.Find("Standard")); - cubeSphereMR.material.color = module.color != null ? module.color.ToColor() : Color.white; + + if (!_materialCache.TryGetValue(module, out var material)) + { + material = new Material(_material); + material.name = planetGO.name; + if (module.material == ProcGenModule.Material.Default) + { + if (!string.IsNullOrEmpty(module.texture)) + { + material.SetTexture($"_BaseTileAlbedo", ImageUtilities.GetTexture(mod, module.texture, wrap: true)); + } + else + { + material.mainTexture = ImageUtilities.MakeSolidColorTexture(1, 1, module.color?.ToColor() ?? Color.white); + } + if (!string.IsNullOrEmpty(module.smoothnessMap)) + { + material.SetTexture($"_BaseTileSmoothnessMap", ImageUtilities.GetTexture(mod, module.smoothnessMap, wrap: true)); + } + if (!string.IsNullOrEmpty(module.normalMap)) + { + material.SetFloat($"_BaseTileBumpStrength", module.normalStrength); + material.SetTexture($"_BaseTileBumpMap", ImageUtilities.GetTexture(mod, module.normalMap, wrap: true)); + } + } + else + { + switch (module.material) + { + case ProcGenModule.Material.Ice: + material.SetTexture($"_BaseTileAlbedo", ImageUtilities.GetTexture(Main.Instance, "Assets/textures/Ice.png", wrap: true)); + break; + case ProcGenModule.Material.Quantum: + material.SetTexture($"_BaseTileAlbedo", ImageUtilities.GetTexture(Main.Instance, "Assets/textures/Quantum.png", wrap: true)); + break; + case ProcGenModule.Material.Rock: + material.SetTexture($"_BaseTileAlbedo", ImageUtilities.GetTexture(Main.Instance, "Assets/textures/Rocks.png", wrap: true)); + break; + default: + break; + } + material.SetFloat($"_BaseTileScale", 5 / module.scale); + if (module.color != null) + { + material.color = module.color.ToColor(); + } + } + + material.SetFloat("_Smoothness", module.smoothness); + material.SetFloat("_Metallic", module.metallic); + + _materialCache[module] = material; + } + + cubeSphereMR.sharedMaterial = material; var cubeSphereMC = icosphere.AddComponent(); cubeSphereMC.sharedMesh = mesh; diff --git a/NewHorizons/Builder/Body/ProxyBuilder.cs b/NewHorizons/Builder/Body/ProxyBuilder.cs index f1567659..3520008d 100644 --- a/NewHorizons/Builder/Body/ProxyBuilder.cs +++ b/NewHorizons/Builder/Body/ProxyBuilder.cs @@ -162,7 +162,7 @@ namespace NewHorizons.Builder.Body GameObject procGen = null; if (body.Config.ProcGen != null) { - procGen = ProcGenBuilder.Make(proxy, null, body.Config.ProcGen); + procGen = ProcGenBuilder.Make(body.Mod, proxy, null, body.Config.ProcGen); if (realSize < body.Config.ProcGen.scale) realSize = body.Config.ProcGen.scale; } diff --git a/NewHorizons/External/Modules/ProcGenModule.cs b/NewHorizons/External/Modules/ProcGenModule.cs index 6c54ced8..273005df 100644 --- a/NewHorizons/External/Modules/ProcGenModule.cs +++ b/NewHorizons/External/Modules/ProcGenModule.cs @@ -1,14 +1,78 @@ -using System.ComponentModel.DataAnnotations; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Runtime.Serialization; using NewHorizons.External.SerializableData; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; namespace NewHorizons.External.Modules { [JsonObject] public class ProcGenModule { + /// + /// Scale height of the proc gen. + /// + [Range(0, double.MaxValue)] public float scale; + + /// + /// Ground color, only applied if no texture or material is chosen. + /// public MColor color; - [Range(0, double.MaxValue)] public float scale; + /// + /// Can pick a preset material with a texture from the base game. Does not work with color or any textures. + /// + public Material material; + + /// + /// Can use a custom texture. Does not work with material or color. + /// + public string texture; + + /// + /// Relative filepath to the texture used for the terrain's smoothness and metallic, which are controlled by the texture's alpha and red channels respectively. Optional. + /// Typically black with variable transparency, when metallic isn't wanted. + /// + public string smoothnessMap; + + /// + /// How "glossy" the surface is, where 0 is diffuse, and 1 is like a mirror. + /// Multiplies with the alpha of the smoothness map if using one. + /// + [Range(0f, 1f)] + [DefaultValue(0f)] + public float smoothness = 0f; + + /// + /// How metallic the surface is, from 0 to 1. + /// Multiplies with the red of the smoothness map if using one. + /// + [Range(0f, 1f)] + [DefaultValue(0f)] + public float metallic = 0f; + + /// + /// Relative filepath to the texture used for the normal (aka bump) map. Optional. + /// + public string normalMap; + + /// + /// Strength of the normal map. Usually 0-1, but can go above, or negative to invert the map. + /// + [DefaultValue(1f)] + public float normalStrength = 1f; + + [JsonConverter(typeof(StringEnumConverter))] + public enum Material + { + [EnumMember(Value = @"default")] Default = 0, + + [EnumMember(Value = @"ice")] Ice = 1, + + [EnumMember(Value = @"quantum")] Quantum = 2, + + [EnumMember(Value = @"rock")] Rock = 3 + } } } \ No newline at end of file diff --git a/NewHorizons/Handlers/PlanetCreationHandler.cs b/NewHorizons/Handlers/PlanetCreationHandler.cs index 6898382f..aaeec71e 100644 --- a/NewHorizons/Handlers/PlanetCreationHandler.cs +++ b/NewHorizons/Handlers/PlanetCreationHandler.cs @@ -581,7 +581,7 @@ namespace NewHorizons.Handlers GameObject procGen = null; if (body.Config.ProcGen != null) { - procGen = ProcGenBuilder.Make(go, sector, body.Config.ProcGen); + procGen = ProcGenBuilder.Make(body.Mod, go, sector, body.Config.ProcGen); } if (body.Config.Star != null) diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index 56d8d645..42a6a9ca 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -309,6 +309,7 @@ namespace NewHorizons ImageUtilities.ClearCache(); AudioUtilities.ClearCache(); AssetBundleUtilities.ClearCache(); + ProcGenBuilder.ClearCache(); } IsSystemReady = false; diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index c811e967..cbe6f892 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -353,16 +353,72 @@ "type": "object", "additionalProperties": false, "properties": { - "color": { - "$ref": "#/definitions/MColor" - }, "scale": { "type": "number", + "description": "Scale height of the proc gen.", "format": "float", "minimum": 0.0 + }, + "color": { + "description": "Ground color, only applied if no texture or material is chosen.", + "$ref": "#/definitions/MColor" + }, + "material": { + "description": "Can pick a preset material with a texture from the base game. Does not work with color or any textures.", + "$ref": "#/definitions/Material" + }, + "texture": { + "type": "string", + "description": "Can use a custom texture. Does not work with material or color." + }, + "smoothnessMap": { + "type": "string", + "description": "Relative filepath to the texture used for the terrain's smoothness and metallic, which are controlled by the texture's alpha and red channels respectively. Optional.\nTypically black with variable transparency, when metallic isn't wanted." + }, + "smoothness": { + "type": "number", + "description": "How \"glossy\" the surface is, where 0 is diffuse, and 1 is like a mirror.\nMultiplies with the alpha of the smoothness map if using one.", + "format": "float", + "default": 0.0, + "maximum": 1.0, + "minimum": 0.0 + }, + "metallic": { + "type": "number", + "description": "How metallic the surface is, from 0 to 1.\nMultiplies with the red of the smoothness map if using one.", + "format": "float", + "default": 0.0, + "maximum": 1.0, + "minimum": 0.0 + }, + "normalMap": { + "type": "string", + "description": "Relative filepath to the texture used for the normal (aka bump) map. Optional." + }, + "normalStrength": { + "type": "number", + "description": "Strength of the normal map. Usually 0-1, but can go above, or negative to invert the map.", + "format": "float", + "default": 1.0 } } }, + "Material": { + "type": "string", + "description": "", + "x-enumNames": [ + "Default", + "Ice", + "Quantum", + "Rock" + ], + "enum": [ + "default", + "ice", + "quantum", + "rock" + ] + }, "AtmosphereModule": { "type": "object", "additionalProperties": false,