Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move voxel loader to Mods.Cnc. #13485

Merged
merged 9 commits into from Jun 14, 2017
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 3 additions & 3 deletions OpenRA.Game/Game.cs
Expand Up @@ -165,7 +165,7 @@ internal static void StartGame(string mapUID, WorldType type)
using (new PerfTimer("PrepareMap"))
map = ModData.PrepareMap(mapUID);
using (new PerfTimer("NewWorld"))
OrderManager.World = new World(map, OrderManager, type);
OrderManager.World = new World(ModData, map, OrderManager, type);

worldRenderer = new WorldRenderer(ModData, OrderManager.World);

Expand Down Expand Up @@ -633,9 +633,9 @@ static void RenderTick()

using (new PerfSample("render_widgets"))
{
Renderer.WorldVoxelRenderer.BeginFrame();
Renderer.WorldModelRenderer.BeginFrame();
Ui.PrepareRenderables();
Renderer.WorldVoxelRenderer.EndFrame();
Renderer.WorldModelRenderer.EndFrame();

Ui.Draw();

Expand Down
23 changes: 17 additions & 6 deletions OpenRA.Game/GameRules/Ruleset.cs
Expand Up @@ -29,6 +29,7 @@ public class Ruleset
public readonly IReadOnlyDictionary<string, MusicInfo> Music;
public readonly TileSet TileSet;
public readonly SequenceProvider Sequences;
public readonly IReadOnlyDictionary<string, MiniYamlNode> ModelSequences;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is better MiniYamlNode than e.g. SequenceInfo like for other sections (MusicInfo, ActorInfo,...)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have a *Info for this: it is up to the mod-defined parser to parse what it wants from the yaml tree.


public Ruleset(
IReadOnlyDictionary<string, ActorInfo> actors,
Expand All @@ -37,7 +38,8 @@ public class Ruleset
IReadOnlyDictionary<string, SoundInfo> notifications,
IReadOnlyDictionary<string, MusicInfo> music,
TileSet tileSet,
SequenceProvider sequences)
SequenceProvider sequences,
IReadOnlyDictionary<string, MiniYamlNode> modelSequences)
{
Actors = actors;
Weapons = weapons;
Expand All @@ -46,6 +48,7 @@ public class Ruleset
Music = music;
TileSet = tileSet;
Sequences = sequences;
ModelSequences = modelSequences;

foreach (var a in Actors.Values)
{
Expand Down Expand Up @@ -119,8 +122,11 @@ public static Ruleset LoadDefaults(ModData modData)
var music = MergeOrDefault("Manifest,Music", fs, m.Music, null, null,
k => new MusicInfo(k.Key, k.Value));

var modelSequences = MergeOrDefault("Manifest,ModelSequences", fs, m.ModelSequences, null, null,
k => k);

// The default ruleset does not include a preferred tileset or sequence set
ruleset = new Ruleset(actors, weapons, voices, notifications, music, null, null);
ruleset = new Ruleset(actors, weapons, voices, notifications, music, null, null, modelSequences);
};

if (modData.IsOnMainThread)
Expand All @@ -145,12 +151,13 @@ public static Ruleset LoadDefaultsForTileSet(ModData modData, string tileSet)
var dr = modData.DefaultRules;
var ts = modData.DefaultTileSets[tileSet];
var sequences = modData.DefaultSequences[tileSet];
return new Ruleset(dr.Actors, dr.Weapons, dr.Voices, dr.Notifications, dr.Music, ts, sequences);

return new Ruleset(dr.Actors, dr.Weapons, dr.Voices, dr.Notifications, dr.Music, ts, sequences, dr.ModelSequences);
}

public static Ruleset Load(ModData modData, IReadOnlyFileSystem fileSystem, string tileSet,
MiniYaml mapRules, MiniYaml mapWeapons, MiniYaml mapVoices, MiniYaml mapNotifications,
MiniYaml mapMusic, MiniYaml mapSequences)
MiniYaml mapMusic, MiniYaml mapSequences, MiniYaml mapModelSequences)
{
var m = modData.Manifest;
var dr = modData.DefaultRules;
Expand Down Expand Up @@ -180,8 +187,12 @@ public static Ruleset LoadDefaultsForTileSet(ModData modData, string tileSet)
var sequences = mapSequences == null ? modData.DefaultSequences[tileSet] :
new SequenceProvider(fileSystem, modData, ts, mapSequences);

// TODO: Add support for custom voxel sequences
ruleset = new Ruleset(actors, weapons, voices, notifications, music, ts, sequences);
var modelSequences = dr.ModelSequences;
if (mapModelSequences != null)
modelSequences = MergeOrDefault("ModelSequences", fileSystem, m.ModelSequences, mapModelSequences, dr.ModelSequences,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can reuse here modelSequences for dr.ModelSequences.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to keep it separate to make it clear that it is replacing modelSequences, not modifying it. This is really just a terniary statement, but the MergeOrDefault branch is too ugly to write it as one.

k => k);

ruleset = new Ruleset(actors, weapons, voices, notifications, music, ts, sequences, modelSequences);
};

