Skip to content

Commit

Permalink
Add support for DrawFlat=yes in OverlayTypes, tweak rendering paramet…
Browse files Browse the repository at this point in the history
…ers for higher accuracy in depth rendering
  • Loading branch information
Rampastring committed Nov 23, 2024
1 parent 5bcc5c4 commit 76aba12
Show file tree
Hide file tree
Showing 10 changed files with 161 additions and 44 deletions.
2 changes: 1 addition & 1 deletion src/TSMapEditor/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public static class Constants
public const float DownwardsDepthRenderSpace = 0.75f;

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

public const string ClipboardMapDataFormatValue = "ScenarioEditorCopiedMapData";
public const string UserDataFolder = "UserData";
Expand Down
5 changes: 5 additions & 0 deletions src/TSMapEditor/Content/Shaders/PalettedColorDraw.fx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
float WorldTextureHeight;
bool ComplexDepth;
bool IncreaseDepthUpwards;
bool DecreaseDepthUpwards;
bool IsShadow;
bool UsePalette;
bool UseRemap;
Expand Down Expand Up @@ -101,6 +102,10 @@ PixelShaderOutput MainPS(VertexShaderOutput input)
{
totalDepth = input.Position.z + ((distanceFromBottom / WorldTextureHeight) * depthMultiplier);
}
else if (DecreaseDepthUpwards)
{
totalDepth = input.Position.z - ((distanceFromBottom / WorldTextureHeight) * depthMultiplier);
}
else
{
totalDepth = input.Position.z;
Expand Down
62 changes: 54 additions & 8 deletions src/TSMapEditor/Rendering/MapView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ public MapView(WindowManager windowManager, Map map, TheaterGraphics theaterGrap
private bool minimapNeedsRefresh;

private List<Structure> structuresToRender = new List<Structure>();
private List<Overlay> flatOverlaysToRender = new List<Overlay>();
private List<GameObject> gameObjectsToRender = new List<GameObject>();
private List<Smudge> smudgesToRender = new List<Smudge>();
private ObjectSpriteRecord objectSpriteRecord = new ObjectSpriteRecord();
Expand Down Expand Up @@ -349,6 +350,7 @@ public void DrawVisibleMapPortion()
refreshStopwatch.Restart();

smudgesToRender.Clear();
flatOverlaysToRender.Clear();
structuresToRender.Clear();
gameObjectsToRender.Clear();

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

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

// Render objects
// Smudges can be drawn as part of regular terrain.
DrawSmudges();

// Same goes for flat overlays.
SetPaletteEffectParams(palettedColorDrawEffect, TheaterGraphics.TheaterPalette.GetTexture(), true, false, 1.0f);
palettedColorDrawEffect.Parameters["DecreaseDepthUpwards"].SetValue(false);
DrawFlatOverlays();

Renderer.PopRenderTarget();

// Render non-flat objects
Renderer.PushRenderTargets(objectsRenderTarget, objectsDepthRenderTarget);

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

DrawSmudges();

// We need to enable this for buildings and game objects.
palettedColorDrawEffect.Parameters["IncreaseDepthUpwards"].SetValue(true);
palettedColorDrawEffect.Parameters["DecreaseDepthUpwards"].SetValue(false);
DrawBuildings();
DrawGameObjects();

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

if ((EditorState.RenderObjectFlags & RenderObjectFlags.Overlay) == RenderObjectFlags.Overlay && tile.Overlay != null && tile.Overlay.OverlayType != null)
AddGameObjectToRender(tile.Overlay);
{
if (tile.Overlay.OverlayType.DrawFlat)
AddFlatOverlayToRender(tile.Overlay);
else
AddGameObjectToRender(tile.Overlay);
}

if ((EditorState.RenderObjectFlags & RenderObjectFlags.Structures) == RenderObjectFlags.Structures)
{
Expand Down Expand Up @@ -570,6 +591,14 @@ private void AddStructureToRender(Structure structure)
structuresToRender.Add(structure);
}

private void AddFlatOverlayToRender(Overlay overlay)
{
if (objectSpriteRecord.ProcessedObjects.Contains(overlay))
return;

flatOverlaysToRender.Add(overlay);
}

private void AddGameObjectToRender(GameObject gameObject)
{
if (objectSpriteRecord.ProcessedObjects.Contains(gameObject))
Expand Down Expand Up @@ -752,6 +781,19 @@ private void DrawSmudges()
Renderer.PopSettings();
}

private void DrawFlatOverlays()
{
flatOverlaysToRender.Sort(CompareGameObjectsForRendering);
for (int i = 0; i < flatOverlaysToRender.Count; i++)
{
DrawObject(flatOverlaysToRender[i]);
objectSpriteRecord.ProcessedObjects.Add(flatOverlaysToRender[i]);
}

ProcessObjectSpriteRecord(false, false, true); // Do not process building shadows yet, let DrawGameObjects do it
objectSpriteRecord.Clear(true);
}

/// <summary>
/// Draws buildings. Due to their large size and non-flat shape in the game world,
/// buildings are rendered with different shader settings from other objects and
Expand All @@ -766,7 +808,7 @@ private void DrawBuildings()
objectSpriteRecord.ProcessedObjects.Add(structuresToRender[i]);
}

ProcessObjectSpriteRecord(true, false); // Do not process building shadows yet, let DrawGameObjects do it
ProcessObjectSpriteRecord(true, false, false); // Do not process building shadows yet, let DrawGameObjects do it
objectSpriteRecord.Clear(true);
}

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

