Merge pull request #9476 from pchote/depthbuffer

Make the TS terrain depth data available for use by shaders.
This commit is contained in:
abcdefg30
2015-10-17 23:45:31 +02:00
30 changed files with 274 additions and 39 deletions

View File

@@ -106,7 +106,7 @@ namespace OpenRA.Graphics
sheet = cachedSheets[mi.Src];
else
{
sheet = new Sheet(mi.Src);
sheet = new Sheet(SheetType.BGRA, mi.Src);
cachedSheets.Add(mi.Src, sheet);
}

View File

@@ -89,6 +89,7 @@ namespace OpenRA
public interface IShader
{
void SetBool(string name, bool value);
void SetVec(string name, float x);
void SetVec(string name, float x, float y);
void SetVec(string name, float[] vec, int length);

View File

@@ -24,6 +24,8 @@ namespace OpenRA.Graphics
byte[] data;
public readonly Size Size;
public readonly SheetType Type;
public byte[] GetData()
{
CreateBuffer();
@@ -32,18 +34,20 @@ namespace OpenRA.Graphics
public bool Buffered { get { return data != null || texture == null; } }
public Sheet(Size size)
public Sheet(SheetType type, Size size)
{
Type = type;
Size = size;
}
public Sheet(ITexture texture)
public Sheet(SheetType type, ITexture texture)
{
Type = type;
this.texture = texture;
Size = texture.Size;
}
public Sheet(string filename)
public Sheet(SheetType type, string filename)
{
using (var stream = GlobalFileSystem.Open(filename))
using (var bitmap = (Bitmap)Image.FromStream(stream))
@@ -54,6 +58,7 @@ namespace OpenRA.Graphics
Util.FastCopyIntoSprite(new Sprite(this, bitmap.Bounds(), TextureChannel.Red), bitmap);
}
Type = type;
ReleaseBuffer();
}

View File

@@ -40,16 +40,16 @@ namespace OpenRA.Graphics
int rowHeight = 0;
Point p;
public static Sheet AllocateSheet(int sheetSize)
public static Sheet AllocateSheet(SheetType type, int sheetSize)
{
return new Sheet(new Size(sheetSize, sheetSize));
return new Sheet(type, new Size(sheetSize, sheetSize));
}
public SheetBuilder(SheetType t)
: this(t, Game.Settings.Graphics.SheetSize) { }
public SheetBuilder(SheetType t, int sheetSize)
: this(t, () => AllocateSheet(sheetSize)) { }
: this(t, () => AllocateSheet(t, sheetSize)) { }
public SheetBuilder(SheetType t, Func<Sheet> allocateSheet)
{

View File

@@ -119,5 +119,10 @@ namespace OpenRA.Graphics
shader.SetVec("r1", zoom * 2f / screen.Width, -zoom * 2f / screen.Height);
shader.SetVec("r2", -1, 1);
}
public void SetDepthPreviewEnabled(bool enabled)
{
shader.SetBool("EnableDepthPreview", enabled);
}
}
}

View File

