Turn ModelRenderer and VoxelCache into traits
This commit is contained in:
committed by
Matthias Mailänder
parent
d427072cc9
commit
686040a316
@@ -48,7 +48,7 @@ namespace OpenRA.Mods.Cnc.FileFormats
|
||||
Transforms[c + ids[k]] = s.ReadFloat();
|
||||
|
||||
Array.Copy(Transforms, 16 * (LimbCount * j + i), testMatrix, 0, 16);
|
||||
if (OpenRA.Graphics.Util.MatrixInverse(testMatrix) == null)
|
||||
if (Util.MatrixInverse(testMatrix) == null)
|
||||
throw new InvalidDataException(
|
||||
$"The transformation matrix for HVA file `{fileName}` section {i} frame {j} is invalid because it is not invertible!");
|
||||
}
|
||||
|
||||
78
OpenRA.Mods.Cnc/Graphics/ModelActorPreview.cs
Normal file
78
OpenRA.Mods.Cnc/Graphics/ModelActorPreview.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
#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.Mods.Cnc.Traits;
|
||||
using OpenRA.Mods.Common.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.Mods.Cnc.Graphics
|
||||
{
|
||||
public class ModelPreview : IActorPreview
|
||||
{
|
||||
readonly ModelRenderer renderer;
|
||||
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(ModelRenderer renderer, 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.renderer = renderer;
|
||||
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(renderer, 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(renderer, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
292
OpenRA.Mods.Cnc/Graphics/ModelRenderable.cs
Normal file
292
OpenRA.Mods.Cnc/Graphics/ModelRenderable.cs
Normal file
@@ -0,0 +1,292 @@
|
||||
#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.Cnc.Traits;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.Mods.Cnc.Graphics
|
||||
{
|
||||
public class ModelRenderable : IPalettedRenderable, IModifyableRenderable
|
||||
{
|
||||
readonly ModelRenderer renderer;
|
||||
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(
|
||||
ModelRenderer renderer, 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(renderer, models, pos, zOffset, camera, scale,
|
||||
lightSource, lightAmbientColor, lightDiffuseColor,
|
||||
color, normals, shadow, 1f,
|
||||
float3.Ones, TintModifiers.None) { }
|
||||
|
||||
public ModelRenderable(
|
||||
ModelRenderer renderer, 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.renderer = renderer;
|
||||
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(
|
||||
renderer, models, Pos, ZOffset, camera, scale,
|
||||
lightSource, lightAmbientColor, lightDiffuseColor,
|
||||
newPalette, normalsPalette, shadowPalette, Alpha, Tint, TintModifiers);
|
||||
}
|
||||
|
||||
public IRenderable WithZOffset(int newOffset)
|
||||
{
|
||||
return new ModelRenderable(
|
||||
renderer, models, Pos, newOffset, camera, scale,
|
||||
lightSource, lightAmbientColor, lightDiffuseColor,
|
||||
Palette, normalsPalette, shadowPalette, Alpha, Tint, TintModifiers);
|
||||
}
|
||||
|
||||
public IRenderable OffsetBy(in WVec vec)
|
||||
{
|
||||
return new ModelRenderable(
|
||||
renderer, 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(
|
||||
renderer, 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(
|
||||
renderer, 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 = model.renderer.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 = Util.ScaleMatrix(model.scale, model.scale, model.scale);
|
||||
var cameraTransform = Util.MakeFloatMatrix(model.camera.AsMatrix());
|
||||
|
||||
foreach (var v in draw)
|
||||
{
|
||||
var bounds = v.Model.Bounds(v.FrameFunc());
|
||||
var rotation = Util.MakeFloatMatrix(v.RotationFunc().AsMatrix());
|
||||
var worldTransform = Util.MatrixMultiply(scaleTransform, rotation);
|
||||
|
||||
var pxPos = pxOrigin + wr.ScreenVectorComponents(v.OffsetFunc());
|
||||
var screenTransform = 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 = 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 = Util.ScaleMatrix(model.scale, model.scale, model.scale);
|
||||
var cameraTransform = 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 = Util.MakeFloatMatrix(v.RotationFunc().AsMatrix());
|
||||
var worldTransform = Util.MatrixMultiply(scaleTransform, rotation);
|
||||
|
||||
var pxPos = pxOrigin + wr.ScreenVectorComponents(v.OffsetFunc());
|
||||
var screenTransform = 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 = 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
154
OpenRA.Mods.Cnc/Graphics/UIModelRenderable.cs
Normal file
154
OpenRA.Mods.Cnc/Graphics/UIModelRenderable.cs
Normal file
@@ -0,0 +1,154 @@
|
||||
#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.Cnc.Traits;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.Mods.Cnc.Graphics
|
||||
{
|
||||
public class UIModelRenderable : IRenderable, IPalettedRenderable
|
||||
{
|
||||
readonly ModelRenderer renderer;
|
||||
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(
|
||||
ModelRenderer renderer, 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.renderer = renderer;
|
||||
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(
|
||||
renderer, 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 = model.renderer.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 = Util.ScaleMatrix(model.scale, model.scale, model.scale);
|
||||
var cameraTransform = 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 = Util.MakeFloatMatrix(v.RotationFunc().AsMatrix());
|
||||
var worldTransform = Util.MatrixMultiply(scaleTransform, rotation);
|
||||
|
||||
var pxPos = pxOrigin + wr.ScreenVectorComponents(v.OffsetFunc());
|
||||
var screenTransform = 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 = 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,8 +74,8 @@ namespace OpenRA.Mods.Cnc.Graphics
|
||||
t[14] *= l.Scale * (l.Bounds[5] - l.Bounds[2]) / l.Size[2];
|
||||
|
||||
// Center, flip and scale
|
||||
t = OpenRA.Graphics.Util.MatrixMultiply(t, OpenRA.Graphics.Util.TranslationMatrix(l.Bounds[0], l.Bounds[1], l.Bounds[2]));
|
||||
t = OpenRA.Graphics.Util.MatrixMultiply(OpenRA.Graphics.Util.ScaleMatrix(l.Scale, -l.Scale, l.Scale), t);
|
||||
t = Util.MatrixMultiply(t, Util.TranslationMatrix(l.Bounds[0], l.Bounds[1], l.Bounds[2]));
|
||||
t = Util.MatrixMultiply(Util.ScaleMatrix(l.Scale, -l.Scale, l.Scale), t);
|
||||
|
||||
return t;
|
||||
}
|
||||
@@ -119,7 +119,7 @@ namespace OpenRA.Mods.Cnc.Graphics
|
||||
};
|
||||
|
||||
// Calculate limb bounding box
|
||||
var bb = OpenRA.Graphics.Util.MatrixAABBMultiply(TransformationMatrix(j, frame), b);
|
||||
var bb = Util.MatrixAABBMultiply(TransformationMatrix(j, frame), b);
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
ret[i] = Math.Min(ret[i], bb[i]);
|
||||
|
||||
@@ -14,7 +14,7 @@ using OpenRA.Mods.Cnc.Traits;
|
||||
using OpenRA.Scripting;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.CnC.Scripting
|
||||
namespace OpenRA.Mods.Cnc.Scripting
|
||||
{
|
||||
[ScriptPropertyGroup("Support Powers")]
|
||||
public class IonCannonProperties : ScriptActorProperties, Requires<IonCannonPowerInfo>
|
||||
|
||||
188
OpenRA.Mods.Cnc/Traits/Render/RenderVoxels.cs
Normal file
188
OpenRA.Mods.Cnc/Traits/Render/RenderVoxels.cs
Normal file
@@ -0,0 +1,188 @@
|
||||
#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.Cnc.Graphics;
|
||||
using OpenRA.Mods.Common;
|
||||
using OpenRA.Mods.Common.Graphics;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Cnc.Traits.Render
|
||||
{
|
||||
public interface IRenderActorPreviewVoxelsInfo : ITraitInfoInterface
|
||||
{
|
||||
IEnumerable<ModelAnimation> RenderPreviewVoxels(IModelCache cache,
|
||||
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 renderer = init.World.WorldActor.Trait<ModelRenderer>();
|
||||
var cache = init.World.WorldActor.Trait<IModelCache>();
|
||||
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(cache, init, this, image, init.GetOrientation(), facings, palette))
|
||||
.ToArray();
|
||||
|
||||
yield return new ModelPreview(renderer, 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;
|
||||
public readonly ModelRenderer Renderer;
|
||||
|
||||
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;
|
||||
Renderer = self.World.WorldActor.Trait<ModelRenderer>();
|
||||
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(
|
||||
Renderer, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
104
OpenRA.Mods.Cnc/Traits/Render/WithVoxelBarrel.cs
Normal file
104
OpenRA.Mods.Cnc/Traits/Render/WithVoxelBarrel.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
#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.Mods.Common.Traits;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Cnc.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(IModelCache cache,
|
||||
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 = cache.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(rv.Renderer.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
68
OpenRA.Mods.Cnc/Traits/Render/WithVoxelBody.cs
Normal file
68
OpenRA.Mods.Cnc/Traits/Render/WithVoxelBody.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
#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.Mods.Common.Traits;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Cnc.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(IModelCache cache,
|
||||
ActorPreviewInitializer init, RenderVoxelsInfo rv, string image, Func<WRot> orientation, int facings, PaletteReference p)
|
||||
{
|
||||
var body = init.Actor.TraitInfo<BodyOrientationInfo>();
|
||||
var model = cache.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 = rv.Renderer.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
67
OpenRA.Mods.Cnc/Traits/Render/WithVoxelTurret.cs
Normal file
67
OpenRA.Mods.Cnc/Traits/Render/WithVoxelTurret.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
#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.Mods.Common.Traits;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Cnc.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(IModelCache cache,
|
||||
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 = cache.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(rv.Renderer.ModelCache.GetModelSequence(rv.Image, Info.Sequence),
|
||||
() => turreted.Position(self), () => turreted.WorldOrientation,
|
||||
() => IsTraitDisabled, () => 0, info.ShowShadow));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,6 @@ using System.Collections.Generic;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Common.Graphics;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Mods.Common.Traits.Render;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
@@ -34,11 +33,11 @@ namespace OpenRA.Mods.Cnc.Traits.Render
|
||||
|
||||
public override object Create(ActorInitializer init) { return new WithVoxelUnloadBody(init.Self, this); }
|
||||
|
||||
public IEnumerable<ModelAnimation> RenderPreviewVoxels(
|
||||
public IEnumerable<ModelAnimation> RenderPreviewVoxels(IModelCache cache,
|
||||
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, IdleSequence);
|
||||
var model = cache.GetModelSequence(image, IdleSequence);
|
||||
yield return new ModelAnimation(model, () => WVec.Zero,
|
||||
() => body.QuantizeOrientation(orientation(), facings),
|
||||
() => false, () => 0, ShowShadow);
|
||||
@@ -57,7 +56,7 @@ namespace OpenRA.Mods.Cnc.Traits.Render
|
||||
var body = self.Trait<BodyOrientation>();
|
||||
rv = self.Trait<RenderVoxels>();
|
||||
|
||||
var idleModel = self.World.ModelCache.GetModelSequence(rv.Image, info.IdleSequence);
|
||||
var idleModel = rv.Renderer.ModelCache.GetModelSequence(rv.Image, info.IdleSequence);
|
||||
modelAnimation = new ModelAnimation(idleModel, () => WVec.Zero,
|
||||
() => body.QuantizeOrientation(self.Orientation),
|
||||
() => docked,
|
||||
@@ -65,7 +64,7 @@ namespace OpenRA.Mods.Cnc.Traits.Render
|
||||
|
||||
rv.Add(modelAnimation);
|
||||
|
||||
var unloadModel = self.World.ModelCache.GetModelSequence(rv.Image, info.UnloadSequence);
|
||||
var unloadModel = rv.Renderer.ModelCache.GetModelSequence(rv.Image, info.UnloadSequence);
|
||||
rv.Add(new ModelAnimation(unloadModel, () => WVec.Zero,
|
||||
() => body.QuantizeOrientation(self.Orientation),
|
||||
() => !docked,
|
||||
|
||||
@@ -14,7 +14,6 @@ using System.Collections.Generic;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Common.Graphics;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Mods.Common.Traits.Render;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
@@ -31,10 +30,10 @@ namespace OpenRA.Mods.Cnc.Traits.Render
|
||||
public readonly bool ShowShadow = true;
|
||||
public override object Create(ActorInitializer init) { return new WithVoxelWalkerBody(init.Self, this); }
|
||||
|
||||
public IEnumerable<ModelAnimation> RenderPreviewVoxels(
|
||||
public IEnumerable<ModelAnimation> RenderPreviewVoxels(IModelCache cache,
|
||||
ActorPreviewInitializer init, RenderVoxelsInfo rv, string image, Func<WRot> orientation, int facings, PaletteReference p)
|
||||
{
|
||||
var model = init.World.ModelCache.GetModelSequence(image, Sequence);
|
||||
var model = cache.GetModelSequence(image, Sequence);
|
||||
var body = init.Actor.TraitInfo<BodyOrientationInfo>();
|
||||
var frame = init.GetValue<BodyAnimationFrameInit, uint>(this, 0);
|
||||
|
||||
@@ -61,7 +60,7 @@ namespace OpenRA.Mods.Cnc.Traits.Render
|
||||
var body = self.Trait<BodyOrientation>();
|
||||
rv = self.Trait<RenderVoxels>();
|
||||
|
||||
var model = self.World.ModelCache.GetModelSequence(rv.Image, info.Sequence);
|
||||
var model = rv.Renderer.ModelCache.GetModelSequence(rv.Image, info.Sequence);
|
||||
frames = model.Frames;
|
||||
modelAnimation = new ModelAnimation(model, () => WVec.Zero,
|
||||
() => body.QuantizeOrientation(self.Orientation),
|
||||
|
||||
393
OpenRA.Mods.Cnc/Traits/World/ModelRenderer.cs
Normal file
393
OpenRA.Mods.Cnc/Traits/World/ModelRenderer.cs
Normal file
@@ -0,0 +1,393 @@
|
||||
#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;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Cnc.Traits
|
||||
{
|
||||
public class ModelRenderProxy
|
||||
{
|
||||
public readonly Sprite Sprite;
|
||||
public readonly Sprite ShadowSprite;
|
||||
public readonly float ShadowDirection;
|
||||
public readonly float3[] ProjectedShadowBounds;
|
||||
|
||||
public ModelRenderProxy(Sprite sprite, Sprite shadowSprite, float3[] projectedShadowBounds, float shadowDirection)
|
||||
{
|
||||
Sprite = sprite;
|
||||
ShadowSprite = shadowSprite;
|
||||
ProjectedShadowBounds = projectedShadowBounds;
|
||||
ShadowDirection = shadowDirection;
|
||||
}
|
||||
}
|
||||
|
||||
[TraitLocation(SystemActors.World | SystemActors.EditorWorld)]
|
||||
[Desc("Render voxels")]
|
||||
public class ModelRendererInfo : TraitInfo, Requires<IModelCacheInfo>
|
||||
{
|
||||
public override object Create(ActorInitializer init) { return new ModelRenderer(this, init.Self); }
|
||||
}
|
||||
|
||||
public sealed class ModelRenderer : IDisposable, IRenderer, INotifyActorDisposing
|
||||
{
|
||||
// Static constants
|
||||
static readonly float[] ShadowDiffuse = new float[] { 0, 0, 0 };
|
||||
static readonly float[] ShadowAmbient = new float[] { 1, 1, 1 };
|
||||
static readonly float2 SpritePadding = new(2, 2);
|
||||
static readonly float[] ZeroVector = new float[] { 0, 0, 0, 1 };
|
||||
static readonly float[] ZVector = new float[] { 0, 0, 1, 1 };
|
||||
static readonly float[] FlipMtx = Util.ScaleMatrix(1, -1, 1);
|
||||
static readonly float[] ShadowScaleFlipMtx = Util.ScaleMatrix(2, -2, 2);
|
||||
static readonly float[] GroundNormal = { 0, 0, 1, 1 };
|
||||
|
||||
readonly Renderer renderer;
|
||||
readonly IShader shader;
|
||||
public readonly IModelCache ModelCache;
|
||||
|
||||
readonly Dictionary<Sheet, IFrameBuffer> mappedBuffers = new();
|
||||
readonly Stack<KeyValuePair<Sheet, IFrameBuffer>> unmappedBuffers = new();
|
||||
readonly List<(Sheet Sheet, Action Func)> doRender = new();
|
||||
readonly int sheetSize;
|
||||
|
||||
SheetBuilder sheetBuilderForFrame;
|
||||
bool isInFrame;
|
||||
|
||||
public void SetPalette(ITexture palette)
|
||||
{
|
||||
shader.SetTexture("Palette", palette);
|
||||
}
|
||||
|
||||
public ModelRenderer(ModelRendererInfo info, Actor self)
|
||||
{
|
||||
renderer = Game.Renderer;
|
||||
shader = renderer.CreateShader(new ModelShaderBindings());
|
||||
renderer.WorldRenderers = renderer.WorldRenderers.Append(this).ToArray();
|
||||
|
||||
ModelCache = self.Trait<IModelCache>();
|
||||
|
||||
sheetSize = Game.Settings.Graphics.SheetSize;
|
||||
var a = 2f / sheetSize;
|
||||
var view = new[]
|
||||
{
|
||||
a, 0, 0, 0,
|
||||
0, -a, 0, 0,
|
||||
0, 0, -2 * a, 0,
|
||||
-1, 1, 0, 1
|
||||
};
|
||||
|
||||
shader.SetMatrix("View", view);
|
||||
}
|
||||
|
||||
public ModelRenderProxy RenderAsync(
|
||||
WorldRenderer wr, IEnumerable<ModelAnimation> models, in WRot camera, float scale,
|
||||
in WRot groundOrientation, in WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor,
|
||||
PaletteReference color, PaletteReference normals, PaletteReference shadowPalette)
|
||||
{
|
||||
if (!isInFrame)
|
||||
throw new InvalidOperationException("BeginFrame has not been called. You cannot render until a frame has been started.");
|
||||
|
||||
// Correct for inverted y-axis
|
||||
var scaleTransform = Util.ScaleMatrix(scale, scale, scale);
|
||||
|
||||
// Correct for bogus light source definition
|
||||
var lightYaw = Util.MakeFloatMatrix(new WRot(WAngle.Zero, WAngle.Zero, -lightSource.Yaw).AsMatrix());
|
||||
var lightPitch = Util.MakeFloatMatrix(new WRot(WAngle.Zero, -lightSource.Pitch, WAngle.Zero).AsMatrix());
|
||||
var ground = Util.MakeFloatMatrix(groundOrientation.AsMatrix());
|
||||
var shadowTransform = Util.MatrixMultiply(Util.MatrixMultiply(lightPitch, lightYaw), Util.MatrixInverse(ground));
|
||||
|
||||
var groundNormal = Util.MatrixVectorMultiply(ground, GroundNormal);
|
||||
|
||||
var invShadowTransform = Util.MatrixInverse(shadowTransform);
|
||||
var cameraTransform = Util.MakeFloatMatrix(camera.AsMatrix());
|
||||
var invCameraTransform = Util.MatrixInverse(cameraTransform);
|
||||
if (invCameraTransform == null)
|
||||
throw new InvalidOperationException("Failed to invert the cameraTransform matrix during RenderAsync.");
|
||||
|
||||
// Sprite rectangle
|
||||
var tl = new float2(float.MaxValue, float.MaxValue);
|
||||
var br = new float2(float.MinValue, float.MinValue);
|
||||
|
||||
// Shadow sprite rectangle
|
||||
var stl = new float2(float.MaxValue, float.MaxValue);
|
||||
var sbr = new float2(float.MinValue, float.MinValue);
|
||||
|
||||
foreach (var m in models)
|
||||
{
|
||||
// Convert screen offset back to world coords
|
||||
var offsetVec = Util.MatrixVectorMultiply(invCameraTransform, wr.ScreenVector(m.OffsetFunc()));
|
||||
var offsetTransform = Util.TranslationMatrix(offsetVec[0], offsetVec[1], offsetVec[2]);
|
||||
|
||||
var worldTransform = Util.MakeFloatMatrix(m.RotationFunc().AsMatrix());
|
||||
worldTransform = Util.MatrixMultiply(scaleTransform, worldTransform);
|
||||
worldTransform = Util.MatrixMultiply(offsetTransform, worldTransform);
|
||||
|
||||
var bounds = m.Model.Bounds(m.FrameFunc());
|
||||
var worldBounds = Util.MatrixAABBMultiply(worldTransform, bounds);
|
||||
var screenBounds = Util.MatrixAABBMultiply(cameraTransform, worldBounds);
|
||||
var shadowBounds = Util.MatrixAABBMultiply(shadowTransform, worldBounds);
|
||||
|
||||
// Aggregate bounds rects
|
||||
tl = float2.Min(tl, new float2(screenBounds[0], screenBounds[1]));
|
||||
br = float2.Max(br, new float2(screenBounds[3], screenBounds[4]));
|
||||
stl = float2.Min(stl, new float2(shadowBounds[0], shadowBounds[1]));
|
||||
sbr = float2.Max(sbr, new float2(shadowBounds[3], shadowBounds[4]));
|
||||
}
|
||||
|
||||
// Inflate rects to ensure rendering is within bounds
|
||||
tl -= SpritePadding;
|
||||
br += SpritePadding;
|
||||
stl -= SpritePadding;
|
||||
sbr += SpritePadding;
|
||||
|
||||
// Corners of the shadow quad, in shadow-space
|
||||
var corners = new float[][]
|
||||
{
|
||||
new[] { stl.X, stl.Y, 0, 1 },
|
||||
new[] { sbr.X, sbr.Y, 0, 1 },
|
||||
new[] { sbr.X, stl.Y, 0, 1 },
|
||||
new[] { stl.X, sbr.Y, 0, 1 }
|
||||
};
|
||||
|
||||
var shadowScreenTransform = Util.MatrixMultiply(cameraTransform, invShadowTransform);
|
||||
var shadowGroundNormal = Util.MatrixVectorMultiply(shadowTransform, groundNormal);
|
||||
var screenCorners = new float3[4];
|
||||
for (var j = 0; j < 4; j++)
|
||||
{
|
||||
// Project to ground plane
|
||||
corners[j][2] = -(corners[j][1] * shadowGroundNormal[1] / shadowGroundNormal[2] +
|
||||
corners[j][0] * shadowGroundNormal[0] / shadowGroundNormal[2]);
|
||||
|
||||
// Rotate to camera-space
|
||||
corners[j] = Util.MatrixVectorMultiply(shadowScreenTransform, corners[j]);
|
||||
screenCorners[j] = new float3(corners[j][0], corners[j][1], 0);
|
||||
}
|
||||
|
||||
// Shadows are rendered at twice the resolution to reduce artifacts
|
||||
CalculateSpriteGeometry(tl, br, 1, out var spriteSize, out var spriteOffset);
|
||||
CalculateSpriteGeometry(stl, sbr, 2, out var shadowSpriteSize, out var shadowSpriteOffset);
|
||||
|
||||
sheetBuilderForFrame ??= new SheetBuilder(SheetType.BGRA, AllocateSheet);
|
||||
|
||||
var sprite = sheetBuilderForFrame.Allocate(spriteSize, 0, spriteOffset);
|
||||
var shadowSprite = sheetBuilderForFrame.Allocate(shadowSpriteSize, 0, shadowSpriteOffset);
|
||||
var sb = sprite.Bounds;
|
||||
var ssb = shadowSprite.Bounds;
|
||||
var spriteCenter = new float2(sb.Left + sb.Width / 2, sb.Top + sb.Height / 2);
|
||||
var shadowCenter = new float2(ssb.Left + ssb.Width / 2, ssb.Top + ssb.Height / 2);
|
||||
|
||||
var translateMtx = Util.TranslationMatrix(spriteCenter.X - spriteOffset.X, sheetSize - (spriteCenter.Y - spriteOffset.Y), 0);
|
||||
var shadowTranslateMtx = Util.TranslationMatrix(shadowCenter.X - shadowSpriteOffset.X, sheetSize - (shadowCenter.Y - shadowSpriteOffset.Y), 0);
|
||||
var correctionTransform = Util.MatrixMultiply(translateMtx, FlipMtx);
|
||||
var shadowCorrectionTransform = Util.MatrixMultiply(shadowTranslateMtx, ShadowScaleFlipMtx);
|
||||
|
||||
doRender.Add((sprite.Sheet, () =>
|
||||
{
|
||||
foreach (var m in models)
|
||||
{
|
||||
// Convert screen offset to world offset
|
||||
var offsetVec = Util.MatrixVectorMultiply(invCameraTransform, wr.ScreenVector(m.OffsetFunc()));
|
||||
var offsetTransform = Util.TranslationMatrix(offsetVec[0], offsetVec[1], offsetVec[2]);
|
||||
|
||||
var rotations = Util.MakeFloatMatrix(m.RotationFunc().AsMatrix());
|
||||
var worldTransform = Util.MatrixMultiply(scaleTransform, rotations);
|
||||
worldTransform = Util.MatrixMultiply(offsetTransform, worldTransform);
|
||||
|
||||
var transform = Util.MatrixMultiply(cameraTransform, worldTransform);
|
||||
transform = Util.MatrixMultiply(correctionTransform, transform);
|
||||
|
||||
var shadow = Util.MatrixMultiply(shadowTransform, worldTransform);
|
||||
shadow = Util.MatrixMultiply(shadowCorrectionTransform, shadow);
|
||||
|
||||
var lightTransform = Util.MatrixMultiply(Util.MatrixInverse(rotations), invShadowTransform);
|
||||
|
||||
var frame = m.FrameFunc();
|
||||
for (uint i = 0; i < m.Model.Sections; i++)
|
||||
{
|
||||
var rd = m.Model.RenderData(i);
|
||||
var t = m.Model.TransformationMatrix(i, frame);
|
||||
var it = Util.MatrixInverse(t);
|
||||
if (it == null)
|
||||
throw new InvalidOperationException($"Failed to invert the transformed matrix of frame {i} during RenderAsync.");
|
||||
|
||||
// Transform light vector from shadow -> world -> limb coords
|
||||
var lightDirection = ExtractRotationVector(Util.MatrixMultiply(it, lightTransform));
|
||||
|
||||
Render(rd, ModelCache, Util.MatrixMultiply(transform, t), lightDirection,
|
||||
lightAmbientColor, lightDiffuseColor, color.TextureMidIndex, normals.TextureMidIndex);
|
||||
|
||||
// Disable shadow normals by forcing zero diffuse and identity ambient light
|
||||
if (m.ShowShadow)
|
||||
Render(rd, ModelCache, Util.MatrixMultiply(shadow, t), lightDirection,
|
||||
ShadowAmbient, ShadowDiffuse, shadowPalette.TextureMidIndex, normals.TextureMidIndex);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
var screenLightVector = Util.MatrixVectorMultiply(invShadowTransform, ZVector);
|
||||
screenLightVector = Util.MatrixVectorMultiply(cameraTransform, screenLightVector);
|
||||
return new ModelRenderProxy(sprite, shadowSprite, screenCorners, -screenLightVector[2] / screenLightVector[1]);
|
||||
}
|
||||
|
||||
static void CalculateSpriteGeometry(float2 tl, float2 br, float scale, out Size size, out int2 offset)
|
||||
{
|
||||
var width = (int)(scale * (br.X - tl.X));
|
||||
var height = (int)(scale * (br.Y - tl.Y));
|
||||
offset = (0.5f * scale * (br + tl)).ToInt2();
|
||||
|
||||
// Width and height must be even to avoid rendering glitches
|
||||
if ((width & 1) == 1)
|
||||
width += 1;
|
||||
if ((height & 1) == 1)
|
||||
height += 1;
|
||||
|
||||
size = new Size(width, height);
|
||||
}
|
||||
|
||||
static float[] ExtractRotationVector(float[] mtx)
|
||||
{
|
||||
var tVec = Util.MatrixVectorMultiply(mtx, ZVector);
|
||||
var tOrigin = Util.MatrixVectorMultiply(mtx, ZeroVector);
|
||||
tVec[0] -= tOrigin[0] * tVec[3] / tOrigin[3];
|
||||
tVec[1] -= tOrigin[1] * tVec[3] / tOrigin[3];
|
||||
tVec[2] -= tOrigin[2] * tVec[3] / tOrigin[3];
|
||||
|
||||
// Renormalize
|
||||
var w = (float)Math.Sqrt(tVec[0] * tVec[0] + tVec[1] * tVec[1] + tVec[2] * tVec[2]);
|
||||
tVec[0] /= w;
|
||||
tVec[1] /= w;
|
||||
tVec[2] /= w;
|
||||
tVec[3] = 1f;
|
||||
|
||||
return tVec;
|
||||
}
|
||||
|
||||
void Render(
|
||||
ModelRenderData renderData,
|
||||
IModelCache cache,
|
||||
float[] t, float[] lightDirection,
|
||||
float[] ambientLight, float[] diffuseLight,
|
||||
float colorPaletteTextureMidIndex, float normalsPaletteTextureMidIndex)
|
||||
{
|
||||
shader.SetTexture("DiffuseTexture", renderData.Sheet.GetTexture());
|
||||
shader.SetVec("PaletteRows", colorPaletteTextureMidIndex, normalsPaletteTextureMidIndex);
|
||||
shader.SetMatrix("TransformMatrix", t);
|
||||
shader.SetVec("LightDirection", lightDirection, 4);
|
||||
shader.SetVec("AmbientLight", ambientLight, 3);
|
||||
shader.SetVec("DiffuseLight", diffuseLight, 3);
|
||||
|
||||
shader.PrepareRender();
|
||||
renderer.DrawBatch(cache.VertexBuffer, shader, renderData.Start, renderData.Count, PrimitiveType.TriangleList);
|
||||
}
|
||||
|
||||
public void BeginFrame()
|
||||
{
|
||||
if (isInFrame)
|
||||
throw new InvalidOperationException("BeginFrame has already been called. A new frame cannot be started until EndFrame has been called.");
|
||||
|
||||
isInFrame = true;
|
||||
|
||||
foreach (var kv in mappedBuffers)
|
||||
unmappedBuffers.Push(kv);
|
||||
mappedBuffers.Clear();
|
||||
}
|
||||
|
||||
IFrameBuffer EnableFrameBuffer(Sheet s)
|
||||
{
|
||||
var fbo = mappedBuffers[s];
|
||||
Game.Renderer.Flush();
|
||||
fbo.Bind();
|
||||
|
||||
Game.Renderer.EnableDepthBuffer();
|
||||
return fbo;
|
||||
}
|
||||
|
||||
static void DisableFrameBuffer(IFrameBuffer fbo)
|
||||
{
|
||||
Game.Renderer.Flush();
|
||||
Game.Renderer.DisableDepthBuffer();
|
||||
fbo.Unbind();
|
||||
}
|
||||
|
||||
public void EndFrame()
|
||||
{
|
||||
if (!isInFrame)
|
||||
throw new InvalidOperationException("BeginFrame has not been called. There is no frame to end.");
|
||||
|
||||
isInFrame = false;
|
||||
sheetBuilderForFrame = null;
|
||||
|
||||
if (doRender.Count == 0)
|
||||
return;
|
||||
|
||||
Sheet currentSheet = null;
|
||||
IFrameBuffer fbo = null;
|
||||
foreach (var v in doRender)
|
||||
{
|
||||
// Change sheet
|
||||
if (v.Sheet != currentSheet)
|
||||
{
|
||||
if (fbo != null)
|
||||
DisableFrameBuffer(fbo);
|
||||
|
||||
currentSheet = v.Sheet;
|
||||
fbo = EnableFrameBuffer(currentSheet);
|
||||
}
|
||||
|
||||
v.Func();
|
||||
}
|
||||
|
||||
if (fbo != null)
|
||||
DisableFrameBuffer(fbo);
|
||||
|
||||
doRender.Clear();
|
||||
}
|
||||
|
||||
public Sheet AllocateSheet()
|
||||
{
|
||||
// Reuse cached fbo
|
||||
if (unmappedBuffers.Count > 0)
|
||||
{
|
||||
var kv = unmappedBuffers.Pop();
|
||||
mappedBuffers.Add(kv.Key, kv.Value);
|
||||
return kv.Key;
|
||||
}
|
||||
|
||||
var framebuffer = renderer.CreateFrameBuffer(new Size(sheetSize, sheetSize));
|
||||
var sheet = new Sheet(SheetType.BGRA, framebuffer.Texture);
|
||||
mappedBuffers.Add(sheet, framebuffer);
|
||||
|
||||
return sheet;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var kvp in mappedBuffers.Concat(unmappedBuffers))
|
||||
{
|
||||
kvp.Key.Dispose();
|
||||
kvp.Value.Dispose();
|
||||
}
|
||||
|
||||
mappedBuffers.Clear();
|
||||
unmappedBuffers.Clear();
|
||||
renderer.WorldRenderers = renderer.WorldRenderers.Where(r => r != this).ToArray();
|
||||
}
|
||||
|
||||
void INotifyActorDisposing.Disposing(Actor a)
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,50 +12,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using OpenRA.FileSystem;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Cnc.Graphics;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Cnc.Graphics
|
||||
namespace OpenRA.Mods.Cnc.Traits
|
||||
{
|
||||
public class VoxelModelSequenceLoader : IModelSequenceLoader
|
||||
[TraitLocation(SystemActors.World | SystemActors.EditorWorld)]
|
||||
[Desc("Loads voxel models.")]
|
||||
public sealed class VoxelCacheInfo : TraitInfo, IModelCacheInfo
|
||||
{
|
||||
public Action<string> OnMissingModelError { get; set; }
|
||||
|
||||
public VoxelModelSequenceLoader(ModData modData) { }
|
||||
|
||||
public IModelCache CacheModels(IReadOnlyFileSystem fileSystem, ModData modData, IReadOnlyDictionary<string, MiniYamlNode> modelSequences)
|
||||
{
|
||||
var cache = new VoxelModelCache(fileSystem);
|
||||
foreach (var kv in modelSequences)
|
||||
{
|
||||
modData.LoadScreen.Display();
|
||||
try
|
||||
{
|
||||
cache.CacheModel(kv.Key, kv.Value.Value);
|
||||
}
|
||||
catch (FileNotFoundException ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
|
||||
// Eat the FileNotFound exceptions from missing sprites
|
||||
OnMissingModelError(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
cache.LoadComplete();
|
||||
|
||||
return cache;
|
||||
}
|
||||
public override object Create(ActorInitializer init) { return new VoxelCache(this, init.Self); }
|
||||
}
|
||||
|
||||
public sealed class VoxelModelCache : IModelCache
|
||||
public sealed class VoxelCache : IModelCache, INotifyActorDisposing, IDisposable
|
||||
{
|
||||
readonly VoxelLoader loader;
|
||||
readonly Dictionary<string, Dictionary<string, IModel>> models = new();
|
||||
|
||||
public VoxelModelCache(IReadOnlyFileSystem fileSystem)
|
||||
public VoxelCache(VoxelCacheInfo info, Actor self)
|
||||
{
|
||||
loader = new VoxelLoader(fileSystem);
|
||||
var map = self.World.Map;
|
||||
loader = new VoxelLoader(map);
|
||||
foreach (var kv in map.Rules.ModelSequences)
|
||||
{
|
||||
Game.ModData.LoadScreen.Display();
|
||||
try
|
||||
{
|
||||
CacheModel(kv.Key, kv.Value.Value);
|
||||
}
|
||||
catch (FileNotFoundException ex)
|
||||
{
|
||||
// Eat the FileNotFound exceptions from missing sprites.
|
||||
Console.WriteLine(ex);
|
||||
Log.Write("debug", ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
loader.RefreshBuffer();
|
||||
loader.Finish();
|
||||
}
|
||||
|
||||
public void CacheModel(string model, MiniYaml definition)
|
||||
@@ -63,12 +58,6 @@ namespace OpenRA.Mods.Cnc.Graphics
|
||||
models.Add(model, definition.ToDictionary(my => LoadVoxel(model, my)));
|
||||
}
|
||||
|
||||
public void LoadComplete()
|
||||
{
|
||||
loader.RefreshBuffer();
|
||||
loader.Finish();
|
||||
}
|
||||
|
||||
IModel LoadVoxel(string unit, MiniYaml info)
|
||||
{
|
||||
var vxl = unit;
|
||||
@@ -120,5 +109,10 @@ namespace OpenRA.Mods.Cnc.Graphics
|
||||
{
|
||||
loader.Dispose();
|
||||
}
|
||||
|
||||
void INotifyActorDisposing.Disposing(Actor a)
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,8 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
|
||||
namespace OpenRA.Mods.Cnc
|
||||
{
|
||||
public static class Util
|
||||
@@ -65,5 +67,249 @@ namespace OpenRA.Mods.Cnc
|
||||
|
||||
return Common.Util.QuantizeFacing(facing, steps);
|
||||
}
|
||||
|
||||
public static float[] IdentityMatrix()
|
||||
{
|
||||
return new float[]
|
||||
{
|
||||
1, 0, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1,
|
||||
};
|
||||
}
|
||||
|
||||
public static float[] ScaleMatrix(float sx, float sy, float sz)
|
||||
{
|
||||
return new float[]
|
||||
{
|
||||
sx, 0, 0, 0,
|
||||
0, sy, 0, 0,
|
||||
0, 0, sz, 0,
|
||||
0, 0, 0, 1,
|
||||
};
|
||||
}
|
||||
|
||||
public static float[] TranslationMatrix(float x, float y, float z)
|
||||
{
|
||||
return new float[]
|
||||
{
|
||||
1, 0, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
x, y, z, 1,
|
||||
};
|
||||
}
|
||||
|
||||
public static float[] MatrixMultiply(float[] lhs, float[] rhs)
|
||||
{
|
||||
var mtx = new float[16];
|
||||
for (var i = 0; i < 4; i++)
|
||||
for (var j = 0; j < 4; j++)
|
||||
{
|
||||
mtx[4 * i + j] = 0;
|
||||
for (var k = 0; k < 4; k++)
|
||||
mtx[4 * i + j] += lhs[4 * k + j] * rhs[4 * i + k];
|
||||
}
|
||||
|
||||
return mtx;
|
||||
}
|
||||
|
||||
public static float[] MatrixVectorMultiply(float[] mtx, float[] vec)
|
||||
{
|
||||
var ret = new float[4];
|
||||
for (var j = 0; j < 4; j++)
|
||||
{
|
||||
ret[j] = 0;
|
||||
for (var k = 0; k < 4; k++)
|
||||
ret[j] += mtx[4 * k + j] * vec[k];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static float[] MatrixInverse(float[] m)
|
||||
{
|
||||
var mtx = new float[16];
|
||||
|
||||
mtx[0] = m[5] * m[10] * m[15] -
|
||||
m[5] * m[11] * m[14] -
|
||||
m[9] * m[6] * m[15] +
|
||||
m[9] * m[7] * m[14] +
|
||||
m[13] * m[6] * m[11] -
|
||||
m[13] * m[7] * m[10];
|
||||
|
||||
mtx[4] = -m[4] * m[10] * m[15] +
|
||||
m[4] * m[11] * m[14] +
|
||||
m[8] * m[6] * m[15] -
|
||||
m[8] * m[7] * m[14] -
|
||||
m[12] * m[6] * m[11] +
|
||||
m[12] * m[7] * m[10];
|
||||
|
||||
mtx[8] = m[4] * m[9] * m[15] -
|
||||
m[4] * m[11] * m[13] -
|
||||
m[8] * m[5] * m[15] +
|
||||
m[8] * m[7] * m[13] +
|
||||
m[12] * m[5] * m[11] -
|
||||
m[12] * m[7] * m[9];
|
||||
|
||||
mtx[12] = -m[4] * m[9] * m[14] +
|
||||
m[4] * m[10] * m[13] +
|
||||
m[8] * m[5] * m[14] -
|
||||
m[8] * m[6] * m[13] -
|
||||
m[12] * m[5] * m[10] +
|
||||
m[12] * m[6] * m[9];
|
||||
|
||||
mtx[1] = -m[1] * m[10] * m[15] +
|
||||
m[1] * m[11] * m[14] +
|
||||
m[9] * m[2] * m[15] -
|
||||
m[9] * m[3] * m[14] -
|
||||
m[13] * m[2] * m[11] +
|
||||
m[13] * m[3] * m[10];
|
||||
|
||||
mtx[5] = m[0] * m[10] * m[15] -
|
||||
m[0] * m[11] * m[14] -
|
||||
m[8] * m[2] * m[15] +
|
||||
m[8] * m[3] * m[14] +
|
||||
m[12] * m[2] * m[11] -
|
||||
m[12] * m[3] * m[10];
|
||||
|
||||
mtx[9] = -m[0] * m[9] * m[15] +
|
||||
m[0] * m[11] * m[13] +
|
||||
m[8] * m[1] * m[15] -
|
||||
m[8] * m[3] * m[13] -
|
||||
m[12] * m[1] * m[11] +
|
||||
m[12] * m[3] * m[9];
|
||||
|
||||
mtx[13] = m[0] * m[9] * m[14] -
|
||||
m[0] * m[10] * m[13] -
|
||||
m[8] * m[1] * m[14] +
|
||||
m[8] * m[2] * m[13] +
|
||||
m[12] * m[1] * m[10] -
|
||||
m[12] * m[2] * m[9];
|
||||
|
||||
mtx[2] = m[1] * m[6] * m[15] -
|
||||
m[1] * m[7] * m[14] -
|
||||
m[5] * m[2] * m[15] +
|
||||
m[5] * m[3] * m[14] +
|
||||
m[13] * m[2] * m[7] -
|
||||
m[13] * m[3] * m[6];
|
||||
|
||||
mtx[6] = -m[0] * m[6] * m[15] +
|
||||
m[0] * m[7] * m[14] +
|
||||
m[4] * m[2] * m[15] -
|
||||
m[4] * m[3] * m[14] -
|
||||
m[12] * m[2] * m[7] +
|
||||
m[12] * m[3] * m[6];
|
||||
|
||||
mtx[10] = m[0] * m[5] * m[15] -
|
||||
m[0] * m[7] * m[13] -
|
||||
m[4] * m[1] * m[15] +
|
||||
m[4] * m[3] * m[13] +
|
||||
m[12] * m[1] * m[7] -
|
||||
m[12] * m[3] * m[5];
|
||||
|
||||
mtx[14] = -m[0] * m[5] * m[14] +
|
||||
m[0] * m[6] * m[13] +
|
||||
m[4] * m[1] * m[14] -
|
||||
m[4] * m[2] * m[13] -
|
||||
m[12] * m[1] * m[6] +
|
||||
m[12] * m[2] * m[5];
|
||||
|
||||
mtx[3] = -m[1] * m[6] * m[11] +
|
||||
m[1] * m[7] * m[10] +
|
||||
m[5] * m[2] * m[11] -
|
||||
m[5] * m[3] * m[10] -
|
||||
m[9] * m[2] * m[7] +
|
||||
m[9] * m[3] * m[6];
|
||||
|
||||
mtx[7] = m[0] * m[6] * m[11] -
|
||||
m[0] * m[7] * m[10] -
|
||||
m[4] * m[2] * m[11] +
|
||||
m[4] * m[3] * m[10] +
|
||||
m[8] * m[2] * m[7] -
|
||||
m[8] * m[3] * m[6];
|
||||
|
||||
mtx[11] = -m[0] * m[5] * m[11] +
|
||||
m[0] * m[7] * m[9] +
|
||||
m[4] * m[1] * m[11] -
|
||||
m[4] * m[3] * m[9] -
|
||||
m[8] * m[1] * m[7] +
|
||||
m[8] * m[3] * m[5];
|
||||
|
||||
mtx[15] = m[0] * m[5] * m[10] -
|
||||
m[0] * m[6] * m[9] -
|
||||
m[4] * m[1] * m[10] +
|
||||
m[4] * m[2] * m[9] +
|
||||
m[8] * m[1] * m[6] -
|
||||
m[8] * m[2] * m[5];
|
||||
|
||||
var det = m[0] * mtx[0] + m[1] * mtx[4] + m[2] * mtx[8] + m[3] * mtx[12];
|
||||
if (det == 0)
|
||||
return null;
|
||||
|
||||
for (var i = 0; i < 16; i++)
|
||||
mtx[i] *= 1 / det;
|
||||
|
||||
return mtx;
|
||||
}
|
||||
|
||||
public static float[] MakeFloatMatrix(Int32Matrix4x4 imtx)
|
||||
{
|
||||
var multipler = 1f / imtx.M44;
|
||||
return new[]
|
||||
{
|
||||
imtx.M11 * multipler,
|
||||
imtx.M12 * multipler,
|
||||
imtx.M13 * multipler,
|
||||
imtx.M14 * multipler,
|
||||
|
||||
imtx.M21 * multipler,
|
||||
imtx.M22 * multipler,
|
||||
imtx.M23 * multipler,
|
||||
imtx.M24 * multipler,
|
||||
|
||||
imtx.M31 * multipler,
|
||||
imtx.M32 * multipler,
|
||||
imtx.M33 * multipler,
|
||||
imtx.M34 * multipler,
|
||||
|
||||
imtx.M41 * multipler,
|
||||
imtx.M42 * multipler,
|
||||
imtx.M43 * multipler,
|
||||
imtx.M44 * multipler,
|
||||
};
|
||||
}
|
||||
|
||||
public static float[] MatrixAABBMultiply(float[] mtx, float[] bounds)
|
||||
{
|
||||
// Corner offsets.
|
||||
var ix = new uint[] { 0, 0, 0, 0, 3, 3, 3, 3 };
|
||||
var iy = new uint[] { 1, 1, 4, 4, 1, 1, 4, 4 };
|
||||
var iz = new uint[] { 2, 5, 2, 5, 2, 5, 2, 5 };
|
||||
|
||||
// Vectors to opposing corner.
|
||||
var ret = new[]
|
||||
{
|
||||
float.MaxValue, float.MaxValue, float.MaxValue,
|
||||
float.MinValue, float.MinValue, float.MinValue
|
||||
};
|
||||
|
||||
// Transform vectors and find new bounding box.
|
||||
for (var i = 0; i < 8; i++)
|
||||
{
|
||||
var vec = new[] { bounds[ix[i]], bounds[iy[i]], bounds[iz[i]], 1 };
|
||||
var tvec = MatrixVectorMultiply(mtx, vec);
|
||||
|
||||
ret[0] = Math.Min(ret[0], tvec[0] / tvec[3]);
|
||||
ret[1] = Math.Min(ret[1], tvec[1] / tvec[3]);
|
||||
ret[2] = Math.Min(ret[2], tvec[2] / tvec[3]);
|
||||
ret[3] = Math.Max(ret[3], tvec[0] / tvec[3]);
|
||||
ret[4] = Math.Max(ret[4], tvec[1] / tvec[3]);
|
||||
ret[5] = Math.Max(ret[5], tvec[2] / tvec[3]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
235
OpenRA.Mods.Cnc/Widgets/ModelWidget.cs
Normal file
235
OpenRA.Mods.Cnc/Widgets/ModelWidget.cs
Normal file
@@ -0,0 +1,235 @@
|
||||
#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.Cnc.Graphics;
|
||||
using OpenRA.Mods.Cnc.Traits;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Cnc.Widgets
|
||||
{
|
||||
public class ModelWidget : Widget, IModelWidget
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
string IModelWidget.Palette => GetPalette();
|
||||
float IModelWidget.Scale => GetScale();
|
||||
|
||||
void IModelWidget.Setup(Func<bool> isVisible, Func<string> getPalette, Func<string> getPlayerPalette,
|
||||
Func<float> getScale, Func<IModel> getVoxel, Func<WRot> getRotation)
|
||||
{
|
||||
IsVisible = isVisible;
|
||||
GetPalette = getPalette;
|
||||
GetPlayerPalette = getPlayerPalette;
|
||||
GetScale = getScale;
|
||||
GetVoxel = getVoxel;
|
||||
GetRotation = getRotation;
|
||||
}
|
||||
|
||||
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 renderer = WorldRenderer.World.WorldActor.Trait<ModelRenderer>();
|
||||
|
||||
var preview = new ModelPreview(
|
||||
renderer,
|
||||
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(
|
||||
renderer,
|
||||
animations, WPos.Zero, origin, 0, camera, scale,
|
||||
WRot.None, cachedLightAmbientColor, cachedLightDiffuseColor,
|
||||
paletteReferencePlayer, paletteReferenceNormals, paletteReferenceShadow);
|
||||
|
||||
renderable = modelRenderable.PrepareRender(WorldRenderer);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user