Add backend plumbing for model loaders.

This commit is contained in:
Paul Chote
2017-06-09 15:41:16 +00:00
committed by reaperrr
parent 086fc88e3e
commit e1cd00c1dd
15 changed files with 136 additions and 14 deletions

View File

@@ -165,7 +165,7 @@ namespace OpenRA
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);

View File

@@ -29,6 +29,7 @@ namespace OpenRA
public readonly IReadOnlyDictionary<string, MusicInfo> Music;
public readonly TileSet TileSet;
public readonly SequenceProvider Sequences;
public readonly IReadOnlyDictionary<string, MiniYamlNode> ModelSequences;
public Ruleset(
IReadOnlyDictionary<string, ActorInfo> actors,
@@ -37,7 +38,8 @@ namespace OpenRA
IReadOnlyDictionary<string, SoundInfo> notifications,
IReadOnlyDictionary<string, MusicInfo> music,
TileSet tileSet,
SequenceProvider sequences)
SequenceProvider sequences,
IReadOnlyDictionary<string, MiniYamlNode> modelSequences)
{
Actors = actors;
Weapons = weapons;
@@ -46,6 +48,7 @@ namespace OpenRA
Music = music;
TileSet = tileSet;
Sequences = sequences;
ModelSequences = modelSequences;
foreach (var a in Actors.Values)
{
@@ -119,8 +122,11 @@ namespace OpenRA
var music = MergeOrDefault("Manifest,Music", fs, m.Music, null, null,
k => new MusicInfo(k.Key, k.Value));
var modelSequences = MergeOrDefault("Manifest,ModelSequences", fs, m.VoxelSequences, 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)
@@ -145,12 +151,13 @@ namespace OpenRA
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;
@@ -180,8 +187,12 @@ namespace OpenRA
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.VoxelSequences, mapModelSequences, dr.ModelSequences,
k => k);
ruleset = new Ruleset(actors, weapons, voices, notifications, music, ts, sequences, modelSequences);
};
if (modData.IsOnMainThread)

View File

@@ -0,0 +1,69 @@
#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);
VoxelRenderData RenderData(uint section);
}
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();
}
}
}

View File

@@ -31,6 +31,17 @@ namespace OpenRA
}
}
public sealed class ModelSequenceFormat : IGlobalModData
{
public readonly string Type;
public readonly IReadOnlyDictionary<string, MiniYaml> Metadata;
public ModelSequenceFormat(MiniYaml yaml)
{
Type = yaml.Value;
Metadata = new ReadOnlyDictionary<string, MiniYaml>(yaml.ToDictionary());
}
}
public class ModMetadata
{
public string Title;

View File

@@ -18,6 +18,7 @@ using System.Linq;
using System.Reflection;
using System.Text;
using OpenRA.FileSystem;
using OpenRA.Graphics;
using OpenRA.Support;
using OpenRA.Traits;
@@ -386,7 +387,7 @@ namespace OpenRA
try
{
Rules = Ruleset.Load(modData, this, Tileset, RuleDefinitions, WeaponDefinitions,
VoiceDefinitions, NotificationDefinitions, MusicDefinitions, SequenceDefinitions);
VoiceDefinitions, NotificationDefinitions, MusicDefinitions, SequenceDefinitions, VoxelSequenceDefinitions);
}
catch (Exception e)
{

View File

@@ -307,8 +307,9 @@ namespace OpenRA
var musicDefinitions = LoadRuleSection(yaml, "Music");
var notificationDefinitions = LoadRuleSection(yaml, "Notifications");
var sequenceDefinitions = LoadRuleSection(yaml, "Sequences");
var modelSequenceDefinitions = LoadRuleSection(yaml, "VoxelSequences");
var rules = Ruleset.Load(modData, this, TileSet, ruleDefinitions, weaponDefinitions,
voiceDefinitions, notificationDefinitions, musicDefinitions, sequenceDefinitions);
voiceDefinitions, notificationDefinitions, musicDefinitions, sequenceDefinitions, modelSequenceDefinitions);
var flagged = Ruleset.DefinesUnsafeCustomRules(modData, this, ruleDefinitions,
weaponDefinitions, voiceDefinitions, notificationDefinitions, sequenceDefinitions);
return Pair.New(rules, flagged);
@@ -390,8 +391,9 @@ namespace OpenRA
var musicDefinitions = LoadRuleSection(rulesYaml, "Music");
var notificationDefinitions = LoadRuleSection(rulesYaml, "Notifications");
var sequenceDefinitions = LoadRuleSection(rulesYaml, "Sequences");
var modelSequenceDefinitions = LoadRuleSection(rulesYaml, "VoxelSequences");
var rules = Ruleset.Load(modData, this, TileSet, ruleDefinitions, weaponDefinitions,
voiceDefinitions, notificationDefinitions, musicDefinitions, sequenceDefinitions);
voiceDefinitions, notificationDefinitions, musicDefinitions, sequenceDefinitions, modelSequenceDefinitions);
var flagged = Ruleset.DefinesUnsafeCustomRules(modData, this, ruleDefinitions,
weaponDefinitions, voiceDefinitions, notificationDefinitions, sequenceDefinitions);
return Pair.New(rules, flagged);

View File

