Add a visualization layer for renderable geometry.
This commit is contained in:
@@ -41,6 +41,7 @@ namespace OpenRA.Graphics
|
|||||||
IRenderable WithZOffset(int newOffset);
|
IRenderable WithZOffset(int newOffset);
|
||||||
IRenderable WithPos(WPos pos);
|
IRenderable WithPos(WPos pos);
|
||||||
void Render(WorldRenderer wr);
|
void Render(WorldRenderer wr);
|
||||||
|
void RenderDebugGeometry(WorldRenderer wr);
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct SpriteRenderable : IRenderable
|
public struct SpriteRenderable : IRenderable
|
||||||
@@ -83,5 +84,11 @@ namespace OpenRA.Graphics
|
|||||||
{
|
{
|
||||||
sprite.DrawAt(wr.ScreenPxPosition(pos) - pxCenter, palette.Index, scale);
|
sprite.DrawAt(wr.ScreenPxPosition(pos) - pxCenter, palette.Index, scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RenderDebugGeometry(WorldRenderer wr)
|
||||||
|
{
|
||||||
|
var offset = wr.ScreenPxPosition(pos) - pxCenter;
|
||||||
|
Game.Renderer.WorldLineRenderer.DrawRect(offset, offset + sprite.size, Color.Red);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,5 +102,131 @@ namespace OpenRA.Graphics
|
|||||||
v.Voxel.Draw(vr, lightAmbientColor, lightDiffuseColor, palette.Index, normalsPalette.Index);
|
v.Voxel.Draw(vr, lightAmbientColor, lightDiffuseColor, palette.Index, normalsPalette.Index);
|
||||||
Game.Renderer.DisableDepthBuffer();
|
Game.Renderer.DisableDepthBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RenderDebugGeometry(WorldRenderer wr)
|
||||||
|
{
|
||||||
|
var draw = voxels.Where(v => v.DisableFunc == null || !v.DisableFunc());
|
||||||
|
var scaleTransform = Util.ScaleMatrix(scale, scale, scale);
|
||||||
|
var pxOrigin = wr.ScreenPosition(pos);
|
||||||
|
|
||||||
|
// Correct for bogus light source definition
|
||||||
|
var shadowTransform = Util.MakeFloatMatrix(new WRot(new WAngle(256) - lightSource.Pitch,
|
||||||
|
WAngle.Zero, lightSource.Yaw + new WAngle(512)).AsMatrix());
|
||||||
|
|
||||||
|
var invShadowTransform = Util.MatrixInverse(shadowTransform);
|
||||||
|
var cameraTransform = Util.MakeFloatMatrix(camera.AsMatrix());
|
||||||
|
|
||||||
|
// TODO: Generalize this once we support sloped terrain
|
||||||
|
var groundNormal = new float[] {0,0,1,1};
|
||||||
|
var groundPos = new float[] {0, 0, 0.5f*(wr.ScreenPosition(pos).Y - wr.ScreenZPosition(pos, 0)), 1};
|
||||||
|
var shadowGroundNormal = Util.MatrixVectorMultiply(shadowTransform, groundNormal);
|
||||||
|
var shadowGroundPos = Util.MatrixVectorMultiply(shadowTransform, groundPos);
|
||||||
|
|
||||||
|
// Sprite rectangle
|
||||||
|
var tl = new float2(float.MaxValue, float.MaxValue);
|
||||||
|
var br = new float2(float.MinValue, float.MinValue);
|
||||||
|
|
||||||
|
// Shadow sprite rectangle
|
||||||
|
var stl = new float2(float.MaxValue, float.MaxValue);
|
||||||
|
var sbr = new float2(float.MinValue, float.MinValue);
|
||||||
|
|
||||||
|
foreach (var v in draw)
|
||||||
|
{
|
||||||
|
var bounds = v.Voxel.Bounds(v.FrameFunc());
|
||||||
|
var worldTransform = v.RotationFunc().Reverse().Aggregate(scaleTransform,
|
||||||
|
(x,y) => Util.MatrixMultiply(x, Util.MakeFloatMatrix(y.AsMatrix())));
|
||||||
|
|
||||||
|
var worldBounds = Util.MatrixAABBMultiply(worldTransform, bounds);
|
||||||
|
var screenBounds = Util.MatrixAABBMultiply(cameraTransform, worldBounds);
|
||||||
|
|
||||||
|
// Aggregate bounds rect
|
||||||
|
var pxOffset = wr.ScreenVector(v.OffsetFunc());
|
||||||
|
var pxPos = pxOrigin + new float2(pxOffset[0], pxOffset[1]);
|
||||||
|
tl = float2.Min(tl, pxPos + new float2(screenBounds[0], screenBounds[1]));
|
||||||
|
br = float2.Max(br, pxPos + new float2(screenBounds[3], screenBounds[4]));
|
||||||
|
|
||||||
|
// Box to render the shadow image from
|
||||||
|
var shadowBounds = Util.MatrixAABBMultiply(shadowTransform, worldBounds);
|
||||||
|
var shadowPxOffset = Util.MatrixVectorMultiply(shadowTransform, pxOffset);
|
||||||
|
|
||||||
|
stl = float2.Min(stl, new float2(shadowPxOffset[0] + shadowBounds[0], shadowPxOffset[1] + shadowBounds[1]));
|
||||||
|
sbr = float2.Max(sbr, new float2(shadowPxOffset[0] + shadowBounds[3], shadowPxOffset[1] + shadowBounds[4]));
|
||||||
|
|
||||||
|
// Draw voxel bounding box
|
||||||
|
var screenTransform = Util.MatrixMultiply(cameraTransform, worldTransform);
|
||||||
|
DrawBoundsBox(pxPos, screenTransform, bounds, Color.Yellow);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inflate rects by 1px each side to ensure rendering is within bounds
|
||||||
|
var pad = new float2(1,1);
|
||||||
|
tl -= pad;
|
||||||
|
br += pad;
|
||||||
|
stl -= pad;
|
||||||
|
sbr += pad;
|
||||||
|
|
||||||
|
// Corners of the shadow quad, in shadow-space
|
||||||
|
var corners = new float[][]
|
||||||
|
{
|
||||||
|
new float[] {stl.X, stl.Y, 0, 1},
|
||||||
|
new float[] {sbr.X, sbr.Y, 0, 1},
|
||||||
|
new float[] {sbr.X, stl.Y, 0, 1},
|
||||||
|
new float[] {stl.X, sbr.Y, 0, 1}
|
||||||
|
};
|
||||||
|
|
||||||
|
var shadowScreenTransform = Util.MatrixMultiply(cameraTransform, invShadowTransform);
|
||||||
|
var screenCorners = new float2[4];
|
||||||
|
for (var j = 0; j < 4; j++)
|
||||||
|
{
|
||||||
|
// Project to ground plane
|
||||||
|
corners[j][2] -= (corners[j][2] - shadowGroundPos[2]) +
|
||||||
|
(corners[j][1] - shadowGroundPos[1])*shadowGroundNormal[1]/shadowGroundNormal[2] +
|
||||||
|
(corners[j][0] - shadowGroundPos[0])*shadowGroundNormal[0]/shadowGroundNormal[2];
|
||||||
|
|
||||||
|
// Rotate to camera-space
|
||||||
|
corners[j] = Util.MatrixVectorMultiply(shadowScreenTransform, corners[j]);
|
||||||
|
screenCorners[j] = pxOrigin + new float2(corners[j][0], corners[j][1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw transformed shadow sprite rect
|
||||||
|
var c = Color.Purple;
|
||||||
|
Game.Renderer.WorldLineRenderer.DrawLine(screenCorners[1], screenCorners[3], c, c);
|
||||||
|
Game.Renderer.WorldLineRenderer.DrawLine(screenCorners[3], screenCorners[0], c, c);
|
||||||
|
Game.Renderer.WorldLineRenderer.DrawLine(screenCorners[0], screenCorners[2], c, c);
|
||||||
|
Game.Renderer.WorldLineRenderer.DrawLine(screenCorners[2], screenCorners[1], c, c);
|
||||||
|
|
||||||
|
// Draw sprite rect
|
||||||
|
Game.Renderer.WorldLineRenderer.DrawRect(tl, br, Color.Red);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DrawBoundsBox(float2 pxPos, float[] transform, float[] bounds, Color c)
|
||||||
|
{
|
||||||
|
// Corner offsets
|
||||||
|
var ix = new uint[] {0,0,0,0,3,3,3,3};
|
||||||
|
var iy = new uint[] {1,1,4,4,1,1,4,4};
|
||||||
|
var iz = new uint[] {2,5,2,5,2,5,2,5};
|
||||||
|
|
||||||
|
var corners = new float2[8];
|
||||||
|
for (var i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
var vec = new float[] {bounds[ix[i]], bounds[iy[i]], bounds[iz[i]], 1};
|
||||||
|
var screen = Util.MatrixVectorMultiply(transform, vec);
|
||||||
|
corners[i] = pxPos + new float2(screen[0], screen[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Game.Renderer.WorldLineRenderer.DrawLine(corners[0], corners[1], c, c);
|
||||||
|
Game.Renderer.WorldLineRenderer.DrawLine(corners[1], corners[3], c, c);
|
||||||
|
Game.Renderer.WorldLineRenderer.DrawLine(corners[3], corners[2], c, c);
|
||||||
|
Game.Renderer.WorldLineRenderer.DrawLine(corners[2], corners[0], c, c);
|
||||||
|
|
||||||
|
Game.Renderer.WorldLineRenderer.DrawLine(corners[4], corners[5], c, c);
|
||||||
|
Game.Renderer.WorldLineRenderer.DrawLine(corners[5], corners[7], c, c);
|
||||||
|
Game.Renderer.WorldLineRenderer.DrawLine(corners[7], corners[6], c, c);
|
||||||
|
Game.Renderer.WorldLineRenderer.DrawLine(corners[6], corners[4], c, c);
|
||||||
|
|
||||||
|
Game.Renderer.WorldLineRenderer.DrawLine(corners[0], corners[4], c, c);
|
||||||
|
Game.Renderer.WorldLineRenderer.DrawLine(corners[1], corners[5], c, c);
|
||||||
|
Game.Renderer.WorldLineRenderer.DrawLine(corners[2], corners[6], c, c);
|
||||||
|
Game.Renderer.WorldLineRenderer.DrawLine(corners[3], corners[7], c, c);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ namespace OpenRA.Graphics
|
|||||||
internal readonly ShroudRenderer shroudRenderer;
|
internal readonly ShroudRenderer shroudRenderer;
|
||||||
internal readonly HardwarePalette palette;
|
internal readonly HardwarePalette palette;
|
||||||
internal Cache<string, PaletteReference> palettes;
|
internal Cache<string, PaletteReference> palettes;
|
||||||
|
Lazy<DeveloperMode> devTrait;
|
||||||
|
|
||||||
internal WorldRenderer(World world)
|
internal WorldRenderer(World world)
|
||||||
{
|
{
|
||||||
@@ -51,6 +52,8 @@ namespace OpenRA.Graphics
|
|||||||
|
|
||||||
terrainRenderer = new TerrainRenderer(world, this);
|
terrainRenderer = new TerrainRenderer(world, this);
|
||||||
shroudRenderer = new ShroudRenderer(world);
|
shroudRenderer = new ShroudRenderer(world);
|
||||||
|
|
||||||
|
devTrait = Lazy.New(() => world.LocalPlayer != null ? world.LocalPlayer.PlayerActor.Trait<DeveloperMode>() : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
PaletteReference CreatePaletteReference(string name)
|
PaletteReference CreatePaletteReference(string name)
|
||||||
@@ -74,14 +77,21 @@ namespace OpenRA.Graphics
|
|||||||
bounds.TopLeftAsCPos().ToPPos(),
|
bounds.TopLeftAsCPos().ToPPos(),
|
||||||
bounds.BottomRightAsCPos().ToPPos());
|
bounds.BottomRightAsCPos().ToPPos());
|
||||||
|
|
||||||
actors.SelectMany(a => a.Render(this))
|
var worldRenderables = actors.SelectMany(a => a.Render(this))
|
||||||
.OrderBy(r => r, comparer)
|
.OrderBy(r => r, comparer);
|
||||||
.Do(rr => rr.Render(this));
|
|
||||||
|
|
||||||
// Effects are drawn on top of all actors
|
// Effects are drawn on top of all actors
|
||||||
|
var effectRenderables = world.Effects.SelectMany(e => e.Render(this));
|
||||||
|
|
||||||
// TODO: Allow effects to be interleaved with actors
|
// TODO: Allow effects to be interleaved with actors
|
||||||
world.Effects.SelectMany(e => e.Render(this))
|
worldRenderables.Do(rr => rr.Render(this));
|
||||||
.Do(rr => rr.Render(this));
|
effectRenderables.Do(rr => rr.Render(this));
|
||||||
|
|
||||||
|
if (devTrait.Value != null && devTrait.Value.ShowDebugGeometry)
|
||||||
|
{
|
||||||
|
worldRenderables.Do(rr => rr.RenderDebugGeometry(this));
|
||||||
|
effectRenderables.Do(rr => rr.RenderDebugGeometry(this));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Draw()
|
public void Draw()
|
||||||
@@ -219,6 +229,13 @@ namespace OpenRA.Graphics
|
|||||||
return new int2((int)Math.Round(px.X), (int)Math.Round(px.Y));
|
return new int2((int)Math.Round(px.X), (int)Math.Round(px.Y));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For scaling vectors to pixel sizes in the voxel renderer
|
||||||
|
public float[] ScreenVector(WVec vec)
|
||||||
|
{
|
||||||
|
var c = Game.CellSize/1024f;
|
||||||
|
return new float[] {c*vec.X, c*vec.Y, c*vec.Z, 1};
|
||||||
|
}
|
||||||
|
|
||||||
public float ScreenZPosition(WPos pos, int zOffset) { return (pos.Y + pos.Z + zOffset)*Game.CellSize/1024f; }
|
public float ScreenZPosition(WPos pos, int zOffset) { return (pos.Y + pos.Z + zOffset)*Game.CellSize/1024f; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ namespace OpenRA.Traits
|
|||||||
public bool UnlimitedPower;
|
public bool UnlimitedPower;
|
||||||
public bool BuildAnywhere;
|
public bool BuildAnywhere;
|
||||||
public bool ShowMuzzles;
|
public bool ShowMuzzles;
|
||||||
|
public bool ShowDebugGeometry;
|
||||||
|
|
||||||
public object Create (ActorInitializer init) { return new DeveloperMode(this); }
|
public object Create (ActorInitializer init) { return new DeveloperMode(this); }
|
||||||
}
|
}
|
||||||
@@ -39,6 +40,7 @@ namespace OpenRA.Traits
|
|||||||
|
|
||||||
// Client size only
|
// Client size only
|
||||||
public bool ShowMuzzles;
|
public bool ShowMuzzles;
|
||||||
|
public bool ShowDebugGeometry;
|
||||||
|
|
||||||
public DeveloperMode(DeveloperModeInfo info)
|
public DeveloperMode(DeveloperModeInfo info)
|
||||||
{
|
{
|
||||||
@@ -50,6 +52,7 @@ namespace OpenRA.Traits
|
|||||||
UnlimitedPower = info.UnlimitedPower;
|
UnlimitedPower = info.UnlimitedPower;
|
||||||
BuildAnywhere = info.BuildAnywhere;
|
BuildAnywhere = info.BuildAnywhere;
|
||||||
ShowMuzzles = info.ShowMuzzles;
|
ShowMuzzles = info.ShowMuzzles;
|
||||||
|
ShowDebugGeometry = info.ShowDebugGeometry;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResolveOrder (Actor self, Order order)
|
public void ResolveOrder (Actor self, Order order)
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ using System.Drawing;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OpenRA;
|
using OpenRA;
|
||||||
|
using OpenRA.Graphics;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
using OpenRA.Widgets;
|
using OpenRA.Widgets;
|
||||||
using XRandom = OpenRA.Thirdparty.Random;
|
using XRandom = OpenRA.Thirdparty.Random;
|
||||||
@@ -68,6 +69,13 @@ namespace OpenRA.Mods.RA.Widgets.Logic
|
|||||||
showMuzzlesCheckbox.OnClick = () => devTrait.ShowMuzzles ^= true;
|
showMuzzlesCheckbox.OnClick = () => devTrait.ShowMuzzles ^= true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var showGeometryCheckbox = widget.GetOrNull<CheckboxWidget>("SHOW_GEOMETRY");
|
||||||
|
if (showGeometryCheckbox != null)
|
||||||
|
{
|
||||||
|
showGeometryCheckbox.IsChecked = () => devTrait.ShowDebugGeometry;
|
||||||
|
showGeometryCheckbox.OnClick = () => devTrait.ShowDebugGeometry ^= true;
|
||||||
|
}
|
||||||
|
|
||||||
var allTechCheckbox = widget.GetOrNull<CheckboxWidget>("ENABLE_TECH");
|
var allTechCheckbox = widget.GetOrNull<CheckboxWidget>("ENABLE_TECH");
|
||||||
if (allTechCheckbox != null)
|
if (allTechCheckbox != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -98,4 +98,10 @@ Container@CHEATS_PANEL:
|
|||||||
Y:235
|
Y:235
|
||||||
Height:20
|
Height:20
|
||||||
Width:200
|
Width:200
|
||||||
Text:Show Muzzle Positions
|
Text:Show Muzzle Positions
|
||||||
|
Checkbox@SHOW_GEOMETRY:
|
||||||
|
X:290
|
||||||
|
Y:265
|
||||||
|
Height:20
|
||||||
|
Width:200
|
||||||
|
Text:Show Render Geometry
|
||||||
@@ -3,7 +3,7 @@ Background@CHEATS_PANEL:
|
|||||||
X:(WINDOW_RIGHT - WIDTH)/2
|
X:(WINDOW_RIGHT - WIDTH)/2
|
||||||
Y:(WINDOW_BOTTOM - HEIGHT)/2
|
Y:(WINDOW_BOTTOM - HEIGHT)/2
|
||||||
Width:350
|
Width:350
|
||||||
Height:475
|
Height:505
|
||||||
Visible:false
|
Visible:false
|
||||||
Children:
|
Children:
|
||||||
Label@LABEL_TITLE:
|
Label@LABEL_TITLE:
|
||||||
@@ -87,21 +87,27 @@ Background@CHEATS_PANEL:
|
|||||||
Height:20
|
Height:20
|
||||||
Width:200
|
Width:200
|
||||||
Text:Show Muzzle Positions
|
Text:Show Muzzle Positions
|
||||||
|
Checkbox@SHOW_GEOMETRY:
|
||||||
|
X:30
|
||||||
|
Y:380
|
||||||
|
Height:20
|
||||||
|
Width:200
|
||||||
|
Text:Show Render Geometry
|
||||||
Checkbox@DESYNC_ARMED:
|
Checkbox@DESYNC_ARMED:
|
||||||
X:30
|
X:30
|
||||||
Y:382
|
Y:412
|
||||||
Width:20
|
Width:20
|
||||||
Height:20
|
Height:20
|
||||||
Button@DESYNC:
|
Button@DESYNC:
|
||||||
X:60
|
X:60
|
||||||
Y:380
|
Y:410
|
||||||
Width:150
|
Width:150
|
||||||
Height:20
|
Height:20
|
||||||
Text: Force Desync
|
Text: Force Desync
|
||||||
Height:25
|
Height:25
|
||||||
Button@CLOSE:
|
Button@CLOSE:
|
||||||
X:30
|
X:30
|
||||||
Y:420
|
Y:450
|
||||||
Width:PARENT_RIGHT - 60
|
Width:PARENT_RIGHT - 60
|
||||||
Height:25
|
Height:25
|
||||||
Text:Close
|
Text:Close
|
||||||
|
|||||||
Reference in New Issue
Block a user