Particle/Vection Fields (#665)

<!-- A new module or something else important -->
## Major features
- New `ParticleFields` module. Add particles to your planet (ex: leaves,
rain, snow, fireflies, and more). Resolves #219

<!-- A new parameter added to a module, or API feature -->
## Minor features
-

<!-- Some improvement that requires no action on the part of add-on
creators i.e., improved star graphics -->
## Improvements
-

<!-- Be sure to reference the existing issue if it exists -->
## Bug fixes
-
This commit is contained in:
Noah Pilarski 2023-08-24 16:32:46 -04:00 committed by GitHub
commit d1072e10cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 325 additions and 53 deletions

View File

@ -1,4 +1,5 @@
using NewHorizons.External.Configs; using NewHorizons.External.Configs;
using NewHorizons.External.Modules;
using NewHorizons.Utility; using NewHorizons.Utility;
using UnityEngine; using UnityEngine;
namespace NewHorizons.Builder.Atmosphere namespace NewHorizons.Builder.Atmosphere
@ -7,6 +8,21 @@ namespace NewHorizons.Builder.Atmosphere
{ {
private static GameObject _rainEmitterPrefab; private static GameObject _rainEmitterPrefab;
private static GameObject _snowEmitterPrefab; private static GameObject _snowEmitterPrefab;
private static GameObject _snowLightEmitterPrefab;
private static GameObject _emberEmitterPrefab;
private static GameObject _leafEmitterPrefab;
private static GameObject _planktonEmitterPrefab;
private static GameObject _bubbleEmitterPrefab;
private static GameObject _currentEmitterPrefab;
private static GameObject _cloudEmitterPrefab;
private static GameObject _crawliesEmitterPrefab;
private static GameObject _firefliesEmitterPrefab;
private static GameObject _pollenEmitterPrefab;
private static GameObject _iceMoteEmitterPrefab;
private static GameObject _rockMoteEmitterPrefab;
private static GameObject _crystalMoteEmitterPrefab;
private static GameObject _sandMoteEmitterPrefab;
private static GameObject _fogEmitterPrefab;
private static bool _isInit; private static bool _isInit;
@ -18,9 +34,24 @@ namespace NewHorizons.Builder.Atmosphere
if (_rainEmitterPrefab == null) _rainEmitterPrefab = SearchUtilities.Find("GiantsDeep_Body/Sector_GD/Sector_GDInterior/Effects_GDInterior/Effects_GD_Rain").InstantiateInactive().Rename("Prefab_Effects_Rain").DontDestroyOnLoad(); if (_rainEmitterPrefab == null) _rainEmitterPrefab = SearchUtilities.Find("GiantsDeep_Body/Sector_GD/Sector_GDInterior/Effects_GDInterior/Effects_GD_Rain").InstantiateInactive().Rename("Prefab_Effects_Rain").DontDestroyOnLoad();
if (_snowEmitterPrefab == null) _snowEmitterPrefab = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Effects_BH/Effects_BH_Snowflakes").InstantiateInactive().Rename("Prefab_Effects_Snowflakes").DontDestroyOnLoad(); if (_snowEmitterPrefab == null) _snowEmitterPrefab = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Effects_BH/Effects_BH_Snowflakes").InstantiateInactive().Rename("Prefab_Effects_Snowflakes").DontDestroyOnLoad();
if (_snowLightEmitterPrefab == null) _snowLightEmitterPrefab = SearchUtilities.Find("TimberHearth_Body/Sector_TH/Effects_TH/Effects_TH_Snowflakes").InstantiateInactive().Rename("Prefab_Effects_SnowflakesLight").DontDestroyOnLoad();
if (_emberEmitterPrefab == null) _emberEmitterPrefab = SearchUtilities.Find("VolcanicMoon_Body/Sector_VM/Effects_VM/Effects_VM_Embers").InstantiateInactive().Rename("Prefab_Effects_Embers").DontDestroyOnLoad();
if (_leafEmitterPrefab == null) _leafEmitterPrefab = SearchUtilities.Find("GiantsDeep_Body/Sector_GD/Sector_GDInterior/Effects_GDInterior/Effects_GD_Leaves").InstantiateInactive().Rename("Prefab_Effects_Leaves").DontDestroyOnLoad();
if (_planktonEmitterPrefab == null) _planktonEmitterPrefab = SearchUtilities.Find("GiantsDeep_Body/Sector_GD/Sector_GDInterior/Effects_GDInterior/Effects_GD_Plankton").InstantiateInactive().Rename("Prefab_Effects_Plankton").DontDestroyOnLoad();
if (_bubbleEmitterPrefab == null) _bubbleEmitterPrefab = SearchUtilities.Find("GiantsDeep_Body/Sector_GD/Sector_GDInterior/Effects_GDInterior/Effects_GD_Bubbles").InstantiateInactive().Rename("Prefab_Effects_Bubbles").DontDestroyOnLoad();
if (_currentEmitterPrefab == null) _currentEmitterPrefab = SearchUtilities.Find("GiantsDeep_Body/Sector_GD/Sector_GDInterior/Effects_GDInterior/Effects_GD_RadialCurrent").InstantiateInactive().Rename("Prefab_Effects_Current").DontDestroyOnLoad();
if (_cloudEmitterPrefab == null) _cloudEmitterPrefab = SearchUtilities.Find("GiantsDeep_Body/Sector_GD/Effects_GD/Effects_GD_Clouds").InstantiateInactive().Rename("Prefab_Effects_Clouds").DontDestroyOnLoad();
if (_crawliesEmitterPrefab == null) _crawliesEmitterPrefab = SearchUtilities.Find("DB_EscapePodDimension_Body/Sector_EscapePodDimension/Effects_EscapePodDimension/Effects_DB_Crawlies (1)").InstantiateInactive().Rename("Prefab_Effects_Crawlies").DontDestroyOnLoad();
if (_firefliesEmitterPrefab == null) _firefliesEmitterPrefab = SearchUtilities.Find("TimberHearth_Body/Sector_TH/Effects_TH/Effects_TH_Fireflies").InstantiateInactive().Rename("Prefab_Effects_Fireflies").DontDestroyOnLoad();
if (_pollenEmitterPrefab == null) _pollenEmitterPrefab = SearchUtilities.Find("TimberHearth_Body/Sector_TH/Effects_TH/Effects_TH_SurfacePollen").InstantiateInactive().Rename("Prefab_Effects_Pollen").DontDestroyOnLoad();
if (_iceMoteEmitterPrefab == null) _iceMoteEmitterPrefab = SearchUtilities.Find("DarkBramble_Body/Effects_DB/Effects_DB_IceMotes").InstantiateInactive().Rename("Prefab_Effects_IceMotes").DontDestroyOnLoad();
if (_rockMoteEmitterPrefab == null) _rockMoteEmitterPrefab = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Effects_BH/Effects_BH_RockMotes").InstantiateInactive().Rename("Prefab_Effects_RockMotes").DontDestroyOnLoad();
if (_crystalMoteEmitterPrefab == null) _crystalMoteEmitterPrefab = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Effects_BH/Effects_BH_CrystalMotes").InstantiateInactive().Rename("Prefab_Effects_CrystalMotes").DontDestroyOnLoad();
if (_sandMoteEmitterPrefab == null) _sandMoteEmitterPrefab = SearchUtilities.Find("CaveTwin_Body/Sector_CaveTwin/Effects_CaveTwin/Effects_HGT_SandMotes").InstantiateInactive().Rename("Prefab_Effects_SandMotes").DontDestroyOnLoad();
if (_fogEmitterPrefab == null) _fogEmitterPrefab = SearchUtilities.Find("DB_EscapePodDimension_Body/Sector_EscapePodDimension/Effects_EscapePodDimension/Effects_DB_Fog (1)").InstantiateInactive().Rename("Prefab_Effects_Fog").DontDestroyOnLoad();
} }
public static void Make(GameObject planetGO, Sector sector, PlanetConfig config, float surfaceSize) public static void Make(GameObject planetGO, Sector sector, PlanetConfig config)
{ {
InitPrefabs(); InitPrefabs();
@ -36,7 +67,7 @@ namespace NewHorizons.Builder.Atmosphere
SCG._dynamicCullingBounds = false; SCG._dynamicCullingBounds = false;
SCG._waitForStreaming = false; SCG._waitForStreaming = false;
var minHeight = surfaceSize; var minHeight = config.Base.surfaceSize;
if (config.HeightMap?.minHeight != null) if (config.HeightMap?.minHeight != null)
{ {
if (config.Water?.size >= config.HeightMap.minHeight) minHeight = config.Water.size; // use sea level if its higher if (config.Water?.size >= config.HeightMap.minHeight) minHeight = config.Water.size; // use sea level if its higher
@ -48,52 +79,56 @@ namespace NewHorizons.Builder.Atmosphere
var maxHeight = config.Atmosphere.size; var maxHeight = config.Atmosphere.size;
if (config.Atmosphere.clouds?.outerCloudRadius != null) maxHeight = config.Atmosphere.clouds.outerCloudRadius; if (config.Atmosphere.clouds?.outerCloudRadius != null) maxHeight = config.Atmosphere.clouds.outerCloudRadius;
if (config.Atmosphere.hasRain) foreach (var particleField in config.ParticleFields)
{ {
var rainGO = Object.Instantiate(_rainEmitterPrefab, effectsGO.transform); var prefab = GetPrefabByType(particleField.type);
rainGO.name = "RainEmitter"; var emitter = Object.Instantiate(prefab, effectsGO.transform);
rainGO.transform.position = planetGO.transform.position; emitter.name = !string.IsNullOrWhiteSpace(particleField.rename) ? particleField.rename : prefab.name.Replace("Prefab_", "");
emitter.transform.position = planetGO.transform.position;
var pvc = rainGO.GetComponent<PlanetaryVectionController>(); var vfe = emitter.GetComponent<VectionFieldEmitter>();
pvc._densityByHeight = new AnimationCurve(new Keyframe[] var pvc = emitter.GetComponent<PlanetaryVectionController>();
pvc._vectionFieldEmitter = vfe;
pvc._densityByHeight = particleField.densityByHeightCurve != null ? particleField.densityByHeightCurve.ToAnimationCurve() : new AnimationCurve(new Keyframe[]
{ {
new Keyframe(minHeight - 0.5f, 0), new Keyframe(minHeight - 0.5f, 0),
new Keyframe(minHeight, 10f), new Keyframe(minHeight, 10f),
new Keyframe(maxHeight, 0f) new Keyframe(maxHeight, 0f)
}); });
pvc._followTarget = particleField.followTarget == ParticleFieldModule.FollowTarget.Probe ? PlanetaryVectionController.FollowTarget.Probe : PlanetaryVectionController.FollowTarget.Player;
pvc._activeInSector = sector;
pvc._exclusionSectors = new Sector[] { };
rainGO.GetComponent<PlanetaryVectionController>()._activeInSector = sector; emitter.SetActive(true);
rainGO.GetComponent<PlanetaryVectionController>()._exclusionSectors = new Sector[] { };
rainGO.SetActive(true);
}
if (config.Atmosphere.hasSnow)
{
var snowGO = new GameObject("SnowEffects");
snowGO.transform.parent = effectsGO.transform;
snowGO.transform.position = planetGO.transform.position;
for (int i = 0; i < 5; i++)
{
var snowEmitter = Object.Instantiate(_snowEmitterPrefab, snowGO.transform);
snowEmitter.name = "SnowEmitter";
snowEmitter.transform.position = planetGO.transform.position;
var pvc = snowEmitter.GetComponent<PlanetaryVectionController>();
pvc._densityByHeight = new AnimationCurve(new Keyframe[]
{
new Keyframe(minHeight - 0.5f, 0),
new Keyframe(minHeight, 10f),
new Keyframe(maxHeight, 0f)
});
snowEmitter.GetComponent<PlanetaryVectionController>()._activeInSector = sector;
snowEmitter.GetComponent<PlanetaryVectionController>()._exclusionSectors = new Sector[] { };
snowEmitter.SetActive(true);
}
} }
effectsGO.transform.position = planetGO.transform.position; effectsGO.transform.position = planetGO.transform.position;
effectsGO.SetActive(true); effectsGO.SetActive(true);
} }
public static GameObject GetPrefabByType(ParticleFieldModule.ParticleFieldType type)
{
return type switch
{
ParticleFieldModule.ParticleFieldType.Rain => _rainEmitterPrefab,
ParticleFieldModule.ParticleFieldType.SnowflakesHeavy => _snowEmitterPrefab,
ParticleFieldModule.ParticleFieldType.SnowflakesLight => _snowLightEmitterPrefab,
ParticleFieldModule.ParticleFieldType.Embers => _emberEmitterPrefab,
ParticleFieldModule.ParticleFieldType.Clouds => _cloudEmitterPrefab,
ParticleFieldModule.ParticleFieldType.Leaves => _leafEmitterPrefab,
ParticleFieldModule.ParticleFieldType.Bubbles => _bubbleEmitterPrefab,
ParticleFieldModule.ParticleFieldType.Fog => _fogEmitterPrefab,
ParticleFieldModule.ParticleFieldType.CrystalMotes => _crystalMoteEmitterPrefab,
ParticleFieldModule.ParticleFieldType.RockMotes => _rockMoteEmitterPrefab,
ParticleFieldModule.ParticleFieldType.IceMotes => _iceMoteEmitterPrefab,
ParticleFieldModule.ParticleFieldType.SandMotes => _sandMoteEmitterPrefab,
ParticleFieldModule.ParticleFieldType.Crawlies => _crawliesEmitterPrefab,
ParticleFieldModule.ParticleFieldType.Fireflies => _firefliesEmitterPrefab,
ParticleFieldModule.ParticleFieldType.Plankton => _planktonEmitterPrefab,
ParticleFieldModule.ParticleFieldType.Pollen => _pollenEmitterPrefab,
ParticleFieldModule.ParticleFieldType.Current => _currentEmitterPrefab,
_ => null,
};
}
} }
} }