ProcessObjectSpriteRecord(false, true);
ProcessObjectSpriteRecord(false, true, false);
}

private void DrawObject(GameObject gameObject)
Expand Down Expand Up @@ -822,7 +864,7 @@ private void DrawObject(GameObject gameObject)
}
}

private void ProcessObjectSpriteRecord(bool complexDepth, bool processShadows)
private void ProcessObjectSpriteRecord(bool complexDepth, bool processShadows, bool alphaBlendNonPalettedSprites)
{
if (objectSpriteRecord.LineEntries.Count > 0)
{
Expand Down Expand Up @@ -860,7 +902,11 @@ private void ProcessObjectSpriteRecord(bool complexDepth, bool processShadows)
if (objectSpriteRecord.NonPalettedSpriteEntries.Count > 0)
{
SetPaletteEffectParams(palettedColorDrawEffect, null, false, false, 1.0f, false, complexDepth);
Renderer.PushSettings(new SpriteBatchSettings(SpriteSortMode.Deferred, BlendState.AlphaBlend, null, objectRenderStencilState, null, palettedColorDrawEffect));
Renderer.PushSettings(new SpriteBatchSettings(SpriteSortMode.Deferred,
alphaBlendNonPalettedSprites ? BlendState.AlphaBlend : BlendState.Opaque,
null,
alphaBlendNonPalettedSprites ? depthRenderStencilState : objectRenderStencilState,
null, palettedColorDrawEffect));

for (int i = 0; i < objectSpriteRecord.NonPalettedSpriteEntries.Count; i++)
{
Expand Down
10 changes: 5 additions & 5 deletions src/TSMapEditor/Rendering/ObjectRenderers/BuildingRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,15 +108,15 @@ protected override bool ShouldRenderReplacementText(Structure gameObject)
return base.ShouldRenderReplacementText(gameObject);
}

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

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

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public static class ObjectDepthAdjustments
public const int Building = 2;
public const int BuildingFoundationLines = 2;
public const int Infantry = 2;
public const int Overlay = 0;
public const int Overlay = 1;
public const int Terrain = 2;
public const int Vehicle = 1;
}
Expand Down
45 changes: 29 additions & 16 deletions src/TSMapEditor/Rendering/ObjectRenderers/ObjectRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ public void Draw(T gameObject, bool checkInCamera)
return;
}

InitDrawForObject(gameObject);

if (frame == null && ShouldRenderReplacementText(gameObject))
{
DrawText(gameObject, false);
Expand Down Expand Up @@ -94,12 +96,12 @@ public virtual Point2D GetDrawPoint(T gameObject)
return drawPoint;
}

public virtual void DrawNonRemap(T gameObject, Point2D drawPoint)
public virtual void InitDrawForObject(T gameObject)
{
// Do nothing by default
}

public virtual void DrawRemap(T gameObject, Point2D drawPoint)
public virtual void DrawNonRemap(T gameObject, Point2D drawPoint)
{
// Do nothing by default
}
Expand Down Expand Up @@ -267,6 +269,9 @@ public virtual void DrawShadow(T gameObject)
if (drawParams.ShapeImage == null)
return;

if (drawParams.ShapeImage.IsPNG)
return;

int shadowFrameIndex = gameObject.GetShadowFrameIndex(drawParams.ShapeImage.GetFrameCount());
if (shadowFrameIndex < 0 && shadowFrameIndex >= drawParams.ShapeImage.GetFrameCount())
return;
Expand All @@ -284,30 +289,38 @@ public virtual void DrawShadow(T gameObject)

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

float depth = GetDepthFromPosition(gameObject, drawingBounds.Bottom);
float depth = GetDepthFromPosition(gameObject, drawingBounds);
// depth += GetDepthAddition(gameObject);
depth += textureHeight / Map.HeightInPixelsWithCellHeight;

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

protected virtual float GetDepthFromPosition(T gameObject, int bottomDrawPoint)
protected virtual float GetDepthFromPosition(T gameObject, Rectangle drawingBounds)
{
// Calculate position-related depth from the bottom draw point of the object.
// Snap the texture height to the southernmost pixel coordinate of the southernmost
// cell that this object's texture overlaps.
var cell = Map.GetTile(gameObject.Position);
int cellY = CellMath.CellTopLeftPointFromCellCoords(gameObject.Position, Map).Y;
int dy = bottomDrawPoint - cellY;
int extraHeightForSnap = dy % Constants.CellHeight;
// Calculate position-related depth from the southernmost edge of the cell of the southernmost texture coordinate of the object.
var cellPixelCoords = CellMath.CellTopLeftPointFromCellCoords(gameObject.Position, Map);
int dy = drawingBounds.Bottom - cellPixelCoords.Y;
int wholeCells = dy / Constants.CellSizeY;
int fraction = dy % Constants.CellSizeY;
int cellY = cellPixelCoords.Y + (wholeCells + 1) * Constants.CellSizeY;

if (fraction > (Constants.CellSizeY * 3) / 2 &&
(drawingBounds.X < cellPixelCoords.X || drawingBounds.Right > cellPixelCoords.X + Constants.CellSizeX))
{
// This object leaks into the neighbouring cells - to another "isometric row"
cellY += Constants.CellSizeY / 2;
}

// Use height from the cell where the object has been placed.
var heightLookupCell = Map.GetTile(gameObject.Position);
int height = 0;
if (cell != null)
if (heightLookupCell != null)
{
height = cell.Level;
height = heightLookupCell.Level;
}

return ((bottomDrawPoint + (height * Constants.CellHeight) + extraHeightForSnap) / (float)Map.HeightInPixelsWithCellHeight) * Constants.DownwardsDepthRenderSpace +
return ((cellY + (height * Constants.CellHeight)) / (float)Map.HeightInPixelsWithCellHeight) * Constants.DownwardsDepthRenderSpace +
(height * Constants.DepthRenderStep);
}

Expand Down Expand Up @@ -420,7 +433,7 @@ protected void DrawShapeImage(T gameObject, ShapeImage image, int frameIndex, Co
}
}

