Skip to content

Commit 76aba12

Browse files
committed
Add support for DrawFlat=yes in OverlayTypes, tweak rendering parameters for higher accuracy in depth rendering
1 parent 5bcc5c4 commit 76aba12

File tree

10 files changed

+161
-44
lines changed

10 files changed

+161
-44
lines changed

src/TSMapEditor/Constants.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ public static class Constants
105105
public const float DownwardsDepthRenderSpace = 0.75f;
106106

107107
// How much of the depth scale (0.0 to 1.0) is reserved for depth increasing as we go up the map height levels.
108-
public static readonly float DepthRenderStep = DepthEpsilon * 2;
108+
public static readonly float DepthRenderStep = DepthEpsilon * 3;
109109

110110
public const string ClipboardMapDataFormatValue = "ScenarioEditorCopiedMapData";
111111
public const string UserDataFolder = "UserData";

src/TSMapEditor/Content/Shaders/PalettedColorDraw.fx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
float WorldTextureHeight;
1717
bool ComplexDepth;
1818
bool IncreaseDepthUpwards;
19+
bool DecreaseDepthUpwards;
1920
bool IsShadow;
2021
bool UsePalette;
2122
bool UseRemap;
@@ -101,6 +102,10 @@ PixelShaderOutput MainPS(VertexShaderOutput input)
101102
{
102103
totalDepth = input.Position.z + ((distanceFromBottom / WorldTextureHeight) * depthMultiplier);
103104
}
105+
else if (DecreaseDepthUpwards)
106+
{
107+
totalDepth = input.Position.z - ((distanceFromBottom / WorldTextureHeight) * depthMultiplier);
108+
}
104109
else
105110
{
106111
totalDepth = input.Position.z;

src/TSMapEditor/Rendering/MapView.cs

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ public MapView(WindowManager windowManager, Map map, TheaterGraphics theaterGrap
112112
private bool minimapNeedsRefresh;
113113

114114
private List<Structure> structuresToRender = new List<Structure>();
115+
private List<Overlay> flatOverlaysToRender = new List<Overlay>();
115116
private List<GameObject> gameObjectsToRender = new List<GameObject>();
116117
private List<Smudge> smudgesToRender = new List<Smudge>();
117118
private ObjectSpriteRecord objectSpriteRecord = new ObjectSpriteRecord();
@@ -349,6 +350,7 @@ public void DrawVisibleMapPortion()
349350
refreshStopwatch.Restart();
350351

351352
smudgesToRender.Clear();
353+
flatOverlaysToRender.Clear();
352354
structuresToRender.Clear();
353355
gameObjectsToRender.Clear();
354356

@@ -367,23 +369,37 @@ public void DrawVisibleMapPortion()
367369
SetPaletteEffectParams(palettedColorDrawEffect, TheaterGraphics.TheaterPalette.GetTexture(), true, false, 1.0f);
368370
palettedColorDrawEffect.Parameters["ComplexDepth"].SetValue(false);
369371
palettedColorDrawEffect.Parameters["IncreaseDepthUpwards"].SetValue(false);
372+
palettedColorDrawEffect.Parameters["DecreaseDepthUpwards"].SetValue(true);
370373
var palettedColorDrawSettings = new SpriteBatchSettings(spriteSortMode, BlendState.Opaque, null, depthRenderStencilState, null, palettedColorDrawEffect);
371374
Renderer.PushSettings(palettedColorDrawSettings);
372375
DoForVisibleCells(DrawTerrainTileAndRegisterObjects);
373376
Renderer.PopSettings();
374377

378+
// We do not need to write to the depth render target when drawing smudges and flat overlays.
379+
// Swap to using only the main map render target.
380+
// At this point of drawing, depth testing is done on depth buffer embedded in the main map render target.
375381
Renderer.PopRenderTarget();
382+
Renderer.PushRenderTarget(mapRenderTarget);
376383

377-
// Render objects
384+
// Smudges can be drawn as part of regular terrain.
385+
DrawSmudges();
386+
387+
// Same goes for flat overlays.
388+
SetPaletteEffectParams(palettedColorDrawEffect, TheaterGraphics.TheaterPalette.GetTexture(), true, false, 1.0f);
389+
palettedColorDrawEffect.Parameters["DecreaseDepthUpwards"].SetValue(false);
390+
DrawFlatOverlays();
391+
392+
Renderer.PopRenderTarget();
393+
394+
// Render non-flat objects
378395
Renderer.PushRenderTargets(objectsRenderTarget, objectsDepthRenderTarget);
379396

380397
if (mapInvalidated)
381398
GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer | ClearOptions.Stencil, Color.Transparent, 0f, 0);
382399

383-
DrawSmudges();
384-
385400
// We need to enable this for buildings and game objects.
386401
palettedColorDrawEffect.Parameters["IncreaseDepthUpwards"].SetValue(true);
402+
palettedColorDrawEffect.Parameters["DecreaseDepthUpwards"].SetValue(false);
387403
DrawBuildings();
388404
DrawGameObjects();
389405

@@ -538,7 +554,12 @@ public void DrawTerrainTileAndRegisterObjects(MapTile tile)
538554
smudgesToRender.Add(tile.Smudge);
539555

540556
if ((EditorState.RenderObjectFlags & RenderObjectFlags.Overlay) == RenderObjectFlags.Overlay && tile.Overlay != null && tile.Overlay.OverlayType != null)
541-
AddGameObjectToRender(tile.Overlay);
557+
{
558+
if (tile.Overlay.OverlayType.DrawFlat)
559+
AddFlatOverlayToRender(tile.Overlay);
560+
else
561+
AddGameObjectToRender(tile.Overlay);
562+
}
542563

543564
if ((EditorState.RenderObjectFlags & RenderObjectFlags.Structures) == RenderObjectFlags.Structures)
544565
{
@@ -570,6 +591,14 @@ private void AddStructureToRender(Structure structure)
570591
structuresToRender.Add(structure);
571592
}
572593

594+
private void AddFlatOverlayToRender(Overlay overlay)
595+
{
596+
if (objectSpriteRecord.ProcessedObjects.Contains(overlay))
597+
return;
598+
599+
flatOverlaysToRender.Add(overlay);
600+
}
601+
573602
private void AddGameObjectToRender(GameObject gameObject)
574603
{
575604
if (objectSpriteRecord.ProcessedObjects.Contains(gameObject))
@@ -752,6 +781,19 @@ private void DrawSmudges()
752781
Renderer.PopSettings();
753782
}
754783

784+
private void DrawFlatOverlays()
785+
{
786+
flatOverlaysToRender.Sort(CompareGameObjectsForRendering);
787+
for (int i = 0; i < flatOverlaysToRender.Count; i++)
788+
{
789+
DrawObject(flatOverlaysToRender[i]);
790+
objectSpriteRecord.ProcessedObjects.Add(flatOverlaysToRender[i]);
791+
}
792+
793+
ProcessObjectSpriteRecord(false, false, true); // Do not process building shadows yet, let DrawGameObjects do it
794+
objectSpriteRecord.Clear(true);
795+
}
796+
755797
/// <summary>
756798
/// Draws buildings. Due to their large size and non-flat shape in the game world,
757799
/// buildings are rendered with different shader settings from other objects and
@@ -766,7 +808,7 @@ private void DrawBuildings()
766808
objectSpriteRecord.ProcessedObjects.Add(structuresToRender[i]);
767809
}
768810