if (modData.IsOnMainThread)
Expand Down
83 changes: 83 additions & 0 deletions OpenRA.Game/Graphics/Model.cs
@@ -0,0 +1,83 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion

using System;
using OpenRA.FileSystem;

namespace OpenRA.Graphics
{
public interface IModel
{
uint Frames { get; }
uint Sections { get; }

float[] TransformationMatrix(uint section, uint frame);
float[] Size { get; }
float[] Bounds(uint frame);
ModelRenderData RenderData(uint section);
}

public struct ModelRenderData
{
public readonly int Start;
public readonly int Count;
public readonly Sheet Sheet;

public ModelRenderData(int start, int count, Sheet sheet)
{
Start = start;
Count = count;
Sheet = sheet;
}
}

public interface IModelCache : IDisposable
{
IModel GetModelSequence(string model, string sequence);
bool HasModelSequence(string model, string sequence);
IVertexBuffer<Vertex> VertexBuffer { get; }
}

public interface IModelSequenceLoader
{
Action<string> OnMissingModelError { get; set; }
IModelCache CacheModels(IReadOnlyFileSystem fileSystem, ModData modData, IReadOnlyDictionary<string, MiniYamlNode> modelDefinitions);
}

public class PlaceholderModelSequenceLoader : IModelSequenceLoader
{
public Action<string> OnMissingModelError { get; set; }

class PlaceholderModelCache : IModelCache
{
public IVertexBuffer<Vertex> VertexBuffer { get { throw new NotImplementedException(); } }

public void Dispose() { }

public IModel GetModelSequence(string model, string sequence)
{
throw new NotImplementedException();
}

public bool HasModelSequence(string model, string sequence)
{
throw new NotImplementedException();
}
}

public PlaceholderModelSequenceLoader(ModData modData) { }

public IModelCache CacheModels(IReadOnlyFileSystem fileSystem, ModData modData, IReadOnlyDictionary<string, MiniYamlNode> modelDefinitions)
{
return new PlaceholderModelCache();
}
}
}
Expand Up @@ -14,18 +14,18 @@

