forked from OpenRA/OpenRA
-
Notifications
You must be signed in to change notification settings - Fork 1
/
TerrainSpriteLayer.cs
145 lines (117 loc) · 4.18 KB
/
TerrainSpriteLayer.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#region Copyright & License Information
/*
* Copyright 2007-2020 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.IO;
using OpenRA.Primitives;
namespace OpenRA.Graphics
{
public sealed class TerrainSpriteLayer : IDisposable
{
public readonly Sheet Sheet;
public readonly BlendMode BlendMode;
readonly Sprite emptySprite;
readonly IVertexBuffer<Vertex> vertexBuffer;
readonly Vertex[] vertices;
readonly HashSet<int> dirtyRows = new HashSet<int>();
readonly int rowStride;
readonly bool restrictToBounds;
readonly WorldRenderer worldRenderer;
readonly Map map;
readonly PaletteReference palette;
public TerrainSpriteLayer(World world, WorldRenderer wr, Sheet sheet, BlendMode blendMode, PaletteReference palette, bool restrictToBounds)
{
worldRenderer = wr;
this.restrictToBounds = restrictToBounds;
Sheet = sheet;
BlendMode = blendMode;
this.palette = palette;
map = world.Map;
rowStride = 6 * map.MapSize.X;
vertices = new Vertex[rowStride * map.MapSize.Y];
vertexBuffer = Game.Renderer.Context.CreateVertexBuffer(vertices.Length);
emptySprite = new Sprite(sheet, Rectangle.Empty, TextureChannel.Alpha);
wr.PaletteInvalidated += UpdatePaletteIndices;
}
void UpdatePaletteIndices()
{
// Everything in the layer uses the same palette,
// so we can fix the indices in one pass
for (var i = 0; i < vertices.Length; i++)
{
var v = vertices[i];
vertices[i] = new Vertex(v.X, v.Y, v.Z, v.S, v.T, v.U, v.V, palette.TextureIndex, v.C);
}
for (var row = 0; row < map.MapSize.Y; row++)
dirtyRows.Add(row);
}
public void Update(CPos cell, Sprite sprite)
{
var xyz = float3.Zero;
if (sprite != null)
{
var cellOrigin = map.CenterOfCell(cell) - new WVec(0, 0, map.Grid.Ramps[map.Ramp[cell]].CenterHeightOffset);
xyz = worldRenderer.Screen3DPosition(cellOrigin) + sprite.Offset - 0.5f * sprite.Size;
}
Update(cell.ToMPos(map.Grid.Type), sprite, xyz);
}
public void Update(MPos uv, Sprite sprite, float3 pos)
{
if (sprite != null)
{
if (sprite.Sheet != Sheet)
throw new InvalidDataException("Attempted to add sprite from a different sheet");
if (sprite.BlendMode != BlendMode)
throw new InvalidDataException("Attempted to add sprite with a different blend mode");
}
else
sprite = emptySprite;
// The vertex buffer does not have geometry for cells outside the map
if (!map.Tiles.Contains(uv))
return;
var offset = rowStride * uv.V + 6 * uv.U;
Util.FastCreateQuad(vertices, pos, sprite, int2.Zero, palette.TextureIndex, offset, sprite.Size);
dirtyRows.Add(uv.V);
}
public void Draw(Viewport viewport)
{
var cells = restrictToBounds ? viewport.VisibleCellsInsideBounds : viewport.AllVisibleCells;
// Only draw the rows that are visible.
var firstRow = cells.CandidateMapCoords.TopLeft.V.Clamp(0, map.MapSize.Y);
var lastRow = (cells.CandidateMapCoords.BottomRight.V + 1).Clamp(firstRow, map.MapSize.Y);
Game.Renderer.Flush();
// Flush any visible changes to the GPU
for (var row = firstRow; row <= lastRow; row++)
{
if (!dirtyRows.Remove(row))
continue;
var rowOffset = rowStride * row;
unsafe
{
// The compiler / language spec won't let us calculate a pointer to
// an offset inside a generic array T[], and so we are forced to
// calculate the start-of-row pointer here to pass in to SetData.
fixed (Vertex* vPtr = &vertices[0])
vertexBuffer.SetData((IntPtr)(vPtr + rowOffset), rowOffset, rowStride);
}
}
Game.Renderer.WorldSpriteRenderer.DrawVertexBuffer(
vertexBuffer, rowStride * firstRow, rowStride * (lastRow - firstRow),
PrimitiveType.TriangleList, Sheet, BlendMode);
Game.Renderer.Flush();
}
public void Dispose()
{
worldRenderer.PaletteInvalidated -= UpdatePaletteIndices;
vertexBuffer.Dispose();
}
}
}