769-
ProcessObjectSpriteRecord(true, false); // Do not process building shadows yet, let DrawGameObjects do it
811+
ProcessObjectSpriteRecord(true, false, false); // Do not process building shadows yet, let DrawGameObjects do it
770812
objectSpriteRecord.Clear(true);
771813
}
772814

@@ -783,7 +825,7 @@ private void DrawGameObjects()
783825
objectSpriteRecord.ProcessedObjects.Add(gameObjectsToRender[i]);
784826
}
785827

786-
ProcessObjectSpriteRecord(false, true);
828+
ProcessObjectSpriteRecord(false, true, false);
787829
}
788830

789831
private void DrawObject(GameObject gameObject)
@@ -822,7 +864,7 @@ private void DrawObject(GameObject gameObject)
822864
}
823865
}
824866

825-
private void ProcessObjectSpriteRecord(bool complexDepth, bool processShadows)
867+
private void ProcessObjectSpriteRecord(bool complexDepth, bool processShadows, bool alphaBlendNonPalettedSprites)
826868
{
827869
if (objectSpriteRecord.LineEntries.Count > 0)
828870
{
@@ -860,7 +902,11 @@ private void ProcessObjectSpriteRecord(bool complexDepth, bool processShadows)
860902
if (objectSpriteRecord.NonPalettedSpriteEntries.Count > 0)
861903
{
862904
SetPaletteEffectParams(palettedColorDrawEffect, null, false, false, 1.0f, false, complexDepth);
863-
Renderer.PushSettings(new SpriteBatchSettings(SpriteSortMode.Deferred, BlendState.AlphaBlend, null, objectRenderStencilState, null, palettedColorDrawEffect));
905+
Renderer.PushSettings(new SpriteBatchSettings(SpriteSortMode.Deferred,
906+
alphaBlendNonPalettedSprites ? BlendState.AlphaBlend : BlendState.Opaque,
907+
null,
908+
alphaBlendNonPalettedSprites ? depthRenderStencilState : objectRenderStencilState,
909+
null, palettedColorDrawEffect));
864910

865911
for (int i = 0; i < objectSpriteRecord.NonPalettedSpriteEntries.Count; i++)
866912
{

src/TSMapEditor/Rendering/ObjectRenderers/BuildingRenderer.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,15 +108,15 @@ protected override bool ShouldRenderReplacementText(Structure gameObject)
108108
return base.ShouldRenderReplacementText(gameObject);
109109
}
110110

111-
protected override float GetDepthFromPosition(Structure gameObject, int bottomDrawPoint)
111+
protected override float GetDepthFromPosition(Structure gameObject, Rectangle drawingBounds)
112112
{
113113
// As buildings can cover multiple cells and can also include turrets, the default implementation
114-
// is not suitable. For example, sprites can be rendered southward of voxel turrets facing north, leading them
115-
// the sprites to have higher depth and overlapping the voxel turrets.
114+
// is not suitable. For example, body sprites can be rendered southward of turrets facing north, leading
115+
// the body sprites to have higher depth and overlapping the turrets.
116116
//
117117
// Instead, for buildings we calculate a positional depth value using the southernmost cell
118118
// of the building's foundation. This depth value is identical for the base building sprite
119-
// and turret, making it easy to draw the voxel either above or below the building
119+
// and turret, making it easy to draw the turret either above or below the building
120120
// by applying DepthEpsilon.
121121
var southernmostCell = GetSouthernmostCell(gameObject);
122122

@@ -126,7 +126,7 @@ protected override float GetDepthFromPosition(Structure gameObject, int bottomDr
126126
height = southernmostCell.Level;
127127
}
128128

129-
return ((CellMath.CellTopLeftPointFromCellCoords(southernmostCell.CoordsToPoint(), Map).Y + Constants.CellSizeY * 2) / (float)Map.HeightInPixelsWithCellHeight) * Constants.DownwardsDepthRenderSpace +
129+
return ((CellMath.CellTopLeftPointFromCellCoords(southernmostCell.CoordsToPoint(), Map).Y + Constants.CellSizeY) / (float)Map.HeightInPixelsWithCellHeight) * Constants.DownwardsDepthRenderSpace +
130130
(height * Constants.DepthRenderStep);
131131
}
132132

src/TSMapEditor/Rendering/ObjectRenderers/ObjectDepthAdjustments.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ public static class ObjectDepthAdjustments
77
public const int Building = 2;
88
public const int BuildingFoundationLines = 2;
99
public const int Infantry = 2;
10-
public const int Overlay = 0;
10+
public const int Overlay = 1;
1111
public const int Terrain = 2;
1212
public const int Vehicle = 1;
1313
}

src/TSMapEditor/Rendering/ObjectRenderers/ObjectRenderer.cs

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ public void Draw(T gameObject, bool checkInCamera)
5252
return;
5353
}
5454

55+
InitDrawForObject(gameObject);
56+
5557
if (frame == null && ShouldRenderReplacementText(gameObject))
5658
{
5759
DrawText(gameObject, false);
@@ -94,12 +96,12 @@ public virtual Point2D GetDrawPoint(T gameObject)
9496
return drawPoint;
9597
}
9698

97-
public virtual void DrawNonRemap(T gameObject, Point2D drawPoint)
99+
public virtual void InitDrawForObject(T gameObject)
98100
{
99101
// Do nothing by default
100102
}
101103

102-
public virtual void DrawRemap(T gameObject, Point2D drawPoint)
104+
public virtual void DrawNonRemap(T gameObject, Point2D drawPoint)
103105
{
104106
// Do nothing by default
105107
}
@@ -267,6 +269,9 @@ public virtual void DrawShadow(T gameObject)
267269
if (drawParams.ShapeImage == null)
268270
return;
269271

272+
if (drawParams.ShapeImage.IsPNG)
273+
return;
274+
270275
int shadowFrameIndex = gameObject.GetShadowFrameIndex(drawParams.ShapeImage.GetFrameCount());
271276
if (shadowFrameIndex < 0 && shadowFrameIndex >= drawParams.ShapeImage.GetFrameCount())
272277
return;
@@ -284,30 +289,38 @@ public virtual void DrawShadow(T gameObject)
284289

285290
float textureHeight = (regularFrame != null && regularFrame.Texture != null) ? (float)regularFrame.Texture.Height : shadowFrame.Texture.Height;
286291

287-
float depth = GetDepthFromPosition(gameObject, drawingBounds.Bottom);
292+
float depth = GetDepthFromPosition(gameObject, drawingBounds);
288293
// depth += GetDepthAddition(gameObject);
289294
depth += textureHeight / Map.HeightInPixelsWithCellHeight;
290295

291296
RenderDependencies.ObjectSpriteRecord.AddGraphicsEntry(new ObjectSpriteEntry(null, texture, drawingBounds, new Color(255, 255, 255, 128), false, true, depth));
292297
}
293298