View File

@ -13,6 +13,7 @@ using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using UnityEngine;
namespace NewHorizons.External.Configs namespace NewHorizons.External.Configs
{ {
@ -167,6 +168,12 @@ namespace NewHorizons.External.Configs
/// </summary> /// </summary>
public WaterModule Water; public WaterModule Water;
/// <summary>
/// Add particle effects in a field around the planet.
/// Also known as Vection Fields.
/// </summary>
public ParticleFieldModule[] ParticleFields;
/// <summary> /// <summary>
/// Add various volumes on this body /// Add various volumes on this body
/// </summary> /// </summary>
@ -341,6 +348,29 @@ namespace NewHorizons.External.Configs
// useBasicCloudShader is obsolete // useBasicCloudShader is obsolete
if (Atmosphere.clouds != null && Atmosphere.clouds.useBasicCloudShader) if (Atmosphere.clouds != null && Atmosphere.clouds.useBasicCloudShader)
Atmosphere.clouds.cloudsPrefab = CloudPrefabType.Basic; Atmosphere.clouds.cloudsPrefab = CloudPrefabType.Basic;
if (Atmosphere.hasRain)
{
if (ParticleFields == null) ParticleFields = new ParticleFieldModule[0];
ParticleFields = ParticleFields.Append(new ParticleFieldModule
{
type = ParticleFieldModule.ParticleFieldType.Rain,
rename = "RainEmitter"
}).ToArray();
}
if (Atmosphere.hasSnow)
{
if (ParticleFields == null) ParticleFields = new ParticleFieldModule[0];
for (int i = 0; i < 5; i++)
{
ParticleFields = ParticleFields.Append(new ParticleFieldModule
{
type = ParticleFieldModule.ParticleFieldType.SnowflakesHeavy,
rename = "SnowEmitter"
}).ToArray();
}
}
} }
if (Props?.tornados != null) if (Props?.tornados != null)

