Turn ModelRenderer and VoxelCache into traits

This commit is contained in:
Gustas
2023-09-19 18:40:57 +03:00
committed by Matthias Mailänder
parent d427072cc9
commit 686040a316
33 changed files with 481 additions and 469 deletions

View File

@@ -1,74 +0,0 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.Generic;
using OpenRA.Graphics;
using OpenRA.Primitives;
namespace OpenRA.Mods.Common.Graphics
{
public class ModelPreview : IActorPreview
{
readonly ModelAnimation[] components;
readonly float scale;
readonly float[] lightAmbientColor;
readonly float[] lightDiffuseColor;
readonly WRot lightSource;
readonly WRot camera;
readonly PaletteReference colorPalette;
readonly PaletteReference normalsPalette;
readonly PaletteReference shadowPalette;
readonly WVec offset;
readonly int zOffset;
public ModelPreview(ModelAnimation[] components, in WVec offset, int zOffset, float scale, WAngle lightPitch, WAngle lightYaw,
float[] lightAmbientColor, float[] lightDiffuseColor, WAngle cameraPitch,
PaletteReference colorPalette, PaletteReference normalsPalette, PaletteReference shadowPalette)
{
this.components = components;
this.scale = scale;
this.lightAmbientColor = lightAmbientColor;
this.lightDiffuseColor = lightDiffuseColor;
lightSource = new WRot(WAngle.Zero, new WAngle(256) - lightPitch, lightYaw);
camera = new WRot(WAngle.Zero, cameraPitch - new WAngle(256), new WAngle(256));
this.colorPalette = colorPalette;
this.normalsPalette = normalsPalette;
this.shadowPalette = shadowPalette;
this.offset = offset;
this.zOffset = zOffset;
}
void IActorPreview.Tick() { /* not supported */ }
IEnumerable<IRenderable> IActorPreview.RenderUI(WorldRenderer wr, int2 pos, float scale)
{
yield return new UIModelRenderable(components, WPos.Zero + offset, pos, zOffset, camera, scale * this.scale,
lightSource, lightAmbientColor, lightDiffuseColor,
colorPalette, normalsPalette, shadowPalette);
}
IEnumerable<IRenderable> IActorPreview.Render(WorldRenderer wr, WPos pos)
{
yield return new ModelRenderable(components, pos + offset, zOffset, camera, scale,
lightSource, lightAmbientColor, lightDiffuseColor,
colorPalette, normalsPalette, shadowPalette);
}
IEnumerable<Rectangle> IActorPreview.ScreenBounds(WorldRenderer wr, WPos pos)
{
foreach (var c in components)
yield return c.ScreenBounds(pos, wr, scale);
}
}
}

View File

