diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj
index 5aa44eec6f..1b7998fdaf 100644
--- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj
+++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj
@@ -440,6 +440,10 @@
+
+
+
+
diff --git a/OpenRA.Mods.RA/Render/RenderVoxels.cs b/OpenRA.Mods.RA/Render/RenderVoxels.cs
new file mode 100755
index 0000000000..ecf9e20960
--- /dev/null
+++ b/OpenRA.Mods.RA/Render/RenderVoxels.cs
@@ -0,0 +1,85 @@
+#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.Linq;
+using OpenRA.FileFormats;
+using OpenRA.Graphics;
+using OpenRA.Traits;
+
+namespace OpenRA.Mods.RA.Render
+{
+ public class RenderVoxelsInfo : ITraitInfo, Requires
+ {
+ [Desc("Defaults to the actor name.")]
+ public readonly string Image = null;
+
+ [Desc("Custom palette name")]
+ public readonly string Palette = null;
+
+ [Desc("Custom PlayerColorPalette: BaseName")]
+ public readonly string PlayerPalette = "player";
+ public readonly string NormalsPalette = "normals";
+
+ [Desc("Change the image size.")]
+ public readonly float Scale = 10;
+
+ public readonly WAngle LightPitch = new WAngle(170); // 60 degrees
+ public readonly WAngle LightYaw = new WAngle(739); // 260 degrees
+ public readonly float[] LightAmbientColor = new float[] {0.6f, 0.6f, 0.6f};
+ public readonly float[] LightDiffuseColor = new float[] {0.4f, 0.4f, 0.4f};
+
+ public virtual object Create(ActorInitializer init) { return new RenderVoxels(init.self, this); }
+ }
+
+ public class RenderVoxels : IRender, INotifyOwnerChanged
+ {
+ Actor self;
+ RenderVoxelsInfo info;
+ List components = new List();
+ IBodyOrientation body;
+ WRot camera;
+ WRot lightSource;
+
+ public RenderVoxels(Actor self, RenderVoxelsInfo info)
+ {
+ this.self = self;
+ this.info = info;
+ body = self.Trait();
+ camera = new WRot(WAngle.Zero, body.CameraPitch - new WAngle(256), new WAngle(256));
+ lightSource = new WRot(WAngle.Zero, info.LightPitch, info.LightYaw - new WAngle(256));
+ }
+
+ bool initializePalettes = true;
+ public void OnOwnerChanged(Actor self, Player oldOwner, Player newOwner) { initializePalettes = true; }
+
+ protected PaletteReference colorPalette, normalsPalette, shadowPalette;
+ public IEnumerable Render(Actor self, WorldRenderer wr)
+ {
+ if (initializePalettes)
+ {
+ var paletteName = info.Palette ?? info.PlayerPalette + self.Owner.InternalName;
+ colorPalette = wr.Palette(paletteName);
+ normalsPalette = wr.Palette(info.NormalsPalette);
+ shadowPalette = wr.Palette("shadow");
+ initializePalettes = false;
+ }
+
+ yield return new VoxelRenderable(components, self.CenterPosition, 0, camera, info.Scale,
+ lightSource, info.LightAmbientColor, info.LightDiffuseColor,
+ colorPalette, normalsPalette, shadowPalette);
+ }
+
+ public string Image { get { return info.Image ?? self.Info.Name; } }
+ public void Add(VoxelAnimation v) { components.Add(v); }
+ public void Remove(VoxelAnimation v) { components.Remove(v); }
+ }
+}
diff --git a/OpenRA.Mods.RA/Render/WithVoxelBarrel.cs b/OpenRA.Mods.RA/Render/WithVoxelBarrel.cs
new file mode 100755
index 0000000000..afb3cd8ac3
--- /dev/null
+++ b/OpenRA.Mods.RA/Render/WithVoxelBarrel.cs
@@ -0,0 +1,75 @@
+#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.Linq;
+using OpenRA.FileFormats;
+using OpenRA.Graphics;
+using OpenRA.Traits;
+
+namespace OpenRA.Mods.RA.Render
+{
+ public class WithVoxelBarrelInfo : ITraitInfo, Requires
+ {
+ [Desc("Voxel sequence name to use")]
+ public readonly string Sequence = "barrel";
+ [Desc("Armament to use for recoil")]
+ public readonly string Armament = "primary";
+ [Desc("Visual offset")]
+ public readonly WVec LocalOffset = WVec.Zero;
+
+ public object Create(ActorInitializer init) { return new WithVoxelBarrel(init.self, this); }
+ }
+
+ public class WithVoxelBarrel
+ {
+ WithVoxelBarrelInfo info;
+ Actor self;
+ Armament armament;
+ Turreted turreted;
+ IBodyOrientation body;
+
+ public WithVoxelBarrel(Actor self, WithVoxelBarrelInfo info)
+ {
+ this.self = self;
+ this.info = info;
+ body = self.Trait();
+ armament = self.TraitsImplementing()
+ .First(a => a.Info.Name == info.Armament);
+ turreted = self.TraitsImplementing()
+ .First(tt => tt.Name == armament.Info.Turret);
+
+ var rv = self.Trait();
+ rv.Add(new VoxelAnimation(VoxelProvider.GetVoxel(rv.Image, info.Sequence),
+ () => BarrelOffset(), () => BarrelRotation(),
+ () => false, () => 0));
+ }
+
+ WVec BarrelOffset()
+ {
+ var localOffset = info.LocalOffset + new WVec(-armament.Recoil, WRange.Zero, WRange.Zero);
+ var turretOffset = turreted != null ? turreted.Position(self) : WVec.Zero;
+ var turretOrientation = turreted != null ? turreted.LocalOrientation(self) : WRot.Zero;
+
+ var quantizedBody = body.QuantizeOrientation(self, self.Orientation);
+ var quantizedTurret = body.QuantizeOrientation(self, turretOrientation);
+ return turretOffset + body.LocalToWorld(localOffset.Rotate(quantizedTurret).Rotate(quantizedBody));
+ }
+
+ IEnumerable BarrelRotation()
+ {
+ var b = self.Orientation;
+ var qb = body.QuantizeOrientation(self, b);
+ yield return turreted.LocalOrientation(self) + WRot.FromYaw(b.Yaw - qb.Yaw);
+ yield return qb;
+ }
+ }
+}
diff --git a/OpenRA.Mods.RA/Render/WithVoxelBody.cs b/OpenRA.Mods.RA/Render/WithVoxelBody.cs
new file mode 100755
index 0000000000..175ac1ed4c
--- /dev/null
+++ b/OpenRA.Mods.RA/Render/WithVoxelBody.cs
@@ -0,0 +1,47 @@
+#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.Linq;
+using OpenRA.FileFormats;
+using OpenRA.Graphics;
+using OpenRA.Traits;
+
+namespace OpenRA.Mods.RA.Render
+{
+ public class WithVoxelBodyInfo : ITraitInfo, Requires
+ {
+ public object Create(ActorInitializer init) { return new WithVoxelBody(init.self); }
+ }
+
+ public class WithVoxelBody : IAutoSelectionSize
+ {
+ int2 size;
+
+ public WithVoxelBody(Actor self)
+ {
+ var body = self.Trait();
+ var rv = self.Trait();
+
+ var voxel = VoxelProvider.GetVoxel(rv.Image, "idle");
+ rv.Add(new VoxelAnimation(voxel, () => WVec.Zero,
+ () => new[]{ body.QuantizeOrientation(self, self.Orientation) },
+ () => false, () => 0));
+
+ // Selection size
+ var rvi = self.Info.Traits.Get();
+ var s = (int)(rvi.Scale*voxel.Size.Aggregate(Math.Max));
+ size = new int2(s, s);
+ }
+
+ public int2 SelectionSize(Actor self) { return size; }
+ }
+}
diff --git a/OpenRA.Mods.RA/Render/WithVoxelTurret.cs b/OpenRA.Mods.RA/Render/WithVoxelTurret.cs
new file mode 100755
index 0000000000..29f47c8fe2
--- /dev/null
+++ b/OpenRA.Mods.RA/Render/WithVoxelTurret.cs
@@ -0,0 +1,58 @@
+#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.Linq;
+using OpenRA.FileFormats;
+using OpenRA.Graphics;
+using OpenRA.Traits;
+
+namespace OpenRA.Mods.RA.Render
+{
+ public class WithVoxelTurretInfo : ITraitInfo, Requires
+ {
+ [Desc("Voxel sequence name to use")]
+ public readonly string Sequence = "turret";
+
+ [Desc("Turreted 'Turret' key to display")]
+ public readonly string Turret = "primary";
+
+ public object Create(ActorInitializer init) { return new WithVoxelTurret(init.self, this); }
+ }
+
+ public class WithVoxelTurret
+ {
+ Actor self;
+ Turreted turreted;
+ IBodyOrientation body;
+
+ public WithVoxelTurret(Actor self, WithVoxelTurretInfo info)
+ {
+ this.self = self;
+ body = self.Trait();
+ turreted = self.TraitsImplementing()
+ .First(tt => tt.Name == info.Turret);
+
+ var rv = self.Trait();
+ rv.Add(new VoxelAnimation(VoxelProvider.GetVoxel(rv.Image, info.Sequence),
+ () => turreted.Position(self), () => TurretRotation(),
+ () => false, () => 0));
+ }
+
+ IEnumerable TurretRotation()
+ {
+ var b = self.Orientation;
+ var qb = body.QuantizeOrientation(self, b);
+ yield return turreted.LocalOrientation(self) + WRot.FromYaw(b.Yaw - qb.Yaw);
+ yield return qb;
+ }
+ }
+}