View File

@ -57,7 +57,7 @@ namespace NewHorizons.External.Modules
/// Colour of fog on the planet, if you put fog. /// Colour of fog on the planet, if you put fog.
/// </summary> /// </summary>
public MColor fogTint; public MColor fogTint;
/// <summary> /// <summary>
/// Relative filepath to the fog color ramp texture, if you put fog. /// Relative filepath to the fog color ramp texture, if you put fog.
/// x axis is angle to sun (left at midnight, right at noon), y axis is distance to camera (close at bottom, far at top). /// x axis is angle to sun (left at midnight, right at noon), y axis is distance to camera (close at bottom, far at top).
@ -75,15 +75,12 @@ namespace NewHorizons.External.Modules
public bool hasTrees; public bool hasTrees;
/// <summary> /// <summary>
/// Does this planet have rain? /// Does this planet have rain?
/// This is equivalent to effects of setting a rain particle/vection field, rain audio volume, and visor effect volume, combined for convenience.
/// For more control over the rain, use those individual components.
/// </summary> /// </summary>
public bool hasRain; public bool hasRain;
/// <summary>
/// Does this planet have snow?
/// </summary>
public bool hasSnow;
/// <summary> /// <summary>
/// Scale height of the atmosphere /// Scale height of the atmosphere
/// </summary> /// </summary>
@ -174,7 +171,6 @@ namespace NewHorizons.External.Modules
/// </summary> /// </summary>
[DefaultValue(0f)] public float rotationSpeed = 0f; [DefaultValue(0f)] public float rotationSpeed = 0f;
#region Obsolete #region Obsolete
/// <summary> /// <summary>
@ -189,6 +185,8 @@ namespace NewHorizons.External.Modules
#region Obsolete #region Obsolete
[Obsolete("HasSnow is deprecated, please use ParticleFields instead")]
public bool hasSnow;
[Obsolete("CloudTint is deprecated, please use CloudInfo instead")] [Obsolete("CloudTint is deprecated, please use CloudInfo instead")]
public MColor cloudTint; public MColor cloudTint;
@ -208,7 +206,8 @@ namespace NewHorizons.External.Modules
[Obsolete("UseBasicCloudShader is deprecated, please use CloudInfo instead")] [Obsolete("UseBasicCloudShader is deprecated, please use CloudInfo instead")]
public bool useBasicCloudShader; public bool useBasicCloudShader;
[DefaultValue(true)] [Obsolete("ShadowsOnClouds is deprecated, please use CloudInfo instead")] [DefaultValue(true)]
[Obsolete("ShadowsOnClouds is deprecated, please use CloudInfo instead")]
public bool shadowsOnClouds = true; public bool shadowsOnClouds = true;
[Obsolete("HasAtmosphere is deprecated, please use UseAtmosphereShader instead")] [Obsolete("HasAtmosphere is deprecated, please use UseAtmosphereShader instead")]

