Merge pull request #4287 from pchote/shroud

Rewrite shroud renderer.
This commit is contained in:
Matthias Mailänder
2013-12-11 01:28:22 -08:00
25 changed files with 437 additions and 265 deletions

View File

@@ -17,6 +17,7 @@ NEW:
Added a new hotkey to select all units on screen (default: CTRL + A).
Added a new hotkey to jump to production buildings (default: TAB).
Changed default hotkey (PageUp/Down) for build palette cycling and made reverse user configurable.
Improved shroud/fog rendering.
Asset Browser:
Fixed crashes when trying to load invalid filenames or sprites with just 1 frame.
Added support for all sprite types.
@@ -73,6 +74,7 @@ NEW:
Disabled the main menu target reticle showing when a window is open.
Added a display of the faction logos when the shellmap is disabled.
Visceriods now heal on Tiberium and move faster there.
Implemented the original shroud artwork.
Dune 2000:
Added buildable concrete walls.
Fixed some cliffs being passable.
@@ -80,6 +82,7 @@ NEW:
Fixed A* debug overlay.
Fixed R8 offsets for sprites with embedded palettes.
Implemented proper spice rendering.
Implemented the original shroud artwork.
Engine:
Replays are now saved in per-mod and per-version folders.
Added password protection support for servers.
@@ -99,6 +102,7 @@ NEW:
Rewritten shp(ts) parser makes more efficient use of texture space.
Added support for the dune 2 shp and pak formats.
Map format 6 requires the RequiresMod to be defined.
Added a multiplicitive blend mode.
Build system and packages:
Added GeoIP to Makefile so it is installed properly.
Added desktop shortcut creation support to the Makefile and Windows installer.
@@ -137,7 +141,8 @@ NEW:
Added OpenRA.Utility --map-upgrade for updating maps from format 5 to format 6.
The map format has been changed. All user-installed maps will be upgraded on the first mod launch, or using OpenRA.Utility --map-upgrade.
Unified sprite loading allows any sprite type to be used anywhere: shp can now be used for terrain, and tmp for units.
Harvestable resource definitions (ResourceTypes) have changed, and now specify their artwork using via sequences.
Harvestable resource definitions (ResourceTypes) have changed, and now specify their artwork using sequences.
Shroud definitions (ShroudRenderer / ShroudPalette) have changed, and now specifies its artwork using sequences.
20130915:
All mods:

View File

