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")) using (new PerfTimer("PrepareMap"))
map = ModData.PrepareMap(mapUID); map = ModData.PrepareMap(mapUID);
using (new PerfTimer("NewWorld")) 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); worldRenderer = new WorldRenderer(ModData, OrderManager.World);

View File

@@ -29,6 +29,7 @@ namespace OpenRA
public readonly IReadOnlyDictionary<string, MusicInfo> Music; public readonly IReadOnlyDictionary<string, MusicInfo> Music;
public readonly TileSet TileSet; public readonly TileSet TileSet;
public readonly SequenceProvider Sequences; public readonly SequenceProvider Sequences;
public readonly IReadOnlyDictionary<string, MiniYamlNode> ModelSequences;
public Ruleset( public Ruleset(
IReadOnlyDictionary<string, ActorInfo> actors, IReadOnlyDictionary<string, ActorInfo> actors,
@@ -37,7 +38,8 @@ namespace OpenRA
IReadOnlyDictionary<string, SoundInfo> notifications, IReadOnlyDictionary<string, SoundInfo> notifications,
IReadOnlyDictionary<string, MusicInfo> music, IReadOnlyDictionary<string, MusicInfo> music,
TileSet tileSet, TileSet tileSet,
SequenceProvider sequences) SequenceProvider sequences,
IReadOnlyDictionary<string, MiniYamlNode> modelSequences)
{ {
Actors = actors; Actors = actors;
Weapons = weapons; Weapons = weapons;
@@ -46,6 +48,7 @@ namespace OpenRA
Music = music; Music = music;
TileSet = tileSet; TileSet = tileSet;
Sequences = sequences; Sequences = sequences;
ModelSequences = modelSequences;
foreach (var a in Actors.Values) foreach (var a in Actors.Values)
{ {
@@ -119,8 +122,11 @@ namespace OpenRA
var music = MergeOrDefault("Manifest,Music", fs, m.Music, null, null, var music = MergeOrDefault("Manifest,Music", fs, m.Music, null, null,
k => new MusicInfo(k.Key, k.Value)); 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 // 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) if (modData.IsOnMainThread)
@@ -145,12 +151,13 @@ namespace OpenRA
var dr = modData.DefaultRules; var dr = modData.DefaultRules;
var ts = modData.DefaultTileSets[tileSet]; var ts = modData.DefaultTileSets[tileSet];
var sequences = modData.DefaultSequences[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, public static Ruleset Load(ModData modData, IReadOnlyFileSystem fileSystem, string tileSet,
MiniYaml mapRules, MiniYaml mapWeapons, MiniYaml mapVoices, MiniYaml mapNotifications, MiniYaml mapRules, MiniYaml mapWeapons, MiniYaml mapVoices, MiniYaml mapNotifications,
MiniYaml mapMusic, MiniYaml mapSequences) MiniYaml mapMusic, MiniYaml mapSequences, MiniYaml mapModelSequences)
{ {
var m = modData.Manifest; var m = modData.Manifest;
var dr = modData.DefaultRules; var dr = modData.DefaultRules;
@@ -180,8 +187,12 @@ namespace OpenRA
var sequences = mapSequences == null ? modData.DefaultSequences[tileSet] : var sequences = mapSequences == null ? modData.DefaultSequences[tileSet] :
new SequenceProvider(fileSystem, modData, ts, mapSequences); new SequenceProvider(fileSystem, modData, ts, mapSequences);
// TODO: Add support for custom voxel sequences var modelSequences = dr.ModelSequences;
ruleset = new Ruleset(actors, weapons, voices, notifications, music, ts, sequences); 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) 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 class ModMetadata
{ {
public string Title; public string Title;

View File

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

View File

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

View File

@@ -30,6 +30,7 @@ namespace OpenRA
public readonly ISoundLoader[] SoundLoaders; public readonly ISoundLoader[] SoundLoaders;
public readonly ISpriteLoader[] SpriteLoaders; public readonly ISpriteLoader[] SpriteLoaders;
public readonly ISpriteSequenceLoader SpriteSequenceLoader; public readonly ISpriteSequenceLoader SpriteSequenceLoader;
public readonly IModelSequenceLoader ModelSequenceLoader;
public ILoadScreen LoadScreen { get; private set; } public ILoadScreen LoadScreen { get; private set; }
public VoxelLoader VoxelLoader { get; private set; } public VoxelLoader VoxelLoader { get; private set; }
public CursorProvider CursorProvider { get; private set; } public CursorProvider CursorProvider { get; private set; }
@@ -73,13 +74,22 @@ namespace OpenRA
var sequenceFormat = Manifest.Get<SpriteSequenceFormat>(); var sequenceFormat = Manifest.Get<SpriteSequenceFormat>();
var sequenceLoader = ObjectCreator.FindType(sequenceFormat.Type + "Loader"); var sequenceLoader = ObjectCreator.FindType(sequenceFormat.Type + "Loader");
var ctor = sequenceLoader != null ? sequenceLoader.GetConstructor(new[] { typeof(ModData) }) : null; var sequenceCtor = sequenceLoader != null ? sequenceLoader.GetConstructor(new[] { typeof(ModData) }) : null;
if (sequenceLoader == null || !sequenceLoader.GetInterfaces().Contains(typeof(ISpriteSequenceLoader)) || ctor == 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)); 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); 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)); defaultRules = Exts.Lazy(() => Ruleset.LoadDefaults(this));
defaultTileSets = Exts.Lazy(() => defaultTileSets = Exts.Lazy(() =>
{ {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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