@@ -1,289 +0,0 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Linq;
using OpenRA.Graphics;
using OpenRA.Primitives;
namespace OpenRA.Mods.Common.Graphics
{
public class ModelRenderable : IPalettedRenderable, IModifyableRenderable
{
readonly IEnumerable<ModelAnimation> models;
readonly WRot camera;
readonly WRot lightSource;
readonly float[] lightAmbientColor;
readonly float[] lightDiffuseColor;
readonly PaletteReference normalsPalette;
readonly PaletteReference shadowPalette;
readonly float scale;
public ModelRenderable(
IEnumerable<ModelAnimation> models, WPos pos, int zOffset, in WRot camera, float scale,
in WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor,
PaletteReference color, PaletteReference normals, PaletteReference shadow)
: this(models, pos, zOffset, camera, scale,
lightSource, lightAmbientColor, lightDiffuseColor,
color, normals, shadow, 1f,
float3.Ones, TintModifiers.None) { }
public ModelRenderable(
IEnumerable<ModelAnimation> models, WPos pos, int zOffset, in WRot camera, float scale,
in WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor,
PaletteReference color, PaletteReference normals, PaletteReference shadow,
float alpha, in float3 tint, TintModifiers tintModifiers)
{
this.models = models;
Pos = pos;
ZOffset = zOffset;
this.scale = scale;
this.camera = camera;
this.lightSource = lightSource;
this.lightAmbientColor = lightAmbientColor;
this.lightDiffuseColor = lightDiffuseColor;
Palette = color;
normalsPalette = normals;
shadowPalette = shadow;
Alpha = alpha;
Tint = tint;
TintModifiers = tintModifiers;
}
public WPos Pos { get; }
public PaletteReference Palette { get; }
public int ZOffset { get; }
public bool IsDecoration => false;
public float Alpha { get; }
public float3 Tint { get; }
public TintModifiers TintModifiers { get; }
public IPalettedRenderable WithPalette(PaletteReference newPalette)
{
return new ModelRenderable(
models, Pos, ZOffset, camera, scale,
lightSource, lightAmbientColor, lightDiffuseColor,
newPalette, normalsPalette, shadowPalette, Alpha, Tint, TintModifiers);
}
public IRenderable WithZOffset(int newOffset)
{
return new ModelRenderable(
models, Pos, newOffset, camera, scale,
lightSource, lightAmbientColor, lightDiffuseColor,
Palette, normalsPalette, shadowPalette, Alpha, Tint, TintModifiers);
}
public IRenderable OffsetBy(in WVec vec)
{
return new ModelRenderable(
models, Pos + vec, ZOffset, camera, scale,
lightSource, lightAmbientColor, lightDiffuseColor,
Palette, normalsPalette, shadowPalette, Alpha, Tint, TintModifiers);
}
public IRenderable AsDecoration() { return this; }
public IModifyableRenderable WithAlpha(float newAlpha)
{
return new ModelRenderable(
models, Pos, ZOffset, camera, scale,
lightSource, lightAmbientColor, lightDiffuseColor,
Palette, normalsPalette, shadowPalette, newAlpha, Tint, TintModifiers);
}
public IModifyableRenderable WithTint(in float3 newTint, TintModifiers newTintModifiers)
{
return new ModelRenderable(
models, Pos, ZOffset, camera, scale,
lightSource, lightAmbientColor, lightDiffuseColor,
Palette, normalsPalette, shadowPalette, Alpha, newTint, newTintModifiers);
}
public IFinalizedRenderable PrepareRender(WorldRenderer wr)
{
return new FinalizedModelRenderable(wr, this);
}
sealed class FinalizedModelRenderable : IFinalizedRenderable
{
readonly ModelRenderable model;
readonly ModelRenderProxy renderProxy;
public FinalizedModelRenderable(WorldRenderer wr, ModelRenderable model)
{
this.model = model;
var draw = model.models.Where(v => v.IsVisible);
var map = wr.World.Map;
var groundOrientation = map.TerrainOrientation(map.CellContaining(model.Pos));
renderProxy = Game.Renderer.WorldModelRenderer.RenderAsync(
wr, draw, model.camera, model.scale, groundOrientation, model.lightSource,
model.lightAmbientColor, model.lightDiffuseColor,
model.Palette, model.normalsPalette, model.shadowPalette);
}
public void Render(WorldRenderer wr)
{
var map = wr.World.Map;
var groundPos = model.Pos - new WVec(0, 0, map.DistanceAboveTerrain(model.Pos).Length);
var groundZ = (float)map.Grid.TileSize.Height * (groundPos.Z - model.Pos.Z) / map.Grid.TileScale;
var pxOrigin = wr.Screen3DPosition(model.Pos);
// HACK: We don't have enough texture channels to pass the depth data to the shader
// so for now just offset everything forward so that the back corner is rendered at pos.
pxOrigin -= new float3(0, 0, Screen3DBounds(wr).Z.X);
// HACK: The previous hack isn't sufficient for the ramp type that is half flat and half
// sloped towards the camera. Offset it by another half cell to avoid clipping.
var cell = map.CellContaining(model.Pos);
if (map.Ramp.Contains(cell) && map.Ramp[cell] == 7)
pxOrigin += new float3(0, 0, 0.5f * map.Grid.TileSize.Height);
var shadowOrigin = pxOrigin - groundZ * new float2(renderProxy.ShadowDirection, 1);
var psb = renderProxy.ProjectedShadowBounds;
var sa = shadowOrigin + psb[0];
var sb = shadowOrigin + psb[2];
var sc = shadowOrigin + psb[1];
var sd = shadowOrigin + psb[3];
var wrsr = Game.Renderer.WorldRgbaSpriteRenderer;
var t = model.Tint;
if (wr.TerrainLighting != null && (model.TintModifiers & TintModifiers.IgnoreWorldTint) == 0)
t *= wr.TerrainLighting.TintAt(model.Pos);
// Shader interprets negative alpha as a flag to use the tint colour directly instead of multiplying the sprite colour
var a = model.Alpha;
if ((model.TintModifiers & TintModifiers.ReplaceColor) != 0)
a *= -1;
wrsr.DrawSprite(renderProxy.ShadowSprite, sa, sb, sc, sd, t, a);
wrsr.DrawSprite(renderProxy.Sprite, pxOrigin - 0.5f * renderProxy.Sprite.Size, 1f, t, a);
}
public void RenderDebugGeometry(WorldRenderer wr)
{
var groundPos = model.Pos - new WVec(0, 0, wr.World.Map.DistanceAboveTerrain(model.Pos).Length);
var groundZ = wr.World.Map.Grid.TileSize.Height * (groundPos.Z - model.Pos.Z) / 1024f;
var pxOrigin = wr.Screen3DPosition(model.Pos);
var shadowOrigin = pxOrigin - groundZ * new float2(renderProxy.ShadowDirection, 1);
// Draw sprite rect
var offset = pxOrigin + renderProxy.Sprite.Offset - 0.5f * renderProxy.Sprite.Size;
var tl = wr.Viewport.WorldToViewPx(offset.XY);
var br = wr.Viewport.WorldToViewPx((offset + renderProxy.Sprite.Size).XY);
Game.Renderer.RgbaColorRenderer.DrawRect(tl, br, 1, Color.Red);
// Draw transformed shadow sprite rect
var c = Color.Purple;
var psb = renderProxy.ProjectedShadowBounds;
Game.Renderer.RgbaColorRenderer.DrawPolygon(new float2[]
{
wr.Viewport.WorldToViewPx(shadowOrigin + psb[1]),
wr.Viewport.WorldToViewPx(shadowOrigin + psb[3]),
wr.Viewport.WorldToViewPx(shadowOrigin + psb[0]),
wr.Viewport.WorldToViewPx(shadowOrigin + psb[2])
}, 1, c);
// Draw bounding box
var draw = model.models.Where(v => v.IsVisible);
var scaleTransform = OpenRA.Graphics.Util.ScaleMatrix(model.scale, model.scale, model.scale);
var cameraTransform = OpenRA.Graphics.Util.MakeFloatMatrix(model.camera.AsMatrix());
foreach (var v in draw)
{
var bounds = v.Model.Bounds(v.FrameFunc());
var rotation = OpenRA.Graphics.Util.MakeFloatMatrix(v.RotationFunc().AsMatrix());
var worldTransform = OpenRA.Graphics.Util.MatrixMultiply(scaleTransform, rotation);
var pxPos = pxOrigin + wr.ScreenVectorComponents(v.OffsetFunc());
var screenTransform = OpenRA.Graphics.Util.MatrixMultiply(cameraTransform, worldTransform);
DrawBoundsBox(wr, pxPos, screenTransform, bounds, 1, Color.Yellow);
}
}
static readonly uint[] CornerXIndex = new uint[] { 0, 0, 0, 0, 3, 3, 3, 3 };
static readonly uint[] CornerYIndex = new uint[] { 1, 1, 4, 4, 1, 1, 4, 4 };
static readonly uint[] CornerZIndex = new uint[] { 2, 5, 2, 5, 2, 5, 2, 5 };
static void DrawBoundsBox(WorldRenderer wr, in float3 pxPos, float[] transform, float[] bounds, float width, Color c)
{
var cr = Game.Renderer.RgbaColorRenderer;
var corners = new float2[8];
for (var i = 0; i < 8; i++)
{
var vec = new[] { bounds[CornerXIndex[i]], bounds[CornerYIndex[i]], bounds[CornerZIndex[i]], 1 };
var screen = OpenRA.Graphics.Util.MatrixVectorMultiply(transform, vec);
corners[i] = wr.Viewport.WorldToViewPx(pxPos + new float3(screen[0], screen[1], screen[2]));
}
// Front face
cr.DrawPolygon(new[] { corners[0], corners[1], corners[3], corners[2] }, width, c);
// Back face
cr.DrawPolygon(new[] { corners[4], corners[5], corners[7], corners[6] }, width, c);
// Horizontal edges
cr.DrawLine(corners[0], corners[4], width, c);
cr.DrawLine(corners[1], corners[5], width, c);
cr.DrawLine(corners[2], corners[6], width, c);
cr.DrawLine(corners[3], corners[7], width, c);
}
public Rectangle ScreenBounds(WorldRenderer wr)
{
return Screen3DBounds(wr).Bounds;
}
(Rectangle Bounds, float2 Z) Screen3DBounds(WorldRenderer wr)
{
var pxOrigin = wr.ScreenPosition(model.Pos);
var draw = model.models.Where(v => v.IsVisible);
var scaleTransform = OpenRA.Graphics.Util.ScaleMatrix(model.scale, model.scale, model.scale);
var cameraTransform = OpenRA.Graphics.Util.MakeFloatMatrix(model.camera.AsMatrix());
var minX = float.MaxValue;
var minY = float.MaxValue;
var minZ = float.MaxValue;
var maxX = float.MinValue;
var maxY = float.MinValue;
var maxZ = float.MinValue;
foreach (var v in draw)
{
var bounds = v.Model.Bounds(v.FrameFunc());
var rotation = OpenRA.Graphics.Util.MakeFloatMatrix(v.RotationFunc().AsMatrix());
var worldTransform = OpenRA.Graphics.Util.MatrixMultiply(scaleTransform, rotation);
var pxPos = pxOrigin + wr.ScreenVectorComponents(v.OffsetFunc());
var screenTransform = OpenRA.Graphics.Util.MatrixMultiply(cameraTransform, worldTransform);
for (var i = 0; i < 8; i++)
{
var vec = new float[] { bounds[CornerXIndex[i]], bounds[CornerYIndex[i]], bounds[CornerZIndex[i]], 1 };
var screen = OpenRA.Graphics.Util.MatrixVectorMultiply(screenTransform, vec);
minX = Math.Min(minX, pxPos.X + screen[0]);
minY = Math.Min(minY, pxPos.Y + screen[1]);
minZ = Math.Min(minZ, pxPos.Z + screen[2]);
maxX = Math.Max(maxX, pxPos.X + screen[0]);
maxY = Math.Max(maxY, pxPos.Y + screen[1]);
maxZ = Math.Max(minZ, pxPos.Z + screen[2]);
}
}
return (Rectangle.FromLTRB((int)minX, (int)minY, (int)maxX, (int)maxY), new float2(minZ, maxZ));
}
}
}
}