@@ -31,7 +31,7 @@ namespace OpenRA.FileFormats.Graphics
IGraphicsDevice Create( Size size, WindowMode windowMode );
}
public enum BlendMode { None, Alpha, Additive, Subtractive }
public enum BlendMode { None, Alpha, Additive, Subtractive, Multiply }
public interface IGraphicsDevice
{

View File

@@ -1,210 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 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. For more information,
* see COPYING.
*/
#endregion
using System.Drawing;
using OpenRA.Traits;
namespace OpenRA.Graphics
{
public class ShroudRenderer
{
World world;
Map map;
Sprite[] shadowBits = Game.modData.SpriteLoader.LoadAllSprites("shadow");
Sprite[,] sprites, fogSprites;
int shroudHash;
bool initializePalettes = true;
PaletteReference fogPalette, shroudPalette;
static readonly byte[][] SpecialShroudTiles =
{
new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
new byte[] { 32, 32, 25, 25, 19, 19, 20, 20 },
new byte[] { 33, 33, 33, 33, 26, 26, 26, 26, 21, 21, 21, 21, 23, 23, 23, 23 },
new byte[] { 36, 36, 36, 36, 30, 30, 30, 30 },
new byte[] { 34, 16, 34, 16, 34, 16, 34, 16, 27, 22, 27, 22, 27, 22, 27, 22 },
new byte[] { 44 },
new byte[] { 37, 37, 37, 37, 37, 37, 37, 37, 31, 31, 31, 31, 31, 31, 31, 31 },
new byte[] { 40 },
new byte[] { 35, 24, 17, 18 },
new byte[] { 39, 39, 29, 29 },
new byte[] { 45 },
new byte[] { 43 },
new byte[] { 38, 28 },
new byte[] { 42 },
new byte[] { 41 },
new byte[] { 46 },
};
public ShroudRenderer(World world)
{
this.world = world;
this.map = world.Map;
sprites = new Sprite[map.MapSize.X, map.MapSize.Y];
fogSprites = new Sprite[map.MapSize.X, map.MapSize.Y];
// Force update on first render
shroudHash = -1;
}
Sprite ChooseShroud(Shroud s, int i, int j)
{
if (!s.IsExplored(i, j))
return shadowBits[0xf];
// bits are for unexploredness: up, right, down, left
var v = 0;
// bits are for unexploredness: TL, TR, BR, BL
var u = 0;
if (!s.IsExplored(i, j - 1)) { v |= 1; u |= 3; }
if (!s.IsExplored(i + 1, j)) { v |= 2; u |= 6; }
if (!s.IsExplored(i, j + 1)) { v |= 4; u |= 12; }
if (!s.IsExplored(i - 1, j)) { v |= 8; u |= 9; }
var uSides = u;
if (!s.IsExplored(i - 1, j - 1)) u |= 1;
if (!s.IsExplored(i + 1, j - 1)) u |= 2;
if (!s.IsExplored(i + 1, j + 1)) u |= 4;
if (!s.IsExplored(i - 1, j + 1)) u |= 8;
return shadowBits[SpecialShroudTiles[u ^ uSides][v]];
}
Sprite ChooseFog(Shroud s, int i, int j)
{
if (!s.IsVisible(i, j)) return shadowBits[0xf];
if (!s.IsExplored(i, j)) return shadowBits[0xf];
// bits are for unexploredness: up, right, down, left
var v = 0;
// bits are for unexploredness: TL, TR, BR, BL
var u = 0;
if (!s.IsVisible(i, j - 1)) { v |= 1; u |= 3; }
if (!s.IsVisible(i + 1, j)) { v |= 2; u |= 6; }
if (!s.IsVisible(i, j + 1)) { v |= 4; u |= 12; }
if (!s.IsVisible(i - 1, j)) { v |= 8; u |= 9; }
var uSides = u;
if (!s.IsVisible(i - 1, j - 1)) u |= 1;
if (!s.IsVisible(i + 1, j - 1)) u |= 2;
if (!s.IsVisible(i + 1, j + 1)) u |= 4;
if (!s.IsVisible(i - 1, j + 1)) u |= 8;
return shadowBits[SpecialShroudTiles[u ^ uSides][v]];
}
void GenerateSprites(Shroud shroud)
{
var hash = shroud != null ? shroud.Hash : 0;
if (shroudHash == hash)
return;
shroudHash = hash;
if (shroud == null)
{
// Players with no shroud see the whole map so we only need to set the edges
var b = map.Bounds;
for (int i = b.Left; i < b.Right; i++)
for (int j = b.Top; j < b.Bottom; j++)
{
var v = 0;
var u = 0;
if (j == b.Top) { v |= 1; u |= 3; }
if (i == b.Right - 1) { v |= 2; u |= 6; }
if (j == b.Bottom - 1) { v |= 4; u |= 12; }
if (i == b.Left) { v |= 8; u |= 9; }
var uSides = u;
if (i == b.Left && j == b.Top) u |= 1;
if (i == b.Right - 1 && j == b.Top) u |= 2;
if (i == b.Right - 1 && j == b.Bottom - 1) u |= 4;
if (i == b.Left && j == b.Bottom - 1) u |= 8;
sprites[i, j] = fogSprites[i, j] = shadowBits[SpecialShroudTiles[u ^ uSides][v]];
}
}
else
{
for (int i = map.Bounds.Left; i < map.Bounds.Right; i++)
for (int j = map.Bounds.Top; j < map.Bounds.Bottom; j++)
sprites[i, j] = ChooseShroud(shroud, i, j);
for (int i = map.Bounds.Left; i < map.Bounds.Right; i++)
for (int j = map.Bounds.Top; j < map.Bounds.Bottom; j++)
fogSprites[i, j] = ChooseFog(shroud, i, j);
}
}
internal void Draw(WorldRenderer wr, Shroud shroud)
{
if (initializePalettes)
{
if (world.LobbyInfo.GlobalSettings.Fog)
fogPalette = wr.Palette("fog");
shroudPalette = world.LobbyInfo.GlobalSettings.Fog ? wr.Palette("shroud") : wr.Palette("shroudfog");
initializePalettes = false;
}
GenerateSprites(shroud);
// We draw the shroud when disabled to hide the sharp map edges
var clipRect = wr.Viewport.CellBounds;
DrawShroud(wr, clipRect, sprites, shroudPalette);
if (world.LobbyInfo.GlobalSettings.Fog)
DrawShroud(wr, clipRect, fogSprites, fogPalette);
}
void DrawShroud(WorldRenderer wr, Rectangle clip, Sprite[,] s, PaletteReference pal)
{
for (var j = clip.Top; j < clip.Bottom; j++)
{
var starti = clip.Left;
var last = shadowBits[0x0f];
for (var i = clip.Left; i < clip.Right; i++)
{
if ((s[i, j] == shadowBits[0x0f] && last == shadowBits[0x0f])
|| (s[i, j] == shadowBits[0] && last == shadowBits[0]))
continue;
if (starti != i)
{
// Stretch a solid black sprite over the rows above
// TODO: This doesn't make sense for isometric terrain
Game.Renderer.WorldSpriteRenderer.DrawSprite(
s[starti, j],
Game.CellSize * new float2(starti, j),
pal,
new float2(Game.CellSize * (i - starti), Game.CellSize));
starti = i + 1;
}
Game.Renderer.WorldSpriteRenderer.DrawSprite(s[i, j], Game.CellSize * new float2(i, j), pal);
starti = i + 1;
last = s[i, j];
}
// Stretch a solid black sprite over the rows to the left
// TODO: This doesn't make sense for isometric terrain
if (starti < clip.Right)
Game.Renderer.WorldSpriteRenderer.DrawSprite(s[starti, j],
Game.CellSize * new float2(starti, j), pal,
new float2(Game.CellSize * (clip.Right - starti), Game.CellSize));
}
}
}
}

View File

@@ -37,7 +37,6 @@ namespace OpenRA.Graphics
public Viewport Viewport { get; private set; }
internal readonly TerrainRenderer terrainRenderer;
internal readonly ShroudRenderer shroudRenderer;
internal readonly HardwarePalette palette;
internal Cache<string, PaletteReference> palettes;
Lazy<DeveloperMode> devTrait;
@@ -56,7 +55,6 @@ namespace OpenRA.Graphics
Theater = new Theater(world.TileSet);
terrainRenderer = new TerrainRenderer(world, this);
shroudRenderer = new ShroudRenderer(world);
devTrait = Lazy.New(() => world.LocalPlayer != null ? world.LocalPlayer.PlayerActor.Trait<DeveloperMode>() : null);
}
@@ -132,7 +130,9 @@ namespace OpenRA.Graphics
world.OrderGenerator.RenderAfterWorld(this, world);
var renderShroud = world.RenderPlayer != null ? world.RenderPlayer.Shroud : null;
shroudRenderer.Draw(this, renderShroud);
foreach (var a in world.ActorsWithTrait<IRenderShroud>())
a.Trait.RenderShroud(this, renderShroud);
if (devTrait.Value != null && devTrait.Value.ShowDebugGeometry)
for (var i = 0; i < renderables.Count; i++)