depthAddition += GetDepthFromPosition(gameObject, drawingBounds.Bottom);
depthAddition += GetDepthFromPosition(gameObject, drawingBounds);
depthAddition += GetDepthAddition(gameObject);

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

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

depthAddition += GetDepthFromPosition(gameObject, drawingBounds.Bottom);
depthAddition += GetDepthFromPosition(gameObject, drawingBounds);
depthAddition += GetDepthAddition(gameObject);

remapColor = ScaleColorToAmbient(remapColor, mapCell.CellLighting);
Expand Down
30 changes: 29 additions & 1 deletion src/TSMapEditor/Rendering/ObjectRenderers/OverlayRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace TSMapEditor.Rendering.ObjectRenderers
{
public class OverlayRenderer : ObjectRenderer<Overlay>
public sealed class OverlayRenderer : ObjectRenderer<Overlay>
{
public OverlayRenderer(RenderDependencies renderDependencies) : base(renderDependencies)
{
Expand All @@ -21,6 +21,34 @@ protected override CommonDrawParams GetDrawParams(Overlay gameObject)
};
}

protected override float GetDepthFromPosition(Overlay gameObject, Rectangle drawingBounds)
{
// Calculate position-related depth from the southernmost edge of the cell of the southernmost texture coordinate of the object.
var cellPixelCoords = CellMath.CellTopLeftPointFromCellCoords(gameObject.Position, Map);
int dy = drawingBounds.Bottom - cellPixelCoords.Y;
int wholeCells = dy / Constants.CellSizeY;
int fraction = dy % Constants.CellSizeY;
int cellY = cellPixelCoords.Y + (wholeCells + 1) * Constants.CellSizeY;

if (fraction > (Constants.CellSizeY * 3) / 2 &&
(drawingBounds.X < cellPixelCoords.X || drawingBounds.Right > cellPixelCoords.X + Constants.CellSizeX))
{
// This object leaks into the neighbouring cells - to another "isometric row"
cellY += Constants.CellSizeY / 2;
}

// Use height from the cell where the object has been placed.
var heightLookupCell = Map.GetTile(gameObject.Position);
int height = 0;
if (heightLookupCell != null)
{
height = heightLookupCell.Level;
}

return ((cellY + (height * Constants.CellHeight)) / (float)Map.HeightInPixelsWithCellHeight) * Constants.DownwardsDepthRenderSpace +
(height * Constants.DepthRenderStep);
}

protected override float GetDepthAddition(Overlay gameObject)
{
if (gameObject.OverlayType.HighBridgeDirection == BridgeDirection.None)
Expand Down
17 changes: 17 additions & 0 deletions src/TSMapEditor/Rendering/ObjectRenderers/TerrainRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,23 @@ protected override CommonDrawParams GetDrawParams(TerrainObject gameObject)
};
}

protected override float GetDepthFromPosition(TerrainObject gameObject, Rectangle drawingBounds)
{
// Terrain objects are not meant to graphically leak to cells below themselves,
// unless specifically configured to (which is handled in GetSouthernmostCell).
// We override this method to prevent the default behaviour.
var southernmostCell = GetSouthernmostCell(gameObject);

int height = 0;
if (southernmostCell != null)
{
height = southernmostCell.Level;
}

return ((CellMath.CellTopLeftPointFromCellCoords(southernmostCell.CoordsToPoint(), Map).Y + Constants.CellSizeY) / (float)Map.HeightInPixelsWithCellHeight) * Constants.DownwardsDepthRenderSpace +
(height * Constants.DepthRenderStep);
}

protected override float GetDepthAddition(TerrainObject gameObject)
{
return Constants.DepthEpsilon * ObjectDepthAdjustments.Terrain;
Expand Down
Loading

0 comments on commit 76aba12

Please sign in to comment.