namespace OpenRA.Graphics
{
public struct VoxelAnimation
public struct ModelAnimation
{
public readonly Voxel Voxel;
public readonly IModel Model;
public readonly Func<WVec> OffsetFunc;
public readonly Func<IEnumerable<WRot>> RotationFunc;
public readonly Func<bool> DisableFunc;
public readonly Func<uint> FrameFunc;
public readonly bool ShowShadow;

public VoxelAnimation(Voxel voxel, Func<WVec> offset, Func<IEnumerable<WRot>> rotation, Func<bool> disable, Func<uint> frame, bool showshadow)
public ModelAnimation(IModel model, Func<WVec> offset, Func<IEnumerable<WRot>> rotation, Func<bool> disable, Func<uint> frame, bool showshadow)
{
Voxel = voxel;
Model = model;
OffsetFunc = offset;
RotationFunc = rotation;
DisableFunc = disable;
Expand Down
Expand Up @@ -17,14 +17,14 @@

namespace OpenRA.Graphics
{
public class VoxelRenderProxy
public class ModelRenderProxy
{
public readonly Sprite Sprite;
public readonly Sprite ShadowSprite;
public readonly float ShadowDirection;
public readonly float3[] ProjectedShadowBounds;

public VoxelRenderProxy(Sprite sprite, Sprite shadowSprite, float3[] projectedShadowBounds, float shadowDirection)
public ModelRenderProxy(Sprite sprite, Sprite shadowSprite, float3[] projectedShadowBounds, float shadowDirection)
{
Sprite = sprite;
ShadowSprite = shadowSprite;
Expand All @@ -33,7 +33,7 @@ public VoxelRenderProxy(Sprite sprite, Sprite shadowSprite, float3[] projectedSh
}
}

public sealed class VoxelRenderer : IDisposable
public sealed class ModelRenderer : IDisposable
{
// Static constants
static readonly float[] ShadowDiffuse = new float[] { 0, 0, 0 };
Expand All @@ -53,7 +53,7 @@ public sealed class VoxelRenderer : IDisposable

SheetBuilder sheetBuilder;

public VoxelRenderer(Renderer renderer, IShader shader)
public ModelRenderer(Renderer renderer, IShader shader)
{
this.renderer = renderer;
this.shader = shader;
Expand All @@ -78,8 +78,8 @@ public void SetViewportParams(Size screen, float zoom, int2 scroll)
shader.SetMatrix("View", view);
}

public VoxelRenderProxy RenderAsync(
WorldRenderer wr, IEnumerable<VoxelAnimation> voxels, WRot camera, float scale,
public ModelRenderProxy RenderAsync(
WorldRenderer wr, IEnumerable<ModelAnimation> models, WRot camera, float scale,
float[] groundNormal, WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor,
PaletteReference color, PaletteReference normals, PaletteReference shadowPalette)
{
Expand All @@ -105,18 +105,18 @@ public void SetViewportParams(Size screen, float zoom, int2 scroll)
var stl = new float2(float.MaxValue, float.MaxValue);
var sbr = new float2(float.MinValue, float.MinValue);

foreach (var v in voxels)
foreach (var m in models)
{
// Convert screen offset back to world coords
var offsetVec = Util.MatrixVectorMultiply(invCameraTransform, wr.ScreenVector(v.OffsetFunc()));
var offsetVec = Util.MatrixVectorMultiply(invCameraTransform, wr.ScreenVector(m.OffsetFunc()));
var offsetTransform = Util.TranslationMatrix(offsetVec[0], offsetVec[1], offsetVec[2]);

var worldTransform = v.RotationFunc().Aggregate(Util.IdentityMatrix(),
var worldTransform = m.RotationFunc().Aggregate(Util.IdentityMatrix(),
(x, y) => Util.MatrixMultiply(Util.MakeFloatMatrix(y.AsMatrix()), x));
worldTransform = Util.MatrixMultiply(scaleTransform, worldTransform);
worldTransform = Util.MatrixMultiply(offsetTransform, worldTransform);

var bounds = v.Voxel.Bounds(v.FrameFunc());
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);
Expand Down Expand Up @@ -177,13 +177,13 @@ public void SetViewportParams(Size screen, float zoom, int2 scroll)

doRender.Add(Pair.New<Sheet, Action>(sprite.Sheet, () =>
{
foreach (var v in voxels)
foreach (var m in models)
{
// Convert screen offset to world offset
var offsetVec = Util.MatrixVectorMultiply(invCameraTransform, wr.ScreenVector(v.OffsetFunc()));
var offsetVec = Util.MatrixVectorMultiply(invCameraTransform, wr.ScreenVector(m.OffsetFunc()));
var offsetTransform = Util.TranslationMatrix(offsetVec[0], offsetVec[1], offsetVec[2]);

var rotations = v.RotationFunc().Aggregate(Util.IdentityMatrix(),
var rotations = m.RotationFunc().Aggregate(Util.IdentityMatrix(),
(x, y) => Util.MatrixMultiply(Util.MakeFloatMatrix(y.AsMatrix()), x));
var worldTransform = Util.MatrixMultiply(scaleTransform, rotations);
worldTransform = Util.MatrixMultiply(offsetTransform, worldTransform);
Expand All @@ -196,32 +196,32 @@ public void SetViewportParams(Size screen, float zoom, int2 scroll)

var lightTransform = Util.MatrixMultiply(Util.MatrixInverse(rotations), invShadowTransform);

var frame = v.FrameFunc();
for (uint i = 0; i < v.Voxel.Limbs; i++)
var frame = m.FrameFunc();
for (uint i = 0; i < m.Model.Sections; i++)
{
var rd = v.Voxel.RenderData(i);
var t = v.Voxel.TransformationMatrix(i, frame);
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 {0} during RenderAsync.".F(i));

// Transform light vector from shadow -> world -> limb coords
var lightDirection = ExtractRotationVector(Util.MatrixMultiply(it, lightTransform));

Render(rd, Util.MatrixMultiply(transform, t), lightDirection,
Render(rd, wr.World.ModelCache, Util.MatrixMultiply(transform, t), lightDirection,
lightAmbientColor, lightDiffuseColor, color.TextureMidIndex, normals.TextureMidIndex);

// Disable shadow normals by forcing zero diffuse and identity ambient light
if (v.ShowShadow)
Render(rd, Util.MatrixMultiply(shadow, t), lightDirection,
if (m.ShowShadow)
Render(rd, wr.World.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 VoxelRenderProxy(sprite, shadowSprite, screenCorners, -screenLightVector[2] / screenLightVector[1]);
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)
Expand Down Expand Up @@ -258,7 +258,8 @@ static float[] ExtractRotationVector(float[] mtx)
}

void Render(
VoxelRenderData renderData,
ModelRenderData renderData,
IModelCache cache,
float[] t, float[] lightDirection,
float[] ambientLight, float[] diffuseLight,
float colorPaletteTextureMidIndex, float normalsPaletteTextureMidIndex)
Expand All @@ -270,7 +271,7 @@ static float[] ExtractRotationVector(float[] mtx)
shader.SetVec("AmbientLight", ambientLight, 3);
shader.SetVec("DiffuseLight", diffuseLight, 3);

shader.Render(() => renderer.DrawBatch(Game.ModData.VoxelLoader.VertexBuffer, renderData.Start, renderData.Count, PrimitiveType.TriangleList));
shader.Render(() => renderer.DrawBatch(cache.VertexBuffer, renderData.Start, renderData.Count, PrimitiveType.TriangleList));
}

public void BeginFrame()
Expand Down