View File

@@ -1,151 +0,0 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Linq;
using OpenRA.Graphics;
using OpenRA.Primitives;
namespace OpenRA.Mods.Common.Graphics
{
public class UIModelRenderable : IRenderable, IPalettedRenderable
{
readonly IEnumerable<ModelAnimation> models;
readonly int2 screenPos;
readonly WRot camera;
readonly WRot lightSource;
readonly float[] lightAmbientColor;
readonly float[] lightDiffuseColor;
readonly PaletteReference normalsPalette;
readonly PaletteReference shadowPalette;
readonly float scale;
public UIModelRenderable(
IEnumerable<ModelAnimation> models, WPos effectiveWorldPos, int2 screenPos, int zOffset,
in WRot camera, float scale, in WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor,
PaletteReference color, PaletteReference normals, PaletteReference shadow)
{
this.models = models;
Pos = effectiveWorldPos;
this.screenPos = screenPos;
ZOffset = zOffset;
this.scale = scale;
this.camera = camera;
this.lightSource = lightSource;
this.lightAmbientColor = lightAmbientColor;
this.lightDiffuseColor = lightDiffuseColor;
Palette = color;
normalsPalette = normals;
shadowPalette = shadow;
}
public WPos Pos { get; }
public PaletteReference Palette { get; }
public int ZOffset { get; }
public bool IsDecoration => false;
public IPalettedRenderable WithPalette(PaletteReference newPalette)
{
return new UIModelRenderable(
models, Pos, screenPos, ZOffset, camera, scale,
lightSource, lightAmbientColor, lightDiffuseColor,
newPalette, normalsPalette, shadowPalette);
}
public IRenderable WithZOffset(int newOffset) { return this; }
public IRenderable OffsetBy(in WVec vec) { return this; }
public IRenderable AsDecoration() { return this; }
public IFinalizedRenderable PrepareRender(WorldRenderer wr)
{
return new FinalizedUIModelRenderable(wr, this);
}
sealed class FinalizedUIModelRenderable : IFinalizedRenderable
{
readonly UIModelRenderable model;
readonly ModelRenderProxy renderProxy;
public FinalizedUIModelRenderable(WorldRenderer wr, UIModelRenderable model)
{
this.model = model;
var draw = model.models.Where(v => v.IsVisible);
renderProxy = Game.Renderer.WorldModelRenderer.RenderAsync(
wr, draw, model.camera, model.scale, WRot.None, model.lightSource,
model.lightAmbientColor, model.lightDiffuseColor,
model.Palette, model.normalsPalette, model.shadowPalette);
}
public void Render(WorldRenderer wr)
{
var pxOrigin = model.screenPos;
var psb = renderProxy.ProjectedShadowBounds;
var sa = pxOrigin + psb[0];
var sb = pxOrigin + psb[2];
var sc = pxOrigin + psb[1];
var sd = pxOrigin + psb[3];
Game.Renderer.RgbaSpriteRenderer.DrawSprite(renderProxy.ShadowSprite, sa, sb, sc, sd, float3.Ones, 1f);
Game.Renderer.RgbaSpriteRenderer.DrawSprite(renderProxy.Sprite, pxOrigin - 0.5f * renderProxy.Sprite.Size);
}
public void RenderDebugGeometry(WorldRenderer wr) { }
public Rectangle ScreenBounds(WorldRenderer wr)
{
return Screen3DBounds(wr).Bounds;
}
static readonly uint[] CornerXIndex = { 0, 0, 0, 0, 3, 3, 3, 3 };
static readonly uint[] CornerYIndex = { 1, 1, 4, 4, 1, 1, 4, 4 };
static readonly uint[] CornerZIndex = { 2, 5, 2, 5, 2, 5, 2, 5 };
(Rectangle Bounds, float2 Z) Screen3DBounds(WorldRenderer wr)
{
var pxOrigin = model.screenPos;
var draw = model.models.Where(v => v.IsVisible);
var scaleTransform = OpenRA.Graphics.Util.ScaleMatrix(model.scale, model.scale, model.scale);
var cameraTransform = OpenRA.Graphics.Util.MakeFloatMatrix(model.camera.AsMatrix());
var minX = float.MaxValue;
var minY = float.MaxValue;
var minZ = float.MaxValue;
var maxX = float.MinValue;
var maxY = float.MinValue;
var maxZ = float.MinValue;
foreach (var v in draw)
{
var bounds = v.Model.Bounds(v.FrameFunc());
var rotation = OpenRA.Graphics.Util.MakeFloatMatrix(v.RotationFunc().AsMatrix());
var worldTransform = OpenRA.Graphics.Util.MatrixMultiply(scaleTransform, rotation);
var pxPos = pxOrigin + wr.ScreenVectorComponents(v.OffsetFunc());
var screenTransform = OpenRA.Graphics.Util.MatrixMultiply(cameraTransform, worldTransform);
for (var i = 0; i < 8; i++)
{
var vec = new float[] { bounds[CornerXIndex[i]], bounds[CornerYIndex[i]], bounds[CornerZIndex[i]], 1 };
var screen = OpenRA.Graphics.Util.MatrixVectorMultiply(screenTransform, vec);
minX = Math.Min(minX, pxPos.X + screen[0]);
minY = Math.Min(minY, pxPos.Y + screen[1]);
minZ = Math.Min(minZ, pxPos.Z + screen[2]);
maxX = Math.Max(maxX, pxPos.X + screen[0]);
maxY = Math.Max(maxY, pxPos.Y + screen[1]);
maxZ = Math.Max(minZ, pxPos.Z + screen[2]);
}
}
return (Rectangle.FromLTRB((int)minX, (int)minY, (int)maxX, (int)maxY), new float2(minZ, maxZ));
}
}
}
}

