Better proc gen (#1045)

## Minor features

- Add visual options for proc gen (i.e., asteroids). Can add a triplanar
texture or use a preset material for quantum, ice, or rocks. Fixes #1014
This commit is contained in:
xen-42 2025-02-15 03:01:55 -05:00 committed by GitHub
commit 730f6f804f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 229 additions and 19 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 KiB

View File

@ -39,7 +39,8 @@ namespace NewHorizons.Builder.Body
{ {
surfaceGravity = belt.gravity, surfaceGravity = belt.gravity,
surfaceSize = size, surfaceSize = size,
gravityFallOff = GravityFallOff.InverseSquared gravityFallOff = GravityFallOff.InverseSquared,
hasFluidDetector = false
}; };
config.Orbit = new OrbitModule() config.Orbit = new OrbitModule()
@ -95,6 +96,7 @@ namespace NewHorizons.Builder.Body
color = new MColor(126, 94, 73) color = new MColor(126, 94, 73)
}; };
} }
config.AmbientLights = new[] { new AmbientLightModule() { outerRadius = size * 1.2f, intensity = 0.1f } };
var asteroid = new NewHorizonsBody(config, mod); var asteroid = new NewHorizonsBody(config, mod);
PlanetCreationHandler.GenerateBody(asteroid); PlanetCreationHandler.GenerateBody(asteroid);

View File

@ -100,6 +100,11 @@ namespace NewHorizons.Builder.Body.Geometry
mesh.normals = normals; mesh.normals = normals;
mesh.uv = uvs; mesh.uv = uvs;
mesh.RecalculateBounds();
// Unity recalculate normals does not support smooth normals
//mesh.RecalculateNormals();
mesh.RecalculateTangents();
return mesh; return mesh;
} }

View File

