Fix ActorPreviewWidget viewport manipulation.

This commit is contained in:
Paul Chote
2019-09-12 21:23:10 +01:00
committed by abcdefg30
parent 46c0b4cf31
commit a8b1762464
6 changed files with 194 additions and 24 deletions

View File

@@ -66,6 +66,22 @@ namespace OpenRA.Graphics
return new IRenderable[] { imageRenderable }; return new IRenderable[] { imageRenderable };
} }
public IRenderable[] RenderUI(int2 pos, WVec offset, int zOffset, PaletteReference palette, float scale)
{
var imagePos = pos - new int2((int)(scale * Image.Size.X / 2), (int)(scale * Image.Size.Y / 2));
var imageRenderable = new UISpriteRenderable(Image, WPos.Zero + offset, imagePos, CurrentSequence.ZOffset + zOffset, palette, scale);
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);
return new IRenderable[] { shadowRenderable, imageRenderable };
}
return new IRenderable[] { imageRenderable };
}
public Rectangle ScreenBounds(WorldRenderer wr, WPos pos, WVec offset, float scale) public Rectangle ScreenBounds(WorldRenderer wr, WPos pos, WVec offset, float scale)
{ {
var xy = wr.ScreenPxPosition(pos) + wr.ScreenPxOffset(offset); var xy = wr.ScreenPxPosition(pos) + wr.ScreenPxOffset(offset);

View File

@@ -22,6 +22,7 @@ namespace OpenRA.Mods.Common.Graphics
{ {
void Tick(); void Tick();
IEnumerable<IRenderable> Render(WorldRenderer wr, WPos pos); IEnumerable<IRenderable> Render(WorldRenderer wr, WPos pos);
IEnumerable<IRenderable> RenderUI(WorldRenderer wr, int2 pos, float scale);
IEnumerable<Rectangle> ScreenBounds(WorldRenderer wr, WPos pos); IEnumerable<Rectangle> ScreenBounds(WorldRenderer wr, WPos pos);
} }

View File

@@ -49,16 +49,23 @@ namespace OpenRA.Mods.Common.Graphics
this.zOffset = zOffset; this.zOffset = zOffset;
} }
public void Tick() { /* not supported */ } void IActorPreview.Tick() { /* not supported */ }
public IEnumerable<IRenderable> Render(WorldRenderer wr, WPos pos) IEnumerable<IRenderable> IActorPreview.RenderUI(WorldRenderer wr, int2 pos, float scale)
{
yield return new UIModelRenderable(components, WPos.Zero + offset, pos, zOffset, camera, scale * this.scale,
lightSource, lightAmbientColor, lightDiffuseColor,
colorPalette, normalsPalette, shadowPalette);
}
IEnumerable<IRenderable> IActorPreview.Render(WorldRenderer wr, WPos pos)
{ {
yield return new ModelRenderable(components, pos + offset, zOffset, camera, scale, yield return new ModelRenderable(components, pos + offset, zOffset, camera, scale,
lightSource, lightAmbientColor, lightDiffuseColor, lightSource, lightAmbientColor, lightDiffuseColor,
colorPalette, normalsPalette, shadowPalette); colorPalette, normalsPalette, shadowPalette);
} }
public IEnumerable<Rectangle> ScreenBounds(WorldRenderer wr, WPos pos) IEnumerable<Rectangle> IActorPreview.ScreenBounds(WorldRenderer wr, WPos pos)
{ {
foreach (var c in components) foreach (var c in components)
yield return c.ScreenBounds(pos, wr, scale); yield return c.ScreenBounds(pos, wr, scale);

View File

@@ -33,14 +33,19 @@ namespace OpenRA.Mods.Common.Graphics
this.scale = scale; this.scale = scale;
} }
public void Tick() { animation.Tick(); } void IActorPreview.Tick() { animation.Tick(); }
public IEnumerable<IRenderable> Render(WorldRenderer wr, WPos pos) IEnumerable<IRenderable> IActorPreview.RenderUI(WorldRenderer wr, int2 pos, float scale)
{
return animation.RenderUI(pos, offset(), zOffset(), pr, scale);
}
IEnumerable<IRenderable> IActorPreview.Render(WorldRenderer wr, WPos pos)
{ {
return animation.Render(pos, offset(), zOffset(), pr, scale); return animation.Render(pos, offset(), zOffset(), pr, scale);
} }
public IEnumerable<Rectangle> ScreenBounds(WorldRenderer wr, WPos pos) IEnumerable<Rectangle> IActorPreview.ScreenBounds(WorldRenderer wr, WPos pos)
{ {
yield return animation.ScreenBounds(wr, pos, offset(), scale); yield return animation.ScreenBounds(wr, pos, offset(), scale);
} }