View File

@ -0,0 +1,79 @@
using NewHorizons.External.Modules.VariableSize;
using NewHorizons.External.SerializableData;
using NewHorizons.External.SerializableEnums;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System;
using System.ComponentModel;
using System.Runtime.Serialization;
namespace NewHorizons.External.Modules
{
[JsonObject]
public class ParticleFieldModule
{
/// <summary>
/// Particle type for this vection field.
/// </summary>
public ParticleFieldType type;
/// <summary>
/// What the particle field activates based on.
/// </summary>
[DefaultValue("player")] public FollowTarget followTarget = FollowTarget.Player;
/// <summary>
/// Density by height curve. Determines how many particles are emitted at different heights.
/// Defaults to a curve based on minimum and maximum heights of various other modules.
/// </summary>
public HeightDensityPair[] densityByHeightCurve;
/// <summary>
/// An optional rename of this object
/// </summary>
public string rename;
[JsonObject]
public class HeightDensityPair
{
/// <summary>
/// A specific radius
/// </summary>
public float height;
/// <summary>
/// The particle count for this radius.
/// </summary>
public float density;
}
[JsonConverter(typeof(StringEnumConverter))]
public enum ParticleFieldType
{
[EnumMember(Value = @"rain")] Rain,
[EnumMember(Value = @"snowflakesHeavy")] SnowflakesHeavy,
[EnumMember(Value = @"snowflakesLight")] SnowflakesLight,
[EnumMember(Value = @"embers")] Embers,
[EnumMember(Value = @"clouds")] Clouds,
[EnumMember(Value = @"leaves")] Leaves,
[EnumMember(Value = @"bubbles")] Bubbles,
[EnumMember(Value = @"fog")] Fog,
[EnumMember(Value = @"crystalMotes")] CrystalMotes,
[EnumMember(Value = @"rockMotes")] RockMotes,
[EnumMember(Value = @"iceMotes")] IceMotes,
[EnumMember(Value = @"sandMotes")] SandMotes,
[EnumMember(Value = @"crawlies")] Crawlies,
[EnumMember(Value = @"fireflies")] Fireflies,
[EnumMember(Value = @"plankton")] Plankton,
[EnumMember(Value = @"pollen")] Pollen,
[EnumMember(Value = @"current")] Current
}
[JsonConverter(typeof(StringEnumConverter))]
public enum FollowTarget
{
[EnumMember(Value = @"player")] Player,
[EnumMember(Value = @"probe")] Probe
}
}
}

View File

@ -654,9 +654,6 @@ namespace NewHorizons.Handlers
} }
} }
if (body.Config.Atmosphere.hasRain || body.Config.Atmosphere.hasSnow)
EffectsBuilder.Make(go, sector, body.Config, surfaceSize);
if (body.Config.Atmosphere.fogSize != 0) if (body.Config.Atmosphere.fogSize != 0)
{ {
fog = FogBuilder.Make(go, sector, body.Config.Atmosphere, body.Mod); fog = FogBuilder.Make(go, sector, body.Config.Atmosphere, body.Mod);
@ -665,6 +662,11 @@ namespace NewHorizons.Handlers
atmosphere = AtmosphereBuilder.Make(go, sector, body.Config.Atmosphere, surfaceSize).GetComponentInChildren<LODGroup>(); atmosphere = AtmosphereBuilder.Make(go, sector, body.Config.Atmosphere, surfaceSize).GetComponentInChildren<LODGroup>();
} }
if (body.Config.ParticleFields != null)
{
EffectsBuilder.Make(go, sector, body.Config);
}
if (body.Config.Props != null) if (body.Config.Props != null)
{ {
PropBuildManager.Make(go, sector, rb, body); PropBuildManager.Make(go, sector, rb, body);

View File

@ -132,6 +132,13 @@
"description": "Add water to this planet", "description": "Add water to this planet",
"$ref": "#/definitions/WaterModule" "$ref": "#/definitions/WaterModule"
}, },
"ParticleFields": {
"type": "array",
"description": "Add particle effects in a field around the planet.\nAlso known as Vection Fields.",
"items": {
"$ref": "#/definitions/ParticleFieldModule"
}
},
"Volumes": { "Volumes": {
"description": "Add various volumes on this body", "description": "Add various volumes on this body",
"$ref": "#/definitions/VolumesModule" "$ref": "#/definitions/VolumesModule"
@ -371,11 +378,7 @@
}, },
"hasRain": { "hasRain": {
"type": "boolean", "type": "boolean",
"description": "Does this planet have rain?" "description": "Does this planet have rain? \nThis is equivalent to effects of setting a rain particle/vection field, rain audio volume, and visor effect volume, combined for convenience.\nFor more control over the rain, use those individual components."
},
"hasSnow": {
"type": "boolean",
"description": "Does this planet have snow?"
}, },
"size": { "size": {
"type": "number", "type": "number",
@ -3583,6 +3586,102 @@
} }
} }
}, },
"ParticleFieldModule": {
"type": "object",
"additionalProperties": false,
"properties": {
"type": {
"description": "Particle type for this vection field.",
"$ref": "#/definitions/ParticleFieldType"
},
"followTarget": {
"description": "What the particle field activates based on.",
"default": "player",
"$ref": "#/definitions/FollowTarget"
},
"densityByHeightCurve": {
"type": "array",
"description": "Density by height curve. Determines how many particles are emitted at different heights.\nDefaults to a curve based on minimum and maximum heights of various other modules.",
"items": {
"$ref": "#/definitions/HeightDensityPair"
}
},
"rename": {
"type": "string",
"description": "An optional rename of this object"
}
}
},
"ParticleFieldType": {
"type": "string",
"description": "",
"x-enumNames": [
"Rain",
"SnowflakesHeavy",
"SnowflakesLight",
"Embers",
"Clouds",
"Leaves",
"Bubbles",
"Fog",
"CrystalMotes",
"RockMotes",
"IceMotes",
"SandMotes",
"Crawlies",
"Fireflies",
"Plankton",
"Pollen",
"Current"
],
"enum": [
"rain",
"snowflakesHeavy",
"snowflakesLight",
"embers",
"clouds",
"leaves",
"bubbles",
"fog",
"crystalMotes",
"rockMotes",
"iceMotes",
"sandMotes",
"crawlies",
"fireflies",
"plankton",
"pollen",
"current"
]
},
"FollowTarget": {
"type": "string",
"description": "",
"x-enumNames": [
"Player",
"Probe"
],
"enum": [
"player",
"probe"
]
},
"HeightDensityPair": {
"type": "object",
"additionalProperties": false,
"properties": {
"height": {
"type": "number",
"description": "A specific radius",
"format": "float"
},
"density": {
"type": "number",
"description": "The particle count for this radius.",
"format": "float"
}
}
},
"VolumesModule": { "VolumesModule": {
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,

View File

@ -1,4 +1,5 @@
using NewHorizons.External.Configs; using NewHorizons.External.Configs;
using NewHorizons.External.Modules.VariableSize;
using NewHorizons.External.SerializableData; using NewHorizons.External.SerializableData;
using NewHorizons.External.SerializableEnums; using NewHorizons.External.SerializableEnums;
using NewHorizons.Utility.OWML; using NewHorizons.Utility.OWML;
@ -12,6 +13,7 @@ using System.Reflection;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using UnityEngine; using UnityEngine;
using static NewHorizons.External.Modules.ParticleFieldModule;
using NomaiCoordinates = NewHorizons.External.Configs.StarSystemConfig.NomaiCoordinates; using NomaiCoordinates = NewHorizons.External.Configs.StarSystemConfig.NomaiCoordinates;
namespace NewHorizons.Utility namespace NewHorizons.Utility
@ -277,5 +279,31 @@ namespace NewHorizons.Utility
{ {
go.transform.rotation = Quaternion.FromToRotation(Vector3.forward, direction); go.transform.rotation = Quaternion.FromToRotation(Vector3.forward, direction);
} }
public static AnimationCurve ToAnimationCurve(this TimeValuePair[] pairs)
{
var curve = new AnimationCurve();
if (pairs != null)
{
foreach (var pair in pairs)
{
curve.AddKey(new Keyframe(pair.time, pair.value));
}
}
return curve;
}
public static AnimationCurve ToAnimationCurve(this HeightDensityPair[] pairs)
{
var curve = new AnimationCurve();
if (pairs != null)
{
foreach (var pair in pairs)
{
curve.AddKey(new Keyframe(pair.height, pair.density));
}
}
return curve;
}
} }
} }