@ -11,7 +11,7 @@ namespace NewHorizons.Builder.Body
{ {
public static class HeightMapBuilder public static class HeightMapBuilder
{ {
public static Shader PlanetShader; private static Shader _planetShader;
// I hate nested functions okay // I hate nested functions okay
private static IModBehaviour _currentMod; private static IModBehaviour _currentMod;
@ -62,7 +62,7 @@ namespace NewHorizons.Builder.Body
cubeSphere.SetActive(false); cubeSphere.SetActive(false);
cubeSphere.transform.SetParent(sector?.transform ?? planetGO.transform, false); cubeSphere.transform.SetParent(sector?.transform ?? planetGO.transform, false);
if (PlanetShader == null) PlanetShader = AssetBundleUtilities.NHAssetBundle.LoadAsset<Shader>("Assets/Shaders/SphereTextureWrapperTriplanar.shader"); if (_planetShader == null) _planetShader = AssetBundleUtilities.NHAssetBundle.LoadAsset<Shader>("Assets/Shaders/SphereTextureWrapperTriplanar.shader");
var stretch = module.stretch != null ? (Vector3)module.stretch : Vector3.one; var stretch = module.stretch != null ? (Vector3)module.stretch : Vector3.one;
@ -115,7 +115,7 @@ namespace NewHorizons.Builder.Body
LODCubeSphere.AddComponent<MeshFilter>().mesh = CubeSphere.Build(resolution, heightMap, module.minHeight, module.maxHeight, stretch); LODCubeSphere.AddComponent<MeshFilter>().mesh = CubeSphere.Build(resolution, heightMap, module.minHeight, module.maxHeight, stretch);
var cubeSphereMR = LODCubeSphere.AddComponent<MeshRenderer>(); var cubeSphereMR = LODCubeSphere.AddComponent<MeshRenderer>();
var material = new Material(PlanetShader); var material = new Material(_planetShader);
cubeSphereMR.material = material; cubeSphereMR.material = material;
material.name = textureMap.name; material.name = textureMap.name;

View File

@ -1,21 +1,50 @@
using NewHorizons.Builder.Body.Geometry; using NewHorizons.Builder.Body.Geometry;
using NewHorizons.External.Modules; using NewHorizons.External.Modules;
using NewHorizons.Utility; using NewHorizons.Utility;
using NewHorizons.Utility.Files;
using OWML.Common;
using System.Collections.Generic;
using UnityEngine; using UnityEngine;
namespace NewHorizons.Builder.Body namespace NewHorizons.Builder.Body
{ {
public static class ProcGenBuilder public static class ProcGenBuilder
{ {
private static Material quantumMaterial; private static Material _material;
private static Material iceMaterial; private static Shader _planetShader;
public static GameObject Make(GameObject planetGO, Sector sector, ProcGenModule module) private static Dictionary<ProcGenModule, Material> _materialCache = new();
public static void ClearCache()
{ {
if (quantumMaterial == null) quantumMaterial = SearchUtilities.FindResourceOfTypeAndName<Material>("Rock_QM_EyeRock_mat"); foreach (var material in _materialCache.Values)
if (iceMaterial == null) iceMaterial = SearchUtilities.FindResourceOfTypeAndName<Material>("Rock_BH_IceSpike_mat"); {
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<Shader>("Assets/Shaders/SphereTextureWrapperTriplanar.shader");
if (_material == null) _material = MakeMaterial();
var icosphere = new GameObject("Icosphere");
icosphere.SetActive(false); icosphere.SetActive(false);
icosphere.transform.parent = sector?.transform ?? planetGO.transform; icosphere.transform.parent = sector?.transform ?? planetGO.transform;
icosphere.transform.rotation = Quaternion.Euler(90, 0, 0); icosphere.transform.rotation = Quaternion.Euler(90, 0, 0);
@ -26,8 +55,61 @@ namespace NewHorizons.Builder.Body
icosphere.AddComponent<MeshFilter>().mesh = mesh; icosphere.AddComponent<MeshFilter>().mesh = mesh;
var cubeSphereMR = icosphere.AddComponent<MeshRenderer>(); var cubeSphereMR = icosphere.AddComponent<MeshRenderer>();
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<MeshCollider>(); var cubeSphereMC = icosphere.AddComponent<MeshCollider>();
cubeSphereMC.sharedMesh = mesh; cubeSphereMC.sharedMesh = mesh;

View File

@ -162,7 +162,7 @@ namespace NewHorizons.Builder.Body
GameObject procGen = null; GameObject procGen = null;
if (body.Config.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; if (realSize < body.Config.ProcGen.scale) realSize = body.Config.ProcGen.scale;
} }

View File

@ -1,14 +1,78 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Runtime.Serialization;
using NewHorizons.External.SerializableData; using NewHorizons.External.SerializableData;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace NewHorizons.External.Modules namespace NewHorizons.External.Modules
{ {
[JsonObject] [JsonObject]
public class ProcGenModule public class ProcGenModule
{ {
/// <summary>
/// Scale height of the proc gen.
/// </summary>
[Range(0, double.MaxValue)] public float scale;
/// <summary>
/// Ground color, only applied if no texture or material is chosen.
/// </summary>
public MColor color; public MColor color;
[Range(0, double.MaxValue)] public float scale; /// <summary>
/// Can pick a preset material with a texture from the base game. Does not work with color or any textures.
/// </summary>
public Material material;
/// <summary>
/// Can use a custom texture. Does not work with material or color.
/// </summary>
public string texture;
/// <summary>
/// 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.
/// </summary>
public string smoothnessMap;
/// <summary>
/// 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.
/// </summary>
[Range(0f, 1f)]
[DefaultValue(0f)]
public float smoothness = 0f;
/// <summary>
/// How metallic the surface is, from 0 to 1.
/// Multiplies with the red of the smoothness map if using one.
/// </summary>
[Range(0f, 1f)]
[DefaultValue(0f)]
public float metallic = 0f;
/// <summary>
/// Relative filepath to the texture used for the normal (aka bump) map. Optional.
/// </summary>
public string normalMap;
/// <summary>
/// Strength of the normal map. Usually 0-1, but can go above, or negative to invert the map.
/// </summary>
[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
}
} }
} }

View File

@ -581,7 +581,7 @@ namespace NewHorizons.Handlers
GameObject procGen = null; GameObject procGen = null;
if (body.Config.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) if (body.Config.Star != null)

View File

@ -309,6 +309,7 @@ namespace NewHorizons
ImageUtilities.ClearCache(); ImageUtilities.ClearCache();
AudioUtilities.ClearCache(); AudioUtilities.ClearCache();
AssetBundleUtilities.ClearCache(); AssetBundleUtilities.ClearCache();
ProcGenBuilder.ClearCache();
} }
IsSystemReady = false; IsSystemReady = false;

View File

@ -353,16 +353,72 @@
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"color": {
"$ref": "#/definitions/MColor"
},
"scale": { "scale": {
"type": "number", "type": "number",
"description": "Scale height of the proc gen.",
"format": "float", "format": "float",
"minimum": 0.0 "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": { "AtmosphereModule": {
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,