View File

@@ -1,181 +0,0 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Graphics;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits.Render
{
public interface IRenderActorPreviewVoxelsInfo : ITraitInfoInterface
{
IEnumerable<ModelAnimation> RenderPreviewVoxels(
ActorPreviewInitializer init, RenderVoxelsInfo rv, string image, Func<WRot> orientation, int facings, PaletteReference p);
}
public class RenderVoxelsInfo : TraitInfo, IRenderActorPreviewInfo, Requires<BodyOrientationInfo>
{
[Desc("Defaults to the actor name.")]
public readonly string Image = null;
[Desc("Custom palette name")]
[PaletteReference]
public readonly string Palette = null;
[PaletteReference]
[Desc("Custom PlayerColorPalette: BaseName")]
public readonly string PlayerPalette = "player";
[PaletteReference]
public readonly string NormalsPalette = "normals";
[PaletteReference]
public readonly string ShadowPalette = "shadow";
[Desc("Change the image size.")]
public readonly float Scale = 12;
public readonly WAngle LightPitch = WAngle.FromDegrees(50);
public readonly WAngle LightYaw = WAngle.FromDegrees(240);
public readonly float[] LightAmbientColor = { 0.6f, 0.6f, 0.6f };
public readonly float[] LightDiffuseColor = { 0.4f, 0.4f, 0.4f };
public override object Create(ActorInitializer init) { return new RenderVoxels(init.Self, this); }
public virtual IEnumerable<IActorPreview> RenderPreview(ActorPreviewInitializer init)
{
var body = init.Actor.TraitInfo<BodyOrientationInfo>();
var faction = init.GetValue<FactionInit, string>(this);
var ownerName = init.Get<OwnerInit>().InternalName;
var sequences = init.World.Map.Sequences;
var image = Image ?? init.Actor.Name;
var facings = body.QuantizedFacings == -1 ?
init.Actor.TraitInfo<IQuantizeBodyOrientationInfo>().QuantizedBodyFacings(init.Actor, sequences, faction) :
body.QuantizedFacings;
var palette = init.WorldRenderer.Palette(Palette ?? PlayerPalette + ownerName);
var components = init.Actor.TraitInfos<IRenderActorPreviewVoxelsInfo>()
.SelectMany(rvpi => rvpi.RenderPreviewVoxels(init, this, image, init.GetOrientation(), facings, palette))
.ToArray();
yield return new ModelPreview(components, WVec.Zero, 0, Scale, LightPitch,
LightYaw, LightAmbientColor, LightDiffuseColor, body.CameraPitch,
palette, init.WorldRenderer.Palette(NormalsPalette), init.WorldRenderer.Palette(ShadowPalette));
}
}
public class RenderVoxels : IRender, ITick, INotifyOwnerChanged
{
sealed class AnimationWrapper
{
readonly ModelAnimation model;
bool cachedVisible;
WVec cachedOffset;
public AnimationWrapper(ModelAnimation model)
{
this.model = model;
}
public bool Tick()
{
// Return to the caller whether the renderable position or size has changed
var visible = model.IsVisible;
var offset = model.OffsetFunc?.Invoke() ?? WVec.Zero;
var updated = visible != cachedVisible || offset != cachedOffset;
cachedVisible = visible;
cachedOffset = offset;
return updated;
}
}
public readonly RenderVoxelsInfo Info;
readonly List<ModelAnimation> components = new();
readonly Dictionary<ModelAnimation, AnimationWrapper> wrappers = new();
readonly Actor self;
readonly BodyOrientation body;
readonly WRot camera;
readonly WRot lightSource;
public RenderVoxels(Actor self, RenderVoxelsInfo info)
{
this.self = self;
Info = info;
body = self.Trait<BodyOrientation>();
camera = new WRot(WAngle.Zero, body.CameraPitch - new WAngle(256), new WAngle(256));
lightSource = new WRot(WAngle.Zero, new WAngle(256) - info.LightPitch, info.LightYaw);
}
bool initializePalettes = true;
public void OnOwnerChanged(Actor self, Player oldOwner, Player newOwner) { initializePalettes = true; }
void ITick.Tick(Actor self)
{
var updated = false;
foreach (var w in wrappers.Values)
updated |= w.Tick();
if (updated)
self.World.ScreenMap.AddOrUpdate(self);
}
protected PaletteReference colorPalette, normalsPalette, shadowPalette;
IEnumerable<IRenderable> IRender.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(Info.ShadowPalette);
initializePalettes = false;
}
return new IRenderable[]
{
new ModelRenderable(
components, self.CenterPosition, 0, camera, Info.Scale,
lightSource, Info.LightAmbientColor, Info.LightDiffuseColor,
colorPalette, normalsPalette, shadowPalette)
};
}
IEnumerable<Rectangle> IRender.ScreenBounds(Actor self, WorldRenderer wr)
{
var pos = self.CenterPosition;
foreach (var c in components)
if (c.IsVisible)
yield return c.ScreenBounds(pos, wr, Info.Scale);
}
public string Image => Info.Image ?? self.Info.Name;
public void Add(ModelAnimation m)
{
components.Add(m);
wrappers.Add(m, new AnimationWrapper(m));
}
public void Remove(ModelAnimation m)
{
components.Remove(m);
wrappers.Remove(m);
}
}
}