View File

@@ -110,7 +110,6 @@
<Compile Include="Graphics\SequenceProvider.cs" />
<Compile Include="Graphics\Sheet.cs" />
<Compile Include="Graphics\SheetBuilder.cs" />
<Compile Include="Graphics\ShroudRenderer.cs" />
<Compile Include="Graphics\Sprite.cs" />
<Compile Include="Graphics\SpriteFont.cs" />
<Compile Include="Graphics\SpriteLoader.cs" />

View File

@@ -191,6 +191,7 @@ namespace OpenRA.Traits
public interface IBlocksBullets { }
public interface IPostRender { void RenderAfterWorld(WorldRenderer wr, Actor self); }
public interface IRenderShroud { void RenderShroud(WorldRenderer wr, Shroud shroud); }
public interface IPostRenderSelection { void RenderAfterWorld(WorldRenderer wr); }
public interface IBodyOrientation

View File

@@ -263,9 +263,7 @@ namespace OpenRA.Traits
public bool IsVisible(CPos xy) { return IsVisible(xy.X, xy.Y); }
public bool IsVisible(int x, int y)
{
// Visibility is allowed to extend beyond the map cordon so that
// the fog tiles are not visible at the edge of the world
if (x < 0 || x >= map.MapSize.X || y < 0 || y >= map.MapSize.Y)
if (!map.IsInMap(x, y))
return false;
if (Disabled || !self.World.LobbyInfo.GlobalSettings.Fog)

View File

@@ -0,0 +1,59 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 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. For more information,
* see COPYING.
*/
#endregion
using System.IO;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
{
class FogPaletteFromR8Info : ITraitInfo
{
[Desc("Internal palette name")]
public readonly string Name = null;
[Desc("Filename to load")]
public readonly string Filename = null;
[Desc("Palette byte offset")]
public readonly long Offset = 0;
public readonly bool AllowModifiers = true;
public readonly bool InvertColor = false;
public object Create(ActorInitializer init) { return new FogPaletteFromR8(this); }
}
class FogPaletteFromR8 : IPalette
{
readonly FogPaletteFromR8Info info;
public FogPaletteFromR8(FogPaletteFromR8Info info) { this.info = info; }
public void InitPalette(WorldRenderer wr)
{
var colors = new uint[256];
using (var s = FileSystem.Open(info.Filename))
{
s.Seek(info.Offset, SeekOrigin.Begin);
for (var i = 0; i < 256; i++)
{
var packed = s.ReadUInt16();
// Fog is rendered with half opacity
colors[i] = (uint)((255 << 24) | ((packed & 0xF800) << 7) | ((packed & 0x7E0) << 4) | ((packed & 0x1f) << 2));
if (info.InvertColor)
colors[i] ^= 0x00FFFFFF;
}
}
wr.AddPalette(info.Name, new Palette(colors), info.AllowModifiers);
}
}
}

View File

@@ -80,6 +80,7 @@
<Compile Include="Render\WithCrumbleOverlay.cs" />
<Compile Include="PaletteFromR8.cs" />
<Compile Include="D2kResourceLayer.cs" />
<Compile Include="FogPaletteFromR8.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<PropertyGroup>

View File

@@ -10,8 +10,8 @@
using System.IO;
using OpenRA.FileFormats;
using OpenRA.Traits;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
{
@@ -24,6 +24,7 @@ namespace OpenRA.Mods.RA
[Desc("Palette byte offset")]
public readonly long Offset = 0;
public readonly bool AllowModifiers = true;
public readonly bool InvertColor = false;
public object Create(ActorInitializer init) { return new PaletteFromR8(this); }
}
@@ -42,10 +43,11 @@ namespace OpenRA.Mods.RA
for (var i = 0; i < 256; i++)
{
// The custom palette is scaled into the range 0-128.
// This makes the move-flash match the original game, but may not be correct in other cases.
var packed = s.ReadUInt16();
colors[i] = (uint)((255 << 24) | ((packed & 0xF800) << 7) | ((packed & 0x7E0) << 4) | ((packed & 0x1f) << 2));
colors[i] = (uint)((255 << 24) | ((packed & 0xF800) << 8) | ((packed & 0x7E0) << 5) | ((packed & 0x1f) << 3));
if (info.InvertColor)
colors[i] ^= 0x00FFFFFF;
}
}

