added in-game SHP viewer with frame slider

This commit is contained in:
Matthias Mailänder
2013-05-15 18:13:33 +02:00
parent ce021a4f62
commit 881fcf1191
10 changed files with 408 additions and 25 deletions

View File

@@ -18,19 +18,21 @@ namespace OpenRA.FileFormats
{
public static class FileSystem
{
static List<IFolder> mountedFolders = new List<IFolder>();
static List<IFolder> MountedFolders = new List<IFolder>();
static Cache<uint, List<IFolder>> allFiles = new Cache<uint, List<IFolder>>( _ => new List<IFolder>() );
public static List<string> FolderPaths = new List<string>();
static void MountInner(IFolder folder)
{
mountedFolders.Add(folder);
MountedFolders.Add(folder);
foreach( var hash in folder.AllFileHashes() )
foreach (var hash in folder.AllFileHashes())
{
var l = allFiles[hash];
if( !l.Contains( folder ) )
l.Add( folder );
if (!l.Contains(folder))
l.Add(folder);
}
}
@@ -78,6 +80,9 @@ namespace OpenRA.FileFormats
if (name.StartsWith("^"))
name = Platform.SupportDir+name.Substring(1);
if (Directory.Exists(name))
FolderPaths.Add(name);
var a = (Action)(() => FileSystem.MountInner(OpenPackage(name)));
if (optional)
@@ -89,28 +94,29 @@ namespace OpenRA.FileFormats
public static void UnmountAll()
{
mountedFolders.Clear();
MountedFolders.Clear();
FolderPaths.Clear();
allFiles = new Cache<uint, List<IFolder>>( _ => new List<IFolder>() );
}
public static bool Unmount(IFolder mount)
{
return (mountedFolders.RemoveAll(f => f == mount) > 0);
return (MountedFolders.RemoveAll(f => f == mount) > 0);
}
public static void Mount(IFolder mount)
{
if (!mountedFolders.Contains(mount)) mountedFolders.Add(mount);
if (!MountedFolders.Contains(mount)) MountedFolders.Add(mount);
}
public static void LoadFromManifest( Manifest manifest )
public static void LoadFromManifest(Manifest manifest)
{
UnmountAll();
foreach (var dir in manifest.Folders) Mount(dir);
foreach (var pkg in manifest.Packages) Mount(pkg);
}
static Stream GetFromCache( Cache<uint, List<IFolder>> index, string filename )
static Stream GetFromCache(Cache<uint, List<IFolder>> index, string filename)
{
var folder = index[PackageEntry.HashFilename(filename)]
.Where(x => x.Exists(filename))
@@ -125,21 +131,21 @@ namespace OpenRA.FileFormats
public static Stream Open(string filename) { return OpenWithExts(filename, ""); }
public static Stream OpenWithExts( string filename, params string[] exts )
public static Stream OpenWithExts(string filename, params string[] exts)
{
if( filename.IndexOfAny( new char[] { '/', '\\' } ) == -1 )
{
foreach( var ext in exts )
{
var s = GetFromCache( allFiles, filename + ext );
if( s != null )
var s = GetFromCache(allFiles, filename + ext);
if (s != null)
return s;
}
}
foreach( var ext in exts )
foreach (var ext in exts)
{
var folder = mountedFolders
var folder = MountedFolders
.Where(x => x.Exists(filename + ext))
.OrderByDescending(x => x.Priority)
.FirstOrDefault();
@@ -151,7 +157,7 @@ namespace OpenRA.FileFormats
throw new FileNotFoundException("File not found: {0}".F(filename), filename);
}
public static bool Exists(string filename) { return mountedFolders.Any(f => f.Exists(filename)); }
public static bool Exists(string filename) { return MountedFolders.Any(f => f.Exists(filename)); }
static Dictionary<string, Assembly> assemblyCache = new Dictionary<string, Assembly>();

View File

@@ -16,11 +16,11 @@ namespace OpenRA.Graphics
{
public class SpriteLoader
{
public SpriteLoader( string[] exts, SheetBuilder sheetBuilder )
public SpriteLoader(string[] exts, SheetBuilder sheetBuilder)
{
SheetBuilder = sheetBuilder;
this.exts = exts;
sprites = new Cache<string, Sprite[]>( LoadSprites );
sprites = new Cache<string, Sprite[]>(LoadSprites);
}
readonly SheetBuilder SheetBuilder;

View File

@@ -18,6 +18,7 @@ namespace OpenRA.Widgets
public string Image = "";
public int Frame = 0;
public string Palette = "chrome";
public bool LoopAnimation = false;
public Func<string> GetImage;
public Func<int> GetFrame;
@@ -26,12 +27,13 @@ namespace OpenRA.Widgets
readonly WorldRenderer worldRenderer;
[ObjectCreator.UseCtor]
public ShpImageWidget( WorldRenderer worldRenderer)
public ShpImageWidget(WorldRenderer worldRenderer)
: base()
{
GetImage = () => { return Image; };
GetFrame = () => { return Frame; };
GetPalette = () => { return Palette; };
this.worldRenderer = worldRenderer;
}
@@ -41,9 +43,12 @@ namespace OpenRA.Widgets
Image = other.Image;
Frame = other.Frame;
Palette = other.Palette;
LoopAnimation = other.LoopAnimation;
GetImage = other.GetImage;
GetFrame = other.GetFrame;
GetPalette = other.GetPalette;
worldRenderer = other.worldRenderer;
}
@@ -68,5 +73,32 @@ namespace OpenRA.Widgets
Game.Renderer.SpriteRenderer.DrawSprite(sprite, RenderOrigin, worldRenderer, palette);
}
public int FrameCount
{
get { return Game.modData.SpriteLoader.LoadAllSprites(Image).Length-1; }
}
public void RenderNextFrame()
{
if (Frame < FrameCount)
Frame++;
else
Frame = 0;
}
public void RenderPreviousFrame()
{
if (Frame > 0)
Frame--;
else
Frame = FrameCount;
}
public override void Tick()
{
if (LoopAnimation)
RenderNextFrame();
}
}
}

View File

@@ -24,10 +24,15 @@ namespace OpenRA.Widgets
public float MinimumValue = 0;
public float MaximumValue = 1;
public float Value = 0;
public Func<float> GetValue;
protected bool isMoving = false;
public SliderWidget() : base() {}
public SliderWidget()
: base()
{
GetValue = () => Value;
}
public SliderWidget(SliderWidget other)
: base(other)
@@ -38,6 +43,7 @@ namespace OpenRA.Widgets
MaximumValue = other.MaximumValue;
Value = other.Value;
TrackHeight = other.TrackHeight;
GetValue = other.GetValue;
}
void UpdateValue(float newValue)
@@ -53,7 +59,7 @@ namespace OpenRA.Widgets
if (mi.Event == MouseInputEvent.Down && !TakeFocus(mi)) return false;
if (!Focused) return false;
switch( mi.Event )
switch(mi.Event)
{
case MouseInputEvent.Up:
isMoving = false;
@@ -99,6 +105,8 @@ namespace OpenRA.Widgets
if (!IsVisible())
return;
Value = GetValue();
var tr = ThumbRect;
var rb = RenderBounds;
var trackWidth = rb.Width;

View File

@@ -436,6 +436,7 @@
<Compile Include="Widgets\ColorMixerWidget.cs" />
<Compile Include="Widgets\HueSliderWidget.cs" />
<Compile Include="Render\WithTurret.cs" />
<Compile Include="Widgets\Logic\AssetBrowserLogic.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">

View File

@@ -0,0 +1,124 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 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. For more information,
* see COPYING.
*/
#endregion
using System;
using System.IO;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.GameRules;
using OpenRA.Graphics;
using OpenRA.Widgets;
namespace OpenRA.Mods.RA.Widgets.Logic
{
public class AssetBrowserLogic
{
Widget panel;
ShpImageWidget spriteImage;
TextFieldWidget filenameInput;
SliderWidget frameSlider;
ButtonWidget playButton;
ButtonWidget pauseButton;
[ObjectCreator.UseCtor]
public AssetBrowserLogic(Widget widget, Action onExit, World world)
{
panel = widget;
spriteImage = panel.Get<ShpImageWidget>("SPRITE");
filenameInput = panel.Get<TextFieldWidget>("FILENAME_INPUT");
filenameInput.Text = spriteImage.Image;
filenameInput.OnEnterKey = () => LoadAsset(filenameInput.Text);
var assetList = panel.Get<ScrollPanelWidget>("ASSET_LIST");
var template = panel.Get<ScrollItemWidget>("ASSET_TEMPLATE");
assetList.RemoveChildren();
foreach (var folder in FileSystem.FolderPaths)
{
if (Directory.Exists(folder))
{
var shps = Directory.GetFiles(folder, "*.shp");
foreach (var shp in shps)
AddAsset(assetList, shp, template);
}
}
frameSlider = panel.Get<SliderWidget>("FRAME_SLIDER");
frameSlider.MaximumValue = (float)spriteImage.FrameCount;
frameSlider.Ticks = spriteImage.FrameCount+1;
frameSlider.OnChange += x => { spriteImage.Frame = (int)Math.Round(x); };
frameSlider.GetValue = () => spriteImage.Frame;
panel.Get<LabelWidget>("FRAME_COUNT").GetText = () => spriteImage.Frame.ToString();
playButton = panel.Get<ButtonWidget>("BUTTON_PLAY");
playButton.OnClick = () =>
{
spriteImage.LoopAnimation = true;
playButton.Visible = false;
pauseButton.Visible = true;
};
pauseButton = panel.Get<ButtonWidget>("BUTTON_PAUSE");
pauseButton.OnClick = () =>
{
spriteImage.LoopAnimation = false;
playButton.Visible = true;
pauseButton.Visible = false;
};
panel.Get<ButtonWidget>("BUTTON_STOP").OnClick = () =>
{
spriteImage.LoopAnimation = false;
frameSlider.Value = 0;
spriteImage.Frame = 0;
playButton.Visible = true;
pauseButton.Visible = false;
};
panel.Get<ButtonWidget>("BUTTON_NEXT").OnClick = () => { spriteImage.RenderNextFrame(); };
panel.Get<ButtonWidget>("BUTTON_PREV").OnClick = () => { spriteImage.RenderPreviousFrame(); };
panel.Get<ButtonWidget>("LOAD_BUTTON").OnClick = () =>
{
LoadAsset(filenameInput.Text);
};
panel.Get<ButtonWidget>("CLOSE_BUTTON").OnClick = () => { Ui.CloseWindow(); onExit(); };
}
void AddAsset(ScrollPanelWidget list, string filepath, ScrollItemWidget template)
{
var sprite = Path.GetFileNameWithoutExtension(filepath);
var item = ScrollItemWidget.Setup(template,
() => spriteImage != null && spriteImage.Image == sprite,
() => LoadAsset(sprite));
item.Get<LabelWidget>("TITLE").GetText = () => sprite;
list.AddChild(item);
}
bool LoadAsset(string filename)
{
if (filename == null)
return false;
filenameInput.Text = filename;
spriteImage.Frame = 0;
spriteImage.Image = filename;
frameSlider.MaximumValue = (float)spriteImage.FrameCount;
frameSlider.Ticks = spriteImage.FrameCount+1;
return true;
}
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2013 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. For more information,
@@ -14,7 +14,6 @@ namespace OpenRA.Mods.RA.Widgets.Logic
{
public class MainMenuButtonsLogic
{
enum MenuType { Main, None }
MenuType Menu = MenuType.Main;
@@ -73,6 +72,15 @@ namespace OpenRA.Mods.RA.Widgets.Logic
});
};
widget.Get<ButtonWidget>("MAINMENU_BUTTON_ASSET_BROWSER").OnClick = () =>
{
Menu = MenuType.None;
Game.OpenWindow("ASSETBROWSER_BG", new WidgetArgs()
{
{ "onExit", () => Menu = MenuType.Main }
});
};
widget.Get<ButtonWidget>("MAINMENU_BUTTON_QUIT").OnClick = () => Game.Exit();
}

View File

@@ -0,0 +1,196 @@
Background@ASSETBROWSER_BG:
Logic:AssetBrowserLogic
X:(WINDOW_RIGHT - WIDTH)/2
Y:(WINDOW_BOTTOM - HEIGHT)/2
Width:700
Height:410
Children:
Label@ASSETBROWSER_TITLE:
X:0
Y:20
Width:PARENT_RIGHT
Height:25
Text:Game Asset Viewer & Converter
Align:Center
Font:Bold
DropDownButton@SOURCE_SELECTOR:
X:40
Y:45
Width:160
Height:25
Font:Bold
Text:Folders
ScrollPanel@ASSET_LIST:
X:40
Y:80
Width:160
Height:190
Children:
ScrollItem@ASSET_TEMPLATE:
Width:PARENT_RIGHT-27
Height:25
X:2
Y:0
Visible:false
Children:
Label@TITLE:
X:10
Width:PARENT_RIGHT-20
Height:25
TextField@FILENAME_INPUT:
X:40
Y:280
Width:140
Height:25
Button@LOAD_BUTTON:
X:40
Y:310
Width:140
Height:25
Text:Load
Font:Bold
Key:return
Label@PREVIEW_TITLE:
X:320
Y:45
Width:PARENT_RIGHT
Height:25
Text:Preview
Background@SPRITE_BG:
X:220
Y:80
Width:250
Height:250
Background:dialog3
Children:
ShpImage@SPRITE:
X:80
Y:80
Width:246
Height:246
Image:fact
Palette:player
Label@ACTIONS_TITLE:
X:PARENT_RIGHT - 150
Y:45
Width:PARENT_RIGHT
Height:25
Text:Actions
Button@REMAP_BUTTON:
X:PARENT_RIGHT - 200
Y:PARENT_BOTTOM - 305
Width:160
Height:25
Text:Change Palette
Font:Bold
Button@IMPORT_BUTTON:
X:PARENT_RIGHT - 200
Y:PARENT_BOTTOM - 270
Width:160
Height:25
Text:Import from PNG
Font:Bold
Button@EXTRACT_BUTTON:
X:PARENT_RIGHT - 200
Y:PARENT_BOTTOM - 235
Width:160
Height:25
Text:Extract to Folder
Font:Bold
Button@EXPORT_BUTTON:
X:PARENT_RIGHT - 200
Y:PARENT_BOTTOM - 200
Width:160
Height:25
Text:Export as PNG
Font:Bold
Button@CLOSE_BUTTON:
X:PARENT_RIGHT - 200
Y:PARENT_BOTTOM - 115
Width:160
Height:25
Text:Close
Font:Bold
Key:escape
Container@FRAME_SELECTOR:
X:45
Y:360
Children:
Button@BUTTON_PREV:
X:0
Y:0
Width:25
Height:25
Children:
Image@IMAGE_PREV:
X:0
Y:0
Width:25
Height:25
ImageCollection:music
ImageName:prev
Button@BUTTON_PLAY:
X:35
Y:0
Width:25
Height:25
Children:
Image@IMAGE_PLAY:
X:0
Y:0
Width:25
Height:25
ImageCollection:music
ImageName:play
Button@BUTTON_PAUSE:
Visible: no
X:35
Y:0
Width:25
Height:25
Children:
Image@IMAGE_PAUSE:
X:0
Y:0
Width:25
Height:25
ImageCollection:music
ImageName:pause
Button@BUTTON_STOP:
X:70
Y:0
Width:25
Height:25
Children:
Image@IMAGE_STOP:
X:0
Y:0
Width:25
Height:25
ImageCollection:music
ImageName:stop
Button@BUTTON_NEXT:
X:105
Y:0
Width:25
Height:25
Children:
Image@IMAGE_NEXT:
X:0
Y:0
Width:25
Height:25
ImageCollection:music
ImageName:next
Slider@FRAME_SLIDER:
X:175
Y:0
Width:410
Height:20
MinimumValue: 0
Label@FRAME_COUNT:
X:610
Y:0
Width:25
Height:25
Font:Bold

View File

@@ -23,7 +23,7 @@ Container@MAINMENU:
X:(WINDOW_RIGHT - WIDTH)/8
Y:(WINDOW_BOTTOM - HEIGHT)/2
Width:250
Height:505
Height:555
Logic:MainMenuButtonsLogic
Children:
Label@MAINMENU_LABEL_TITLE:
@@ -83,11 +83,18 @@ Container@MAINMENU:
Height:35
Text:Replay Viewer
Font:Bold
Button@MAINMENU_BUTTON_QUIT:
Button@MAINMENU_BUTTON_ASSET_BROWSER:
X:45
Y:430
Width:160
Height:35
Text:Asset Browser
Font:Bold
Button@MAINMENU_BUTTON_QUIT:
X:45
Y:480
Width:160
Height:35
Text:Quit
Font:Bold
Background@PERF_BG:

View File

@@ -73,6 +73,7 @@ ChromeLayout:
mods/ra/chrome/cheats.yaml
mods/ra/chrome/musicplayer.yaml
mods/ra/chrome/tooltips.yaml
mods/ra/chrome/assetbrowser.yaml
Weapons:
mods/ra/weapons.yaml