294-
protected virtual float GetDepthFromPosition(T gameObject, int bottomDrawPoint)
299+
protected virtual float GetDepthFromPosition(T gameObject, Rectangle drawingBounds)
295300
{
296-
// Calculate position-related depth from the bottom draw point of the object.
297-
// Snap the texture height to the southernmost pixel coordinate of the southernmost
298-
// cell that this object's texture overlaps.
299-
var cell = Map.GetTile(gameObject.Position);
300-
int cellY = CellMath.CellTopLeftPointFromCellCoords(gameObject.Position, Map).Y;
301-
int dy = bottomDrawPoint - cellY;
302-
int extraHeightForSnap = dy % Constants.CellHeight;
301+
// Calculate position-related depth from the southernmost edge of the cell of the southernmost texture coordinate of the object.
302+
var cellPixelCoords = CellMath.CellTopLeftPointFromCellCoords(gameObject.Position, Map);
303+
int dy = drawingBounds.Bottom - cellPixelCoords.Y;
304+
int wholeCells = dy / Constants.CellSizeY;
305+
int fraction = dy % Constants.CellSizeY;
306+
int cellY = cellPixelCoords.Y + (wholeCells + 1) * Constants.CellSizeY;
307+
308+
if (fraction > (Constants.CellSizeY * 3) / 2 &&
309+
(drawingBounds.X < cellPixelCoords.X || drawingBounds.Right > cellPixelCoords.X + Constants.CellSizeX))
310+
{
311+
// This object leaks into the neighbouring cells - to another "isometric row"
312+
cellY += Constants.CellSizeY / 2;
313+
}
303314

315+
// Use height from the cell where the object has been placed.
316+
var heightLookupCell = Map.GetTile(gameObject.Position);
304317
int height = 0;
305-
if (cell != null)
318+
if (heightLookupCell != null)
306319
{
307-
height = cell.Level;
320+
height = heightLookupCell.Level;
308321
}
309322

310-
return ((bottomDrawPoint + (height * Constants.CellHeight) + extraHeightForSnap) / (float)Map.HeightInPixelsWithCellHeight) * Constants.DownwardsDepthRenderSpace +
323+
return ((cellY + (height * Constants.CellHeight)) / (float)Map.HeightInPixelsWithCellHeight) * Constants.DownwardsDepthRenderSpace +
311324
(height * Constants.DepthRenderStep);
312325
}
313326