View File

@@ -482,6 +482,7 @@
<Compile Include="Widgets\Logic\SettingsLogic.cs" />
<Compile Include="AttackBomber.cs" />
<Compile Include="Effects\Rank.cs" />
<Compile Include="ShroudRenderer.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">

View File

@@ -15,8 +15,6 @@ using OpenRA.Traits;
namespace OpenRA.Mods.RA
{
public enum ShroudPaletteType { Shroud, Fog, Combined };
[Desc("Adds the hard-coded shroud palette to the game")]
class ShroudPaletteInfo : ITraitInfo
{
@@ -24,7 +22,7 @@ namespace OpenRA.Mods.RA
public readonly string Name = "shroud";
[Desc("Palette type")]
public readonly ShroudPaletteType Type = ShroudPaletteType.Combined;
public readonly bool Fog = false;
public object Create(ActorInitializer init) { return new ShroudPalette(this); }
}
@@ -37,35 +35,24 @@ namespace OpenRA.Mods.RA
public void InitPalette(WorldRenderer wr)
{
var c = info.Type == ShroudPaletteType.Shroud ? Shroud :
info.Type == ShroudPaletteType.Fog ? Fog : Combined;
var c = info.Fog ? Fog : Shroud;
wr.AddPalette(info.Name, new Palette(Exts.MakeArray(256, i => (uint)c[i % 8].ToArgb())), false);
}
static Color[] Shroud = new[] {
Color.Transparent, Color.Green,
Color.Blue, Color.Yellow,
Color.Black,
Color.FromArgb(128,0,0,0),
Color.Transparent,
Color.Transparent
};
static Color[] Fog = new[] {
Color.Transparent, Color.Green,
Color.Blue, Color.Yellow,
Color.FromArgb(128,0,0,0),
Color.FromArgb(128,0,0,0),
Color.FromArgb(128,0,0,0),
Color.FromArgb(64,0,0,0)
Color.FromArgb(96,0,0,0),
Color.FromArgb(64,0,0,0),
Color.FromArgb(32,0,0,0)
};
static Color[] Combined = new[] {
static Color[] Shroud = new[] {
Color.Transparent, Color.Green,
Color.Blue, Color.Yellow,
Color.Black,
Color.FromArgb(192,0,0,0),
Color.FromArgb(160,0,0,0),
Color.FromArgb(128,0,0,0),
Color.FromArgb(64,0,0,0)
};

View File

@@ -0,0 +1,267 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 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. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.FileFormats.Graphics;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
{
public class ShroudRendererInfo : ITraitInfo
{
public string Sequence = "shroud";
public string[] Variants = new[] { "shroud" };
[Desc("Bitfield of shroud directions for each frame. Lower four bits are",
"corners clockwise from TL; upper four are edges clockwise from top")]
public int[] Index = new[] { 12, 9, 8, 3, 1, 6, 4, 2, 13, 11, 7, 14 };
[Desc("Use the upper four bits when calculating frame")]
public bool UseExtendedIndex = false;
[Desc("Palette index for synthesized unexplored tile")]
public int ShroudColor = 12;
public BlendMode ShroudBlend = BlendMode.Alpha;
public object Create(ActorInitializer init) { return new ShroudRenderer(init.world, this); }
}
public class ShroudRenderer : IRenderShroud, IWorldLoaded
{
struct ShroudTile
{
public CPos Position;
public float2 ScreenPosition;
public int Variant;
public Sprite Fog;
public Sprite Shroud;
}
Sprite[] sprites;
Sprite unexploredTile;
int[] spriteMap;
ShroudTile[] tiles;
int tileStride, variantStride;
int shroudHash;
PaletteReference fogPalette, shroudPalette;
Rectangle bounds;
bool useExtendedIndex;
public ShroudRenderer(World world, ShroudRendererInfo info)
{
var map = world.Map;
bounds = map.Bounds;
useExtendedIndex = info.UseExtendedIndex;
tiles = new ShroudTile[map.MapSize.X * map.MapSize.Y];
tileStride = map.MapSize.X;
// Force update on first render
shroudHash = -1;
// Load sprite variants
sprites = new Sprite[info.Variants.Length * info.Index.Length];
variantStride = info.Index.Length;
for (var j = 0; j < info.Variants.Length; j++)
{
var seq = SequenceProvider.GetSequence(info.Sequence, info.Variants[j]);
for (var i = 0; i < info.Index.Length; i++)
sprites[j * variantStride + i] = seq.GetSprite(i);
}
// Mapping of shrouded directions -> sprite index
spriteMap = new int[useExtendedIndex ? 256 : 16];
for (var i = 0; i < info.Index.Length; i++)
spriteMap[info.Index[i]] = i;
// Set individual tile variants to reduce tiling
for (var i = 0; i < tiles.Length; i++)
tiles[i].Variant = Game.CosmeticRandom.Next(info.Variants.Length);
// Synthesize unexplored tile if it isn't defined
if (!info.Index.Contains(0))
{
var size = new Size(Game.modData.Manifest.TileSize, Game.modData.Manifest.TileSize);
var data = Exts.MakeArray<byte>(size.Width * size.Height, _ => (byte)info.ShroudColor);
var s = Game.modData.SheetBuilder.Add(data, size);
unexploredTile = new Sprite(s.sheet, s.bounds, s.offset, s.channel, info.ShroudBlend);
}
else
unexploredTile = sprites[spriteMap[0]];
}
static int FoggedEdges(Shroud s, CPos p, bool useExtendedIndex)
{
if (!s.IsVisible(p.X, p.Y))
return 15;
// If a side is shrouded then we also count the corners
var u = 0;
if (!s.IsVisible(p.X, p.Y - 1)) u |= 0x13;
if (!s.IsVisible(p.X + 1, p.Y)) u |= 0x26;
if (!s.IsVisible(p.X, p.Y + 1)) u |= 0x4C;
if (!s.IsVisible(p.X - 1, p.Y)) u |= 0x89;
var uside = u & 0x0F;
if (!s.IsVisible(p.X - 1, p.Y - 1)) u |= 0x01;
if (!s.IsVisible(p.X + 1, p.Y - 1)) u |= 0x02;
if (!s.IsVisible(p.X + 1, p.Y + 1)) u |= 0x04;
if (!s.IsVisible(p.X - 1, p.Y + 1)) u |= 0x08;
// RA provides a set of frames for tiles with shrouded
// corners but unshrouded edges. We want to detect this
// situation without breaking the edge -> corner enabling
// in other combinations. The XOR turns off the corner
// bits that are enabled twice, which gives the behavior
// we want here.
return useExtendedIndex ? u ^ uside : u & 0x0F;
}
static int ShroudedEdges(Shroud s, CPos p, bool useExtendedIndex)
{
if (!s.IsExplored(p.X, p.Y))
return 15;
// If a side is shrouded then we also count the corners
var u = 0;
if (!s.IsExplored(p.X, p.Y - 1)) u |= 0x13;
if (!s.IsExplored(p.X + 1, p.Y)) u |= 0x26;
if (!s.IsExplored(p.X, p.Y + 1)) u |= 0x4C;
if (!s.IsExplored(p.X - 1, p.Y)) u |= 0x89;
var uside = u & 0x0F;
if (!s.IsExplored(p.X - 1, p.Y - 1)) u |= 0x01;
if (!s.IsExplored(p.X + 1, p.Y - 1)) u |= 0x02;
if (!s.IsExplored(p.X + 1, p.Y + 1)) u |= 0x04;
if (!s.IsExplored(p.X - 1, p.Y + 1)) u |= 0x08;
// RA provides a set of frames for tiles with shrouded
// corners but unshrouded edges. We want to detect this
// situation without breaking the edge -> corner enabling
// in other combinations. The XOR turns off the corner
// bits that are enabled twice, which gives the behavior
// we want here.
return useExtendedIndex ? u ^ uside : u & 0x0F;
}
static int ObserverShroudedEdges(CPos p, Rectangle bounds, bool useExtendedIndex)
{
var u = 0;
if (p.Y == bounds.Top) u |= 0x13;
if (p.X == bounds.Right - 1) u |= 0x26;
if (p.Y == bounds.Bottom - 1) u |= 0x4C;
if (p.X == bounds.Left) u |= 0x89;
var uside = u & 0x0F;
if (p.X == bounds.Left && p.Y == bounds.Top) u |= 0x01;
if (p.X == bounds.Right - 1 && p.Y == bounds.Top) u |= 0x02;
if (p.X == bounds.Right - 1 && p.Y == bounds.Bottom - 1) u |= 0x04;
if (p.X == bounds.Left && p.Y == bounds.Bottom - 1) u |= 0x08;
return useExtendedIndex ? u ^ uside : u & 0x0F;
}
public void WorldLoaded(World w, WorldRenderer wr)
{
// Cache the tile positions to avoid unnecessary calculations
for (var i = bounds.Left; i < bounds.Right; i++)
{
for (var j = bounds.Top; j < bounds.Bottom; j++)
{
var k = j * tileStride + i;
tiles[k].Position = new CPos(i, j);
tiles[k].ScreenPosition = wr.ScreenPosition(tiles[k].Position.CenterPosition);
}
}
if (w.LobbyInfo.GlobalSettings.Fog)
fogPalette = wr.Palette("fog");
shroudPalette = wr.Palette("shroud");
}
Sprite GetTile(int flags, int variant)
{
if (flags == 0)
return null;
if (flags == 15)
return unexploredTile;
return sprites[variant * variantStride + spriteMap[flags]];
}
void Update(Shroud shroud)
{
var hash = shroud != null ? shroud.Hash : 0;
if (shroudHash == hash)
return;
shroudHash = hash;
if (shroud == null)
{
// Players with no shroud see the whole map so we only need to set the edges
for (var k = 0; k < tiles.Length; k++)
{
var shrouded = ObserverShroudedEdges(tiles[k].Position, bounds, useExtendedIndex);
tiles[k].Shroud = GetTile(shrouded, tiles[k].Variant);
tiles[k].Fog = GetTile(shrouded, tiles[k].Variant);
}
}
else
{
for (var k = 0; k < tiles.Length; k++)
{
var shrouded = ShroudedEdges(shroud, tiles[k].Position, useExtendedIndex);
var fogged = FoggedEdges(shroud, tiles[k].Position, useExtendedIndex);
tiles[k].Shroud = GetTile(shrouded, tiles[k].Variant);
tiles[k].Fog = GetTile(fogged, tiles[k].Variant);
}
}
}
public void RenderShroud(WorldRenderer wr, Shroud shroud)
{
Update(shroud);
var clip = wr.Viewport.CellBounds;
var width = clip.Width;
for (var j = clip.Top; j < clip.Bottom; j++)
{
var start = j * tileStride + clip.Left;
for (var k = 0; k < width; k++)
{
var s = tiles[start + k].Shroud;
var f = tiles[start + k].Fog;
if (s != null)
{
var pos = tiles[start + k].ScreenPosition - 0.5f * s.size;
Game.Renderer.WorldSpriteRenderer.DrawSprite(s, pos, shroudPalette);
}
if (f != null)
{
var pos = tiles[start + k].ScreenPosition - 0.5f * f.size;
Game.Renderer.WorldSpriteRenderer.DrawSprite(f, pos, fogPalette);
}
}
}
}
}
}

View File

@@ -177,6 +177,12 @@ namespace OpenRA.Renderer.Sdl2
ErrorHandler.CheckGlError();
Gl.glBlendEquation(Gl.GL_FUNC_REVERSE_SUBTRACT);
break;
case BlendMode.Multiply:
Gl.glEnable(Gl.GL_BLEND);
ErrorHandler.CheckGlError();
Gl.glBlendFuncSeparate(Gl.GL_DST_COLOR, Gl.GL_ZERO, Gl.GL_ONE, Gl.GL_ONE_MINUS_SRC_ALPHA);
ErrorHandler.CheckGlError();
break;
}
ErrorHandler.CheckGlError();

View File

@@ -179,6 +179,12 @@ namespace OpenRA.Renderer.SdlCommon
ErrorHandler.CheckGlError();
Gl.glBlendEquation(Gl.GL_FUNC_REVERSE_SUBTRACT);
break;
case BlendMode.Multiply:
Gl.glEnable(Gl.GL_BLEND);
ErrorHandler.CheckGlError();
Gl.glBlendFuncSeparate(Gl.GL_DST_COLOR, Gl.GL_ZERO, Gl.GL_ONE, Gl.GL_ONE_MINUS_SRC_ALPHA);
ErrorHandler.CheckGlError();
break;
}
ErrorHandler.CheckGlError();
}

