Added rotation logic to the renderer to enable the use of Interpolated Facings.
This commit is contained in:
committed by
Matthias Mailänder
parent
e060d6eb05
commit
a1a50d6c98
@@ -50,37 +50,41 @@ namespace OpenRA.Graphics
|
||||
}
|
||||
|
||||
public int CurrentFrame => backwards ? CurrentSequence.Length - frame - 1 : frame;
|
||||
|
||||
public Sprite Image => CurrentSequence.GetSprite(CurrentFrame, facingFunc());
|
||||
|
||||
public IRenderable[] Render(WPos pos, in WVec offset, int zOffset, PaletteReference palette)
|
||||
{
|
||||
var tintModifiers = CurrentSequence.IgnoreWorldTint ? TintModifiers.IgnoreWorldTint : TintModifiers.None;
|
||||
var alpha = CurrentSequence.GetAlpha(CurrentFrame);
|
||||
var imageRenderable = new SpriteRenderable(Image, pos, offset, CurrentSequence.ZOffset + zOffset, palette, CurrentSequence.Scale, alpha, float3.Ones, tintModifiers, IsDecoration);
|
||||
var (image, rotation) = CurrentSequence.GetSpriteWithRotation(CurrentFrame, facingFunc());
|
||||
var imageRenderable = new SpriteRenderable(image, pos, offset, CurrentSequence.ZOffset + zOffset, palette, CurrentSequence.Scale, alpha, float3.Ones, tintModifiers, IsDecoration,
|
||||
rotation);
|
||||
|
||||
if (CurrentSequence.ShadowStart >= 0)
|
||||
{
|
||||
var shadow = CurrentSequence.GetShadow(CurrentFrame, facingFunc());
|
||||
var shadowRenderable = new SpriteRenderable(shadow, pos, offset, CurrentSequence.ShadowZOffset + zOffset, palette, CurrentSequence.Scale, 1f, float3.Ones, tintModifiers, true);
|
||||
var shadowRenderable = new SpriteRenderable(shadow, pos, offset, CurrentSequence.ShadowZOffset + zOffset, palette, CurrentSequence.Scale, 1f, float3.Ones, tintModifiers,
|
||||
true, rotation);
|
||||
return new IRenderable[] { shadowRenderable, imageRenderable };
|
||||
}
|
||||
|
||||
return new IRenderable[] { imageRenderable };
|
||||
}
|
||||
|
||||
public IRenderable[] RenderUI(WorldRenderer wr, int2 pos, in WVec offset, int zOffset, PaletteReference palette, float scale = 1f)
|
||||
public IRenderable[] RenderUI(WorldRenderer wr, int2 pos, in WVec offset, int zOffset, PaletteReference palette, float scale = 1f, float rotation = 0f)
|
||||
{
|
||||
scale *= CurrentSequence.Scale;
|
||||
var screenOffset = (scale * wr.ScreenVectorComponents(offset)).XY.ToInt2();
|
||||
var imagePos = pos + screenOffset - new int2((int)(scale * Image.Size.X / 2), (int)(scale * Image.Size.Y / 2));
|
||||
var alpha = CurrentSequence.GetAlpha(CurrentFrame);
|
||||
var imageRenderable = new UISpriteRenderable(Image, WPos.Zero + offset, imagePos, CurrentSequence.ZOffset + zOffset, palette, scale, alpha);
|
||||
var imageRenderable = new UISpriteRenderable(Image, WPos.Zero + offset, imagePos, CurrentSequence.ZOffset + zOffset, palette, scale, alpha, rotation);
|
||||
|
||||
if (CurrentSequence.ShadowStart >= 0)
|
||||
{
|
||||
var shadow = CurrentSequence.GetShadow(CurrentFrame, facingFunc());
|
||||
var shadowPos = pos - new int2((int)(scale * shadow.Size.X / 2), (int)(scale * shadow.Size.Y / 2));
|
||||
var shadowRenderable = new UISpriteRenderable(shadow, WPos.Zero + offset, shadowPos, CurrentSequence.ShadowZOffset + zOffset, palette, scale);
|
||||
var shadowRenderable = new UISpriteRenderable(shadow, WPos.Zero + offset, shadowPos, CurrentSequence.ShadowZOffset + zOffset, palette, scale, 1f, rotation);
|
||||
return new IRenderable[] { shadowRenderable, imageRenderable };
|
||||
}
|
||||
|
||||
|
||||
@@ -22,28 +22,28 @@ namespace OpenRA.Graphics
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public void DrawSprite(Sprite s, in float3 location, in float3 scale)
|
||||
public void DrawSprite(Sprite s, in float3 location, in float3 scale, float rotation = 0f)
|
||||
{
|
||||
if (s.Channel != TextureChannel.RGBA)
|
||||
throw new InvalidOperationException("DrawRGBASprite requires a RGBA sprite.");
|
||||
|
||||
parent.DrawSprite(s, 0, location, scale);
|
||||
parent.DrawSprite(s, 0, location, scale, rotation);
|
||||
}
|
||||
|
||||
public void DrawSprite(Sprite s, in float3 location, float scale = 1f)
|
||||
public void DrawSprite(Sprite s, in float3 location, float scale = 1f, float rotation = 0f)
|
||||
{
|
||||
if (s.Channel != TextureChannel.RGBA)
|
||||
throw new InvalidOperationException("DrawRGBASprite requires a RGBA sprite.");
|
||||
|
||||
parent.DrawSprite(s, 0, location, scale);
|
||||
parent.DrawSprite(s, 0, location, scale, rotation);
|
||||
}
|
||||
|
||||
public void DrawSprite(Sprite s, in float3 location, float scale, in float3 tint, float alpha)
|
||||
public void DrawSprite(Sprite s, in float3 location, float scale, in float3 tint, float alpha, float rotation = 0f)
|
||||
{
|
||||
if (s.Channel != TextureChannel.RGBA)
|
||||
throw new InvalidOperationException("DrawRGBASprite requires a RGBA sprite.");
|
||||
|
||||
parent.DrawSprite(s, 0, location, scale, tint, alpha);
|
||||
parent.DrawSprite(s, 0, location, scale, tint, alpha, rotation);
|
||||
}
|
||||
|
||||
public void DrawSprite(Sprite s, in float3 a, in float3 b, in float3 c, in float3 d, in float3 tint, float alpha)
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace OpenRA.Graphics
|
||||
int Length { get; }
|
||||
int Stride { get; }
|
||||
int Facings { get; }
|
||||
int InterpolatedFacings { get; }
|
||||
int Tick { get; }
|
||||
int ZOffset { get; }
|
||||
int ShadowStart { get; }
|
||||
@@ -37,6 +38,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
Sprite GetSprite(int frame);
|
||||
Sprite GetSprite(int frame, WAngle facing);
|
||||
(Sprite, WAngle) GetSpriteWithRotation(int frame, WAngle facing);
|
||||
Sprite GetShadow(int frame, WAngle facing);
|
||||
float GetAlpha(int frame);
|
||||
}
|
||||
|
||||
@@ -25,12 +25,14 @@ namespace OpenRA.Graphics
|
||||
readonly int zOffset;
|
||||
readonly PaletteReference palette;
|
||||
readonly float scale;
|
||||
readonly WAngle rotation = WAngle.Zero;
|
||||
readonly float3 tint;
|
||||
readonly TintModifiers tintModifiers;
|
||||
readonly float alpha;
|
||||
readonly bool isDecoration;
|
||||
|
||||
public SpriteRenderable(Sprite sprite, WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale, float alpha, float3 tint, TintModifiers tintModifiers, bool isDecoration)
|
||||
public SpriteRenderable(Sprite sprite, WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale, float alpha,
|
||||
float3 tint, TintModifiers tintModifiers, bool isDecoration, WAngle rotation)
|
||||
{
|
||||
this.sprite = sprite;
|
||||
this.pos = pos;
|
||||
@@ -38,6 +40,7 @@ namespace OpenRA.Graphics
|
||||
this.zOffset = zOffset;
|
||||
this.palette = palette;
|
||||
this.scale = scale;
|
||||
this.rotation = rotation;
|
||||
this.tint = tint;
|
||||
this.isDecoration = isDecoration;
|
||||
this.tintModifiers = tintModifiers;
|
||||
@@ -50,6 +53,10 @@ namespace OpenRA.Graphics
|
||||
this.palette = null;
|
||||
}
|
||||
|
||||
public SpriteRenderable(Sprite sprite, WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale, float alpha,
|
||||
float3 tint, TintModifiers tintModifiers, bool isDecoration)
|
||||
: this(sprite, pos, offset, zOffset, palette, scale, alpha, tint, tintModifiers, isDecoration, WAngle.Zero) { }
|
||||
|
||||
public WPos Pos => pos + offset;
|
||||
public WVec Offset => offset;
|
||||
public PaletteReference Palette => palette;
|
||||
@@ -60,19 +67,34 @@ namespace OpenRA.Graphics
|
||||
public float3 Tint => tint;
|
||||
public TintModifiers TintModifiers => tintModifiers;
|
||||
|
||||
public IPalettedRenderable WithPalette(PaletteReference newPalette) { return new SpriteRenderable(sprite, pos, offset, zOffset, newPalette, scale, alpha, tint, tintModifiers, isDecoration); }
|
||||
public IRenderable WithZOffset(int newOffset) { return new SpriteRenderable(sprite, pos, offset, newOffset, palette, scale, alpha, tint, tintModifiers, isDecoration); }
|
||||
public IRenderable OffsetBy(in WVec vec) { return new SpriteRenderable(sprite, pos + vec, offset, zOffset, palette, scale, alpha, tint, tintModifiers, isDecoration); }
|
||||
public IRenderable AsDecoration() { return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, alpha, tint, tintModifiers, true); }
|
||||
public IPalettedRenderable WithPalette(PaletteReference newPalette)
|
||||
{
|
||||
return new SpriteRenderable(sprite, pos, offset, zOffset, newPalette, scale, alpha, tint, tintModifiers, isDecoration, rotation);
|
||||
}
|
||||
|
||||
public IRenderable WithZOffset(int newOffset)
|
||||
{
|
||||
return new SpriteRenderable(sprite, pos, offset, newOffset, palette, scale, alpha, tint, tintModifiers, isDecoration, rotation);
|
||||
}
|
||||
|
||||
public IRenderable OffsetBy(in WVec vec)
|
||||
{
|
||||
return new SpriteRenderable(sprite, pos + vec, offset, zOffset, palette, scale, alpha, tint, tintModifiers, isDecoration, rotation);
|
||||
}
|
||||
|
||||
public IRenderable AsDecoration()
|
||||
{
|
||||
return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, alpha, tint, tintModifiers, true, rotation);
|
||||
}
|
||||
|
||||
public IModifyableRenderable WithAlpha(float newAlpha)
|
||||
{
|
||||
return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, newAlpha, tint, tintModifiers, isDecoration);
|
||||
return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, newAlpha, tint, tintModifiers, isDecoration, rotation);
|
||||
}
|
||||
|
||||
public IModifyableRenderable WithTint(in float3 newTint, TintModifiers newTintModifiers)
|
||||
{
|
||||
return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, alpha, newTint, newTintModifiers, isDecoration);
|
||||
return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, alpha, newTint, newTintModifiers, isDecoration, rotation);
|
||||
}
|
||||
|
||||
float3 ScreenPosition(WorldRenderer wr)
|
||||
@@ -94,7 +116,7 @@ namespace OpenRA.Graphics
|
||||
if ((tintModifiers & TintModifiers.ReplaceColor) != 0)
|
||||
a *= -1;
|
||||
|
||||
wsr.DrawSprite(sprite, palette, ScreenPosition(wr), scale, t, a);
|
||||
wsr.DrawSprite(sprite, palette, ScreenPosition(wr), scale, t, a, rotation.RendererRadians());
|
||||
}
|
||||
|
||||
public void RenderDebugGeometry(WorldRenderer wr)
|
||||
@@ -102,13 +124,16 @@ namespace OpenRA.Graphics
|
||||
var pos = ScreenPosition(wr) + sprite.Offset;
|
||||
var tl = wr.Viewport.WorldToViewPx(pos);
|
||||
var br = wr.Viewport.WorldToViewPx(pos + sprite.Size);
|
||||
Game.Renderer.RgbaColorRenderer.DrawRect(tl, br, 1, Color.Red);
|
||||
if (rotation == WAngle.Zero)
|
||||
Game.Renderer.RgbaColorRenderer.DrawRect(tl, br, 1, Color.Red);
|
||||
else
|
||||
Game.Renderer.RgbaColorRenderer.DrawPolygon(Util.RotateQuad(tl, br - tl, rotation.RendererRadians()), 1, Color.Red);
|
||||
}
|
||||
|
||||
public Rectangle ScreenBounds(WorldRenderer wr)
|
||||
{
|
||||
var screenOffset = ScreenPosition(wr) + sprite.Offset;
|
||||
return new Rectangle((int)screenOffset.X, (int)screenOffset.Y, (int)sprite.Size.X, (int)sprite.Size.Y);
|
||||
return Util.BoundingRectangle(screenOffset, sprite.Size, rotation.RendererRadians());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,35 +126,40 @@ namespace OpenRA.Graphics
|
||||
return pal.TextureIndex;
|
||||
}
|
||||
|
||||
internal void DrawSprite(Sprite s, float paletteTextureIndex, in float3 location, in float3 scale)
|
||||
internal void DrawSprite(Sprite s, float paletteTextureIndex, in float3 location, in float3 scale, float rotation = 0f)
|
||||
{
|
||||
var samplers = SetRenderStateForSprite(s);
|
||||
Util.FastCreateQuad(vertices, location + scale * s.Offset, s, samplers, paletteTextureIndex, nv, scale * s.Size, float3.Ones, 1f);
|
||||
Util.FastCreateQuad(vertices, location + scale * s.Offset, s, samplers, paletteTextureIndex, nv, scale * s.Size, float3.Ones,
|
||||
1f, rotation);
|
||||
nv += 6;
|
||||
}
|
||||
|
||||
internal void DrawSprite(Sprite s, float paletteTextureIndex, in float3 location, float scale)
|
||||
internal void DrawSprite(Sprite s, float paletteTextureIndex, in float3 location, float scale, float rotation = 0f)
|
||||
{
|
||||
var samplers = SetRenderStateForSprite(s);
|
||||
Util.FastCreateQuad(vertices, location + scale * s.Offset, s, samplers, paletteTextureIndex, nv, scale * s.Size, float3.Ones, 1f);
|
||||
Util.FastCreateQuad(vertices, location + scale * s.Offset, s, samplers, paletteTextureIndex, nv, scale * s.Size, float3.Ones,
|
||||
1f, rotation);
|
||||
nv += 6;
|
||||
}
|
||||
|
||||
public void DrawSprite(Sprite s, PaletteReference pal, in float3 location, float scale = 1f)
|
||||
public void DrawSprite(Sprite s, PaletteReference pal, in float3 location, float scale = 1f, float rotation = 0f)
|
||||
{
|
||||
DrawSprite(s, ResolveTextureIndex(s, pal), location, scale);
|
||||
DrawSprite(s, ResolveTextureIndex(s, pal), location, scale, rotation);
|
||||
}
|
||||
|
||||
internal void DrawSprite(Sprite s, float paletteTextureIndex, in float3 location, float scale, in float3 tint, float alpha)
|
||||
internal void DrawSprite(Sprite s, float paletteTextureIndex, in float3 location, float scale, in float3 tint, float alpha,
|
||||
float rotation = 0f)
|
||||
{
|
||||
var samplers = SetRenderStateForSprite(s);
|
||||
Util.FastCreateQuad(vertices, location + scale * s.Offset, s, samplers, paletteTextureIndex, nv, scale * s.Size, tint, alpha);
|
||||
Util.FastCreateQuad(vertices, location + scale * s.Offset, s, samplers, paletteTextureIndex, nv, scale * s.Size, tint, alpha,
|
||||
rotation);
|
||||
nv += 6;
|
||||
}
|
||||
|
||||
public void DrawSprite(Sprite s, PaletteReference pal, in float3 location, float scale, in float3 tint, float alpha)
|
||||
public void DrawSprite(Sprite s, PaletteReference pal, in float3 location, float scale, in float3 tint, float alpha,
|
||||
float rotation = 0f)
|
||||
{
|
||||
DrawSprite(s, ResolveTextureIndex(s, pal), location, scale, tint, alpha);
|
||||
DrawSprite(s, ResolveTextureIndex(s, pal), location, scale, tint, alpha, rotation);
|
||||
}
|
||||
|
||||
internal void DrawSprite(Sprite s, float paletteTextureIndex, in float3 a, in float3 b, in float3 c, in float3 d, in float3 tint, float alpha)
|
||||
|
||||
@@ -22,8 +22,9 @@ namespace OpenRA.Graphics
|
||||
readonly PaletteReference palette;
|
||||
readonly float scale;
|
||||
readonly float alpha;
|
||||
readonly float rotation = 0f;
|
||||
|
||||
public UISpriteRenderable(Sprite sprite, WPos effectiveWorldPos, int2 screenPos, int zOffset, PaletteReference palette, float scale = 1f, float alpha = 1f)
|
||||
public UISpriteRenderable(Sprite sprite, WPos effectiveWorldPos, int2 screenPos, int zOffset, PaletteReference palette, float scale = 1f, float alpha = 1f, float rotation = 0f)
|
||||
{
|
||||
this.sprite = sprite;
|
||||
this.effectiveWorldPos = effectiveWorldPos;
|
||||
@@ -32,6 +33,7 @@ namespace OpenRA.Graphics
|
||||
this.palette = palette;
|
||||
this.scale = scale;
|
||||
this.alpha = alpha;
|
||||
this.rotation = rotation;
|
||||
|
||||
// PERF: Remove useless palette assignments for RGBA sprites
|
||||
// HACK: This is working around the fact that palettes are defined on traits rather than sequences
|
||||
@@ -48,7 +50,7 @@ namespace OpenRA.Graphics
|
||||
public PaletteReference Palette => palette;
|
||||
public int ZOffset => zOffset;
|
||||
|
||||
public IPalettedRenderable WithPalette(PaletteReference newPalette) { return new UISpriteRenderable(sprite, effectiveWorldPos, screenPos, zOffset, newPalette, scale, alpha); }
|
||||
public IPalettedRenderable WithPalette(PaletteReference newPalette) { return new UISpriteRenderable(sprite, effectiveWorldPos, screenPos, zOffset, newPalette, scale, alpha, rotation); }
|
||||
public IRenderable WithZOffset(int newOffset) { return this; }
|
||||
public IRenderable OffsetBy(in WVec vec) { return this; }
|
||||
public IRenderable AsDecoration() { return this; }
|
||||
@@ -56,19 +58,22 @@ namespace OpenRA.Graphics
|
||||
public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; }
|
||||
public void Render(WorldRenderer wr)
|
||||
{
|
||||
Game.Renderer.SpriteRenderer.DrawSprite(sprite, palette, screenPos, scale, float3.Ones, alpha);
|
||||
Game.Renderer.SpriteRenderer.DrawSprite(sprite, palette, screenPos, scale, float3.Ones, alpha, rotation);
|
||||
}
|
||||
|
||||
public void RenderDebugGeometry(WorldRenderer wr)
|
||||
{
|
||||
var offset = screenPos + sprite.Offset.XY;
|
||||
Game.Renderer.RgbaColorRenderer.DrawRect(offset, offset + sprite.Size.XY, 1, Color.Red);
|
||||
if (rotation == 0f)
|
||||
Game.Renderer.RgbaColorRenderer.DrawRect(offset, offset + sprite.Size.XY, 1, Color.Red);
|
||||
else
|
||||
Game.Renderer.RgbaColorRenderer.DrawPolygon(Util.RotateQuad(offset, sprite.Size, rotation), 1, Color.Red);
|
||||
}
|
||||
|
||||
public Rectangle ScreenBounds(WorldRenderer wr)
|
||||
{
|
||||
var offset = screenPos + sprite.Offset;
|
||||
return new Rectangle((int)offset.X, (int)offset.Y, (int)sprite.Size.X, (int)sprite.Size.Y);
|
||||
return Util.BoundingRectangle(offset, sprite.Size, rotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,12 +20,44 @@ namespace OpenRA.Graphics
|
||||
// yes, our channel order is nuts.
|
||||
static readonly int[] ChannelMasks = { 2, 1, 0, 3 };
|
||||
|
||||
public static void FastCreateQuad(Vertex[] vertices, in float3 o, Sprite r, int2 samplers, float paletteTextureIndex, int nv, in float3 size, in float3 tint, float alpha)
|
||||
public static void FastCreateQuad(Vertex[] vertices, in float3 o, Sprite r, int2 samplers, float paletteTextureIndex, int nv,
|
||||
in float3 size, in float3 tint, float alpha, float rotation = 0f)
|
||||
{
|
||||
var b = new float3(o.X + size.X, o.Y, o.Z);
|
||||
var c = new float3(o.X + size.X, o.Y + size.Y, o.Z + size.Z);
|
||||
var d = new float3(o.X, o.Y + size.Y, o.Z + size.Z);
|
||||
FastCreateQuad(vertices, o, b, c, d, r, samplers, paletteTextureIndex, tint, alpha, nv);
|
||||
float3 a, b, c, d;
|
||||
|
||||
// Rotate sprite if rotation angle is not equal to 0
|
||||
if (rotation != 0f)
|
||||
{
|
||||
var center = o + 0.5f * size;
|
||||
var angleSin = (float)Math.Sin(-rotation);
|
||||
var angleCos = (float)Math.Cos(-rotation);
|
||||
|
||||
// Rotated offset for +/- x with +/- y
|
||||
var ra = 0.5f * new float3(
|
||||
size.X * angleCos - size.Y * angleSin,
|
||||
size.X * angleSin + size.Y * angleCos,
|
||||
(size.X * angleSin + size.Y * angleCos) * size.Z / size.Y);
|
||||
|
||||
// Rotated offset for +/- x with -/+ y
|
||||
var rb = 0.5f * new float3(
|
||||
size.X * angleCos + size.Y * angleSin,
|
||||
size.X * angleSin - size.Y * angleCos,
|
||||
(size.X * angleSin - size.Y * angleCos) * size.Z / size.Y);
|
||||
|
||||
a = center - ra;
|
||||
b = center + rb;
|
||||
c = center + ra;
|
||||
d = center - rb;
|
||||
}
|
||||
else
|
||||
{
|
||||
a = o;
|
||||
b = new float3(o.X + size.X, o.Y, o.Z);
|
||||
c = new float3(o.X + size.X, o.Y + size.Y, o.Z + size.Z);
|
||||
d = new float3(o.X, o.Y + size.Y, o.Z + size.Z);
|
||||
}
|
||||
|
||||
FastCreateQuad(vertices, a, b, c, d, r, samplers, paletteTextureIndex, tint, alpha, nv);
|
||||
}
|
||||
|
||||
public static void FastCreateQuad(Vertex[] vertices,
|
||||
@@ -191,6 +223,69 @@ namespace OpenRA.Graphics
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Rotates a quad about its center in the x-y plane.</summary>
|
||||
/// <param name="tl">The top left vertex of the quad</param>
|
||||
/// <param name="size">A float3 containing the X, Y, and Z lengths of the quad</param>
|
||||
/// <param name="rotation">The number of radians to rotate by</param>
|
||||
/// <returns>An array of four vertices representing the rotated quad (top-left, top-right, bottom-right, bottom-left)</returns>
|
||||
public static float3[] RotateQuad(float3 tl, float3 size, float rotation)
|
||||
{
|
||||
var center = tl + 0.5f * size;
|
||||
var angleSin = (float)Math.Sin(-rotation);
|
||||
var angleCos = (float)Math.Cos(-rotation);
|
||||
|
||||
// Rotated offset for +/- x with +/- y
|
||||
var ra = 0.5f * new float3(
|
||||
size.X * angleCos - size.Y * angleSin,
|
||||
size.X * angleSin + size.Y * angleCos,
|
||||
(size.X * angleSin + size.Y * angleCos) * size.Z / size.Y);
|
||||
|
||||
// Rotated offset for +/- x with -/+ y
|
||||
var rb = 0.5f * new float3(
|
||||
size.X * angleCos + size.Y * angleSin,
|
||||
size.X * angleSin - size.Y * angleCos,
|
||||
(size.X * angleSin - size.Y * angleCos) * size.Z / size.Y);
|
||||
|
||||
return new float3[]
|
||||
{
|
||||
center - ra,
|
||||
center + rb,
|
||||
center + ra,
|
||||
center - rb
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the bounds of an object. Used for determining which objects need to be rendered on screen, and which do not.
|
||||
/// </summary>
|
||||
/// <param name="offset">The top left vertex of the object</param>
|
||||
/// <param name="size">A float 3 containing the X, Y, and Z lengths of the object</param>
|
||||
/// <param name="rotation">The angle to rotate the object by (use 0f if there is no rotation)</param>
|
||||
public static Rectangle BoundingRectangle(float3 offset, float3 size, float rotation)
|
||||
{
|
||||
if (rotation == 0f)
|
||||
return new Rectangle((int)offset.X, (int)offset.Y, (int)size.X, (int)size.Y);
|
||||
|
||||
var rotatedQuad = Util.RotateQuad(offset, size, rotation);
|
||||
var minX = rotatedQuad[0].X;
|
||||
var maxX = rotatedQuad[0].X;
|
||||
var minY = rotatedQuad[0].Y;
|
||||
var maxY = rotatedQuad[0].Y;
|
||||
for (var i = 1; i < rotatedQuad.Length; i++)
|
||||
{
|
||||
minX = Math.Min(rotatedQuad[i].X, minX);
|
||||
maxX = Math.Max(rotatedQuad[i].X, maxX);
|
||||
minY = Math.Min(rotatedQuad[i].Y, minY);
|
||||
maxY = Math.Max(rotatedQuad[i].Y, maxY);
|
||||
}
|
||||
|
||||
return new Rectangle(
|
||||
(int)minX,
|
||||
(int)minY,
|
||||
(int)Math.Ceiling(maxX) - (int)minX,
|
||||
(int)Math.Ceiling(maxY) - (int)minY);
|
||||
}
|
||||
|
||||
public static Color PremultiplyAlpha(Color c)
|
||||
{
|
||||
if (c.A == byte.MaxValue)
|
||||
|
||||
@@ -108,6 +108,7 @@ namespace OpenRA.Mods.Common.Graphics
|
||||
int ISpriteSequence.Length => throw exception;
|
||||
int ISpriteSequence.Stride => throw exception;
|
||||
int ISpriteSequence.Facings => throw exception;
|
||||
int ISpriteSequence.InterpolatedFacings => throw exception;
|
||||
int ISpriteSequence.Tick => throw exception;
|
||||
int ISpriteSequence.ZOffset => throw exception;
|
||||
int ISpriteSequence.ShadowStart => throw exception;
|
||||
@@ -118,6 +119,7 @@ namespace OpenRA.Mods.Common.Graphics
|
||||
float ISpriteSequence.Scale => throw exception;
|
||||
Sprite ISpriteSequence.GetSprite(int frame) { throw exception; }
|
||||
Sprite ISpriteSequence.GetSprite(int frame, WAngle facing) { throw exception; }
|
||||
(Sprite, WAngle) ISpriteSequence.GetSpriteWithRotation(int frame, WAngle facing) { throw exception; }
|
||||
Sprite ISpriteSequence.GetShadow(int frame, WAngle facing) { throw exception; }
|
||||
float ISpriteSequence.GetAlpha(int frame) { throw exception; }
|
||||
}
|
||||
@@ -167,6 +169,11 @@ namespace OpenRA.Mods.Common.Graphics
|
||||
int ISpriteSequence.Facings => facings;
|
||||
protected int facings;
|
||||
|
||||
[Desc("The amount of directions the unit faces. Use negative values to rotate counter-clockwise.")]
|
||||
static readonly SpriteSequenceField<int> InterpolatedFacings = new SpriteSequenceField<int>(nameof(InterpolatedFacings), 1);
|
||||
int ISpriteSequence.InterpolatedFacings => interpolatedFacings;
|
||||
protected int interpolatedFacings;
|
||||
|
||||
[Desc("Time (in milliseconds at default game speed) to wait until playing the next frame in the animation.")]
|
||||
static readonly SpriteSequenceField<int> Tick = new SpriteSequenceField<int>(nameof(Tick), 40);
|
||||
int ISpriteSequence.Tick => tick;
|
||||
@@ -305,6 +312,11 @@ namespace OpenRA.Mods.Common.Graphics
|
||||
var zRamp = LoadField(d, ZRamp);
|
||||
|
||||
facings = LoadField(d, Facings);
|
||||
interpolatedFacings = LoadField(d, nameof(InterpolatedFacings), -1);
|
||||
if (interpolatedFacings != -1 && (interpolatedFacings <= 1 || interpolatedFacings <= Math.Abs(facings) || interpolatedFacings > 1024
|
||||
|| !Exts.IsPowerOf2(interpolatedFacings)))
|
||||
throw new YamlException($"InterpolatedFacings must be greater than Facings, within the range of 2 to 1024, and a power of 2.");
|
||||
|
||||
if (facings < 0)
|
||||
{
|
||||
reverseFacings = true;
|
||||
@@ -531,6 +543,17 @@ namespace OpenRA.Mods.Common.Graphics
|
||||
return GetSprite(start, frame, facing);
|
||||
}
|
||||
|
||||
public (Sprite, WAngle) GetSpriteWithRotation(int frame, WAngle facing)
|
||||
{
|
||||
var rotation = WAngle.Zero;
|
||||
|
||||
// Note: Error checking is not done here as it is done on load
|
||||
if (interpolatedFacings != -1)
|
||||
rotation = Util.GetInterpolatedFacing(facing, Math.Abs(facings), interpolatedFacings);
|
||||
|
||||
return (GetSprite(start, frame, facing), rotation);
|
||||
}
|
||||
|
||||
public Sprite GetShadow(int frame, WAngle facing)
|
||||
{
|
||||
return shadowStart >= 0 ? GetSprite(shadowStart, frame, facing) : null;
|
||||
|
||||
@@ -29,7 +29,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
throw new InvalidOperationException("Actor " + ai.Name + " is missing sequence to quantize facings from.");
|
||||
|
||||
var rsi = ai.TraitInfo<RenderSpritesInfo>();
|
||||
return sequenceProvider.GetSequence(rsi.GetImage(ai, race), Sequence).Facings;
|
||||
var seq = sequenceProvider.GetSequence(rsi.GetImage(ai, race), Sequence);
|
||||
return seq.InterpolatedFacings == -1 ? seq.Facings : seq.InterpolatedFacings;
|
||||
}
|
||||
|
||||
public override object Create(ActorInitializer init) { return new QuantizeFacingsFromSequence(this); }
|
||||
|
||||
@@ -67,11 +67,28 @@ namespace OpenRA.Mods.Common
|
||||
/// </summary>
|
||||
public static int IndexFacing(WAngle facing, int numFrames)
|
||||
{
|
||||
// 1024 here is the max angle, so we divide the max angle by the total number of facings (numFrames)
|
||||
var step = 1024 / numFrames;
|
||||
var a = (facing.Angle + step / 2) & 1023;
|
||||
return a / step;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the remainder angle after rounding to the nearest whole step / facing
|
||||
/// </summary>
|
||||
public static WAngle AngleDiffToStep(WAngle facing, int numFrames)
|
||||
{
|
||||
var step = 1024 / numFrames;
|
||||
var a = (facing.Angle + step / 2) & 1023;
|
||||
return new WAngle(a % step - step / 2);
|
||||
}
|
||||
|
||||
public static WAngle GetInterpolatedFacing(WAngle facing, int facings, int interpolatedFacings)
|
||||
{
|
||||
var step = 1024 / interpolatedFacings;
|
||||
return new WAngle(AngleDiffToStep(facing, facings).Angle / step * step);
|
||||
}
|
||||
|
||||
/// <summary>Rounds the given facing value to the nearest quantized step.</summary>
|
||||
public static WAngle QuantizeFacing(WAngle facing, int steps)
|
||||
{
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
mig:
|
||||
idle:
|
||||
Facings: 16
|
||||
InterpolatedFacings: 64
|
||||
icon: migicon
|
||||
|
||||
yak:
|
||||
idle:
|
||||
Facings: 16
|
||||
InterpolatedFacings: 64
|
||||
muzzle: minigun
|
||||
Length: 6
|
||||
Facings: 8
|
||||
@@ -66,10 +68,12 @@ tran2husk:
|
||||
u2:
|
||||
idle:
|
||||
Facings: 16
|
||||
InterpolatedFacings: 64
|
||||
|
||||
badr:
|
||||
idle:
|
||||
Facings: 16
|
||||
InterpolatedFacings: 64
|
||||
|
||||
mh60:
|
||||
idle:
|
||||
|
||||
Reference in New Issue
Block a user