@@ -420,7 +433,7 @@ protected void DrawShapeImage(T gameObject, ShapeImage image, int frameIndex, Co
420433
}
421434
}
422435

423-
depthAddition += GetDepthFromPosition(gameObject, drawingBounds.Bottom);
436+
depthAddition += GetDepthFromPosition(gameObject, drawingBounds);
424437
depthAddition += GetDepthAddition(gameObject);
425438

426439
RenderFrame(gameObject, frame, remapFrame, color, drawRemap, remapColor,
@@ -470,7 +483,7 @@ protected void DrawVoxelModel(T gameObject, VoxelModel model, byte facing, RampT
470483

471484
Rectangle drawingBounds = GetTextureDrawCoords(gameObject, frame, drawPoint);
472485

473-
depthAddition += GetDepthFromPosition(gameObject, drawingBounds.Bottom);
486+
depthAddition += GetDepthFromPosition(gameObject, drawingBounds);
474487
depthAddition += GetDepthAddition(gameObject);
475488

476489
remapColor = ScaleColorToAmbient(remapColor, mapCell.CellLighting);

src/TSMapEditor/Rendering/ObjectRenderers/OverlayRenderer.cs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
namespace TSMapEditor.Rendering.ObjectRenderers
66
{
7-
public class OverlayRenderer : ObjectRenderer<Overlay>
7+
public sealed class OverlayRenderer : ObjectRenderer<Overlay>
88
{
99
public OverlayRenderer(RenderDependencies renderDependencies) : base(renderDependencies)
1010
{
@@ -21,6 +21,34 @@ protected override CommonDrawParams GetDrawParams(Overlay gameObject)
2121
};
2222
}
2323

24+
protected override float GetDepthFromPosition(Overlay gameObject, Rectangle drawingBounds)
25+
{
26+
// Calculate position-related depth from the southernmost edge of the cell of the southernmost texture coordinate of the object.
27+
var cellPixelCoords = CellMath.CellTopLeftPointFromCellCoords(gameObject.Position, Map);
28+
int dy = drawingBounds.Bottom - cellPixelCoords.Y;
29+
int wholeCells = dy / Constants.CellSizeY;
30+
int fraction = dy % Constants.CellSizeY;
31+
int cellY = cellPixelCoords.Y + (wholeCells + 1) * Constants.CellSizeY;
32+
33+
if (fraction > (Constants.CellSizeY * 3) / 2 &&
34+
(drawingBounds.X < cellPixelCoords.X || drawingBounds.Right > cellPixelCoords.X + Constants.CellSizeX))
35+
{
36+
// This object leaks into the neighbouring cells - to another "isometric row"
37+
cellY += Constants.CellSizeY / 2;
38+
}
39+
40+
// Use height from the cell where the object has been placed.
41+
var heightLookupCell = Map.GetTile(gameObject.Position);
42+
int height = 0;
43+
if (heightLookupCell != null)
44+
{
45+
height = heightLookupCell.Level;
46+
}
47+
48+
return ((cellY + (height * Constants.CellHeight)) / (float)Map.HeightInPixelsWithCellHeight) * Constants.DownwardsDepthRenderSpace +
49+
(height * Constants.DepthRenderStep);
50+
}
51+
2452
protected override float GetDepthAddition(Overlay gameObject)
2553
{
2654
if (gameObject.OverlayType.HighBridgeDirection == BridgeDirection.None)

src/TSMapEditor/Rendering/ObjectRenderers/TerrainRenderer.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,23 @@ protected override CommonDrawParams GetDrawParams(TerrainObject gameObject)
2121
};
2222
}
2323

24+
protected override float GetDepthFromPosition(TerrainObject gameObject, Rectangle drawingBounds)
25+
{
26+
// Terrain objects are not meant to graphically leak to cells below themselves,
27+
// unless specifically configured to (which is handled in GetSouthernmostCell).
28+
// We override this method to prevent the default behaviour.
29+
var southernmostCell = GetSouthernmostCell(gameObject);
30+
31+
int height = 0;
32+
if (southernmostCell != null)
33+
{
34+
height = southernmostCell.Level;
35+
}
36+
37+
return ((CellMath.CellTopLeftPointFromCellCoords(southernmostCell.CoordsToPoint(), Map).Y + Constants.CellSizeY) / (float)Map.HeightInPixelsWithCellHeight) * Constants.DownwardsDepthRenderSpace +
38+
(height * Constants.DepthRenderStep);
39+
}
40+
2441
protected override float GetDepthAddition(TerrainObject gameObject)
2542
{
2643
return Constants.DepthEpsilon * ObjectDepthAdjustments.Terrain;

0 commit comments

Comments
 (0)