Binary file not shown.

View File

@@ -273,10 +273,9 @@ World:
Type: Shroud
ShroudPalette@fog:
Name: fog
Type: Fog
ShroudPalette@combined:
Name: shroudfog
Type: Combined
Fog: true
ShroudRenderer:
Variants: typea, typeb, typec, typed
Country@gdi:
Name: GDI
Race: gdi

View File

@@ -360,4 +360,18 @@ resources:
bti11: bti11
Length: *
bti12: bti12
Length: *
Length: *
shroud:
typea: shadow
Start: 0
Length: 12
typeb: shadow
Start: 12
Length: 12
typec: shadow
Start: 24
Length: 12
typed: shadow
Start: 36
Length: 12

Binary file not shown.

View File

@@ -414,21 +414,28 @@ World:
Name: moveflash
Filename: DATA.R8
Offset: 2572352
A: 64
InvertColor: true
PaletteFromRGBA@disabled:
Name: disabled
R: 0
G: 0
B: 0
A: 180
ShroudPalette@shroud:
Type: Shroud
ShroudPalette@fog:
PaletteFromR8@shroud:
Name: shroud
Filename: DATA.R8
Offset: 12007
InvertColor: true
FogPaletteFromR8@fog:
Name: fog
Type: Fog
ShroudPalette@combined:
Name: shroudfog
Type: Combined
Filename: DATA.R8
Offset: 12007
InvertColor: true
ShroudRenderer:
Variants: typea, typeb, typec, typed
Index: 11, 3, 7, 9, 6, 13, 12, 14, 4, 8, 2, 1, 5, 10
ShroudColor: 31
ShroudBlend: Multiply
Country@Atreides:
Name: Atreides
Race: atreides