@@ -30,6 +30,7 @@ namespace OpenRA
public readonly ISoundLoader[] SoundLoaders;
public readonly ISpriteLoader[] SpriteLoaders;
public readonly ISpriteSequenceLoader SpriteSequenceLoader;
public readonly IModelSequenceLoader ModelSequenceLoader;
public ILoadScreen LoadScreen { get; private set; }
public VoxelLoader VoxelLoader { get; private set; }
public CursorProvider CursorProvider { get; private set; }
@@ -73,13 +74,22 @@ namespace OpenRA
var sequenceFormat = Manifest.Get<SpriteSequenceFormat>();
var sequenceLoader = ObjectCreator.FindType(sequenceFormat.Type + "Loader");
var ctor = sequenceLoader != null ? sequenceLoader.GetConstructor(new[] { typeof(ModData) }) : null;
if (sequenceLoader == null || !sequenceLoader.GetInterfaces().Contains(typeof(ISpriteSequenceLoader)) || ctor == null)
var sequenceCtor = sequenceLoader != null ? sequenceLoader.GetConstructor(new[] { typeof(ModData) }) : null;
if (sequenceLoader == null || !sequenceLoader.GetInterfaces().Contains(typeof(ISpriteSequenceLoader)) || sequenceCtor == null)
throw new InvalidOperationException("Unable to find a sequence loader for type '{0}'.".F(sequenceFormat.Type));
SpriteSequenceLoader = (ISpriteSequenceLoader)ctor.Invoke(new[] { this });
SpriteSequenceLoader = (ISpriteSequenceLoader)sequenceCtor.Invoke(new[] { this });
SpriteSequenceLoader.OnMissingSpriteError = s => Log.Write("debug", s);
var modelFormat = Manifest.Get<ModelSequenceFormat>();
var modelLoader = ObjectCreator.FindType(modelFormat.Type + "Loader");
var modelCtor = modelLoader != null ? modelLoader.GetConstructor(new[] { typeof(ModData) }) : null;
if (modelLoader == null || !modelLoader.GetInterfaces().Contains(typeof(IModelSequenceLoader)) || modelCtor == null)
throw new InvalidOperationException("Unable to find a model loader for type '{0}'.".F(modelFormat.Type));
ModelSequenceLoader = (IModelSequenceLoader)modelCtor.Invoke(new[] { this });
ModelSequenceLoader.OnMissingModelError = s => Log.Write("debug", s);
defaultRules = Exts.Lazy(() => Ruleset.LoadDefaults(this));
defaultTileSets = Exts.Lazy(() =>
{

View File

@@ -256,6 +256,7 @@
<Compile Include="CryptoUtil.cs" />
<Compile Include="Support\VariableExpression.cs" />
<Compile Include="ExternalMods.cs" />
<Compile Include="Graphics\Model.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="FileSystem\Folder.cs" />

View File

@@ -40,6 +40,7 @@ namespace OpenRA
public Session LobbyInfo { get { return OrderManager.LobbyInfo; } }
public readonly MersenneTwister SharedRandom;
public readonly IModelCache ModelCache;
public Player[] Players = new Player[0];
@@ -147,7 +148,7 @@ namespace OpenRA
}
}
internal World(Map map, OrderManager orderManager, WorldType type)
internal World(ModData modData, Map map, OrderManager orderManager, WorldType type)
{
Type = type;
OrderManager = orderManager;
@@ -156,6 +157,8 @@ namespace OpenRA
Timestep = orderManager.LobbyInfo.GlobalSettings.Timestep;
SharedRandom = new MersenneTwister(orderManager.LobbyInfo.GlobalSettings.RandomSeed);
ModelCache = modData.ModelSequenceLoader.CacheModels(map, modData, map.Rules.ModelSequences);
var worldActorType = type == WorldType.Editor ? "EditorWorld" : "World";
WorldActor = CreateActor(worldActorType, new TypeDictionary());
ActorMap = WorldActor.Trait<IActorMap>();
@@ -437,6 +440,8 @@ namespace OpenRA
Game.Sound.StopAudio();
Game.Sound.StopVideo();
ModelCache.Dispose();
// Dispose newer actors first, and the world actor last
foreach (var a in actors.Values.Reverse())
a.Dispose();

View File

@@ -30,3 +30,5 @@ SoundFormats:
SpriteFormats:
SpriteSequenceFormat: DefaultSpriteSequence
ModelSequenceFormat: PlaceholderModelSequence

View File

@@ -197,6 +197,8 @@ SpriteSequenceFormat: TilesetSpecificSpriteSequence
DESERT: .des
JUNGLE: .jun
ModelSequenceFormat: PlaceholderModelSequence
GameSpeeds:
slower:
Name: Slower

View File

@@ -174,6 +174,8 @@ SpriteFormats: R8, ShpTD, TmpRA
SpriteSequenceFormat: DefaultSpriteSequence
ModelSequenceFormat: PlaceholderModelSequence
GameSpeeds:
slower:
Name: Slower

View File

@@ -55,3 +55,5 @@ SoundFormats:
SpriteFormats: ShpTD
SpriteSequenceFormat: DefaultSpriteSequence
ModelSequenceFormat: PlaceholderModelSequence

View File

@@ -200,6 +200,8 @@ SpriteSequenceFormat: TilesetSpecificSpriteSequence
INTERIOR: .int
DESERT: .des
ModelSequenceFormat: PlaceholderModelSequence
GameSpeeds:
slower:
Name: Slower

View File

@@ -236,6 +236,8 @@ SpriteSequenceFormat: TilesetSpecificSpriteSequence
TEMPERATE: t
SNOW: a
ModelSequenceFormat: PlaceholderModelSequence
GameSpeeds:
slower:
Name: Slower