View File

@@ -0,0 +1,155 @@
#region Copyright & License Information
/*
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Primitives;
namespace OpenRA.Mods.Common.Graphics
{
public struct UIModelRenderable : IRenderable
{
readonly IEnumerable<ModelAnimation> models;
readonly WPos effectiveWorldPos;
readonly int2 screenPos;
readonly int zOffset;
readonly WRot camera;
readonly WRot lightSource;
readonly float[] lightAmbientColor;
readonly float[] lightDiffuseColor;
readonly PaletteReference palette;
readonly PaletteReference normalsPalette;
readonly PaletteReference shadowPalette;
readonly float scale;
public UIModelRenderable(
IEnumerable<ModelAnimation> models, WPos effectiveWorldPos, int2 screenPos, int zOffset,
WRot camera, float scale, WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor,
PaletteReference color, PaletteReference normals, PaletteReference shadow)
{
this.models = models;
this.effectiveWorldPos = effectiveWorldPos;
this.screenPos = screenPos;
this.zOffset = zOffset;
this.scale = scale;
this.camera = camera;
this.lightSource = lightSource;
this.lightAmbientColor = lightAmbientColor;
this.lightDiffuseColor = lightDiffuseColor;
palette = color;
normalsPalette = normals;
shadowPalette = shadow;
}
public WPos Pos { get { return effectiveWorldPos; } }
public PaletteReference Palette { get { return palette; } }
public int ZOffset { get { return zOffset; } }
public bool IsDecoration { get { return false; } }
public IRenderable WithPalette(PaletteReference newPalette)
{
return new UIModelRenderable(
models, effectiveWorldPos, screenPos, zOffset, camera, scale,
lightSource, lightAmbientColor, lightDiffuseColor,
newPalette, normalsPalette, shadowPalette);
}
public IRenderable WithZOffset(int newOffset) { return this; }
public IRenderable OffsetBy(WVec vec) { return this; }
public IRenderable AsDecoration() { return this; }
static readonly float[] GroundNormal = { 0, 0, 1, 1 };
public IFinalizedRenderable PrepareRender(WorldRenderer wr)
{
return new FinalizedUIModelRenderable(wr, this);
}
struct FinalizedUIModelRenderable : IFinalizedRenderable
{
readonly UIModelRenderable model;
readonly ModelRenderProxy renderProxy;
public FinalizedUIModelRenderable(WorldRenderer wr, UIModelRenderable model)
{
this.model = model;
var draw = model.models.Where(v => v.IsVisible);
renderProxy = Game.Renderer.WorldModelRenderer.RenderAsync(
wr, draw, model.camera, model.scale, GroundNormal, model.lightSource,
model.lightAmbientColor, model.lightDiffuseColor,
model.palette, model.normalsPalette, model.shadowPalette);
}
public void Render(WorldRenderer wr)
{
var pxOrigin = model.screenPos;
var psb = renderProxy.ProjectedShadowBounds;
var sa = pxOrigin + psb[0];
var sb = pxOrigin + psb[2];
var sc = pxOrigin + psb[1];
var sd = pxOrigin + psb[3];
Game.Renderer.RgbaSpriteRenderer.DrawSprite(renderProxy.ShadowSprite, sa, sb, sc, sd);
Game.Renderer.RgbaSpriteRenderer.DrawSprite(renderProxy.Sprite, pxOrigin - 0.5f * renderProxy.Sprite.Size);
}
public void RenderDebugGeometry(WorldRenderer wr) { }
public Rectangle ScreenBounds(WorldRenderer wr)
{
return Screen3DBounds(wr).First;
}
static readonly uint[] CornerXIndex = { 0, 0, 0, 0, 3, 3, 3, 3 };
static readonly uint[] CornerYIndex = { 1, 1, 4, 4, 1, 1, 4, 4 };
static readonly uint[] CornerZIndex = { 2, 5, 2, 5, 2, 5, 2, 5 };
Pair<Rectangle, float2> Screen3DBounds(WorldRenderer wr)
{
var pxOrigin = model.screenPos;
var draw = model.models.Where(v => v.IsVisible);
var scaleTransform = OpenRA.Graphics.Util.ScaleMatrix(model.scale, model.scale, model.scale);
var cameraTransform = OpenRA.Graphics.Util.MakeFloatMatrix(model.camera.AsMatrix());
var minX = float.MaxValue;
var minY = float.MaxValue;
var minZ = float.MaxValue;
var maxX = float.MinValue;
var maxY = float.MinValue;
var maxZ = float.MinValue;
foreach (var v in draw)
{
var bounds = v.Model.Bounds(v.FrameFunc());
var worldTransform = v.RotationFunc().Reverse().Aggregate(scaleTransform,
(x, y) => OpenRA.Graphics.Util.MatrixMultiply(x, OpenRA.Graphics.Util.MakeFloatMatrix(y.AsMatrix())));
var pxPos = pxOrigin + wr.ScreenVectorComponents(v.OffsetFunc());
var screenTransform = OpenRA.Graphics.Util.MatrixMultiply(cameraTransform, worldTransform);
for (var i = 0; i < 8; i++)
{
var vec = new float[] { bounds[CornerXIndex[i]], bounds[CornerYIndex[i]], bounds[CornerZIndex[i]], 1 };
var screen = OpenRA.Graphics.Util.MatrixVectorMultiply(screenTransform, vec);
minX = Math.Min(minX, pxPos.X + screen[0]);
minY = Math.Min(minY, pxPos.Y + screen[1]);
minZ = Math.Min(minZ, pxPos.Z + screen[2]);
maxX = Math.Max(maxX, pxPos.X + screen[0]);
maxY = Math.Max(maxY, pxPos.Y + screen[1]);
maxZ = Math.Max(minZ, pxPos.Z + screen[2]);
}
}
return Pair.New(Rectangle.FromLTRB((int)minX, (int)minY, (int)maxX, (int)maxY), new float2(minZ, maxZ));
}
}
}
}

View File

@@ -72,8 +72,11 @@ namespace OpenRA.Mods.Common.Widgets
IFinalizedRenderable[] renderables; IFinalizedRenderable[] renderables;
public override void PrepareRenderables() public override void PrepareRenderables()
{ {
var scale = GetScale();
var origin = RenderOrigin + PreviewOffset + new int2(RenderBounds.Size.Width / 2, RenderBounds.Size.Height / 2);
renderables = preview renderables = preview
.SelectMany(p => p.Render(worldRenderer, WPos.Zero)) .SelectMany(p => p.RenderUI(worldRenderer, origin, scale))
.OrderBy(WorldRenderer.RenderableScreenZPositionComparisonKey) .OrderBy(WorldRenderer.RenderableScreenZPositionComparisonKey)
.Select(r => r.PrepareRender(worldRenderer)) .Select(r => r.PrepareRender(worldRenderer))
.ToArray(); .ToArray();
@@ -81,25 +84,8 @@ namespace OpenRA.Mods.Common.Widgets
public override void Draw() public override void Draw()
{ {
// HACK: The split between world and UI shaders is a giant PITA because it isn't
// feasible to maintain two parallel sets of renderables for the two cases.
// Instead, we temporarily hijack the world rendering context and set the position
// and zoom values to give the desired screen position and size.
var scale = GetScale();
var origin = RenderOrigin + new int2(RenderBounds.Size.Width / 2, RenderBounds.Size.Height / 2);
// The scale affects world -> screen transform, which we don't want when drawing the (fixed) UI.
if (scale != 1f)
origin = (1f / scale * origin.ToFloat2()).ToInt2();
Game.Renderer.Flush();
Game.Renderer.SetViewportParams(-origin - PreviewOffset, scale);
foreach (var r in renderables) foreach (var r in renderables)
r.Render(worldRenderer); r.Render(worldRenderer);
Game.Renderer.Flush();
Game.Renderer.SetViewportParams(worldRenderer.Viewport.TopLeft, worldRenderer.Viewport.Zoom);
} }
public override void Tick() public override void Tick()