View File

@@ -304,10 +304,32 @@ moveflsh:
Start: 3621
Length: 5
Tick: 80
BlendMode: Subtractive
BlendMode: Multiply
resources:
spice: BLOXBASE
Frames: 748, 749, 750, 751, 752, 753, 754, 755, 756, 757, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, 797, 798, 799, 300, 301, 320, 321
Length: 54
Offset: -16,-16
shroud:
typea: DATA
Start: 40
Length: 14
Offset: -16,-16
BlendMode: Multiply
typeb: DATA
Start: 56
Length: 14
Offset: -16,-16
BlendMode: Multiply
typec: DATA
Start: 72
Length: 14
Offset: -16,-16
BlendMode: Multiply
typed: DATA
Start: 88
Length: 14
Offset: -16,-16
BlendMode: Multiply

View File

@@ -616,10 +616,10 @@ World:
Type: Shroud
ShroudPalette@fog:
Name: fog
Type: Fog
ShroudPalette@combined:
Name: shroudfog
Type: Combined
Fog: true
ShroudRenderer:
Index: 255, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 20, 40, 56, 65, 97, 130, 148, 194, 24, 33, 66, 132, 28, 41, 67, 134, 1, 2, 4, 8, 3, 6, 12, 9, 7, 14, 13, 11, 5, 10, 15, 255
UseExtendedIndex: true
Country@0:
Name: Allies
Race: allies

View File

@@ -486,4 +486,8 @@ resources:
gem03: gem03
Length: *
gem04: gem04
Length: *
shroud:
shroud: shadow
Length: *

View File

@@ -101,10 +101,10 @@ World:
Type: Shroud
ShroudPalette@fog:
Name: fog
Type: Fog
ShroudPalette@combined:
Name: shroudfog
Type: Combined
Fog: true
ShroudRenderer:
Index: 255, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 20, 40, 56, 65, 97, 130, 148, 194, 24, 33, 66, 132, 28, 41, 67, 134, 1, 2, 4, 8, 3, 6, 12, 9, 7, 14, 13, 11, 5, 10, 15, 255
UseExtendedIndex: true
VoxelNormalsPalette@normals:
Name: normals
Type: TiberianSun

View File

@@ -253,4 +253,8 @@ moveflsh:
# TODO: placeholder
resources:
fake: shadow
Length: *
shroud:
shroud: shadow
Length: *