(61d00a474) v0.9.7.1
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user