@@ -43,16 +43,18 @@ namespace OpenRA.Graphics
{
this.tileset = tileset;
var allocated = false;
var type = tileset.EnableDepth ? SheetType.DualIndexed : SheetType.Indexed;
Func<Sheet> allocate = () =>
{
if (allocated)
throw new SheetOverflowException("Terrain sheet overflow. Try increasing the tileset SheetSize parameter.");
allocated = true;
return new Sheet(new Size(tileset.SheetSize, tileset.SheetSize));
return new Sheet(type, new Size(tileset.SheetSize, tileset.SheetSize));
};
sheetBuilder = new SheetBuilder(SheetType.Indexed, allocate);
sheetBuilder = new SheetBuilder(type, allocate);
random = new MersenneTwister();
var frameCache = new FrameCache(Game.ModData.SpriteLoaders);
@@ -63,8 +65,19 @@ namespace OpenRA.Graphics
foreach (var i in t.Value.Images)
{
var allFrames = frameCache[i];
var frames = t.Value.Frames != null ? t.Value.Frames.Select(f => allFrames[f]).ToArray() : allFrames;
variants.Add(frames.Select(f => sheetBuilder.Add(f)).ToArray());
var frameCount = tileset.EnableDepth ? allFrames.Length / 2 : allFrames.Length;
var indices = t.Value.Frames != null ? t.Value.Frames : Enumerable.Range(0, frameCount);
variants.Add(indices.Select(j =>
{
var f = allFrames[j];
var s = sheetBuilder.Allocate(f.Size, f.Offset);
Util.FastCopyIntoChannel(s, 0, f.Data);
if (tileset.EnableDepth)
Util.FastCopyIntoChannel(s, 1, allFrames[j + frameCount].Data);
return s;
}).ToArray());
}
var allSprites = variants.SelectMany(s => s);

View File

@@ -19,7 +19,7 @@ namespace OpenRA.Graphics
{
// yes, our channel order is nuts.
static readonly int[] ChannelMasks = { 2, 1, 0, 3 };
static readonly float[] ChannelSelect = { 0.75f, 0.25f, -0.25f, -0.75f };
static readonly float[] ChannelSelect = { 0.2f, 0.4f, 0.6f, 0.8f };
public static void FastCreateQuad(Vertex[] vertices, float2 o, Sprite r, float paletteTextureIndex, int nv, float2 size)
{
@@ -32,6 +32,8 @@ namespace OpenRA.Graphics
public static void FastCreateQuad(Vertex[] vertices, float2 a, float2 b, float2 c, float2 d, Sprite r, float paletteTextureIndex, int nv)
{
var attribC = ChannelSelect[(int)r.Channel];
if (r.Sheet.Type == SheetType.DualIndexed)
attribC *= -1;
vertices[nv] = new Vertex(a, r.Left, r.Top, paletteTextureIndex, attribC);
vertices[nv + 1] = new Vertex(b, r.Right, r.Top, paletteTextureIndex, attribC);

View File

@@ -52,7 +52,7 @@ namespace OpenRA.Graphics
if (allocated)
throw new SheetOverflowException("");
allocated = true;
return SheetBuilder.AllocateSheet(Game.Settings.Graphics.SheetSize);
return SheetBuilder.AllocateSheet(SheetType.DualIndexed, Game.Settings.Graphics.SheetSize);
};
return new SheetBuilder(SheetType.DualIndexed, allocate);

View File

@@ -331,7 +331,7 @@ namespace OpenRA.Graphics
var size = new Size(renderer.SheetSize, renderer.SheetSize);
var framebuffer = renderer.Device.CreateFrameBuffer(size);
var sheet = new Sheet(framebuffer.Texture);
var sheet = new Sheet(SheetType.DualIndexed, framebuffer.Texture);
mappedBuffers.Add(sheet, framebuffer);
return sheet;

View File

@@ -132,6 +132,9 @@ namespace OpenRA.Graphics
if (World.WorldActor.Disposed)
return;
if (devTrait.Value != null)
Game.Renderer.WorldSpriteRenderer.SetDepthPreviewEnabled(devTrait.Value.ShowDepthPreview);
RefreshPalette();
if (World.Type == WorldType.Shellmap && !Game.Settings.Game.ShowShellmap)

View File

@@ -174,6 +174,7 @@ namespace OpenRA
public readonly Color[] HeightDebugColors = new[] { Color.Red };
public readonly string[] EditorTemplateOrder;
public readonly bool IgnoreTileSpriteOffsets;
public readonly bool EnableDepth = false;
[FieldLoader.Ignore]
public readonly IReadOnlyDictionary<ushort, TerrainTemplateInfo> Templates;

View File

@@ -23,6 +23,7 @@ namespace OpenRA.Traits
public bool BuildAnywhere;
public bool ShowCombatGeometry;
public bool ShowDebugGeometry;
public bool ShowDepthPreview;
public object Create(ActorInitializer init) { return new DeveloperMode(this); }
}
@@ -41,6 +42,8 @@ namespace OpenRA.Traits
// Client side only
public bool ShowCombatGeometry;
public bool ShowDebugGeometry;
public bool ShowDepthPreview;
public bool EnableAll;
public DeveloperMode(DeveloperModeInfo info)
@@ -54,6 +57,7 @@ namespace OpenRA.Traits
BuildAnywhere = info.BuildAnywhere;
ShowCombatGeometry = info.ShowCombatGeometry;
ShowDebugGeometry = info.ShowDebugGeometry;
ShowDepthPreview = info.ShowDepthPreview;
}
public void ResolveOrder(Actor self, Order order)

View File

@@ -41,7 +41,7 @@ namespace OpenRA.Mods.Cnc
r = Game.Renderer;
if (r == null) return;
sheet = new Sheet(Platform.ResolvePath(loadInfo["Image"]));
sheet = new Sheet(SheetType.BGRA, Platform.ResolvePath(loadInfo["Image"]));
var res = r.Resolution;
bounds = new Rectangle(0, 0, res.Width, res.Height);

View File

@@ -40,7 +40,7 @@ namespace OpenRA.Mods.Common.LoadScreens
if (info.ContainsKey("Image"))
{
sheet = new Sheet(Platform.ResolvePath(info["Image"]));
sheet = new Sheet(SheetType.BGRA, Platform.ResolvePath(info["Image"]));
logo = new Sprite(sheet, new Rectangle(0, 0, 256, 256), TextureChannel.Alpha);
stripe = new Sprite(sheet, new Rectangle(256, 0, 256, 256), TextureChannel.Alpha);
stripeRect = new Rectangle(0, r.Resolution.Height / 2 - 128, r.Resolution.Width, 256);

View File

@@ -22,7 +22,7 @@ namespace OpenRA.Mods.Common.LoadScreens
public void Init(Manifest m, Dictionary<string, string> info)
{
var sheet = new Sheet(info["Image"]);
var sheet = new Sheet(SheetType.BGRA, info["Image"]);
var res = Game.Renderer.Resolution;
bounds = new Rectangle(0, 0, res.Width, res.Height);
sprite = new Sprite(sheet, new Rectangle(0, 0, 1024, 480), TextureChannel.Alpha);

View File

@@ -55,7 +55,7 @@ namespace OpenRA.Mods.Common.Widgets
back = new byte[4 * 256 * 256];
var rect = new Rectangle((int)(255 * SRange[0]), (int)(255 * (1 - VRange[1])), (int)(255 * (SRange[1] - SRange[0])) + 1, (int)(255 * (VRange[1] - VRange[0])) + 1);
var mixerSheet = new Sheet(new Size(256, 256));
var mixerSheet = new Sheet(SheetType.BGRA, new Size(256, 256));
mixerSheet.GetTexture().SetData(front, 256, 256);
mixerSprite = new Sprite(mixerSheet, rect, TextureChannel.Alpha);
GenerateBitmap();

View File

@@ -28,7 +28,7 @@ namespace OpenRA.Mods.Common.Widgets
using (var hueBitmap = new Bitmap(256, 256))
{
var hueSheet = new Sheet(new Size(256, 256));
var hueSheet = new Sheet(SheetType.BGRA, new Size(256, 256));
hueSprite = new Sprite(hueSheet, new Rectangle(0, 0, 256, 1), TextureChannel.Alpha);
var bitmapData = hueBitmap.LockBits(hueBitmap.Bounds(),

View File

@@ -81,6 +81,13 @@ namespace OpenRA.Mods.Common.Widgets.Logic
showTerrainGeometryCheckbox.OnClick = () => terrainGeometryTrait.Enabled ^= true;
}
var showDepthPreviewCheckbox = widget.GetOrNull<CheckboxWidget>("SHOW_DEPTH_PREVIEW");
if (showDepthPreviewCheckbox != null)
{
showDepthPreviewCheckbox.IsChecked = () => devTrait.ShowDepthPreview;
showDepthPreviewCheckbox.OnClick = () => devTrait.ShowDepthPreview ^= true;
}
var allTechCheckbox = widget.GetOrNull<CheckboxWidget>("ENABLE_TECH");
if (allTechCheckbox != null)
{

View File

@@ -77,7 +77,7 @@ namespace OpenRA.Mods.Common.Widgets
base.Initialize(args);
// The four layers are stored in a 2x2 grid within a single texture
radarSheet = new Sheet(new Size(2 * previewWidth, 2 * previewHeight).NextPowerOf2());
radarSheet = new Sheet(SheetType.BGRA, new Size(2 * previewWidth, 2 * previewHeight).NextPowerOf2());
radarSheet.CreateBuffer();
radarData = radarSheet.GetData();

View File

@@ -69,7 +69,7 @@ namespace OpenRA.Mods.Common.Widgets
var size = Math.Max(video.Width, video.Height);
var textureSize = Exts.NextPowerOf2(size);
var videoSheet = new Sheet(new Size(textureSize, textureSize));
var videoSheet = new Sheet(SheetType.BGRA, new Size(textureSize, textureSize));
videoSheet.GetTexture().ScaleFilter = TextureScaleFilter.Linear;
videoSheet.GetTexture().SetData(video.FrameData);
@@ -99,7 +99,7 @@ namespace OpenRA.Mods.Common.Widgets
for (var y = 0; y < scaledHeight; y += 2)
overlay[y, 0] = black;
var overlaySheet = new Sheet(new Size(1, Exts.NextPowerOf2(scaledHeight)));
var overlaySheet = new Sheet(SheetType.BGRA, new Size(1, Exts.NextPowerOf2(scaledHeight)));
overlaySheet.GetTexture().SetData(overlay);
overlaySprite = new Sprite(overlaySheet, new Rectangle(0, 0, 1, scaledHeight), TextureChannel.Alpha);
}

View File

@@ -148,6 +148,17 @@ namespace OpenRA.Platforms.Default
textures[texUnit] = t;
}
public void SetBool(string name, bool value)
{
VerifyThreadAffinity();
GL.UseProgram(program);
ErrorHandler.CheckGlError();
var param = GL.GetUniformLocation(program, name);
ErrorHandler.CheckGlError();
GL.Uniform1(param, value ? 1 : 0);
ErrorHandler.CheckGlError();
}
public void SetVec(string name, float x)
{
VerifyThreadAffinity();

View File

@@ -63,6 +63,7 @@ namespace OpenRA.Platforms.Null
public class NullShader : IShader
{
public void SetBool(string name, bool value) { }
public void SetVec(string name, float x) { }
public void SetVec(string name, float x, float y) { }
public void SetVec(string name, float[] vec, int length) { }

View File

@@ -1,8 +1,26 @@
uniform sampler2D DiffuseTexture, Palette;
uniform bool EnableDepthPreview;
varying vec4 TexCoord;
varying vec4 ChannelMask;
varying vec4 DepthMask;
void main()
{
vec4 x = texture2D(DiffuseTexture, gl_TexCoord[0].st);
vec2 p = vec2(dot(x, gl_TexCoord[1]), gl_TexCoord[0].p);
gl_FragColor = texture2D(Palette, p);
vec4 x = texture2D(DiffuseTexture, TexCoord.st);
vec2 p = vec2(dot(x, ChannelMask), TexCoord.p);
vec4 c = texture2D(Palette, p);
// Discard any transparent fragments (both color and depth)
if (c.a == 0.0)
discard;
if (EnableDepthPreview && length(DepthMask) > 0.0)
{
float depth = dot(x, DepthMask);
gl_FragColor = vec4(depth, depth, depth, 1);
}
else
gl_FragColor = c;
}

View File

@@ -1,18 +1,42 @@
uniform vec2 Scroll;
uniform vec2 r1,r2; // matrix elements
vec4 DecodeChannelMask( float x )
varying vec4 TexCoord;
varying vec4 ChannelMask;
varying vec4 DepthMask;
vec4 DecodeChannelMask(float x)
{
float y = abs(x);
if (y > 0.7)
return vec4(0,0,0,1);
if (y > 0.5)
return vec4(0,0,1,0);
if (y > 0.3)
return vec4(0,1,0,0);
else
return vec4(1,0,0,0);
}
vec4 DecodeDepthChannelMask(float x)
{
if (x > 0.0)
return (x > 0.5) ? vec4(1,0,0,0) : vec4(0,1,0,0);
return vec4(0,0,0,0);
if (x < -0.7)
return vec4(1,0,0,0);
if (x < -0.5)
return vec4(0,0,0,1);
if (x < -0.3)
return vec4(0,0,1,0);
else
return (x < -0.5) ? vec4(0,0,0,1) : vec4(0,0,1,0);
return vec4(0,1,0,0);
}
void main()
{
vec2 p = (gl_Vertex.xy - Scroll.xy)*r1 + r2;
vec2 p = (gl_Vertex.xy - Scroll.xy) * r1 + r2;
gl_Position = vec4(p.x,p.y,0,1);
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_TexCoord[1] = DecodeChannelMask(gl_MultiTexCoord0.w);
TexCoord = gl_MultiTexCoord0;
ChannelMask = DecodeChannelMask(gl_MultiTexCoord0.w);
DepthMask = DecodeDepthChannelMask(gl_MultiTexCoord0.w);
}

View File

@@ -4,14 +4,18 @@ uniform vec2 PaletteRows;
uniform vec4 LightDirection;
uniform vec3 AmbientLight, DiffuseLight;
varying vec4 TexCoord;
varying vec4 ChannelMask;
varying vec4 NormalsMask;
void main()
{
vec4 x = texture2D(DiffuseTexture, gl_TexCoord[0].st);
vec4 color = texture2D(Palette, vec2(dot(x, gl_TexCoord[1]), PaletteRows.x));
vec4 x = texture2D(DiffuseTexture, TexCoord.st);
vec4 color = texture2D(Palette, vec2(dot(x, ChannelMask), PaletteRows.x));
if (color.a < 0.01)
discard;
vec4 normal = (2.0 * texture2D(Palette, vec2(dot(x, gl_TexCoord[2]), PaletteRows.y)) - 1.0);
vec4 normal = (2.0 * texture2D(Palette, vec2(dot(x, NormalsMask), PaletteRows.y)) - 1.0);
vec3 intensity = AmbientLight + DiffuseLight * max(dot(normal, LightDirection), 0.0);
gl_FragColor = vec4(intensity * color.rgb, color.a);
}

View File

@@ -1,7 +1,11 @@
uniform mat4 View;
uniform mat4 TransformMatrix;
vec4 DecodeChannelMask(float x)
varying vec4 TexCoord;
varying vec4 ChannelMask;
varying vec4 NormalsMask;
vec4 DecodeMask(float x)
{
if (x > 0.0)
return (x > 0.5) ? vec4(1,0,0,0) : vec4(0,1,0,0);
@@ -12,7 +16,7 @@ vec4 DecodeChannelMask(float x)
void main()
{
gl_Position = View*TransformMatrix*gl_Vertex;
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_TexCoord[1] = DecodeChannelMask(gl_MultiTexCoord0.z);
gl_TexCoord[2] = DecodeChannelMask(gl_MultiTexCoord0.w);
TexCoord = gl_MultiTexCoord0;
ChannelMask = DecodeMask(gl_MultiTexCoord0.z);
NormalsMask = DecodeMask(gl_MultiTexCoord0.w);
}

View File

@@ -0,0 +1,130 @@
Container@DEBUG_PANEL:
Logic: DebugMenuLogic
Y: 10
Width: PARENT_RIGHT
Height: PARENT_BOTTOM
Children:
Label@LABLE_TITLE:
Y: 25
Font: Bold
Text: Debug Options
Align: Center
Width: PARENT_RIGHT
Checkbox@INSTANT_BUILD:
X: 45
Y: 45
Width: 200
Height: 20
Font: Regular
Text: Instant Build Speed
Checkbox@ENABLE_TECH:
X: 45
Y: 75
Width: 200
Height: 20
Font: Regular
Text: Build Everything
Checkbox@BUILD_ANYWHERE:
X: 45
Y: 105
Width: 200
Height: 20
Font: Regular
Text: Build Anywhere
Checkbox@UNLIMITED_POWER:
X: 290
Y: 45
Width: 200
Height: 20
Font: Regular
Text: Unlimited Power
Checkbox@INSTANT_CHARGE:
X: 290
Y: 75
Width: 200
Height: 20
Font: Regular
Text: Instant Charge Time
Checkbox@DISABLE_VISIBILITY_CHECKS:
X: 290
Y: 105
Height: 20
Width: 200
Font: Regular
Text: Disable visibility checks
Button@GIVE_CASH:
X: 90
Y: 150
Width: 140
Height: 30
Font: Bold
Text: Give $20,000
Button@GROW_RESOURCES:
X: 271
Y: 150
Width: 140
Height: 30
Font: Bold
Text: Grow Resources
Button@GIVE_EXPLORATION:
X: 90
Y: 200
Width: 140
Height: 30
Font: Bold
Text: Clear Shroud
Button@RESET_EXPLORATION:
X: 271
Y: 200
Width: 140
Height: 30
Font: Bold
Text: Reset Shroud
Label@VISUALIZATIONS_TITLE:
Y: 255
Font: Bold
Text: Visualizations
Align: Center
Width: PARENT_RIGHT
Checkbox@SHOW_UNIT_PATHS:
X: 45
Y: 275
Width: 200
Height: 20
Font: Regular
Text: Show Unit Paths
Checkbox@SHOW_ASTAR:
X: 45
Y: 305
Height: 20
Width: 200
Font: Regular
Text: Show A* Cost
Checkbox@SHOW_DEPTH_PREVIEW:
X: 45
Y: 335
Height: 20
Width: 200
Font: Regular
Text: Show Depth Data
Checkbox@SHOW_COMBATOVERLAY:
X: 290
Y: 275
Height: 20
Width: 200
Font: Regular
Text: Show Combat Geometry
Checkbox@SHOW_GEOMETRY:
X: 290
Y: 305
Height: 20
Width: 200
Font: Regular
Text: Show Render Geometry
Checkbox@SHOW_TERRAIN_OVERLAY:
X: 290
Y: 335
Height: 20
Width: 200
Font: Regular
Text: Show Terrain Geometry

View File

@@ -145,7 +145,7 @@ ChromeLayout:
./mods/ts/chrome/ingame-observerstats.yaml
./mods/ts/chrome/ingame-player.yaml
./mods/ra/chrome/ingame-perf.yaml
./mods/ra/chrome/ingame-debug.yaml
./mods/ts/chrome/ingame-debug.yaml
./mods/ra/chrome/mainmenu.yaml
./mods/ra/chrome/settings.yaml
./mods/ra/chrome/credits.yaml

View File

@@ -5,6 +5,7 @@ General:
HeightDebugColors: 128,0,0,0, 128,0,0,68, 128,0,0,136, 128,0,0,204, 128,0,0,255, 128,68,0,204, 128,136,0,136, 128,204,0,68, 128,255,17,0, 128,255,85,0, 128,255,153,0, 128,255,221,0, 128,221,255,0, 128,153,255,0, 128,85,255,0, 128,17,255,0
EditorTemplateOrder: Bendy Dirt Roads, Blank, Bridges, Civilian Buildings, Clear, Clear/Rough LAT, Cliff Pieces, Cliff Set, Cliff/Water pieces, Dead Oil Tanker, Destroyable Cliffs, Dirt Road Junctions, Dirt Road Slopes, DirtTrackTunnel Floor, DirtTunnel Floor, Grey/Clear LAT, House, Ice Flow, Ice Ramps, Ice shore, Misc Buildings, Monorail Slopes, Paved Road Ends, Paved Road Slopes, Paved Roads, Pavement, Pavement (Use for LAT), Pavement/Clear LAT, Ramp edge fixup, Rough ground, Rough lat, Ruins, Shore Pieces, Slope Set Pieces, Straight Dirt Roads, TrackTunnel Floor, TrainBridges, Tunnel Side, Tunnels, Water, Water slopes, Waterfalls, Waterfalls-B, Waterfalls-C, Waterfalls-D
SheetSize: 2048
EnableDepth: true
Terrain:
TerrainType@Clear:

View File

@@ -5,6 +5,7 @@ General:
HeightDebugColors: 128,0,0,0, 128,0,0,68, 128,0,0,136, 128,0,0,204, 128,0,0,255, 128,68,0,204, 128,136,0,136, 128,204,0,68, 128,255,17,0, 128,255,85,0, 128,255,153,0, 128,255,221,0, 128,221,255,0, 128,153,255,0, 128,85,255,0, 128,17,255,0
EditorTemplateOrder: Misc Buildings, Clear, Cliff Pieces, Ice Flow, House, Blank, Ice Ramps, Cliff Set, Civilian Buildings, Shore Pieces, Rough LAT tile, Clear/Rough LAT, Cliff/Water pieces, Bendy Dirt Roads, Dirt Road Junctions, Straight Dirt Roads, Bridges, Paved Roads, Water, Dirt Road Slopes, Slope Set Pieces, Dead Oil Tanker, Ruins, Waterfalls, Ground 01, Ground 02, Sand, Sand/Clear LAT, Rough ground, Paved Road Ends, TrainBridges, Pavement, Pavement/Clear LAT, Paved road bits, Green, Green/Clear LAT, Ramp edge fixup, Water slopes, Pavement (Use for LAT), Paved Road Slopes, Monorail Slopes, Waterfalls-B, Waterfalls-C, Waterfalls-D, Tunnel Floor, Tunnel Side, TrackTunnel Floor, Destroyable Cliffs, Water Caves, Scrin Wreckage, DirtTrackTunnel Floor, DirtTunnel Floor
SheetSize: 2048
EnableDepth: true
Terrain:
TerrainType@Clear: