-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTerrain.cs
More file actions
176 lines (145 loc) · 4.99 KB
/
Copy pathTerrain.cs
File metadata and controls
176 lines (145 loc) · 4.99 KB
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
namespace RustMapViewer;
public sealed class HeightMap
{
public const float TerrainHeight = 1000f;
public const float TerrainOffsetY = -500f;
private readonly short[] _data;
public readonly int Res;
public readonly float WorldSize;
private readonly float _half;
public HeightMap(byte[] raw, float worldSize)
{
Res = (int)Math.Sqrt(raw.Length / 2.0);
if (Res * Res * 2 != raw.Length)
{
throw new InvalidDataException($"Unexpected terrain map length {raw.Length}");
}
_data = new short[Res * Res];
Buffer.BlockCopy(raw, 0, _data, 0, raw.Length);
WorldSize = worldSize;
_half = worldSize * 0.5f;
}
public float GetHeightIdx(int x, int z) => _data[z * Res + x] * (1f / 32766f) * TerrainHeight + TerrainOffsetY;
public float GetHeight(float worldX, float worldZ)
{
float gx = Math.Clamp((worldX + _half) / WorldSize, 0f, 1f) * (Res - 1);
float gz = Math.Clamp((worldZ + _half) / WorldSize, 0f, 1f) * (Res - 1);
int x0 = (int)gx;
int z0 = (int)gz;
int x1 = Math.Min(x0 + 1, Res - 1);
int z1 = Math.Min(z0 + 1, Res - 1);
float fx = gx - x0;
float fz = gz - z0;
float h00 = GetHeightIdx(x0, z0);
float h10 = GetHeightIdx(x1, z0);
float h01 = GetHeightIdx(x0, z1);
float h11 = GetHeightIdx(x1, z1);
return float.Lerp(float.Lerp(h00, h10, fx), float.Lerp(h01, h11, fx), fz);
}
public float TexelSize => WorldSize / (Res - 1);
public (float worldX, float worldZ) IdxToWorld(int x, int z) => (x * TexelSize - _half, z * TexelSize - _half);
public byte[] RawBytes()
{
byte[] bytes = new byte[_data.Length * 2];
Buffer.BlockCopy(_data, 0, bytes, 0, bytes.Length);
return bytes;
}
public HeightMap Clone() => new(RawBytes(), WorldSize);
public void RaiseIdx(int x, int z, float worldY)
{
short encoded = (short)Math.Clamp((worldY - TerrainOffsetY) / TerrainHeight * 32766f, short.MinValue, short.MaxValue);
int index = z * Res + x;
if (encoded > _data[index])
{
_data[index] = encoded;
}
}
public int WorldToIdxX(float worldX) => Math.Clamp((int)((worldX + _half) / WorldSize * (Res - 1) + 0.5f), 0, Res - 1);
public int WorldToIdxZ(float worldZ) => Math.Clamp((int)((worldZ + _half) / WorldSize * (Res - 1) + 0.5f), 0, Res - 1);
}
public sealed class TopologyMap
{
private readonly int[] _data;
public readonly int Res;
private readonly float _worldSize;
private readonly float _half;
public TopologyMap(byte[] raw, float worldSize)
{
Res = (int)Math.Sqrt(raw.Length / 4.0);
if (Res * Res * 4 != raw.Length)
{
throw new InvalidDataException($"Unexpected topology map length {raw.Length}");
}
_data = new int[Res * Res];
Buffer.BlockCopy(raw, 0, _data, 0, raw.Length);
_worldSize = worldSize;
_half = worldSize * 0.5f;
}
public int GetTopologyIdx(int x, int z) => _data[z * Res + x];
public int GetTopology(float worldX, float worldZ)
{
int x = Math.Clamp((int)((worldX + _half) / _worldSize * Res), 0, Res - 1);
int z = Math.Clamp((int)((worldZ + _half) / _worldSize * Res), 0, Res - 1);
return _data[z * Res + x];
}
}
public sealed class SplatMap
{
public const int Channels = 8;
private readonly byte[] _data;
public readonly int Res;
private readonly float _worldSize;
private readonly float _half;
public SplatMap(byte[] raw, float worldSize)
{
Res = (int)Math.Sqrt(raw.Length / (double)Channels);
if (Res * Res * Channels != raw.Length)
{
throw new InvalidDataException($"Unexpected splat map length {raw.Length}");
}
_data = raw;
_worldSize = worldSize;
_half = worldSize * 0.5f;
}
public float GetSplat(float worldX, float worldZ, int channel)
{
float gx = Math.Clamp((worldX + _half) / _worldSize, 0f, 1f) * (Res - 1);
float gz = Math.Clamp((worldZ + _half) / _worldSize, 0f, 1f) * (Res - 1);
int x0 = (int)gx;
int z0 = (int)gz;
int x1 = Math.Min(x0 + 1, Res - 1);
int z1 = Math.Min(z0 + 1, Res - 1);
float fx = gx - x0;
float fz = gz - z0;
int plane = channel * Res;
float v00 = _data[(plane + z0) * Res + x0];
float v10 = _data[(plane + z0) * Res + x1];
float v01 = _data[(plane + z1) * Res + x0];
float v11 = _data[(plane + z1) * Res + x1];
return float.Lerp(float.Lerp(v00, v10, fx), float.Lerp(v01, v11, fx), fz) * (1f / 255f);
}
public byte Raw(int channel, int x, int z) => _data[(channel * Res + z) * Res + x];
}
public static class Topo
{
public const int Field = 1;
public const int Cliff = 2;
public const int Summit = 4;
public const int Ocean = 0x80;
public const int Monument = 0x400;
public const int Road = 0x800;
public const int Roadside = 0x1000;
public const int Swamp = 0x2000;
public const int River = 0x4000;
public const int Riverside = 0x8000;
public const int Lake = 0x10000;
public const int Lakeside = 0x20000;
public const int Offshore = 0x40000;
public const int Rail = 0x80000;
public const int Railside = 0x100000;
public const int Building = 0x200000;
public const int Cliffside = 0x400000;
public const int Mountain = 0x800000;
public const int Water = Ocean | River | Lake | Offshore;
public const int WaterSide = Riverside | Lakeside | Swamp;
}