View File

@@ -1,103 +0,0 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Graphics;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits.Render
{
public class WithVoxelBarrelInfo : ConditionalTraitInfo, IRenderActorPreviewVoxelsInfo, Requires<RenderVoxelsInfo>, Requires<ArmamentInfo>, Requires<TurretedInfo>
{
[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;
[Desc("Rotate the barrel relative to the body")]
public readonly WRot LocalOrientation = WRot.None;
[Desc("Defines if the Voxel should have a shadow.")]
public readonly bool ShowShadow = true;
public override object Create(ActorInitializer init) { return new WithVoxelBarrel(init.Self, this); }
public IEnumerable<ModelAnimation> RenderPreviewVoxels(
ActorPreviewInitializer init, RenderVoxelsInfo rv, string image, Func<WRot> orientation, int facings, PaletteReference p)
{
if (!EnabledByDefault)
yield break;
var body = init.Actor.TraitInfo<BodyOrientationInfo>();
var armament = init.Actor.TraitInfos<ArmamentInfo>()
.First(a => a.Name == Armament);
var t = init.Actor.TraitInfos<TurretedInfo>()
.First(tt => tt.Turret == armament.Turret);
var model = init.World.ModelCache.GetModelSequence(image, Sequence);
var turretOrientation = t.PreviewOrientation(init, orientation, facings);
WVec BarrelOffset() => body.LocalToWorld(t.Offset + LocalOffset.Rotate(turretOrientation()));
WRot BarrelOrientation() => LocalOrientation.Rotate(turretOrientation());
yield return new ModelAnimation(model, BarrelOffset, BarrelOrientation, () => false, () => 0, ShowShadow);
}
}
public class WithVoxelBarrel : ConditionalTrait<WithVoxelBarrelInfo>
{
readonly Actor self;
readonly Armament armament;
readonly Turreted turreted;
readonly BodyOrientation body;
public WithVoxelBarrel(Actor self, WithVoxelBarrelInfo info)
: base(info)
{
this.self = self;
body = self.Trait<BodyOrientation>();
armament = self.TraitsImplementing<Armament>()
.First(a => a.Info.Name == Info.Armament);
turreted = self.TraitsImplementing<Turreted>()
.First(tt => tt.Name == armament.Info.Turret);
var rv = self.Trait<RenderVoxels>();
rv.Add(new ModelAnimation(self.World.ModelCache.GetModelSequence(rv.Image, Info.Sequence),
BarrelOffset, BarrelRotation,
() => IsTraitDisabled, () => 0, info.ShowShadow));
}
WVec BarrelOffset()
{
// Barrel offset in turret coordinates
var localOffset = Info.LocalOffset + new WVec(-armament.Recoil, WDist.Zero, WDist.Zero);
// Turret coordinates to body coordinates
var bodyOrientation = body.QuantizeOrientation(self.Orientation);
localOffset = localOffset.Rotate(turreted.WorldOrientation) + turreted.Offset.Rotate(bodyOrientation);
// Body coordinates to world coordinates
return body.LocalToWorld(localOffset);
}
WRot BarrelRotation()
{
return Info.LocalOrientation.Rotate(turreted.WorldOrientation);
}
}
}

View File

@@ -1,67 +0,0 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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 OpenRA.Graphics;
using OpenRA.Mods.Common.Graphics;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits.Render
{
[Desc("Also returns a default selection size that is calculated automatically from the voxel dimensions.")]
public class WithVoxelBodyInfo : ConditionalTraitInfo, IRenderActorPreviewVoxelsInfo, Requires<RenderVoxelsInfo>
{
public readonly string Sequence = "idle";
public readonly WVec Offset;
[Desc("Defines if the Voxel should have a shadow.")]
public readonly bool ShowShadow = true;
public override object Create(ActorInitializer init) { return new WithVoxelBody(init.Self, this); }
public IEnumerable<ModelAnimation> RenderPreviewVoxels(
ActorPreviewInitializer init, RenderVoxelsInfo rv, string image, Func<WRot> orientation, int facings, PaletteReference p)
{
var body = init.Actor.TraitInfo<BodyOrientationInfo>();
var model = init.World.ModelCache.GetModelSequence(image, Sequence);
yield return new ModelAnimation(model, () => Offset,
() => body.QuantizeOrientation(orientation(), facings),
() => false, () => 0, ShowShadow);
}
}
public class WithVoxelBody : ConditionalTrait<WithVoxelBodyInfo>, IAutoMouseBounds
{
readonly ModelAnimation modelAnimation;
readonly RenderVoxels rv;
public WithVoxelBody(Actor self, WithVoxelBodyInfo info)
: base(info)
{
var body = self.Trait<BodyOrientation>();
rv = self.Trait<RenderVoxels>();
var model = self.World.ModelCache.GetModelSequence(rv.Image, info.Sequence);
modelAnimation = new ModelAnimation(model, () => info.Offset,
() => body.QuantizeOrientation(self.Orientation),
() => IsTraitDisabled, () => 0, info.ShowShadow);
rv.Add(modelAnimation);
}
Rectangle IAutoMouseBounds.AutoMouseoverBounds(Actor self, WorldRenderer wr)
{
return modelAnimation.ScreenBounds(self.CenterPosition, wr, rv.Info.Scale);
}
}
}

View File

@@ -1,66 +0,0 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Graphics;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits.Render
{
public class WithVoxelTurretInfo : ConditionalTraitInfo, IRenderActorPreviewVoxelsInfo, Requires<RenderVoxelsInfo>, Requires<TurretedInfo>
{
[Desc("Voxel sequence name to use")]
public readonly string Sequence = "turret";
[Desc("Turreted 'Turret' key to display")]
public readonly string Turret = "primary";
[Desc("Defines if the Voxel should have a shadow.")]
public readonly bool ShowShadow = true;
public override object Create(ActorInitializer init) { return new WithVoxelTurret(init.Self, this); }
public IEnumerable<ModelAnimation> RenderPreviewVoxels(
ActorPreviewInitializer init, RenderVoxelsInfo rv, string image, Func<WRot> orientation, int facings, PaletteReference p)
{
if (!EnabledByDefault)
yield break;
var t = init.Actor.TraitInfos<TurretedInfo>()
.First(tt => tt.Turret == Turret);
var model = init.World.ModelCache.GetModelSequence(image, Sequence);
var turretOffset = t.PreviewPosition(init, orientation);
var turretOrientation = t.PreviewOrientation(init, orientation, facings);
yield return new ModelAnimation(model, turretOffset, turretOrientation, () => false, () => 0, ShowShadow);
}
}
public class WithVoxelTurret : ConditionalTrait<WithVoxelTurretInfo>
{
readonly Turreted turreted;
public WithVoxelTurret(Actor self, WithVoxelTurretInfo info)
: base(info)
{
turreted = self.TraitsImplementing<Turreted>()
.First(tt => tt.Name == Info.Turret);
var rv = self.Trait<RenderVoxels>();
rv.Add(new ModelAnimation(self.World.ModelCache.GetModelSequence(rv.Image, Info.Sequence),
() => turreted.Position(self), () => turreted.WorldOrientation,
() => IsTraitDisabled, () => 0, info.ShowShadow));
}
}
}

View File

@@ -151,17 +151,17 @@ namespace OpenRA.Mods.Common.Widgets.Logic
if (playerWidget != null)
playerWidget.IsVisible = () => isVideoLoaded && !isLoadError;
var modelWidget = panel.GetOrNull<ModelWidget>("VOXEL");
if (modelWidget != null)
if (panel.GetOrNull<Widget>("VOXEL") is IModelWidget modelWidget)
{
modelWidget.GetVoxel = () => currentVoxel;
currentPalette = modelWidget.Palette;
modelScale = modelWidget.Scale;
modelWidget.GetPalette = () => currentPalette;
modelWidget.GetPlayerPalette = () => currentPalette;
modelWidget.GetRotation = () => modelOrientation;
modelWidget.IsVisible = () => !isVideoLoaded && !isLoadError && currentVoxel != null;
modelWidget.GetScale = () => modelScale;
modelWidget.Setup(
() => !isVideoLoaded && !isLoadError && currentVoxel != null,
() => currentPalette,
() => currentPalette,
() => modelScale,
() => currentVoxel,
() => modelOrientation);
}
var errorLabelWidget = panel.GetOrNull("ERROR");
@@ -530,7 +530,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
else if (allowedModelExtensions.Contains(fileExtension))
{
var voxelName = Path.GetFileNameWithoutExtension(filename);
currentVoxel = world.ModelCache.GetModel(prefix + voxelName);
currentVoxel = world.WorldActor.Trait<IModelCache>().GetModel(prefix + voxelName);
currentSprites = null;
}
else if (allowedAudioExtensions.Contains(fileExtension))

View File

@@ -1,216 +0,0 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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 OpenRA.Graphics;
using OpenRA.Mods.Common.Graphics;
using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets
{
public class ModelWidget : Widget
{
public string Palette = "terrain";
public string PlayerPalette = "player";
public string NormalsPalette = "normals";
public string ShadowPalette = "shadow";
public float Scale = 10f;
public int LightPitch = 142;
public int LightYaw = 682;
public float[] LightAmbientColor = new float[] { 0.6f, 0.6f, 0.6f };
public float[] LightDiffuseColor = new float[] { 0.4f, 0.4f, 0.4f };
public WRot Rotation = WRot.None;
public WAngle CameraAngle = WAngle.FromDegrees(40);
public Func<string> GetPalette;
public Func<string> GetPlayerPalette;
public Func<string> GetNormalsPalette;
public Func<string> GetShadowPalette;
public Func<float[]> GetLightAmbientColor;
public Func<float[]> GetLightDiffuseColor;
public Func<float> GetScale;
public Func<int> GetLightPitch;
public Func<int> GetLightYaw;
public Func<IModel> GetVoxel;
public Func<WRot> GetRotation;
public Func<WAngle> GetCameraAngle;
public int2 IdealPreviewSize { get; private set; }
protected readonly WorldRenderer WorldRenderer;
IFinalizedRenderable renderable;
[ObjectCreator.UseCtor]
public ModelWidget(WorldRenderer worldRenderer)
{
GetPalette = () => Palette;
GetPlayerPalette = () => PlayerPalette;
GetNormalsPalette = () => NormalsPalette;
GetShadowPalette = () => ShadowPalette;
GetLightAmbientColor = () => LightAmbientColor;
GetLightDiffuseColor = () => LightDiffuseColor;
GetScale = () => Scale;
GetRotation = () => Rotation;
GetLightPitch = () => LightPitch;
GetLightYaw = () => LightYaw;
GetCameraAngle = () => CameraAngle;
WorldRenderer = worldRenderer;
}
protected ModelWidget(ModelWidget other)
: base(other)
{
Palette = other.Palette;
GetPalette = other.GetPalette;
GetVoxel = other.GetVoxel;
WorldRenderer = other.WorldRenderer;
}
public override Widget Clone()
{
return new ModelWidget(this);
}
IModel cachedVoxel;
string cachedPalette;
string cachedPlayerPalette;
string cachedNormalsPalette;
string cachedShadowPalette;
float cachedScale;
WRot cachedRotation;
float[] cachedLightAmbientColor = new float[] { 0, 0, 0 };
float[] cachedLightDiffuseColor = new float[] { 0, 0, 0 };
int cachedLightPitch;
int cachedLightYaw;
WAngle cachedCameraAngle;
PaletteReference paletteReference;
PaletteReference paletteReferencePlayer;
PaletteReference paletteReferenceNormals;
PaletteReference paletteReferenceShadow;
public override void Draw()
{
if (renderable == null)
return;
renderable.Render(WorldRenderer);
}
public override void PrepareRenderables()
{
var voxel = GetVoxel();
var palette = GetPalette();
var playerPalette = GetPlayerPalette();
var normalsPalette = GetNormalsPalette();
var shadowPalette = GetShadowPalette();
var scale = GetScale();
var rotation = GetRotation();
var lightAmbientColor = GetLightAmbientColor();
var lightDiffuseColor = GetLightDiffuseColor();
var lightPitch = GetLightPitch();
var lightYaw = GetLightYaw();
var cameraAngle = GetCameraAngle();
if (voxel == null || palette == null)
return;
if (voxel != cachedVoxel)
cachedVoxel = voxel;
if (palette != cachedPalette)
{
if (string.IsNullOrEmpty(palette) && string.IsNullOrEmpty(playerPalette))
return;
var paletteName = string.IsNullOrEmpty(palette) ? playerPalette : palette;
paletteReference = WorldRenderer.Palette(paletteName);
cachedPalette = paletteName;
}
if (playerPalette != cachedPlayerPalette)
{
paletteReferencePlayer = WorldRenderer.Palette(playerPalette);
cachedPlayerPalette = playerPalette;
}
if (normalsPalette != cachedNormalsPalette)
{
paletteReferenceNormals = WorldRenderer.Palette(normalsPalette);
cachedNormalsPalette = normalsPalette;
}
if (shadowPalette != cachedShadowPalette)
{
paletteReferenceShadow = WorldRenderer.Palette(shadowPalette);
cachedShadowPalette = shadowPalette;
}
if (scale != cachedScale)
cachedScale = scale;
if (rotation != cachedRotation)
cachedRotation = rotation;
if (lightPitch != cachedLightPitch)
cachedLightPitch = lightPitch;
if (lightYaw != cachedLightYaw)
cachedLightYaw = lightYaw;
if (cachedLightAmbientColor[0] != lightAmbientColor[0] || cachedLightAmbientColor[1] != lightAmbientColor[1] || cachedLightAmbientColor[2] != lightAmbientColor[2])
cachedLightAmbientColor = lightAmbientColor;
if (cachedLightDiffuseColor[0] != lightDiffuseColor[0] || cachedLightDiffuseColor[1] != lightDiffuseColor[1] || cachedLightDiffuseColor[2] != lightDiffuseColor[2])
cachedLightDiffuseColor = lightDiffuseColor;
if (cameraAngle != cachedCameraAngle)
cachedCameraAngle = cameraAngle;
if (cachedVoxel == null)
return;
var animation = new ModelAnimation(
cachedVoxel,
() => WVec.Zero,
() => cachedRotation,
() => false,
() => 0,
true);
var animations = new ModelAnimation[] { animation };
var preview = new ModelPreview(
new ModelAnimation[] { animation }, WVec.Zero, 0,
cachedScale,
new WAngle(cachedLightPitch),
new WAngle(cachedLightYaw),
cachedLightAmbientColor,
cachedLightDiffuseColor,
cachedCameraAngle,
paletteReference,
paletteReferenceNormals,
paletteReferenceShadow);
var screenBounds = animation.ScreenBounds(WPos.Zero, WorldRenderer, scale);
IdealPreviewSize = new int2(screenBounds.Width, screenBounds.Height);
var origin = RenderOrigin + new int2(RenderBounds.Size.Width / 2, RenderBounds.Size.Height / 2);
var camera = new WRot(WAngle.Zero, cachedCameraAngle - new WAngle(256), new WAngle(256));
var modelRenderable = new UIModelRenderable(
animations, WPos.Zero, origin, 0, camera, scale,
WRot.None, cachedLightAmbientColor, cachedLightDiffuseColor,
paletteReferencePlayer, paletteReferenceNormals, paletteReferenceShadow);
renderable = modelRenderable.PrepareRender(WorldRenderer);
}
}
}