(61d00a474) v0.9.7.1
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class ConditionalSprite
|
||||
{
|
||||
public void Remove()
|
||||
{
|
||||
Sprite?.Remove();
|
||||
Sprite = null;
|
||||
DeformableSprite?.Remove();
|
||||
DeformableSprite = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
using SpriteParams = Barotrauma.RagdollParams.SpriteParams;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class DecorativeSprite : ISerializableEntity
|
||||
{
|
||||
public string Name => $"Decorative Sprite";
|
||||
public Dictionary<string, SerializableProperty> SerializableProperties { get; set; }
|
||||
|
||||
public Sprite Sprite { get; private set; }
|
||||
|
||||
public enum AnimationType
|
||||
{
|
||||
None,
|
||||
Sine,
|
||||
Noise
|
||||
}
|
||||
|
||||
[Serialize("0,0", true), Editable]
|
||||
public Vector2 Offset { get; private set; }
|
||||
|
||||
[Serialize(AnimationType.None, false), Editable]
|
||||
public AnimationType OffsetAnim { get; private set; }
|
||||
|
||||
[Serialize(0.0f, true), Editable]
|
||||
public float OffsetAnimSpeed { get; private set; }
|
||||
|
||||
private float rotationSpeedRadians;
|
||||
[Serialize(0.0f, true), Editable]
|
||||
public float RotationSpeed
|
||||
{
|
||||
get
|
||||
{
|
||||
return MathHelper.ToDegrees(rotationSpeedRadians);
|
||||
}
|
||||
private set
|
||||
{
|
||||
rotationSpeedRadians = MathHelper.ToRadians(value);
|
||||
}
|
||||
}
|
||||
|
||||
private float rotationRadians;
|
||||
[Serialize(0.0f, true), Editable]
|
||||
public float Rotation
|
||||
{
|
||||
get
|
||||
{
|
||||
return MathHelper.ToDegrees(rotationRadians);
|
||||
}
|
||||
private set
|
||||
{
|
||||
rotationRadians = MathHelper.ToRadians(value);
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(AnimationType.None, false), Editable]
|
||||
public AnimationType RotationAnim { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// If > 0, only one sprite of the same group is used (chosen randomly)
|
||||
/// </summary>
|
||||
[Serialize(0, false, description: "If > 0, only one sprite of the same group is used (chosen randomly)"), Editable(ReadOnly = true)]
|
||||
public int RandomGroupID { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The sprite is only drawn if these conditions are fulfilled
|
||||
/// </summary>
|
||||
internal List<PropertyConditional> IsActiveConditionals { get; private set; } = new List<PropertyConditional>();
|
||||
/// <summary>
|
||||
/// The sprite is only animated if these conditions are fulfilled
|
||||
/// </summary>
|
||||
internal List<PropertyConditional> AnimationConditionals { get; private set; } = new List<PropertyConditional>();
|
||||
|
||||
public DecorativeSprite(XElement element, string path = "", string file = "", bool lazyLoad = false)
|
||||
{
|
||||
Sprite = new Sprite(element, path, file, lazyLoad: lazyLoad);
|
||||
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
List<PropertyConditional> conditionalList = null;
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "conditional":
|
||||
case "isactiveconditional":
|
||||
conditionalList = IsActiveConditionals;
|
||||
break;
|
||||
case "animationconditional":
|
||||
conditionalList = AnimationConditionals;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
foreach (XAttribute attribute in subElement.Attributes())
|
||||
{
|
||||
if (PropertyConditional.IsValid(attribute))
|
||||
{
|
||||
conditionalList.Add(new PropertyConditional(attribute));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2 GetOffset(ref float offsetState)
|
||||
{
|
||||
if (OffsetAnimSpeed <= 0.0f)
|
||||
{
|
||||
return Offset;
|
||||
}
|
||||
switch (OffsetAnim)
|
||||
{
|
||||
case AnimationType.Sine:
|
||||
offsetState = offsetState % (MathHelper.TwoPi / OffsetAnimSpeed);
|
||||
return Offset * (float)Math.Sin(offsetState * OffsetAnimSpeed);
|
||||
case AnimationType.Noise:
|
||||
offsetState = offsetState % (1.0f / (OffsetAnimSpeed * 0.1f));
|
||||
|
||||
float t = offsetState * 0.1f * OffsetAnimSpeed;
|
||||
return new Vector2(
|
||||
Offset.X * (PerlinNoise.GetPerlin(t, t) - 0.5f),
|
||||
Offset.Y * (PerlinNoise.GetPerlin(t + 0.5f, t + 0.5f) - 0.5f));
|
||||
default:
|
||||
return Offset;
|
||||
}
|
||||
}
|
||||
|
||||
public float GetRotation(ref float rotationState)
|
||||
{
|
||||
if (rotationSpeedRadians <= 0.0f)
|
||||
{
|
||||
return rotationRadians;
|
||||
}
|
||||
switch (RotationAnim)
|
||||
{
|
||||
case AnimationType.Sine:
|
||||
rotationState = rotationState % (MathHelper.TwoPi / rotationSpeedRadians);
|
||||
return rotationRadians * (float)Math.Sin(rotationState * rotationSpeedRadians);
|
||||
case AnimationType.Noise:
|
||||
rotationState = rotationState % (1.0f / rotationSpeedRadians);
|
||||
return rotationRadians * (PerlinNoise.GetPerlin(rotationState * rotationSpeedRadians, rotationState * rotationSpeedRadians) - 0.5f);
|
||||
default:
|
||||
return rotationState * rotationSpeedRadians;
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove()
|
||||
{
|
||||
Sprite?.Remove();
|
||||
Sprite = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma.SpriteDeformations
|
||||
{
|
||||
class CustomDeformationParams : SpriteDeformationParams
|
||||
{
|
||||
[Editable(MinValueFloat = 0.0f, MaxValueFloat = 10.0f),
|
||||
Serialize(0.0f, true, description: "How fast the deformation \"oscillates\" back and forth. " +
|
||||
"For example, if the sprite is stretched up, setting this value above zero would make it do a wave-like movement up and down.")]
|
||||
public override float Frequency { get; set; } = 1;
|
||||
|
||||
[Serialize(1.0f, true, description: "The \"strength\" of the deformation."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10.0f)]
|
||||
public float Amplitude { get; set; }
|
||||
|
||||
public CustomDeformationParams(XElement element) : base(element)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class CustomDeformation : SpriteDeformation
|
||||
{
|
||||
private List<Vector2[]> deformRows = new List<Vector2[]>();
|
||||
|
||||
private CustomDeformationParams CustomDeformationParams => Params as CustomDeformationParams;
|
||||
|
||||
public override float Phase
|
||||
{
|
||||
get { return phase; }
|
||||
set
|
||||
{
|
||||
phase = value;
|
||||
//phase %= MathHelper.TwoPi;
|
||||
}
|
||||
}
|
||||
private float phase;
|
||||
|
||||
public CustomDeformation(XElement element) : base(element, new CustomDeformationParams(element))
|
||||
{
|
||||
phase = Rand.Range(0.0f, MathHelper.TwoPi);
|
||||
|
||||
if (element == null)
|
||||
{
|
||||
deformRows.Add(new Vector2[] { Vector2.Zero, Vector2.Zero });
|
||||
deformRows.Add(new Vector2[] { Vector2.Zero, Vector2.Zero });
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; ; i++)
|
||||
{
|
||||
string row = element.GetAttributeString("row" + i, "");
|
||||
if (string.IsNullOrWhiteSpace(row)) break;
|
||||
|
||||
string[] splitRow = row.Split(' ');
|
||||
Vector2[] rowVectors = new Vector2[splitRow.Length];
|
||||
for (int j = 0; j < splitRow.Length; j++)
|
||||
{
|
||||
rowVectors[j] = XMLExtensions.ParseVector2(splitRow[j]);
|
||||
}
|
||||
deformRows.Add(rowVectors);
|
||||
}
|
||||
}
|
||||
|
||||
if (deformRows.Count() == 0 || deformRows.First() == null || deformRows.First().Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var configDeformation = new Vector2[deformRows.First().Length, deformRows.Count];
|
||||
for (int x = 0; x < configDeformation.GetLength(0); x++)
|
||||
{
|
||||
for (int y = 0; y < configDeformation.GetLength(1); y++)
|
||||
{
|
||||
configDeformation[x, y] = deformRows[y][x];
|
||||
}
|
||||
}
|
||||
|
||||
//construct an array for the desired resolution,
|
||||
//interpolating values if the resolution configured in the xml is smaller
|
||||
//deformation = new Vector2[Resolution.X, Resolution.Y];
|
||||
float divX = 1.0f / Resolution.X, divY = 1.0f / Resolution.Y;
|
||||
for (int x = 0; x < Resolution.X; x++)
|
||||
{
|
||||
float normalizedX = x / (float)(Resolution.X - 1);
|
||||
for (int y = 0; y < Resolution.Y; y++)
|
||||
{
|
||||
float normalizedY = y / (float)(Resolution.Y - 1);
|
||||
|
||||
Point indexTopLeft = new Point(
|
||||
Math.Min((int)Math.Floor(normalizedX * (configDeformation.GetLength(0) - 1)), configDeformation.GetLength(0) - 1),
|
||||
Math.Min((int)Math.Floor(normalizedY * (configDeformation.GetLength(1) - 1)), configDeformation.GetLength(1) - 1));
|
||||
Point indexBottomRight = new Point(
|
||||
Math.Min(indexTopLeft.X + 1, configDeformation.GetLength(0) - 1),
|
||||
Math.Min(indexTopLeft.Y + 1, configDeformation.GetLength(1) - 1));
|
||||
|
||||
Vector2 deformTopLeft = configDeformation[indexTopLeft.X, indexTopLeft.Y];
|
||||
Vector2 deformTopRight = configDeformation[indexBottomRight.X, indexTopLeft.Y];
|
||||
Vector2 deformBottomLeft = configDeformation[indexTopLeft.X, indexBottomRight.Y];
|
||||
Vector2 deformBottomRight = configDeformation[indexBottomRight.X, indexBottomRight.Y];
|
||||
|
||||
Deformation[x, y] = Vector2.Lerp(
|
||||
Vector2.Lerp(deformTopLeft, deformTopRight, (normalizedX % divX) / divX),
|
||||
Vector2.Lerp(deformBottomLeft, deformBottomRight, (normalizedX % divX) / divX),
|
||||
(normalizedY % divY) / divY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void GetDeformation(out Vector2[,] deformation, out float multiplier)
|
||||
{
|
||||
deformation = Deformation;
|
||||
multiplier = CustomDeformationParams.Frequency <= 0.0f ?
|
||||
CustomDeformationParams.Amplitude :
|
||||
(float)Math.Sin(phase) * CustomDeformationParams.Amplitude;
|
||||
multiplier *= Params.Strength;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (!Params.UseMovementSine)
|
||||
{
|
||||
phase += deltaTime * CustomDeformationParams.Frequency;
|
||||
phase %= MathHelper.TwoPi;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Save(XElement element)
|
||||
{
|
||||
base.Save(element);
|
||||
for (int i = 0; i < deformRows.Count; i++)
|
||||
{
|
||||
element.Add(new XAttribute("row" + i, string.Join(" ", deformRows[i].Select(r => XMLExtensions.Vector2ToString(r)))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma.SpriteDeformations
|
||||
{
|
||||
class InflateParams : SpriteDeformationParams
|
||||
{
|
||||
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, DecimalCount = 2, ValueStep = 1)]
|
||||
public override float Frequency { get; set; } = 1;
|
||||
[Serialize(1.0f, true), Editable(MinValueFloat = 0.01f, MaxValueFloat = 10.0f, DecimalCount = 2, ValueStep = 0.1f)]
|
||||
public float Scale { get; set; }
|
||||
|
||||
public InflateParams(XElement element) : base(element)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class Inflate : SpriteDeformation
|
||||
{
|
||||
public override float Phase
|
||||
{
|
||||
get { return phase; }
|
||||
set
|
||||
{
|
||||
phase = value;
|
||||
//phase %= MathHelper.TwoPi;
|
||||
}
|
||||
}
|
||||
private float phase;
|
||||
|
||||
private Vector2[,] deformation;
|
||||
|
||||
private InflateParams InflateParams => Params as InflateParams;
|
||||
|
||||
public Inflate(XElement element) : base(element, new InflateParams(element))
|
||||
{
|
||||
deformation = new Vector2[Resolution.X, Resolution.Y];
|
||||
for (int x = 0; x < Resolution.X; x++)
|
||||
{
|
||||
float normalizedX = x / (float)(Resolution.X - 1);
|
||||
for (int y = 0; y < Resolution.Y; y++)
|
||||
{
|
||||
float normalizedY = y / (float)(Resolution.X - 1);
|
||||
|
||||
Vector2 centerDiff = new Vector2(normalizedX - 0.5f, normalizedY - 0.5f);
|
||||
float centerDist = centerDiff.Length() * 2.0f;
|
||||
if (centerDist == 0.0f) continue;
|
||||
|
||||
deformation[x, y] = (centerDiff / centerDist) * Math.Min(1.0f, centerDist);
|
||||
}
|
||||
}
|
||||
|
||||
phase = Rand.Range(0.0f, MathHelper.TwoPi);
|
||||
}
|
||||
|
||||
protected override void GetDeformation(out Vector2[,] deformation, out float multiplier)
|
||||
{
|
||||
deformation = this.deformation;
|
||||
multiplier = InflateParams.Frequency <= 0.0f ? InflateParams.Scale : (float)(Math.Sin(phase) + 1.0f) / 2.0f * InflateParams.Scale;
|
||||
multiplier *= Params.Strength;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (!Params.UseMovementSine)
|
||||
{
|
||||
phase += deltaTime * InflateParams.Frequency;
|
||||
phase %= MathHelper.TwoPi;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma.SpriteDeformations
|
||||
{
|
||||
class JointBendDeformationParams : SpriteDeformationParams
|
||||
{
|
||||
public JointBendDeformationParams(XElement element) : base(element)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does a rotational deformations around pivot points at the edges of the sprite.
|
||||
/// </summary>
|
||||
class JointBendDeformation : SpriteDeformation
|
||||
{
|
||||
//how much to bend at the right side of the sprite
|
||||
private float bendRight;
|
||||
public float BendRight
|
||||
{
|
||||
get { return bendRight; }
|
||||
set { bendRight = MathHelper.Clamp(value, -MaxRotationInRadians, MaxRotationInRadians); }
|
||||
}
|
||||
//the pivot point to rotate the right side around
|
||||
public Vector2 BendRightRefPos = new Vector2(1.0f, 0.5f);
|
||||
|
||||
private float bendLeft;
|
||||
public float BendLeft
|
||||
{
|
||||
get { return bendLeft; }
|
||||
set { bendLeft = MathHelper.Clamp(value, -MaxRotationInRadians, MaxRotationInRadians); }
|
||||
}
|
||||
public Vector2 BendLeftRefPos = new Vector2(0.0f, 0.5f);
|
||||
|
||||
private float bendUp;
|
||||
public float BendUp
|
||||
{
|
||||
get { return bendUp; }
|
||||
set { bendUp = MathHelper.Clamp(value, -MaxRotationInRadians, MaxRotationInRadians); }
|
||||
}
|
||||
public Vector2 BendUpRefPos = new Vector2(0.5f, 0.0f);
|
||||
|
||||
private float bendDown;
|
||||
public float BendDown
|
||||
{
|
||||
get { return bendDown; }
|
||||
set { bendDown = MathHelper.Clamp(value, -MaxRotationInRadians, MaxRotationInRadians); }
|
||||
}
|
||||
public Vector2 BendDownRefPos = new Vector2(0.5f, 1.0f);
|
||||
|
||||
public Vector2 Scale = Vector2.Zero;
|
||||
|
||||
private float MaxRotationInRadians => MathHelper.ToRadians(Params.MaxRotation);
|
||||
|
||||
public JointBendDeformation(XElement element) : base(element, new JointBendDeformationParams(element)) { }
|
||||
|
||||
protected override void GetDeformation(out Vector2[,] deformation, out float multiplier)
|
||||
{
|
||||
deformation = Deformation;
|
||||
multiplier = 1.0f;// this.multiplier;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
Vector2 normalizedPos = Vector2.Zero;
|
||||
for (int x = 0; x < Resolution.X; x++)
|
||||
{
|
||||
normalizedPos.X = x / (float)(Resolution.X - 1);
|
||||
for (int y = 0; y < Resolution.Y; y++)
|
||||
{
|
||||
normalizedPos.Y = y / (float)(Resolution.Y - 1);
|
||||
Deformation[x, y] = Vector2.Zero;
|
||||
|
||||
if (Math.Abs(BendLeft) > 0.001f)
|
||||
{
|
||||
float strength = 1.0f - normalizedPos.X;//(1.0f - Math.Max(normalizedPos.X - BendLeftRefPos.X, 0.0f) / (1.0f - BendLeftRefPos.X));
|
||||
strength = (strength - 0.5f) * 2.0f;
|
||||
if (strength > 0.0f)
|
||||
{
|
||||
Vector2 rotatedP = RotatePointAroundTarget(normalizedPos, BendLeftRefPos, BendLeft * strength * Params.Strength);
|
||||
Vector2 offset = rotatedP - normalizedPos;
|
||||
offset.X *= Scale.Y / Scale.X;
|
||||
Deformation[x, y] += offset;
|
||||
}
|
||||
}
|
||||
if (Math.Abs(BendRight) > 0.001f)
|
||||
{
|
||||
float strength = normalizedPos.X;//(1.0f - Math.Max(BendRightRefPos.X - normalizedPos.X, 0.0f) / (BendRightRefPos.X));
|
||||
strength = (strength - 0.5f) * 2.0f;
|
||||
if (strength > 0.0f)
|
||||
{
|
||||
Vector2 rotatedP = RotatePointAroundTarget(normalizedPos, BendRightRefPos, BendRight * strength * Params.Strength);
|
||||
Vector2 offset = rotatedP - normalizedPos;
|
||||
offset.X *= Scale.Y / Scale.X;
|
||||
Deformation[x, y] += offset;
|
||||
}
|
||||
}
|
||||
|
||||
if (Math.Abs(BendUp) > 0.001f)
|
||||
{
|
||||
float strength = 1.0f - normalizedPos.Y;//(1.0f - Math.Max(normalizedPos.Y - BendUpRefPos.Y, 0.0f) / (1.0f - BendUpRefPos.Y));
|
||||
strength = (strength - 0.5f) * 2.0f;
|
||||
if (strength > 0.0f)
|
||||
{
|
||||
Vector2 rotatedP = RotatePointAroundTarget(normalizedPos, BendUpRefPos, BendUp * strength * Params.Strength);
|
||||
Vector2 offset = rotatedP - normalizedPos;
|
||||
offset.Y *= Scale.X / Scale.Y;
|
||||
Deformation[x, y] += offset;
|
||||
}
|
||||
}
|
||||
if (Math.Abs(BendDown) > 0.001f)
|
||||
{
|
||||
float strength = normalizedPos.Y;//(1.0f - Math.Max(BendDownRefPos.Y - normalizedPos.Y, 0.0f) / (BendDownRefPos.Y));
|
||||
strength = (strength - 0.5f) * 2.0f;
|
||||
if (strength > 0.0f)
|
||||
{
|
||||
Vector2 rotatedP = RotatePointAroundTarget(normalizedPos, BendDownRefPos, BendDown * strength * Params.Strength);
|
||||
Vector2 offset = rotatedP - normalizedPos;
|
||||
offset.Y *= Scale.X / Scale.Y;
|
||||
Deformation[x, y] += offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Vector2 RotatePointAroundTarget(Vector2 point, Vector2 target, float angle)
|
||||
{
|
||||
return RotatePointAroundTarget(point, target, (float)Math.Sin(angle), (float)Math.Cos(angle));
|
||||
}
|
||||
|
||||
public static Vector2 RotatePointAroundTarget(Vector2 point, Vector2 target, float sin, float cos)
|
||||
{
|
||||
Vector2 dir = point - target;
|
||||
var x = (cos * dir.X) - (sin * dir.Y) + target.X;
|
||||
var y = (sin * dir.X) + (cos * dir.Y) + target.Y;
|
||||
return new Vector2(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.SpriteDeformations
|
||||
{
|
||||
class NoiseDeformationParams : SpriteDeformationParams
|
||||
{
|
||||
[Serialize(0.0f, true, description: "The frequency of the noise."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, DecimalCount = 2, ValueStep = 1f)]
|
||||
public override float Frequency { get; set; }
|
||||
|
||||
[Serialize(1.0f, true, description: "How much the noise distorts the sprite."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10.0f, DecimalCount = 2, ValueStep = 0.01f)]
|
||||
public float Amplitude { get; set; }
|
||||
|
||||
[Serialize(0.0f, true, description: "How fast the noise changes."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10.0f, DecimalCount = 2, ValueStep = 0.01f)]
|
||||
public float ChangeSpeed { get; set; }
|
||||
|
||||
public NoiseDeformationParams(XElement element) : base(element)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class NoiseDeformation : SpriteDeformation
|
||||
{
|
||||
private NoiseDeformationParams NoiseDeformationParams => Params as NoiseDeformationParams;
|
||||
|
||||
private float phase;
|
||||
|
||||
public NoiseDeformation(XElement element) : base(element, new NoiseDeformationParams(element))
|
||||
{
|
||||
phase = Rand.Range(0.0f, 255.0f);
|
||||
UpdateNoise();
|
||||
}
|
||||
|
||||
private void UpdateNoise()
|
||||
{
|
||||
for (int x = 0; x < Resolution.X; x++)
|
||||
{
|
||||
float normalizedX = x / (float)(Resolution.X - 1) * NoiseDeformationParams.Frequency;
|
||||
for (int y = 0; y < Resolution.Y; y++)
|
||||
{
|
||||
float normalizedY = y / (float)(Resolution.X - 1) * NoiseDeformationParams.Frequency;
|
||||
|
||||
Deformation[x, y] = new Vector2(
|
||||
PerlinNoise.GetPerlin(normalizedX + phase, normalizedY + phase) - 0.5f,
|
||||
PerlinNoise.GetPerlin(normalizedY - phase, normalizedX - phase) - 0.5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void GetDeformation(out Vector2[,] deformation, out float multiplier)
|
||||
{
|
||||
deformation = Deformation;
|
||||
multiplier = NoiseDeformationParams.Amplitude * Params.Strength;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (NoiseDeformationParams.ChangeSpeed > 0.0f)
|
||||
{
|
||||
phase += deltaTime * NoiseDeformationParams.ChangeSpeed / 100.0f;
|
||||
phase %= 1.0f;
|
||||
UpdateNoise();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.SpriteDeformations
|
||||
{
|
||||
class PositionalDeformationParams : SpriteDeformationParams
|
||||
{
|
||||
/// <summary>
|
||||
/// 0 = no falloff, the entire sprite is stretched
|
||||
/// 1 = stretching the center of the sprite has no effect at the edges
|
||||
/// </summary>
|
||||
[Serialize(0.0f, true, description: "0 = no falloff, the entire sprite is stretched, 1 = stretching the center of the sprite has no effect at the edges."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f)]
|
||||
public float Falloff { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum stretch per vertex (1 = the size of the sprite)
|
||||
/// </summary>
|
||||
[Serialize(1.0f, true, description: "Maximum stretch per vertex (1 = the size of the sprite)"), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10.0f)]
|
||||
public float MaxDeformation { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// How fast the sprite reacts to being stretched
|
||||
/// </summary>
|
||||
[Serialize(1.0f, true, description: "How fast the sprite reacts to being stretched"), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10.0f)]
|
||||
public float ReactionSpeed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// How fast the sprite returns back to normal after stretching ends
|
||||
/// </summary>
|
||||
[Serialize(0.1f, true, description: "How fast the sprite returns back to normal after stretching ends"), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10.0f)]
|
||||
public float RecoverSpeed { get; set; }
|
||||
|
||||
public PositionalDeformationParams(XElement element) : base(element)
|
||||
{
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Stretch a position in the deformable sprite to some direction
|
||||
/// </summary>
|
||||
class PositionalDeformation : SpriteDeformation
|
||||
{
|
||||
public enum ReactionType
|
||||
{
|
||||
ReactToTriggerers
|
||||
}
|
||||
|
||||
public ReactionType Type;
|
||||
|
||||
private PositionalDeformationParams positionalDeformationParams => Params as PositionalDeformationParams;
|
||||
|
||||
public PositionalDeformation(XElement element) : base(element, new PositionalDeformationParams(element))
|
||||
{
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (positionalDeformationParams.RecoverSpeed <= 0.0f) return;
|
||||
|
||||
for (int x = 0; x < Resolution.X; x++)
|
||||
{
|
||||
for (int y = 0; y < Resolution.Y; y++)
|
||||
{
|
||||
if (Deformation[x,y].LengthSquared() < 0.0001f)
|
||||
{
|
||||
Deformation[x, y] = Vector2.Zero;
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector2 reduction = Deformation[x, y];
|
||||
Deformation[x, y] -= reduction.ClampLength(positionalDeformationParams.RecoverSpeed) * deltaTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Deform(Vector2 worldPosition, Vector2 amount, float deltaTime, Matrix transformMatrix)
|
||||
{
|
||||
Vector2 pos = Vector2.Transform(worldPosition, transformMatrix);
|
||||
Point deformIndex = new Point((int)(pos.X * (Resolution.X - 1)), (int)(pos.Y * (Resolution.Y - 1)));
|
||||
|
||||
if (deformIndex.X < 0 || deformIndex.Y < 0) return;
|
||||
if (deformIndex.X >= Resolution.X || deformIndex.Y >= Resolution.Y) return;
|
||||
|
||||
amount = amount.ClampLength(positionalDeformationParams.MaxDeformation);
|
||||
|
||||
float invFalloff = 1.0f - positionalDeformationParams.Falloff;
|
||||
|
||||
for (int x = 0; x < Resolution.X; x++)
|
||||
{
|
||||
float normalizedDiffX = Math.Abs(x - deformIndex.X) / (Resolution.X * 0.5f);
|
||||
for (int y = 0; y < Resolution.Y; y++)
|
||||
{
|
||||
float normalizedDiffY = Math.Abs(y - deformIndex.Y) / (Resolution.Y * 0.5f);
|
||||
Vector2 targetDeformation = amount * MathHelper.Clamp(1.0f - new Vector2(normalizedDiffX, normalizedDiffY).Length() * positionalDeformationParams.Falloff, 0.0f, 1.0f);
|
||||
|
||||
Vector2 diff = targetDeformation - Deformation[x, y];
|
||||
Deformation[x, y] += diff.ClampLength(positionalDeformationParams.ReactionSpeed) * deltaTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void GetDeformation(out Vector2[,] deformation, out float multiplier)
|
||||
{
|
||||
deformation = Deformation;
|
||||
multiplier = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
+250
@@ -0,0 +1,250 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma.SpriteDeformations
|
||||
{
|
||||
abstract class SpriteDeformationParams : ISerializableEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// A negative value means that the deformation is used only by one sprite only (default).
|
||||
/// A positive value means that this deformation is or could be used for multiple sprites.
|
||||
/// This behaviour is not automatic, and has to be implemented for any particular case separately (currently only used in Limbs).
|
||||
/// </summary>
|
||||
[Serialize(-1, true), Editable(minValue: -1, maxValue: 100)]
|
||||
public int Sync
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize("", true)]
|
||||
public string TypeName
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(SpriteDeformation.DeformationBlendMode.Add, true), Editable]
|
||||
public SpriteDeformation.DeformationBlendMode BlendMode
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public string Name => $"Deformation ({TypeName})";
|
||||
|
||||
[Serialize(1.0f, true), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2, ValueStep = 0.01f)]
|
||||
public float Strength { get; private set; }
|
||||
|
||||
[Serialize(90f, true), Editable(MinValueFloat = 0, MaxValueFloat = 90)]
|
||||
public float MaxRotation { get; private set; }
|
||||
|
||||
[Serialize(false, true), Editable]
|
||||
public bool UseMovementSine { get; set; }
|
||||
|
||||
[Serialize(false, true), Editable]
|
||||
public bool StopWhenHostIsDead { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Only used if UseMovementSine is enabled. Multiplier for Pi.
|
||||
/// </summary>
|
||||
[Serialize(0f, true), Editable]
|
||||
public float SineOffset { get; set; }
|
||||
|
||||
public virtual float Frequency { get; set; } = 1;
|
||||
|
||||
public Dictionary<string, SerializableProperty> SerializableProperties
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defined in the shader.
|
||||
/// </summary>
|
||||
public static readonly Point ShaderMaxResolution = new Point(15, 15);
|
||||
|
||||
private Point _resolution;
|
||||
[Serialize("2,2", true)]
|
||||
public Point Resolution
|
||||
{
|
||||
get { return _resolution; }
|
||||
set
|
||||
{
|
||||
if (_resolution == value) { return; }
|
||||
_resolution = value.Clamp(new Point(2, 2), ShaderMaxResolution);
|
||||
}
|
||||
}
|
||||
|
||||
public SpriteDeformationParams(XElement element)
|
||||
{
|
||||
if (element != null)
|
||||
{
|
||||
TypeName = element.GetAttributeString("type", "").ToLowerInvariant();
|
||||
}
|
||||
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class SpriteDeformation
|
||||
{
|
||||
public enum DeformationBlendMode
|
||||
{
|
||||
Add,
|
||||
Multiply,
|
||||
Override
|
||||
}
|
||||
|
||||
public virtual float Phase { get; set; }
|
||||
|
||||
protected Vector2[,] Deformation { get; private set; }
|
||||
|
||||
public SpriteDeformationParams Params { get; set; }
|
||||
|
||||
private static readonly string[] deformationTypes = new string[] { "Inflate", "Custom", "Noise", "BendJoint", "ReactToTriggerers" };
|
||||
public static IEnumerable<string> DeformationTypes
|
||||
{
|
||||
get { return deformationTypes; }
|
||||
}
|
||||
|
||||
public Point Resolution
|
||||
{
|
||||
get { return Params.Resolution; }
|
||||
set { SetResolution(value); }
|
||||
}
|
||||
|
||||
public string TypeName => Params.TypeName;
|
||||
|
||||
public int Sync => Params.Sync;
|
||||
|
||||
public static SpriteDeformation Load(string deformationType, string parentDebugName)
|
||||
{
|
||||
return Load(null, deformationType, parentDebugName);
|
||||
}
|
||||
public static SpriteDeformation Load(XElement element, string parentDebugName)
|
||||
{
|
||||
return Load(element, null, parentDebugName);
|
||||
}
|
||||
|
||||
private static SpriteDeformation Load(XElement element, string deformationType, string parentDebugName)
|
||||
{
|
||||
string typeName = deformationType;
|
||||
|
||||
if (element != null)
|
||||
{
|
||||
typeName = element.GetAttributeString("typename", null) ?? element.GetAttributeString("type", "");
|
||||
}
|
||||
|
||||
SpriteDeformation newDeformation = null;
|
||||
switch (typeName.ToLowerInvariant())
|
||||
{
|
||||
case "inflate":
|
||||
newDeformation = new Inflate(element);
|
||||
break;
|
||||
case "custom":
|
||||
newDeformation = new CustomDeformation(element);
|
||||
break;
|
||||
case "noise":
|
||||
newDeformation = new NoiseDeformation(element);
|
||||
break;
|
||||
case "jointbend":
|
||||
case "bendjoint":
|
||||
newDeformation = new JointBendDeformation(element);
|
||||
break;
|
||||
case "reacttotriggerers":
|
||||
return new PositionalDeformation(element);
|
||||
default:
|
||||
if (Enum.TryParse(typeName, out PositionalDeformation.ReactionType reactionType))
|
||||
{
|
||||
newDeformation = new PositionalDeformation(element)
|
||||
{
|
||||
Type = reactionType
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError("Could not load sprite deformation animation in " + parentDebugName + " - \"" + typeName + "\" is not a valid deformation type.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (newDeformation != null)
|
||||
{
|
||||
newDeformation.Params.TypeName = typeName;
|
||||
}
|
||||
return newDeformation;
|
||||
}
|
||||
|
||||
protected SpriteDeformation(XElement element, SpriteDeformationParams deformationParams)
|
||||
{
|
||||
this.Params = deformationParams;
|
||||
SerializableProperty.DeserializeProperties(deformationParams, element);
|
||||
Deformation = new Vector2[deformationParams.Resolution.X, deformationParams.Resolution.Y];
|
||||
}
|
||||
|
||||
public void SetResolution(Point resolution)
|
||||
{
|
||||
Params.Resolution = resolution;
|
||||
Deformation = new Vector2[Params.Resolution.X, Params.Resolution.Y];
|
||||
}
|
||||
|
||||
protected abstract void GetDeformation(out Vector2[,] deformation, out float multiplier);
|
||||
|
||||
public abstract void Update(float deltaTime);
|
||||
|
||||
public static Vector2[,] GetDeformation(IEnumerable<SpriteDeformation> animations, Vector2 scale)
|
||||
{
|
||||
foreach (SpriteDeformation animation in animations)
|
||||
{
|
||||
if (animation.Params.Resolution.X != animation.Deformation.GetLength(0) ||
|
||||
animation.Params.Resolution.Y != animation.Deformation.GetLength(1))
|
||||
{
|
||||
animation.Deformation = new Vector2[animation.Params.Resolution.X, animation.Params.Resolution.Y];
|
||||
}
|
||||
}
|
||||
|
||||
Point resolution = animations.First().Resolution;
|
||||
if (animations.Any(a => a.Resolution != resolution))
|
||||
{
|
||||
DebugConsole.ThrowError("All animations must have the same resolution! Using the lowest resolution.");
|
||||
resolution = animations.OrderBy(anim => anim.Resolution.X + anim.Resolution.Y).First().Resolution;
|
||||
animations.ForEach(a => a.Resolution = resolution);
|
||||
}
|
||||
|
||||
Vector2[,] deformation = new Vector2[resolution.X, resolution.Y];
|
||||
foreach (SpriteDeformation animation in animations)
|
||||
{
|
||||
animation.GetDeformation(out Vector2[,] animDeformation, out float multiplier);
|
||||
|
||||
for (int x = 0; x < resolution.X; x++)
|
||||
{
|
||||
for (int y = 0; y < resolution.Y; y++)
|
||||
{
|
||||
switch (animation.Params.BlendMode)
|
||||
{
|
||||
case DeformationBlendMode.Override:
|
||||
deformation[x,y] = animDeformation[x,y] * scale * multiplier;
|
||||
break;
|
||||
case DeformationBlendMode.Add:
|
||||
deformation[x, y] += animDeformation[x, y] * scale * multiplier;
|
||||
break;
|
||||
case DeformationBlendMode.Multiply:
|
||||
deformation[x, y] *= animDeformation[x, y] * multiplier;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return deformation;
|
||||
}
|
||||
|
||||
public virtual void Save(XElement element)
|
||||
{
|
||||
SerializableProperty.SerializeProperties(Params, element);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,410 @@
|
||||
using Barotrauma.SpriteDeformations;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class DeformableSprite
|
||||
{
|
||||
private static List<DeformableSprite> list = new List<DeformableSprite>();
|
||||
|
||||
private bool initialized = false;
|
||||
|
||||
private int triangleCount;
|
||||
|
||||
private VertexBuffer vertexBuffer, flippedVertexBuffer;
|
||||
private IndexBuffer indexBuffer;
|
||||
|
||||
private Vector2 uvTopLeft, uvBottomRight;
|
||||
private Vector2 uvTopLeftFlipped, uvBottomRightFlipped;
|
||||
|
||||
private Vector2[] deformAmount;
|
||||
private int deformArrayWidth, deformArrayHeight;
|
||||
|
||||
private int subDivX, subDivY;
|
||||
|
||||
private static Effect effect;
|
||||
public static Effect Effect
|
||||
{
|
||||
get { return effect; }
|
||||
}
|
||||
|
||||
private Point spritePos;
|
||||
private Point spriteSize;
|
||||
|
||||
partial void InitProjSpecific(XElement element, int? subdivisionsX, int? subdivisionsY, bool lazyLoad)
|
||||
{
|
||||
if (effect == null)
|
||||
{
|
||||
#if WINDOWS
|
||||
effect = GameMain.Instance.Content.Load<Effect>("Effects/deformshader");
|
||||
#endif
|
||||
#if LINUX || OSX
|
||||
effect = GameMain.Instance.Content.Load<Effect>("Effects/deformshader_opengl");
|
||||
#endif
|
||||
}
|
||||
|
||||
//use subdivisions configured in the xml if the arguments passed to the method are null
|
||||
Vector2 subdivisionsInXml = element.GetAttributeVector2("subdivisions", Vector2.One);
|
||||
subDivX = subdivisionsX ?? (int)subdivisionsInXml.X;
|
||||
subDivY = subdivisionsY ?? (int)subdivisionsInXml.Y;
|
||||
|
||||
if (subDivX <= 0 || subDivY <= 0)
|
||||
{
|
||||
throw new ArgumentException("Deformable sprites must have one or more subdivisions on each axis.");
|
||||
}
|
||||
|
||||
if (!lazyLoad)
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
list.Add(this);
|
||||
}
|
||||
|
||||
public void EnsureLazyLoaded()
|
||||
{
|
||||
if (!initialized) { Init(); }
|
||||
}
|
||||
|
||||
private void Init()
|
||||
{
|
||||
if (initialized) { return; }
|
||||
initialized = true;
|
||||
|
||||
foreach (DeformableSprite existing in list)
|
||||
{
|
||||
if (!existing.initialized || existing == this) { continue; }
|
||||
//share vertex and index buffers if there's already
|
||||
//an existing sprite with the same texture and subdivisions
|
||||
if (existing.Sprite.Texture == Sprite.Texture &&
|
||||
existing.subDivX == subDivX &&
|
||||
existing.subDivY == subDivY &&
|
||||
existing.Sprite.SourceRect == Sprite.SourceRect)
|
||||
{
|
||||
vertexBuffer = existing.vertexBuffer;
|
||||
flippedVertexBuffer = existing.flippedVertexBuffer;
|
||||
indexBuffer = existing.indexBuffer;
|
||||
triangleCount = existing.triangleCount;
|
||||
uvTopLeft = existing.uvTopLeft;
|
||||
uvBottomRight = existing.uvBottomRight;
|
||||
uvTopLeftFlipped = existing.uvTopLeftFlipped;
|
||||
uvBottomRightFlipped = existing.uvBottomRightFlipped;
|
||||
|
||||
Deform(new Vector2[,]
|
||||
{
|
||||
{ Vector2.Zero, Vector2.Zero },
|
||||
{ Vector2.Zero, Vector2.Zero }
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Sprite.Texture != null)
|
||||
{
|
||||
SetupVertexBuffers();
|
||||
SetupIndexBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupVertexBuffers()
|
||||
{
|
||||
Vector2 textureSize = new Vector2(Sprite.Texture.Width, Sprite.Texture.Height);
|
||||
var pos = Sprite.SourceRect.Location;
|
||||
var size = Sprite.SourceRect.Size;
|
||||
|
||||
uvTopLeft = Vector2.Divide(pos.ToVector2(), textureSize);
|
||||
uvBottomRight = Vector2.Divide((pos + size).ToVector2(), textureSize);
|
||||
uvTopLeftFlipped = Vector2.Divide(new Vector2(pos.X + size.X, pos.Y), textureSize);
|
||||
uvBottomRightFlipped = Vector2.Divide(new Vector2(pos.X, pos.Y + size.Y), textureSize);
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
bool flip = i == 1;
|
||||
|
||||
var vertices = new VertexPositionColorTexture[(subDivX + 1) * (subDivY + 1)];
|
||||
for (int x = 0; x <= subDivX; x++)
|
||||
{
|
||||
for (int y = 0; y <= subDivY; y++)
|
||||
{
|
||||
//{0,0} -> {1,1}
|
||||
Vector2 relativePos = new Vector2(x / (float)subDivX, y / (float)subDivY);
|
||||
|
||||
Vector2 uvCoord = flip ?
|
||||
uvTopLeftFlipped + (uvBottomRightFlipped - uvTopLeftFlipped) * relativePos :
|
||||
uvTopLeft + (uvBottomRight - uvTopLeft) * relativePos;
|
||||
|
||||
vertices[x + y * (subDivX + 1)] = new VertexPositionColorTexture(
|
||||
position: new Vector3(relativePos.X * Sprite.SourceRect.Width, relativePos.Y * Sprite.SourceRect.Height, 0.0f),
|
||||
color: Color.White,
|
||||
textureCoordinate: uvCoord);
|
||||
}
|
||||
}
|
||||
|
||||
if (flip)
|
||||
{
|
||||
if (flippedVertexBuffer != null && flippedVertexBuffer.VertexCount != vertices.Length)
|
||||
{
|
||||
flippedVertexBuffer.Dispose();
|
||||
flippedVertexBuffer = null;
|
||||
}
|
||||
if (flippedVertexBuffer == null)
|
||||
{
|
||||
flippedVertexBuffer = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, vertices.Length, BufferUsage.None);
|
||||
}
|
||||
flippedVertexBuffer.SetData(vertices);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (vertexBuffer != null && vertexBuffer.VertexCount != vertices.Length)
|
||||
{
|
||||
vertexBuffer.Dispose();
|
||||
vertexBuffer = null;
|
||||
}
|
||||
if (vertexBuffer == null)
|
||||
{
|
||||
vertexBuffer = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, vertices.Length, BufferUsage.None);
|
||||
}
|
||||
vertexBuffer.SetData(vertices);
|
||||
}
|
||||
}
|
||||
|
||||
spritePos = Sprite.SourceRect.Location;
|
||||
spriteSize = Sprite.SourceRect.Size;
|
||||
}
|
||||
|
||||
private void SetupIndexBuffer()
|
||||
{
|
||||
triangleCount = subDivX * subDivY * 2;
|
||||
var indices = new ushort[triangleCount * 3];
|
||||
int offset = 0;
|
||||
for (int i = 0; i < triangleCount / 2; i++)
|
||||
{
|
||||
indices[i * 6] = (ushort)(i + offset + 1);
|
||||
indices[i * 6 + 1] = (ushort)(i + offset + (subDivX + 1) + 1);
|
||||
indices[i * 6 + 2] = (ushort)(i + offset + (subDivX + 1));
|
||||
|
||||
indices[i * 6 + 3] = (ushort)(i + offset);
|
||||
indices[i * 6 + 4] = (ushort)(i + offset + 1);
|
||||
indices[i * 6 + 5] = (ushort)(i + offset + (subDivX + 1));
|
||||
|
||||
if ((i + 1) % subDivX == 0) offset++;
|
||||
}
|
||||
|
||||
indexBuffer?.Dispose();
|
||||
indexBuffer = null;
|
||||
|
||||
indexBuffer = new IndexBuffer(GameMain.Instance.GraphicsDevice, IndexElementSize.SixteenBits, indices.Length, BufferUsage.None);
|
||||
indexBuffer.SetData(indices);
|
||||
|
||||
Deform(new Vector2[,]
|
||||
{
|
||||
{ Vector2.Zero, Vector2.Zero },
|
||||
{ Vector2.Zero, Vector2.Zero }
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deform the vertices of the sprite using an arbitrary function. The in-parameter of the function is the
|
||||
/// normalized position of the vertex (i.e. 0,0 = top-left corner of the sprite, 1,1 = bottom-right) and the output
|
||||
/// is the amount of deformation.
|
||||
/// </summary>
|
||||
public void Deform(Func<Vector2, Vector2> deformFunction)
|
||||
{
|
||||
if (!initialized) { Init(); }
|
||||
|
||||
var deformAmount = new Vector2[subDivX + 1, subDivY + 1];
|
||||
for (int x = 0; x <= subDivX; x++)
|
||||
{
|
||||
for (int y = 0; y <= subDivY; y++)
|
||||
{
|
||||
deformAmount[x, y] = deformFunction(new Vector2(x / (float)subDivX, y / (float)subDivY));
|
||||
}
|
||||
}
|
||||
Deform(deformAmount);
|
||||
}
|
||||
|
||||
public void Deform(Vector2[,] deform)
|
||||
{
|
||||
if (!initialized) { Init(); }
|
||||
|
||||
deformArrayWidth = deform.GetLength(0);
|
||||
deformArrayHeight = deform.GetLength(1);
|
||||
if (deformAmount == null || deformAmount.Length != deformArrayWidth * deformArrayHeight)
|
||||
{
|
||||
deformAmount = new Vector2[deformArrayWidth * deformArrayHeight];
|
||||
}
|
||||
for (int x = 0; x < deformArrayWidth; x++)
|
||||
{
|
||||
for (int y = 0; y < deformArrayHeight; y++)
|
||||
{
|
||||
deformAmount[x + y * deformArrayWidth] = deform[x, y];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
Deform(new Vector2[,]
|
||||
{
|
||||
{ Vector2.Zero, Vector2.Zero },
|
||||
{ Vector2.Zero, Vector2.Zero }
|
||||
});
|
||||
}
|
||||
|
||||
public Matrix GetTransform(Vector3 pos, Vector2 origin, float rotate, Vector2 scale)
|
||||
{
|
||||
if (!initialized) { Init(); }
|
||||
|
||||
return
|
||||
Matrix.CreateTranslation(-origin.X, -origin.Y, 0) *
|
||||
Matrix.CreateScale(scale.X, -scale.Y, 1.0f) *
|
||||
Matrix.CreateRotationZ(-rotate) *
|
||||
Matrix.CreateTranslation(pos);
|
||||
}
|
||||
|
||||
public void Draw(Camera cam, Vector3 pos, Vector2 origin, float rotate, Vector2 scale, Color color, bool flip = false, bool mirror = false)
|
||||
{
|
||||
if (Sprite.Texture == null) { return; }
|
||||
if (!initialized) { Init(); }
|
||||
|
||||
// If the source rect is modified, we should recalculate the vertex buffer.
|
||||
if (Sprite.SourceRect.Location != spritePos || Sprite.SourceRect.Size != spriteSize)
|
||||
{
|
||||
SetupVertexBuffers();
|
||||
}
|
||||
|
||||
#if (LINUX || OSX)
|
||||
effect.Parameters["TextureSampler+xTexture"].SetValue(Sprite.Texture);
|
||||
#else
|
||||
effect.Parameters["xTexture"].SetValue(Sprite.Texture);
|
||||
#endif
|
||||
|
||||
Matrix matrix = GetTransform(pos, origin, rotate, scale);
|
||||
effect.Parameters["xTransform"].SetValue(matrix * cam.ShaderTransform
|
||||
* Matrix.CreateOrthographic(GameMain.GraphicsWidth, GameMain.GraphicsHeight, -1, 1) * 0.5f);
|
||||
effect.Parameters["tintColor"].SetValue(color.ToVector4());
|
||||
effect.Parameters["deformArray"].SetValue(deformAmount);
|
||||
effect.Parameters["deformArrayWidth"].SetValue(deformArrayWidth);
|
||||
effect.Parameters["deformArrayHeight"].SetValue(deformArrayHeight);
|
||||
if (mirror)
|
||||
{
|
||||
flip = !flip;
|
||||
}
|
||||
effect.Parameters["uvTopLeft"].SetValue(flip ? uvTopLeftFlipped : uvTopLeft);
|
||||
effect.Parameters["uvBottomRight"].SetValue(flip ? uvBottomRightFlipped : uvBottomRight);
|
||||
effect.GraphicsDevice.SetVertexBuffer(flip ? flippedVertexBuffer : vertexBuffer);
|
||||
effect.GraphicsDevice.Indices = indexBuffer;
|
||||
effect.CurrentTechnique.Passes[0].Apply();
|
||||
effect.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, triangleCount);
|
||||
}
|
||||
|
||||
public void Remove()
|
||||
{
|
||||
Sprite?.Remove();
|
||||
Sprite = null;
|
||||
|
||||
list.Remove(this);
|
||||
|
||||
foreach (DeformableSprite otherSprite in list)
|
||||
{
|
||||
//another sprite is using the same vertex buffer, cannot dispose it yet
|
||||
if (otherSprite.vertexBuffer == vertexBuffer) return;
|
||||
}
|
||||
|
||||
vertexBuffer?.Dispose();
|
||||
vertexBuffer = null;
|
||||
flippedVertexBuffer?.Dispose();
|
||||
flippedVertexBuffer = null;
|
||||
indexBuffer?.Dispose();
|
||||
indexBuffer = null;
|
||||
}
|
||||
|
||||
#region Editing
|
||||
|
||||
public GUIComponent CreateEditor(GUIComponent parent, List<SpriteDeformation> deformations, string parentDebugName)
|
||||
{
|
||||
var container = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.0f), parent.RectTransform))
|
||||
{
|
||||
AbsoluteSpacing = 5,
|
||||
CanBeFocused = true
|
||||
};
|
||||
|
||||
new GUITextBlock(new RectTransform(new Point(container.Rect.Width, (int)(60 * GUI.Scale)), container.RectTransform) { IsFixedSize = true },
|
||||
"Sprite Deformations", textAlignment: Alignment.BottomCenter, font: GUI.LargeFont);
|
||||
|
||||
var resolutionField = GUI.CreatePointField(new Point(subDivX + 1, subDivY + 1), (int)(30 * GUI.Scale), "Resolution", container.RectTransform,
|
||||
"How many vertices the deformable sprite has on the x and y axes. Larger values make the deformations look smoother, but are more performance intensive.");
|
||||
resolutionField.RectTransform.IsFixedSize = true;
|
||||
GUINumberInput xField = null, yField = null;
|
||||
|
||||
foreach (GUIComponent child in resolutionField.GetAllChildren())
|
||||
{
|
||||
if (yField == null)
|
||||
{
|
||||
yField = child as GUINumberInput;
|
||||
}
|
||||
else
|
||||
{
|
||||
xField = child as GUINumberInput;
|
||||
if (xField != null) break;
|
||||
}
|
||||
}
|
||||
xField.MinValueInt = 2;
|
||||
xField.MaxValueInt = SpriteDeformationParams.ShaderMaxResolution.X - 1;
|
||||
xField.OnValueChanged = (numberInput) => { ChangeResolution(); };
|
||||
yField.MinValueInt = 2;
|
||||
yField.MaxValueInt = SpriteDeformationParams.ShaderMaxResolution.Y - 1;
|
||||
yField.OnValueChanged = (numberInput) => { ChangeResolution(); };
|
||||
|
||||
void ChangeResolution()
|
||||
{
|
||||
subDivX = xField.IntValue - 1;
|
||||
subDivY = yField.IntValue - 1;
|
||||
|
||||
foreach (SpriteDeformation deformation in deformations)
|
||||
{
|
||||
deformation.SetResolution(new Point(xField.IntValue, yField.IntValue));
|
||||
}
|
||||
SetupVertexBuffers();
|
||||
SetupIndexBuffer();
|
||||
}
|
||||
|
||||
foreach (SpriteDeformation deformation in deformations)
|
||||
{
|
||||
var deformEditor = new SerializableEntityEditor(container.RectTransform, deformation.Params,
|
||||
inGame: false, showName: true, titleFont: GUI.SubHeadingFont);
|
||||
deformEditor.RectTransform.MinSize = new Point(deformEditor.Rect.Width, deformEditor.Rect.Height);
|
||||
}
|
||||
|
||||
var deformationDD = new GUIDropDown(new RectTransform(new Point(container.Rect.Width, 30), container.RectTransform), "Add new sprite deformation");
|
||||
deformationDD.OnSelected = (selected, userdata) =>
|
||||
{
|
||||
deformations.Add(SpriteDeformation.Load((string)userdata, parentDebugName));
|
||||
deformationDD.Text = "Add new sprite deformation";
|
||||
return false;
|
||||
};
|
||||
|
||||
foreach (string deformationType in SpriteDeformation.DeformationTypes)
|
||||
{
|
||||
deformationDD.AddItem(deformationType, deformationType);
|
||||
}
|
||||
|
||||
container.RectTransform.Resize(new Point(
|
||||
container.Rect.Width, container.Children.Sum(c => c.Rect.Height + container.AbsoluteSpacing)), false);
|
||||
|
||||
container.RectTransform.MinSize = new Point(0, container.Rect.Height);
|
||||
container.RectTransform.MaxSize = new Point(int.MaxValue, container.Rect.Height);
|
||||
container.RectTransform.IsFixedSize = true;
|
||||
container.Recalculate();
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,339 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public partial class Sprite
|
||||
{
|
||||
private bool cannotBeLoaded;
|
||||
|
||||
protected Texture2D texture;
|
||||
public Texture2D Texture
|
||||
{
|
||||
get
|
||||
{
|
||||
EnsureLazyLoaded();
|
||||
return texture;
|
||||
}
|
||||
}
|
||||
|
||||
public Sprite(Texture2D texture, Rectangle? sourceRectangle, Vector2? newOffset, float newRotation = 0.0f)
|
||||
{
|
||||
this.texture = texture;
|
||||
|
||||
sourceRect = sourceRectangle ?? new Rectangle(0, 0, texture.Width, texture.Height);
|
||||
|
||||
offset = newOffset ?? Vector2.Zero;
|
||||
|
||||
size = new Vector2(sourceRect.Width, sourceRect.Height);
|
||||
|
||||
origin = Vector2.Zero;
|
||||
|
||||
effects = SpriteEffects.None;
|
||||
|
||||
rotation = newRotation;
|
||||
|
||||
AddToList(this);
|
||||
}
|
||||
|
||||
partial void LoadTexture(ref Vector4 sourceVector, ref bool shouldReturn)
|
||||
{
|
||||
texture = LoadTexture(this.FilePath);
|
||||
|
||||
if (texture == null)
|
||||
{
|
||||
shouldReturn = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (sourceVector.Z == 0.0f) sourceVector.Z = texture.Width;
|
||||
if (sourceVector.W == 0.0f) sourceVector.W = texture.Height;
|
||||
}
|
||||
|
||||
public void EnsureLazyLoaded()
|
||||
{
|
||||
if (!lazyLoad || texture != null || cannotBeLoaded) { return; }
|
||||
|
||||
Vector4 sourceVector = Vector4.Zero;
|
||||
bool temp2 = false;
|
||||
LoadTexture(ref sourceVector, ref temp2);
|
||||
if (sourceRect.Width == 0 && sourceRect.Height == 0)
|
||||
{
|
||||
sourceRect = new Rectangle((int)sourceVector.X, (int)sourceVector.Y, (int)sourceVector.Z, (int)sourceVector.W);
|
||||
size = SourceElement.GetAttributeVector2("size", Vector2.One);
|
||||
size.X *= sourceRect.Width;
|
||||
size.Y *= sourceRect.Height;
|
||||
RelativeOrigin = SourceElement.GetAttributeVector2("origin", new Vector2(0.5f, 0.5f));
|
||||
}
|
||||
foreach (Sprite s in LoadedSprites)
|
||||
{
|
||||
if (s == this) { continue; }
|
||||
if (s.FullPath == FullPath && s.texture != null) { s.texture = texture; }
|
||||
}
|
||||
if (texture == null)
|
||||
{
|
||||
cannotBeLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void ReloadTexture(bool updateAllSprites = false) => ReloadTexture(updateAllSprites ? LoadedSprites.Where(s => s.Texture == texture) : new Sprite[] { this });
|
||||
|
||||
public void ReloadTexture(IEnumerable<Sprite> spritesToUpdate)
|
||||
{
|
||||
texture.Dispose();
|
||||
texture = TextureLoader.FromFile(FilePath);
|
||||
foreach (Sprite sprite in spritesToUpdate)
|
||||
{
|
||||
sprite.texture = texture;
|
||||
}
|
||||
}
|
||||
|
||||
partial void CalculateSourceRect()
|
||||
{
|
||||
sourceRect = new Rectangle(0, 0, texture.Width, texture.Height);
|
||||
}
|
||||
|
||||
public static Texture2D LoadTexture(string file)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(file))
|
||||
{
|
||||
Texture2D t = null;
|
||||
CrossThread.RequestExecutionOnMainThread(() =>
|
||||
{
|
||||
t = new Texture2D(GameMain.GraphicsDeviceManager.GraphicsDevice, 1, 1);
|
||||
});
|
||||
return t;
|
||||
}
|
||||
file = Path.GetFullPath(file);
|
||||
foreach (Sprite s in LoadedSprites)
|
||||
{
|
||||
if (s.FullPath == file && s.texture != null && !s.texture.IsDisposed) { return s.texture; }
|
||||
}
|
||||
|
||||
if (File.Exists(file))
|
||||
{
|
||||
ToolBox.IsProperFilenameCase(file);
|
||||
return TextureLoader.FromFile(file);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError("Sprite \"" + file + "\" not found!");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, Vector2 pos, float rotate = 0.0f, float scale = 1.0f, SpriteEffects spriteEffect = SpriteEffects.None)
|
||||
{
|
||||
this.Draw(spriteBatch, pos, Color.White, rotate, scale, spriteEffect);
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, Vector2 pos, Color color, float rotate = 0.0f, float scale = 1.0f, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = null)
|
||||
{
|
||||
this.Draw(spriteBatch, pos, color, this.origin, rotate, new Vector2(scale, scale), spriteEffect, depth);
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, Vector2 pos, Color color, Vector2 origin, float rotate = 0.0f, float scale = 1.0f, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = null)
|
||||
{
|
||||
this.Draw(spriteBatch, pos, color, origin, rotate, new Vector2(scale, scale), spriteEffect, depth);
|
||||
}
|
||||
|
||||
public virtual void Draw(SpriteBatch spriteBatch, Vector2 pos, Color color, Vector2 origin, float rotate, Vector2 scale, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = null)
|
||||
{
|
||||
if (Texture == null) { return; }
|
||||
//DrawSilhouette(spriteBatch, pos, origin, rotate, scale, spriteEffect, depth);
|
||||
spriteBatch.Draw(texture, pos + offset, sourceRect, color, rotation + rotate, origin, scale, spriteEffect, depth ?? this.depth);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a silhouette for the sprite (or outline if the sprite is rendered on top of it)
|
||||
/// </summary>
|
||||
public void DrawSilhouette(SpriteBatch spriteBatch, Vector2 pos, Vector2 origin, float rotate, Vector2 scale, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = null)
|
||||
{
|
||||
if (Texture == null) { return; }
|
||||
for (int x = -1; x <= 1; x += 2)
|
||||
{
|
||||
for (int y = -1; y <= 1; y += 2)
|
||||
{
|
||||
spriteBatch.Draw(texture, pos + offset + new Vector2(x, y), sourceRect, Color.Black, rotation + rotate, origin, scale, spriteEffect, (depth ?? this.depth) + 0.01f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawTiled(SpriteBatch spriteBatch, Vector2 position, Vector2 targetSize,
|
||||
Rectangle? rect = null, Color? color = null, Point? startOffset = null, Vector2? textureScale = null, float? depth = null)
|
||||
{
|
||||
if (Texture == null) { return; }
|
||||
//Init optional values
|
||||
Vector2 drawOffset = startOffset.HasValue ? new Vector2(startOffset.Value.X, startOffset.Value.Y) : Vector2.Zero;
|
||||
Vector2 scale = textureScale ?? Vector2.One;
|
||||
Color drawColor = color ?? Color.White;
|
||||
|
||||
bool flipHorizontal = (effects & SpriteEffects.FlipHorizontally) != 0;
|
||||
bool flipVertical = (effects & SpriteEffects.FlipVertically) != 0;
|
||||
|
||||
//wrap the drawOffset inside the sourceRect
|
||||
drawOffset.X = (drawOffset.X / scale.X) % sourceRect.Width;
|
||||
drawOffset.Y = (drawOffset.Y / scale.Y) % sourceRect.Height;
|
||||
if (flipHorizontal)
|
||||
{
|
||||
float diff = targetSize.X % (sourceRect.Width * scale.X);
|
||||
drawOffset.X += (sourceRect.Width * scale.X - diff) / scale.X;
|
||||
}
|
||||
if (flipVertical)
|
||||
{
|
||||
float diff = targetSize.Y % (sourceRect.Height * scale.Y);
|
||||
drawOffset.Y += (sourceRect.Height * scale.Y - diff) / scale.Y;
|
||||
}
|
||||
|
||||
//how many times the texture needs to be drawn on the x-axis
|
||||
int xTiles = (int)Math.Ceiling((targetSize.X + drawOffset.X * scale.X) / (sourceRect.Width * scale.X));
|
||||
//how many times the texture needs to be drawn on the y-axis
|
||||
int yTiles = (int)Math.Ceiling((targetSize.Y + drawOffset.Y * scale.Y) / (sourceRect.Height * scale.Y));
|
||||
|
||||
//where the current tile is being drawn;
|
||||
Vector2 currDrawPosition = position - drawOffset;
|
||||
//which part of the texture we are currently drawing
|
||||
Rectangle texPerspective = sourceRect;
|
||||
|
||||
|
||||
for (int x = 0; x < xTiles; x++)
|
||||
{
|
||||
texPerspective.X = sourceRect.X;
|
||||
texPerspective.Width = sourceRect.Width;
|
||||
texPerspective.Height = sourceRect.Height;
|
||||
|
||||
//offset to the left, draw a partial slice
|
||||
if (currDrawPosition.X < position.X)
|
||||
{
|
||||
float diff = (position.X - currDrawPosition.X);
|
||||
currDrawPosition.X += diff;
|
||||
texPerspective.Width -= (int)diff;
|
||||
if (!flipHorizontal)
|
||||
{
|
||||
texPerspective.X += (int)diff;
|
||||
}
|
||||
}
|
||||
//drawing an offset flipped sprite, need to draw an extra slice to the left side
|
||||
if (currDrawPosition.X > position.X && x == 0)
|
||||
{
|
||||
if (flipHorizontal)
|
||||
{
|
||||
int sliceWidth = (int)((currDrawPosition.X - position.X) * scale.X);
|
||||
|
||||
Vector2 slicePos = currDrawPosition;
|
||||
slicePos.X = position.X;
|
||||
Rectangle sliceRect = texPerspective;
|
||||
sliceRect.X = SourceRect.X;
|
||||
sliceRect.Width = (int)(sliceWidth / scale.X);
|
||||
|
||||
if (flipVertical)
|
||||
{
|
||||
slicePos.Y += size.Y;
|
||||
}
|
||||
|
||||
spriteBatch.Draw(texture, slicePos, sliceRect, drawColor, rotation, Vector2.Zero, scale, effects, depth ?? this.depth);
|
||||
currDrawPosition.X = slicePos.X + sliceWidth;
|
||||
}
|
||||
}
|
||||
//make sure the rightmost tiles don't go over the right side
|
||||
if (x == xTiles - 1)
|
||||
{
|
||||
int diff = (int)(((currDrawPosition.X + texPerspective.Width * scale.X) - (position.X + targetSize.X)) / scale.X);
|
||||
texPerspective.Width -= diff;
|
||||
if (flipHorizontal)
|
||||
{
|
||||
texPerspective.X += diff;
|
||||
}
|
||||
}
|
||||
|
||||
currDrawPosition.Y = position.Y - drawOffset.Y;
|
||||
|
||||
for (int y = 0; y < yTiles; y++)
|
||||
{
|
||||
texPerspective.Y = sourceRect.Y;
|
||||
texPerspective.Height = sourceRect.Height;
|
||||
|
||||
//offset above the top, draw a partial slice
|
||||
if (currDrawPosition.Y < position.Y)
|
||||
{
|
||||
float diff = (position.Y - currDrawPosition.Y);
|
||||
currDrawPosition.Y += diff;
|
||||
texPerspective.Height -= (int)diff;
|
||||
if (!flipVertical)
|
||||
{
|
||||
texPerspective.Y += (int)diff;
|
||||
}
|
||||
}
|
||||
|
||||
//drawing an offset flipped sprite, need to draw an extra slice to the top
|
||||
if (currDrawPosition.Y > position.Y && y == 0)
|
||||
{
|
||||
if (flipVertical)
|
||||
{
|
||||
int sliceHeight = (int)((currDrawPosition.Y - position.Y) * scale.Y);
|
||||
|
||||
Vector2 slicePos = currDrawPosition;
|
||||
slicePos.Y = position.Y;
|
||||
Rectangle sliceRect = texPerspective;
|
||||
sliceRect.Y = SourceRect.Y;
|
||||
sliceRect.Height = (int)(sliceHeight / scale.Y);
|
||||
|
||||
spriteBatch.Draw(texture, slicePos, sliceRect, drawColor, rotation, Vector2.Zero, scale, effects, depth ?? this.depth);
|
||||
|
||||
currDrawPosition.Y = slicePos.Y + sliceHeight;
|
||||
}
|
||||
}
|
||||
|
||||
//make sure the bottommost tiles don't go over the bottom
|
||||
if (y == yTiles - 1)
|
||||
{
|
||||
int diff = (int)(((currDrawPosition.Y + texPerspective.Height * scale.Y) - (position.Y + targetSize.Y)) / scale.Y);
|
||||
texPerspective.Height -= diff;
|
||||
if (flipVertical)
|
||||
{
|
||||
texPerspective.Y += diff;
|
||||
}
|
||||
}
|
||||
|
||||
spriteBatch.Draw(texture, currDrawPosition,
|
||||
texPerspective, drawColor, rotation, Vector2.Zero, scale, effects, depth ?? this.depth);
|
||||
|
||||
currDrawPosition.Y += texPerspective.Height * scale.Y;
|
||||
}
|
||||
|
||||
currDrawPosition.X += texPerspective.Width * scale.X;
|
||||
}
|
||||
}
|
||||
|
||||
partial void DisposeTexture()
|
||||
{
|
||||
//check if another sprite is using the same texture
|
||||
if (!string.IsNullOrEmpty(FilePath)) //file can be empty if the sprite is created directly from a Texture2D instance
|
||||
{
|
||||
lock (list)
|
||||
{
|
||||
foreach (Sprite s in LoadedSprites)
|
||||
{
|
||||
if (s.FullPath == FullPath) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//if not, free the texture
|
||||
if (texture != null)
|
||||
{
|
||||
CrossThread.RequestExecutionOnMainThread(() =>
|
||||
{
|
||||
texture.Dispose();
|
||||
});
|
||||
texture = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class SpriteSheet : Sprite
|
||||
{
|
||||
public override void Draw(SpriteBatch spriteBatch, Vector2 pos, Color color, Vector2 origin, float rotate, Vector2 scale, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = default(float?))
|
||||
{
|
||||
if (texture == null) return;
|
||||
|
||||
spriteBatch.Draw(texture, pos + offset, sourceRects[0], color, rotation + rotate, origin, scale, spriteEffect, depth == null ? this.depth : (float)depth);
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, int spriteIndex, Vector2 pos, Color color, Vector2 origin, float rotate, Vector2 scale, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = default(float?))
|
||||
{
|
||||
if (texture == null) return;
|
||||
|
||||
spriteBatch.Draw(texture, pos + offset, sourceRects[MathHelper.Clamp(spriteIndex, 0, sourceRects.Length - 1)], color, rotation + rotate, origin, scale, spriteEffect, depth == null ? this.depth : (float)depth);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user