mirror of
https://github.com/LazyDuchess/OpenTS2.git
synced 2025-01-22 16:21:47 -05:00
Merge branch 'master' of https://github.com/LazyDuchess/OpenTS2
This commit is contained in:
commit
26efd8281b
7 changed files with 4960 additions and 27 deletions
4789
Assets/Scenes/Tests/EffectsTest.unity
generated
4789
Assets/Scenes/Tests/EffectsTest.unity
generated
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,5 @@
|
|||
using OpenTS2.Content.DBPF.Effects.Types;
|
||||
using System;
|
||||
using OpenTS2.Content.DBPF.Effects.Types;
|
||||
using OpenTS2.Files.Formats.DBPF.Types;
|
||||
using OpenTS2.Files.Utils;
|
||||
using UnityEngine;
|
||||
|
@ -76,11 +77,29 @@ namespace OpenTS2.Content.DBPF.Effects
|
|||
RandomWalkTurnX = randomWalkTurnX;
|
||||
RandomWalkTurnY = randomWalkTurnY;
|
||||
}
|
||||
|
||||
public bool IsFlagSet(ParticleFlagBits flag)
|
||||
{
|
||||
var mask = 1UL << (int)flag;
|
||||
return (Flags & mask) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
public enum ParticleFlagBits
|
||||
{
|
||||
/// <summary>
|
||||
/// This flag is set if the particle emitter should be an ellipsoid shape.
|
||||
/// </summary>
|
||||
EmitterIsEllipsoid = 8,
|
||||
}
|
||||
|
||||
public struct ParticleLife
|
||||
{
|
||||
/// <summary>
|
||||
/// Particles get a lifetime in a random range between this vector's start and end.
|
||||
/// </summary>
|
||||
public Vector2 Life { get; }
|
||||
|
||||
public float LifePreRoll { get; }
|
||||
|
||||
public ParticleLife(Vector2 life, float lifePreRoll)
|
||||
|
@ -97,7 +116,16 @@ namespace OpenTS2.Content.DBPF.Effects
|
|||
|
||||
public BoundingBox EmitDirection { get; }
|
||||
public Vector2 EmitSpeed { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The volume of the emitter defined by its corners. This can either be a cuboid shape, an ellipsoid or a
|
||||
/// torus depending on the flags and EmitTorusWidth.
|
||||
/// </summary>
|
||||
public BoundingBox EmitVolume { get; }
|
||||
|
||||
/// <summary>
|
||||
/// If set to a value greater than 0, emitter is a torus shape.
|
||||
/// </summary>
|
||||
public float EmitTorusWidth { get; }
|
||||
|
||||
public FloatCurve RateCurve { get; }
|
||||
|
@ -152,6 +180,61 @@ namespace OpenTS2.Content.DBPF.Effects
|
|||
Colors = colors;
|
||||
ColorVary = colorVary;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This returns the range of colors to use for start of the particle.
|
||||
/// </summary>
|
||||
public readonly (Color, Color) GetStartingColorRange()
|
||||
{
|
||||
var minimum = new Color(Colors[0].x - ColorVary[0],
|
||||
Colors[0].y - ColorVary[1],
|
||||
Colors[0].z - ColorVary[2],
|
||||
Math.Max(AlphaCurve.Curve[0] - AlphaVary, 0));
|
||||
var maximum = new Color(Colors[0].x + ColorVary[0],
|
||||
Colors[0].y + ColorVary[1],
|
||||
Colors[0].z + ColorVary[2],
|
||||
Math.Min(AlphaCurve.Curve[0] + AlphaVary, 1));
|
||||
return (minimum, maximum);
|
||||
}
|
||||
|
||||
public readonly (Gradient, Gradient) GetColorGradientsOverTime()
|
||||
{
|
||||
var minColorKeys = new GradientColorKey[Colors.Length];
|
||||
var maxColorKeys = new GradientColorKey[Colors.Length];
|
||||
|
||||
// Unity supports a maximum of 8 keys.
|
||||
Debug.Assert(Colors.Length <= 8);
|
||||
for (var i = 0; i < Colors.Length; i++)
|
||||
{
|
||||
var time = ((float)i) / Colors.Length;
|
||||
|
||||
var color = Colors[i];
|
||||
minColorKeys[i] = new GradientColorKey(
|
||||
new Color(color.x - ColorVary[0], color.y - ColorVary[1], color.z - ColorVary[2]), time);
|
||||
maxColorKeys[i] = new GradientColorKey(
|
||||
new Color(color.x + ColorVary[0], color.y + ColorVary[1], color.z + ColorVary[2]), time);
|
||||
}
|
||||
|
||||
// Unity supports a maximum of 8 keys.
|
||||
Debug.Assert(AlphaCurve.Curve.Length <= 8);
|
||||
|
||||
var minAlphaKeys = new GradientAlphaKey[AlphaCurve.Curve.Length];
|
||||
var maxAlphaKeys = new GradientAlphaKey[AlphaCurve.Curve.Length];
|
||||
for (var i = 0; i < AlphaCurve.Curve.Length; i++)
|
||||
{
|
||||
var time = ((float)i) / AlphaCurve.Curve.Length;
|
||||
|
||||
var alpha = AlphaCurve.Curve[i];
|
||||
minAlphaKeys[i] = new GradientAlphaKey(Math.Max(alpha - AlphaVary, 0), time);
|
||||
maxAlphaKeys[i] = new GradientAlphaKey(Math.Min(alpha + AlphaVary, 1), time);
|
||||
}
|
||||
|
||||
var minGradient = new Gradient();
|
||||
minGradient.SetKeys(minColorKeys, minAlphaKeys);
|
||||
var maxGradient = new Gradient();
|
||||
maxGradient.SetKeys(maxColorKeys, maxAlphaKeys);
|
||||
return (minGradient, maxGradient);
|
||||
}
|
||||
}
|
||||
|
||||
public struct ParticleDrawing
|
||||
|
@ -225,7 +308,9 @@ namespace OpenTS2.Content.DBPF.Effects
|
|||
}
|
||||
|
||||
// These are methods common to both Particle and MetaParticles.
|
||||
|
||||
#region Particle and MetaParticle common readers
|
||||
|
||||
public static class ParticleHelper
|
||||
{
|
||||
internal static ParticleLife ReadParticleLife(IoBuffer reader)
|
||||
|
@ -288,5 +373,6 @@ namespace OpenTS2.Content.DBPF.Effects
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
|
@ -16,14 +16,14 @@ namespace OpenTS2.Content.DBPF.Effects
|
|||
public struct EffectDescription
|
||||
{
|
||||
public string EffectName { get; }
|
||||
public byte BlockType { get; }
|
||||
public int BlockIndex { get; }
|
||||
public byte EffectType { get; }
|
||||
public int EffectIndex { get; }
|
||||
|
||||
private EffectDescription(string effectName, byte blockType, int blockIndex)
|
||||
private EffectDescription(string effectName, byte effectType, int effectIndex)
|
||||
{
|
||||
EffectName = effectName;
|
||||
BlockType = blockType;
|
||||
BlockIndex = blockIndex;
|
||||
EffectType = effectType;
|
||||
EffectIndex = effectIndex;
|
||||
}
|
||||
|
||||
public static EffectDescription Deserialize(IoBuffer reader, ushort version)
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace OpenTS2.Content.DBPF.Effects.Types
|
|||
Curve = curve;
|
||||
}
|
||||
|
||||
public AnimationCurve ToUnityCurve()
|
||||
public ParticleSystem.MinMaxCurve ToUnityCurve()
|
||||
{
|
||||
var keyframes = new Keyframe[Curve.Length];
|
||||
for (var i = 0; i < Curve.Length; i++)
|
||||
|
@ -23,7 +23,21 @@ namespace OpenTS2.Content.DBPF.Effects.Types
|
|||
keyframes[i] = new Keyframe(i, Curve[i]);
|
||||
}
|
||||
|
||||
return new AnimationCurve(keyframes);
|
||||
return new ParticleSystem.MinMaxCurve(1.0f, new AnimationCurve(keyframes));
|
||||
}
|
||||
|
||||
public ParticleSystem.MinMaxCurve ToUnityCurveWithVariance(float vary)
|
||||
{
|
||||
var lowerKeyframes = new Keyframe[Curve.Length];
|
||||
var upperKeyframes = new Keyframe[Curve.Length];
|
||||
for (var i = 0; i < Curve.Length; i++)
|
||||
{
|
||||
lowerKeyframes[i] = new Keyframe(i, Curve[i] - vary);
|
||||
upperKeyframes[i] = new Keyframe(i, Curve[i] + vary);
|
||||
}
|
||||
|
||||
return new ParticleSystem.MinMaxCurve(1.0f,
|
||||
new AnimationCurve(lowerKeyframes), new AnimationCurve(upperKeyframes));
|
||||
}
|
||||
|
||||
public static FloatCurve Deserialize(IoBuffer reader)
|
||||
|
|
|
@ -72,12 +72,12 @@ namespace OpenTS2.Content.DBPF
|
|||
|
||||
public IBaseEffect GetEffectFromVisualEffectDescription(EffectDescription description)
|
||||
{
|
||||
return description.BlockType switch
|
||||
return description.EffectType switch
|
||||
{
|
||||
1 => ParticleEffects[description.BlockIndex],
|
||||
6 => SequenceEffects[description.BlockIndex],
|
||||
1 => ParticleEffects[description.EffectIndex],
|
||||
6 => SequenceEffects[description.EffectIndex],
|
||||
_ => throw new NotImplementedException(
|
||||
$"Block type {description.BlockType} in VisualEffect not supported")
|
||||
$"Block type {description.EffectType} in VisualEffect not supported")
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace OpenTS2.Scenes
|
|||
private EffectsAsset _effects;
|
||||
private List<Material> _particleMaterials = new List<Material>();
|
||||
|
||||
private void Start()
|
||||
private void Awake()
|
||||
{
|
||||
var contentProvider = ContentProvider.Get();
|
||||
// Load effects package.
|
||||
|
@ -27,6 +27,11 @@ namespace OpenTS2.Scenes
|
|||
Filesystem.GetPackagesInDirectory(Filesystem.GetDataPathForProduct(ProductFlags.BaseGame) + "/Res/Effects"));
|
||||
_effects = contentProvider.GetAsset<EffectsAsset>(new ResourceKey(instanceID: 1, groupID: GroupIDs.Effects, typeID: TypeIDs.EFFECTS));
|
||||
|
||||
Debug.Assert(_effects != null, "Couldn't find effects");
|
||||
|
||||
// Apply a sims to unity space transformation.
|
||||
GetComponent<ParticleSystem>().transform.Rotate(-90, 0, 0);
|
||||
GetComponent<ParticleSystem>().transform.localScale = new Vector3(1, -1, 1);
|
||||
// Disable emission on the base particle system, only children will emit.
|
||||
var emissionModule = GetComponent<ParticleSystem>().emission;
|
||||
emissionModule.enabled = false;
|
||||
|
@ -53,7 +58,7 @@ namespace OpenTS2.Scenes
|
|||
{
|
||||
case ParticleEffect e:
|
||||
var subSystem = CreateForParticleEffect(effectDescription, e);
|
||||
subSystem.transform.parent = unityParticleSystem.transform;
|
||||
subSystem.transform.SetParent(unityParticleSystem.transform, worldPositionStays:false);
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException($"Effect type {effect} not supported");
|
||||
|
@ -70,30 +75,71 @@ namespace OpenTS2.Scenes
|
|||
system.Stop(withChildren:true, ParticleSystemStopBehavior.StopEmittingAndClear);
|
||||
|
||||
var emission = system.emission;
|
||||
var emissionRateOverTime = emission.rateOverTime;
|
||||
emissionRateOverTime.curve = effect.Emission.RateCurve.ToUnityCurve();
|
||||
emission.rateOverTime = effect.Emission.RateCurve.ToUnityCurve();
|
||||
|
||||
// Set the emitter shape and drection.
|
||||
var shape = system.shape;
|
||||
if (effect.Emission.EmitTorusWidth > 0)
|
||||
{
|
||||
shape.shapeType = ParticleSystemShapeType.Donut;
|
||||
}
|
||||
else if (effect.IsFlagSet(ParticleFlagBits.EmitterIsEllipsoid))
|
||||
{
|
||||
shape.shapeType = ParticleSystemShapeType.Sphere;
|
||||
}
|
||||
else
|
||||
{
|
||||
shape.shapeType = ParticleSystemShapeType.Box;
|
||||
}
|
||||
|
||||
var main = system.main;
|
||||
main.duration = effect.Life.Life[0];
|
||||
main.startSize = effect.Size.SizeCurve.ToUnityCurveWithVariance(effect.Size.SizeVary);
|
||||
main.startSpeed =
|
||||
new ParticleSystem.MinMaxCurve(min: effect.Emission.EmitSpeed[0], max: effect.Emission.EmitSpeed[1]);
|
||||
main.startLifetime = new ParticleSystem.MinMaxCurve(min:effect.Life.Life[0], max:effect.Life.Life[1]);
|
||||
|
||||
// Set colors.
|
||||
var (minColor, maxColor) = effect.Color.GetStartingColorRange();
|
||||
main.startColor = new ParticleSystem.MinMaxGradient(minColor, maxColor);
|
||||
|
||||
var colorOverLifetime = system.colorOverLifetime;
|
||||
var (minColorGradient, maxColorGradient) = effect.Color.GetColorGradientsOverTime();
|
||||
colorOverLifetime.color = new ParticleSystem.MinMaxGradient(minColorGradient, maxColorGradient);
|
||||
colorOverLifetime.enabled = true;
|
||||
|
||||
if (effect.Drawing.MaterialName != "")
|
||||
{
|
||||
var textureAsset = ContentProvider.Get().GetAsset<ScenegraphTextureAsset>(new ResourceKey(
|
||||
$"{effect.Drawing.MaterialName}_txtr", GroupIDs.Scenegraph, TypeIDs.SCENEGRAPH_TXTR));
|
||||
|
||||
// TODO: temporarily using the standard shader here.
|
||||
var material = new Material(Shader.Find("OpenTS2/StandardMaterial/AlphaBlended"))
|
||||
{
|
||||
mainTexture = textureAsset.GetSelectedImageAsUnityTexture(ContentProvider.Get())
|
||||
};
|
||||
var material = MakeParticleMaterial(textureAsset.GetSelectedImageAsUnityTexture(ContentProvider.Get()));
|
||||
_particleMaterials.Add(material);
|
||||
|
||||
system.GetComponent<Renderer>().sharedMaterial = material;
|
||||
|
||||
Debug.Log($"material: {effect.Drawing.MaterialName}");
|
||||
}
|
||||
Debug.Log($"effect idx: {description.EffectIndex}");
|
||||
|
||||
return particleObject;
|
||||
}
|
||||
|
||||
private Material MakeParticleMaterial(Texture texture)
|
||||
{
|
||||
var material = new Material(Shader.Find("Particles/Standard Surface"))
|
||||
{
|
||||
mainTexture = texture
|
||||
};
|
||||
// These are jacked from https://github.com/Unity-Technologies/UnityCsReference/blob/master/Editor/Mono/Inspector/StandardParticlesShaderGUI.cs#L637
|
||||
// for now, may want to do our own shader in the future.
|
||||
material.SetOverrideTag("RenderType", "Transparent");
|
||||
material.SetFloat("_BlendOp", (float)UnityEngine.Rendering.BlendOp.Add);
|
||||
material.SetFloat("_SrcBlend", (float)UnityEngine.Rendering.BlendMode.SrcAlpha);
|
||||
material.SetFloat("_DstBlend", (float)UnityEngine.Rendering.BlendMode.One);
|
||||
material.SetFloat("_ZWrite", 0.0f);
|
||||
material.DisableKeyword("_ALPHATEST_ON");
|
||||
material.EnableKeyword("_ALPHABLEND_ON");
|
||||
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
|
||||
material.DisableKeyword("_ALPHAMODULATE_ON");
|
||||
material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent;
|
||||
return material;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -164,6 +164,6 @@ public class EffectsCodecTest
|
|||
|
||||
var description = visualEffect.Descriptions[0];
|
||||
Assert.That(description.EffectName, Is.EqualTo("construction_cursor_dust"));
|
||||
Assert.That(description.BlockIndex, Is.EqualTo(0));
|
||||
Assert.That(description.EffectIndex, Is.EqualTo(0));
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue