Skip to content

Commit cc52d18

Browse files
committed
Fix edge cases where depth was incorrect, use bottom of object's cell as reference instead of top of the cell
1 parent 7419e2c commit cc52d18

File tree

6 files changed

+66
-56
lines changed

6 files changed

+66
-56
lines changed

src/TSMapEditor/GameMath/CellMath.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ namespace TSMapEditor.GameMath
88
/// </summary>
99
public static class CellMath
1010
{
11+
public static int CellBottomPointFromCellCoords(Point2D cellCoords, Map map)
12+
{
13+
int cy = (cellCoords.X - 1) * (Constants.CellSizeY / 2);
14+
int diff = map.Size.X - cellCoords.Y;
15+
cy -= diff * (Constants.CellSizeY / 2);
16+
return cy + Constants.MapYBaseline + Constants.CellSizeY;
17+
}
18+
1119
public static Point2D CellTopLeftPointFromCellCoords_NoBaseline(Point2D cellCoords, Map map)
1220
{
1321
int cx = (cellCoords.X - 1) * (Constants.CellSizeX / 2);

src/TSMapEditor/Rendering/MapView.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ public void RefreshRenderTargets()
291291
{
292292
ClearRenderTargets();
293293

294-
mapRenderTarget = CreateFullMapRenderTarget(SurfaceFormat.Color, DepthFormat.Depth24);
294+
mapRenderTarget = CreateFullMapRenderTarget(SurfaceFormat.Color, DepthFormat.Depth24Stencil8);
295295
transparencyRenderTarget = CreateFullMapRenderTarget(SurfaceFormat.Color);
296296
transparencyPerFrameRenderTarget = CreateFullMapRenderTarget(SurfaceFormat.Color);
297297
compositeRenderTarget = CreateFullMapRenderTarget(SurfaceFormat.Color, DepthFormat.Depth24);
@@ -347,7 +347,7 @@ private void CreateDepthStencilStates()
347347
{
348348
DepthBufferEnable = true,
349349
DepthBufferWriteEnable = true,
350-
DepthBufferFunction = CompareFunction.GreaterEqual,
350+
DepthBufferFunction = CompareFunction.Greater,
351351
StencilEnable = true,
352352
StencilFail = StencilOperation.Keep,
353353
StencilPass = StencilOperation.Replace,
@@ -1031,7 +1031,7 @@ private void ProcessObjectSpriteRecord(bool noDepthWriting, bool processShadows,
10311031
if (objectSpriteRecord.LineEntries.Count > 0)
10321032
{
10331033
SetPaletteEffectParams(palettedColorDrawEffect, null, false, false, false);
1034-
Renderer.PushSettings(new SpriteBatchSettings(SpriteSortMode.Deferred, BlendState.Opaque, null, noDepthWriting ? depthReadStencilState : objectRenderStencilState, null, palettedColorDrawEffect));
1034+
Renderer.PushSettings(new SpriteBatchSettings(SpriteSortMode.Deferred, BlendState.AlphaBlend, null, noDepthWriting ? depthReadStencilState : objectRenderStencilState, null, palettedColorDrawEffect));
10351035

10361036
for (int i = 0; i < objectSpriteRecord.LineEntries.Count; i++)
10371037
{
@@ -1081,15 +1081,15 @@ private void ProcessObjectSpriteRecord(bool noDepthWriting, bool processShadows,
10811081
if (processShadows && objectSpriteRecord.ShadowEntries.Count > 0)
10821082
{
10831083
SetPaletteEffectParams(palettedColorDrawEffect, null, false, false, true);
1084-
gameObjectBatcher.Begin(palettedColorDrawEffect, noDepthWriting ? depthReadStencilState : objectRenderStencilState);
1085-
GraphicsDevice.BlendState = BlendState.AlphaBlend;
1084+
gameObjectBatcher.Begin(palettedColorDrawEffect, shadowRenderStencilState);
1085+
// GraphicsDevice.BlendState = BlendState.AlphaBlend;
10861086

10871087
for (int i = 0; i < objectSpriteRecord.ShadowEntries.Count; i++)
10881088
{
10891089
var spriteEntry = objectSpriteRecord.ShadowEntries[i];
10901090

1091-
// It doesn't really matter what we give as color to the shadow. Shadows also have no use for the custom data
1092-
gameObjectBatcher.Draw(spriteEntry.Texture, spriteEntry.DrawingBounds, spriteEntry.SourceRectangle, new Color(1.0f, 1.0f, 1.0f, 0f),
1091+
// It doesn't really matter what we give as color to the shadow
1092+
gameObjectBatcher.Draw(spriteEntry.Texture, spriteEntry.DrawingBounds, spriteEntry.SourceRectangle, Color.White,
10931093
spriteEntry.DepthRectangle);
10941094
}
10951095

@@ -1099,7 +1099,7 @@ private void ProcessObjectSpriteRecord(bool noDepthWriting, bool processShadows,
10991099
if (objectSpriteRecord.TextEntries.Count > 0)
11001100
{
11011101
SetPaletteEffectParams(palettedColorDrawEffect, null, false, false, false);
1102-
Renderer.PushSettings(new SpriteBatchSettings(SpriteSortMode.Deferred, BlendState.Opaque, null, depthRenderStencilState, null, palettedColorDrawEffect));
1102+
Renderer.PushSettings(new SpriteBatchSettings(SpriteSortMode.Deferred, BlendState.AlphaBlend, null, depthRenderStencilState, null, palettedColorDrawEffect));
11031103

11041104
for (int i = 0; i < objectSpriteRecord.TextEntries.Count; i++)
11051105
{

src/TSMapEditor/Rendering/ObjectRenderers/BuildingRenderer.cs

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,10 @@ public BuildingRenderer(RenderDependencies renderDependencies) : base(renderDepe
1717

1818
private AnimRenderer buildingAnimRenderer;
1919

20-
Rectangle lowestDrawRectangle;
2120
DepthRectangle cachedDepth;
2221

2322
public override void InitDrawForObject(Structure gameObject)
2423
{
25-
lowestDrawRectangle = Rectangle.Empty;
2624
cachedDepth = new DepthRectangle(-1f, -1f);
2725
}
2826

@@ -135,29 +133,49 @@ private DepthRectangle GetDepthForAnimation(Structure gameObject, Rectangle draw
135133
bottom += heightReferenceCell.Level * Constants.CellHeight;
136134
}
137135

138-
int yReference = CellMath.CellTopLeftPointFromCellCoords(southernmostFoundationCellCoords, Map).Y;
136+
int yReference = CellMath.CellBottomPointFromCellCoords(southernmostFoundationCellCoords, Map);
139137

140138
float topDepth = CellMath.GetDepthForPixelInCube(y, 0, yReference, heightReferenceCell, Map);
141139
float bottomDepth = CellMath.GetDepthForPixelInCube(bottom, 0, yReference, heightReferenceCell, Map);
142140

143141
return new DepthRectangle(topDepth, bottomDepth);
144142
}
145143

144+
protected override DepthRectangle GetShadowDepthFromPosition(Structure gameObject, Rectangle drawingBounds)
145+
{
146+
// The default behaviour for shadows is to call GetDepthFromPosition,
147+
// but the adjusted behaviour of GetDepthFromPosition intended for the rendering
148+
// of turrets and other on-top-of-the-building objects results in shadows
149+
// being too close to the "camera".
150+
151+
// This implementation fixes the issue by calculating depth from the building's
152+
// lowest pixel (at the building's base), not from its highest pixel.
153+
154+
var southernmostFoundationCellCoords = gameObject.GetSouthernmostFoundationCell();
155+
var heightReferenceCell = Map.GetTile(gameObject.Position);
156+
157+
// drawingBounds includes effect of height, which is undesirable for depth rendering
158+
int bottom = drawingBounds.Bottom;
159+
160+
if (heightReferenceCell != null && !RenderDependencies.EditorState.Is2DMode)
161+
{
162+
bottom += heightReferenceCell.Level * Constants.CellHeight;
163+
}
164+
165+
int yReference = CellMath.CellBottomPointFromCellCoords(southernmostFoundationCellCoords, Map);
166+
167+
float depthAtBottom = CellMath.GetDepthForPixelInCube(bottom, 0, yReference, heightReferenceCell, Map);
168+
return new DepthRectangle(depthAtBottom, depthAtBottom);
169+
}
170+
146171
protected override DepthRectangle GetDepthFromPosition(Structure gameObject, Rectangle drawingBounds)
147172
{
148-
// Because buildings can include turrets, the default implementation
173+
// Because buildings have a customized depth implementation and can layer several
174+
// sprites on top of each other, the default implementation
149175
// is not suitable. For example, bodies can be larger than turrets,
150176
// leading the bodies to have higher depth and overlapping turrets.
151177
//
152-
// To fix this, we normalize everything to use the maximum depth based on the frame
153-
// that is drawn southernmost.
154-
if (lowestDrawRectangle.Bottom >= drawingBounds.Bottom)
155-
{
156-
return cachedDepth;
157-
}
158-
159-
float foundationCenterXPoint = GetFoundationCenterXPoint(gameObject);
160-
int distRight = (int)(drawingBounds.Width * (1.0f - foundationCenterXPoint));
178+
// To fix this, we normalize everything to use the maximum depth.
161179

162180
var southernmostFoundationCellCoords = gameObject.GetSouthernmostFoundationCell();
163181
var heightReferenceCell = Map.GetTile(gameObject.Position);
@@ -170,12 +188,15 @@ protected override DepthRectangle GetDepthFromPosition(Structure gameObject, Rec
170188
y += heightReferenceCell.Level * Constants.CellHeight;
171189
}
172190

173-
int yReference = CellMath.CellTopLeftPointFromCellCoords(southernmostFoundationCellCoords, Map).Y;
191+
int yReference = CellMath.CellBottomPointFromCellCoords(southernmostFoundationCellCoords, Map);
174192

175193
// Used for drawing turrets and stuff, just return maximum depth since they must be on top of the building
176194
float maxDepth = CellMath.GetDepthForPixelInCube(y, 0, yReference, heightReferenceCell, Map);
195+
196+
if (maxDepth < cachedDepth.TopLeft)
197+
return cachedDepth;
198+
177199
cachedDepth = new DepthRectangle(maxDepth, maxDepth);
178-
lowestDrawRectangle = drawingBounds;
179200

180201
return cachedDepth;
181202
}
@@ -198,7 +219,7 @@ protected override DepthRectangle GetDepthFromPosition(Structure gameObject, Rec
198219
bottom += heightReferenceCell.Level * Constants.CellHeight;
199220
}
200221

201-
int yReference = CellMath.CellTopLeftPointFromCellCoords(southernmostFoundationCellCoords, Map).Y;
222+
int yReference = CellMath.CellBottomPointFromCellCoords(southernmostFoundationCellCoords, Map);
202223

203224
float depthTopLeft = CellMath.GetDepthForPixelInCube(y, distLeft, yReference, heightReferenceCell, Map);
204225
float depthTopRight = CellMath.GetDepthForPixelInCube(y, 0, yReference, heightReferenceCell, Map);
@@ -234,7 +255,7 @@ protected override DepthRectangle GetDepthFromPosition(Structure gameObject, Rec
234255
bottom += heightReferenceCell.Level * Constants.CellHeight;
235256
}
236257

237-
int yReference = CellMath.CellTopLeftPointFromCellCoords(southernmostFoundationCellCoords, Map).Y;
258+
int yReference = CellMath.CellBottomPointFromCellCoords(southernmostFoundationCellCoords, Map);
238259

239260
float depthTopLeft = CellMath.GetDepthForPixelInCube(y, 0, yReference, heightReferenceCell, Map);
240261
float depthTopRight = CellMath.GetDepthForPixelInCube(y, distRight, yReference, heightReferenceCell, Map);
@@ -504,11 +525,11 @@ private void DrawVoxelTurret(Structure gameObject, Point2D drawPoint, in CommonD
504525
{
505526
DrawVoxelModel(gameObject, drawParams.TurretVoxel,
506527
gameObject.Facing, RampType.None, nonRemapColor, true, gameObject.GetRemapColor(),
507-
affectedByLighting, turretDrawPoint, Constants.DepthEpsilon * 2);
528+
affectedByLighting, turretDrawPoint, Constants.DepthEpsilon);
508529

509530
DrawVoxelModel(gameObject, drawParams.BarrelVoxel,
510531
gameObject.Facing, RampType.None, nonRemapColor, true, gameObject.GetRemapColor(),
511-
affectedByLighting, turretDrawPoint, Constants.DepthEpsilon * 3);
532+
affectedByLighting, turretDrawPoint, Constants.DepthEpsilon * 3); // appears to need a 3x multiplier due to float imprecision
512533
}
513534
else
514535
{

src/TSMapEditor/Rendering/ObjectRenderers/ObjectDepthAdjustments.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ public static class ObjectDepthAdjustments
1010
public const int Overlay = 150;
1111
public const int Terrain = 300;
1212
public const int Vehicle = 150;
13+
public const int Shadow = 0;
1314
}
1415
}

src/TSMapEditor/Rendering/ObjectRenderers/ObjectRenderer.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -308,10 +308,10 @@ public virtual void DrawShadow(T gameObject)
308308

309309
Rectangle drawingBounds = GetTextureDrawCoords(gameObject, shadowFrame, drawPoint);
310310

311-
float textureHeight = (regularFrame != null && regularFrame.Texture != null) ? (float)regularFrame.SourceRectangle.Height : shadowFrame.SourceRectangle.Height;
311+
float textureHeight = (regularFrame != null && regularFrame.Texture != null) ? regularFrame.SourceRectangle.Height : shadowFrame.SourceRectangle.Height;
312312

313313
DepthRectangle depth = GetShadowDepthFromPosition(gameObject, drawingBounds);
314-
depth += GetDepthAddition(gameObject);
314+
depth += Constants.DepthEpsilon * ObjectDepthAdjustments.Shadow;
315315

316316
RenderDependencies.ObjectSpriteRecord.AddGraphicsEntry(new ObjectSpriteEntry(null, shadowFrame, drawingBounds, new Color(255, 255, 255, 128), false, true, depth));
317317
}
@@ -322,7 +322,7 @@ protected virtual DepthRectangle GetDepthFromPosition(T gameObject, Rectangle dr
322322
var cell = Map.GetTile(gameObject.Position);
323323
int y = drawingBounds.Y;
324324
int bottom = drawingBounds.Bottom;
325-
int yReference = CellMath.CellTopLeftPointFromCellCoords(gameObject.Position, Map).Y;
325+
int yReference = CellMath.CellBottomPointFromCellCoords(gameObject.Position, Map);
326326
if (cell != null && !RenderDependencies.EditorState.Is2DMode)
327327
{
328328
y += cell.Level * Constants.CellHeight;

src/TSMapEditor/Rendering/ObjectRenderers/UnitRenderer.cs

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -35,24 +35,6 @@ public override void InitDrawForObject(Unit gameObject)
3535
cachedDepth = new DepthRectangle(-1f, -1f);
3636
}
3737

38-
protected override DepthRectangle GetDepthFromPosition(Unit gameObject, Rectangle drawingBounds)
39-
{
40-
// Because vehicles can include turrets, the default implementation
41-
// is not suitable. For example, bodies can be rendered southward of turrets
42-
// facing north, leading the bodies to have higher depth and overlapping turrets.
43-
//
44-
// To fix this, we normalize everything to use the maximum depth based on the frame
45-
// that is drawn southernmost.
46-
if (lowestDrawRectangle.Bottom >= drawingBounds.Bottom)
47-
{
48-
return cachedDepth;
49-
}
50-
51-
lowestDrawRectangle = drawingBounds;
52-
cachedDepth = base.GetDepthFromPosition(gameObject, drawingBounds);
53-
return cachedDepth;
54-
}
55-
5638
protected override float GetDepthAddition(Unit gameObject)
5739
{
5840
if (gameObject.High)
@@ -73,10 +55,9 @@ protected override void Render(Unit gameObject, Point2D drawPoint, in CommonDraw
7355
{
7456
bool affectedByLighting = RenderDependencies.EditorState.IsLighting;
7557

76-
// We need to calculate depth earlier so it can also be used for potential turrets
7758
if (gameObject.UnitType.ArtConfig.Voxel)
7859
{
79-
RenderVoxelModel(gameObject, drawPoint, drawParams.MainVoxel, affectedByLighting, 0, true);
60+
RenderVoxelModel(gameObject, drawPoint, drawParams.MainVoxel, affectedByLighting, 0);
8061
}
8162
else
8263
{
@@ -103,18 +84,18 @@ protected override void Render(Unit gameObject, Point2D drawPoint, in CommonDraw
10384
if (gameObject.Facing is > facingStartDrawAbove and <= facingEndDrawAbove)
10485
{
10586
if (gameObject.UnitType.ArtConfig.Voxel)
106-
RenderVoxelModel(gameObject, drawPoint + turretOffset, drawParams.TurretVoxel, affectedByLighting, Constants.DepthEpsilon, true);
87+
RenderVoxelModel(gameObject, drawPoint + turretOffset, drawParams.TurretVoxel, affectedByLighting, Constants.DepthEpsilon);
10788
else
10889
RenderTurretShape(gameObject, drawPoint, drawParams, Constants.DepthEpsilon);
10990

110-
RenderVoxelModel(gameObject, drawPoint + turretOffset, drawParams.BarrelVoxel, affectedByLighting, Constants.DepthEpsilon * 2, true);
91+
RenderVoxelModel(gameObject, drawPoint + turretOffset, drawParams.BarrelVoxel, affectedByLighting, Constants.DepthEpsilon * 2);
11192
}
11293
else
11394
{
114-
RenderVoxelModel(gameObject, drawPoint + turretOffset, drawParams.BarrelVoxel, affectedByLighting, Constants.DepthEpsilon, true);
95+
RenderVoxelModel(gameObject, drawPoint + turretOffset, drawParams.BarrelVoxel, affectedByLighting, Constants.DepthEpsilon);
11596

11697
if (gameObject.UnitType.ArtConfig.Voxel)
117-
RenderVoxelModel(gameObject, drawPoint + turretOffset, drawParams.TurretVoxel, affectedByLighting, Constants.DepthEpsilon * 2, true);
98+
RenderVoxelModel(gameObject, drawPoint + turretOffset, drawParams.TurretVoxel, affectedByLighting, Constants.DepthEpsilon * 2);
11899
else
119100
RenderTurretShape(gameObject, drawPoint, drawParams, Constants.DepthEpsilon * 2);
120101
}
@@ -151,8 +132,7 @@ private void RenderTurretShape(Unit gameObject, Point2D drawPoint,
151132
}
152133

153134
private void RenderVoxelModel(Unit gameObject, Point2D drawPoint,
154-
VoxelModel model, bool affectedByLighting, float depthAddition,
155-
bool compensateForBottomGap)
135+
VoxelModel model, bool affectedByLighting, float depthAddition)
156136
{
157137
var unitTile = Map.GetTile(gameObject.Position.X, gameObject.Position.Y);
158138

0 commit comments

Comments
 (0)