mirror of
https://github.com/Phantop/LADXHD.git
synced 2024-10-31 20:04:18 +00:00
589 lines
27 KiB
C#
589 lines
27 KiB
C#
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.IO;
|
|||
|
using Microsoft.Xna.Framework;
|
|||
|
using Microsoft.Xna.Framework.Content;
|
|||
|
using Microsoft.Xna.Framework.Graphics;
|
|||
|
using ProjectZ.Base;
|
|||
|
using ProjectZ.Base.UI;
|
|||
|
using ProjectZ.InGame.SaveLoad;
|
|||
|
using ProjectZ.InGame.Things;
|
|||
|
using Keys = Microsoft.Xna.Framework.Input.Keys;
|
|||
|
#if WINDOWS
|
|||
|
using System.Windows.Forms;
|
|||
|
#endif
|
|||
|
|
|||
|
namespace ProjectZ.Editor
|
|||
|
{
|
|||
|
internal class SpriteAtlasScreen : InGame.Screens.Screen
|
|||
|
{
|
|||
|
private readonly EditorCamera _camera = new EditorCamera();
|
|||
|
|
|||
|
private readonly SpriteAtlasSerialization.SpriteAtlas _spriteAtlas = new SpriteAtlasSerialization.SpriteAtlas();
|
|||
|
private List<SpriteAtlasSerialization.AtlasEntry> _sourceData => _spriteAtlas.Data;
|
|||
|
private UiEditList<SpriteAtlasSerialization.AtlasEntry> _spriteAtlasList;
|
|||
|
|
|||
|
private int _spriteIndex;
|
|||
|
private SpriteAtlasSerialization.AtlasEntry _selectedEntry;
|
|||
|
|
|||
|
private List<int> _moveEntries = new List<int>();
|
|||
|
|
|||
|
private Texture2D _sprTexture;
|
|||
|
private Color[] _colorData;
|
|||
|
|
|||
|
private Texture2D _sprSelectionTexture;
|
|||
|
private Color[] _colorDataSelection;
|
|||
|
|
|||
|
private Point _lastPosition;
|
|||
|
private Point _currentPosition;
|
|||
|
private Point _selectionStart;
|
|||
|
private Point _selectionEnd;
|
|||
|
|
|||
|
private UiNumberInput _atlasScale;
|
|||
|
private UiNumberInput _inputSourceX;
|
|||
|
private UiNumberInput _inputSourceY;
|
|||
|
private UiNumberInput _inputSourceWidth;
|
|||
|
private UiNumberInput _inputSourceHeight;
|
|||
|
private UiNumberInput _inputSourceOriginX;
|
|||
|
private UiNumberInput _inputSourceOriginY;
|
|||
|
private UiTextInput _inputEntryName;
|
|||
|
|
|||
|
private string _lastFileName;
|
|||
|
|
|||
|
private const int LeftBarWidth = 200;
|
|||
|
private const int RightBarWidth = 250;
|
|||
|
private const int TileSize = 2;
|
|||
|
|
|||
|
private bool _selecting;
|
|||
|
private bool _imageWasEdited;
|
|||
|
|
|||
|
public SpriteAtlasScreen(string screenId) : base(screenId) { }
|
|||
|
|
|||
|
public override void Load(ContentManager content)
|
|||
|
{
|
|||
|
var buttonDist = 5;
|
|||
|
var buttonWidth = LeftBarWidth - buttonDist * 2;
|
|||
|
var buttonWidthHalf = (LeftBarWidth - buttonDist * 3) / 2;
|
|||
|
var buttonHeight = 30;
|
|||
|
var labelHeight = Resources.EditorFontHeight;
|
|||
|
var buttonQWidth = LeftBarWidth - 15 - buttonHeight;
|
|||
|
var posY = Values.ToolBarHeight + buttonDist;
|
|||
|
|
|||
|
var screenId = Values.EditorUiSpriteAtlas;
|
|||
|
|
|||
|
Game1.EditorUi.AddElement(new UiRectangle(new Rectangle(0, 0, 0, 0), "leftBar", screenId, Values.ColorBackgroundLight, Color.White,
|
|||
|
ui => { ui.Rectangle = new Rectangle(0, Values.ToolBarHeight, LeftBarWidth, Game1.WindowHeight - Values.ToolBarHeight); }));
|
|||
|
|
|||
|
Game1.EditorUi.AddElement(new UiRectangle(new Rectangle(0, 0, 0, 0), "rightBar", screenId, Values.ColorBackgroundLight, Color.White,
|
|||
|
ui => { ui.Rectangle = new Rectangle(Game1.WindowWidth - RightBarWidth, Values.ToolBarHeight, RightBarWidth, Game1.WindowHeight - Values.ToolBarHeight); }));
|
|||
|
|
|||
|
posY = Values.ToolBarHeight + buttonDist;
|
|||
|
|
|||
|
// load button
|
|||
|
Game1.EditorUi.AddElement(new UiButton(new Rectangle(5, posY, buttonWidth, buttonHeight), Resources.EditorFont,
|
|||
|
"load", "bt1", screenId, null, ui => { LoadSprite(); }));
|
|||
|
// save button
|
|||
|
Game1.EditorUi.AddElement(new UiButton(new Rectangle(5, posY += buttonHeight + buttonDist, buttonWidth, buttonHeight), Resources.EditorFont,
|
|||
|
"save as...", "bt1", screenId, null, ui => { SaveSpriteAtlasDialog(); }));
|
|||
|
Game1.EditorUi.AddElement(new UiButton(new Rectangle(5, posY += buttonHeight + buttonDist, buttonWidth, buttonHeight), Resources.EditorFont,
|
|||
|
"save...", "bt1", screenId, null, ui => { SaveSpriteAtlas(_lastFileName); }));
|
|||
|
|
|||
|
Game1.EditorUi.AddElement(_atlasScale = new UiNumberInput(new Rectangle(5, posY += buttonHeight + buttonDist, buttonWidth, buttonHeight),
|
|||
|
Resources.EditorFont, 0, 1, 16, 1, "Scaling", screenId, null,
|
|||
|
ui =>
|
|||
|
{
|
|||
|
_spriteAtlas.Scale = (int)((UiNumberInput)ui).Value;
|
|||
|
}));
|
|||
|
|
|||
|
Game1.EditorUi.AddElement(new UiLabel(new Rectangle(buttonDist, posY += buttonHeight + buttonDist * 3, buttonWidth, labelHeight),
|
|||
|
Resources.EditorFont, "Source Rectangle", "sourceHeader", screenId, null));
|
|||
|
|
|||
|
var minValue = 0;
|
|||
|
var maxValue = 10000;
|
|||
|
|
|||
|
_inputSourceX = new UiNumberInput(new Rectangle(buttonDist, posY += labelHeight + buttonDist, buttonWidthHalf, buttonHeight),
|
|||
|
Resources.EditorFont, 0, minValue, maxValue, 1, "sourceX", screenId, null,
|
|||
|
ui =>
|
|||
|
{
|
|||
|
FixSelectedPart();
|
|||
|
_sourceData[_spriteIndex].SourceRectangle.X = (int)((UiNumberInput)ui).Value;
|
|||
|
});
|
|||
|
_inputSourceY = new UiNumberInput(new Rectangle(buttonDist * 2 + buttonWidthHalf, posY, buttonWidthHalf, buttonHeight),
|
|||
|
Resources.EditorFont, 0, minValue, maxValue, 1, "sourceY", screenId, null,
|
|||
|
ui =>
|
|||
|
{
|
|||
|
FixSelectedPart();
|
|||
|
_sourceData[_spriteIndex].SourceRectangle.Y = (int)((UiNumberInput)ui).Value;
|
|||
|
});
|
|||
|
_inputSourceWidth = new UiNumberInput(new Rectangle(buttonDist, posY += buttonHeight + buttonDist, buttonWidthHalf, buttonHeight),
|
|||
|
Resources.EditorFont, 0, minValue, maxValue, 1, "sourceWidth", screenId, null, ui =>
|
|||
|
{
|
|||
|
FixSelectedPart();
|
|||
|
_sourceData[_spriteIndex].SourceRectangle.Width = (int)((UiNumberInput)ui).Value;
|
|||
|
});
|
|||
|
_inputSourceHeight = new UiNumberInput(new Rectangle(buttonDist * 2 + buttonWidthHalf, posY, buttonWidthHalf, buttonHeight),
|
|||
|
Resources.EditorFont, 0, minValue, maxValue, 1, "sourceHeight", screenId, null, ui =>
|
|||
|
{
|
|||
|
FixSelectedPart();
|
|||
|
_sourceData[_spriteIndex].SourceRectangle.Height = (int)((UiNumberInput)ui).Value;
|
|||
|
});
|
|||
|
|
|||
|
Game1.EditorUi.AddElement(new UiLabel(new Rectangle(buttonDist, posY += buttonHeight + buttonDist * 3, buttonWidth, labelHeight),
|
|||
|
Resources.EditorFont, "Origin", "originLabel", screenId, null));
|
|||
|
|
|||
|
Game1.EditorUi.AddElement(_inputSourceOriginX = new UiNumberInput(new Rectangle(buttonDist, posY += labelHeight + buttonDist, buttonWidthHalf, buttonHeight),
|
|||
|
Resources.EditorFont, 0, minValue, maxValue, 1, "originX", screenId, null,
|
|||
|
ui =>
|
|||
|
{
|
|||
|
_sourceData[_spriteIndex].Origin.X = (int)((UiNumberInput)ui).Value;
|
|||
|
}));
|
|||
|
Game1.EditorUi.AddElement(_inputSourceOriginY = new UiNumberInput(new Rectangle(buttonDist * 2 + buttonWidthHalf, posY, buttonWidthHalf, buttonHeight),
|
|||
|
Resources.EditorFont, 0, minValue, maxValue, 1, "originY", screenId, null,
|
|||
|
ui =>
|
|||
|
{
|
|||
|
_sourceData[_spriteIndex].Origin.Y = (int)((UiNumberInput)ui).Value;
|
|||
|
}));
|
|||
|
|
|||
|
Game1.EditorUi.AddElement(_inputSourceX);
|
|||
|
Game1.EditorUi.AddElement(_inputSourceY);
|
|||
|
Game1.EditorUi.AddElement(_inputSourceWidth);
|
|||
|
Game1.EditorUi.AddElement(_inputSourceHeight);
|
|||
|
|
|||
|
var buttonWidthRight = RightBarWidth - buttonDist * 2;
|
|||
|
var buttonWidthHalfRight = (RightBarWidth - buttonDist * 3) / 2;
|
|||
|
var buttonHeightRight = 25;
|
|||
|
|
|||
|
posY = Values.ToolBarHeight + buttonDist;
|
|||
|
var inputPosY = posY;
|
|||
|
_inputEntryName = new UiTextInput(new Rectangle(0, 0, buttonWidthRight, 35), Resources.EditorFontMonoSpace, 32, "inputSpriteName", screenId,
|
|||
|
element => element.Rectangle = new Rectangle(Game1.WindowWidth - RightBarWidth + buttonDist, inputPosY, buttonWidthRight, 45),
|
|||
|
element =>
|
|||
|
{
|
|||
|
if (_sourceData.Count > _spriteIndex)
|
|||
|
_sourceData[_spriteIndex].EntryId = ((UiTextInput)element).StrValue;
|
|||
|
});
|
|||
|
Game1.EditorUi.AddElement(_inputEntryName);
|
|||
|
|
|||
|
posY += 45 + buttonDist;
|
|||
|
var buttonPosY0 = posY;
|
|||
|
Game1.EditorUi.AddElement(new UiButton(Rectangle.Empty, Resources.EditorFont, "add", "bt1", screenId,
|
|||
|
element => element.Rectangle = new Rectangle(Game1.WindowWidth - RightBarWidth + buttonDist, buttonPosY0, buttonWidthHalfRight, buttonHeight),
|
|||
|
ui => { AddAtlasEntry(); }));
|
|||
|
|
|||
|
var buttonPosY1 = posY;
|
|||
|
Game1.EditorUi.AddElement(new UiButton(Rectangle.Empty, Resources.EditorFont, "remove", "bt2", screenId,
|
|||
|
element => element.Rectangle = new Rectangle(Game1.WindowWidth - RightBarWidth + buttonDist * 2 + buttonWidthHalfRight, buttonPosY1, buttonWidthHalfRight, buttonHeight),
|
|||
|
ui => { RemoveAtlasEntry(); }));
|
|||
|
|
|||
|
posY += buttonHeight + buttonDist;
|
|||
|
var buttonPosY2 = posY;
|
|||
|
Game1.EditorUi.AddElement(new UiButton(Rectangle.Empty, Resources.EditorFont, "sort", "bt3", screenId,
|
|||
|
element => element.Rectangle = new Rectangle(Game1.WindowWidth - RightBarWidth + buttonDist, buttonPosY2, buttonWidthRight, buttonHeight),
|
|||
|
ui => { SortAtlasEntry(); }));
|
|||
|
|
|||
|
posY += buttonHeight + buttonDist;
|
|||
|
Game1.EditorUi.AddElement(_spriteAtlasList = new UiEditList<SpriteAtlasSerialization.AtlasEntry>(
|
|||
|
Rectangle.Empty, Resources.EditorFontSmallMonoSpace, _sourceData, "bt4", screenId, element =>
|
|||
|
{
|
|||
|
element.Rectangle = new Rectangle(Game1.WindowWidth - RightBarWidth, posY, RightBarWidth, Game1.WindowHeight - posY);
|
|||
|
var selectedEntry = ((UiEditList<SpriteAtlasSerialization.AtlasEntry>)element).SelectedEntry;
|
|||
|
if (selectedEntry != _spriteIndex)
|
|||
|
{
|
|||
|
// do not fix anything if just the order of the list was changed
|
|||
|
if (_sourceData[selectedEntry] != _selectedEntry)
|
|||
|
FixSelectedPart();
|
|||
|
|
|||
|
_spriteIndex = selectedEntry;
|
|||
|
_selectedEntry = _sourceData[_spriteIndex];
|
|||
|
|
|||
|
UpdateInputUi();
|
|||
|
}
|
|||
|
}));
|
|||
|
}
|
|||
|
|
|||
|
public override void Update(GameTime gameTime)
|
|||
|
{
|
|||
|
Game1.EditorUi.CurrentScreen = Values.EditorUiSpriteAtlas;
|
|||
|
|
|||
|
if (_sprTexture == null)
|
|||
|
return;
|
|||
|
|
|||
|
// update the camera
|
|||
|
var mousePosition = InputHandler.MousePosition();
|
|||
|
|
|||
|
if (InputHandler.MouseIntersect(new Rectangle(LeftBarWidth, Values.ToolBarHeight,
|
|||
|
Game1.WindowWidth - LeftBarWidth - RightBarWidth, Game1.WindowHeight - Values.ToolBarHeight)))
|
|||
|
{
|
|||
|
_currentPosition = new Point(
|
|||
|
(int)((InputHandler.MousePosition().X - _camera.Location.X) / _camera.Scale),
|
|||
|
(int)((InputHandler.MousePosition().Y - _camera.Location.Y) / _camera.Scale));
|
|||
|
|
|||
|
if (InputHandler.MouseLeftStart())
|
|||
|
{
|
|||
|
FixSelectedPart();
|
|||
|
|
|||
|
if (InputHandler.KeyDown(Keys.LeftShift))
|
|||
|
{
|
|||
|
SelectSprite(_currentPosition);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
_selecting = true;
|
|||
|
_selectionStart = _currentPosition;
|
|||
|
}
|
|||
|
}
|
|||
|
if (InputHandler.MouseLeftDown() && _selecting)
|
|||
|
{
|
|||
|
_selectionEnd = _currentPosition;
|
|||
|
|
|||
|
var selectionStart = new Point(Math.Min(_selectionStart.X, _selectionEnd.X), Math.Min(_selectionStart.Y, _selectionEnd.Y));
|
|||
|
var selectionEnd = new Point(Math.Max(_selectionStart.X, _selectionEnd.X) + 1, Math.Max(_selectionStart.Y, _selectionEnd.Y) + 1);
|
|||
|
|
|||
|
_sourceData[_spriteIndex].SourceRectangle.X = selectionStart.X;
|
|||
|
_sourceData[_spriteIndex].SourceRectangle.Y = selectionStart.Y;
|
|||
|
_sourceData[_spriteIndex].SourceRectangle.Width = selectionEnd.X - selectionStart.X;
|
|||
|
_sourceData[_spriteIndex].SourceRectangle.Height = selectionEnd.Y - selectionStart.Y;
|
|||
|
|
|||
|
UpdateInputUi();
|
|||
|
}
|
|||
|
if (InputHandler.MouseLeftReleased())
|
|||
|
{
|
|||
|
_selecting = false;
|
|||
|
}
|
|||
|
|
|||
|
if (InputHandler.MouseRightStart() && _sprSelectionTexture == null)
|
|||
|
{
|
|||
|
var selectionSource = _sourceData[_spriteIndex].SourceRectangle;
|
|||
|
|
|||
|
if (selectionSource.Width > 0 && selectionSource.Height > 0)
|
|||
|
{
|
|||
|
_colorDataSelection = new Color[selectionSource.Width * selectionSource.Height];
|
|||
|
|
|||
|
// fill the color data array
|
|||
|
for (var y = 0; y < selectionSource.Height; y++)
|
|||
|
for (var x = 0; x < selectionSource.Width; x++)
|
|||
|
{
|
|||
|
var originX = selectionSource.X + x;
|
|||
|
var originY = selectionSource.Y + y;
|
|||
|
|
|||
|
if (0 <= originX && originX < _sprTexture.Width &&
|
|||
|
0 <= originY && originY < _sprTexture.Height)
|
|||
|
{
|
|||
|
_colorDataSelection[x + y * selectionSource.Width] =
|
|||
|
_colorData[originX + originY * _sprTexture.Width];
|
|||
|
|
|||
|
// remove the data from the base texture
|
|||
|
_colorData[originX + originY * _sprTexture.Width] = Color.Transparent;
|
|||
|
}
|
|||
|
else
|
|||
|
_colorDataSelection[x + y * selectionSource.Width] = Color.Transparent;
|
|||
|
}
|
|||
|
|
|||
|
_sprSelectionTexture = new Texture2D(Game1.Graphics.GraphicsDevice, selectionSource.Width, selectionSource.Height);
|
|||
|
_sprSelectionTexture.SetData(_colorDataSelection);
|
|||
|
|
|||
|
_sprTexture.SetData(_colorData);
|
|||
|
|
|||
|
// get a list of entries that will be moved
|
|||
|
_moveEntries.Clear();
|
|||
|
for (var i = 0; i < _sourceData.Count; i++)
|
|||
|
{
|
|||
|
if (i != _spriteIndex &&
|
|||
|
_sourceData[_spriteIndex].SourceRectangle.Contains(_sourceData[i].SourceRectangle))
|
|||
|
{
|
|||
|
_moveEntries.Add(i);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
if (InputHandler.MouseRightDown())
|
|||
|
{
|
|||
|
var offset = new Point(_currentPosition.X - _lastPosition.X, _currentPosition.Y - _lastPosition.Y);
|
|||
|
MoveSelection(offset);
|
|||
|
}
|
|||
|
|
|||
|
if (InputHandler.MouseWheelUp())
|
|||
|
_camera.Zoom(1, mousePosition);
|
|||
|
if (InputHandler.MouseWheelDown())
|
|||
|
_camera.Zoom(-1, mousePosition);
|
|||
|
}
|
|||
|
|
|||
|
if (!InputHandler.MouseMiddleStart() && InputHandler.MouseMiddleDown())
|
|||
|
_camera.Location += mousePosition - InputHandler.LastMousePosition();
|
|||
|
|
|||
|
_lastPosition = _currentPosition;
|
|||
|
}
|
|||
|
|
|||
|
public override void Draw(SpriteBatch spriteBatch)
|
|||
|
{
|
|||
|
if (_sprTexture == null)
|
|||
|
return;
|
|||
|
|
|||
|
spriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointWrap, null, null, null, _camera.TransformMatrix);
|
|||
|
|
|||
|
// draw the tiled background
|
|||
|
spriteBatch.Draw(Resources.SprTiledBlock, new Rectangle(0, 0, _sprTexture.Width, _sprTexture.Height),
|
|||
|
new Rectangle(0, 0, (int)(_sprTexture.Width / (float)TileSize * 2), (int)(_sprTexture.Height / (float)TileSize * 2)), Color.White);
|
|||
|
|
|||
|
// draw the sprite
|
|||
|
spriteBatch.Draw(_sprTexture, Vector2.Zero, Color.White);
|
|||
|
|
|||
|
// draw the sprite selection
|
|||
|
if (_sprSelectionTexture != null)
|
|||
|
spriteBatch.Draw(_sprSelectionTexture,
|
|||
|
new Vector2(_sourceData[_spriteIndex].SourceRectangle.X,
|
|||
|
_sourceData[_spriteIndex].SourceRectangle.Y), Color.White);
|
|||
|
|
|||
|
// draw current position of the mouse
|
|||
|
spriteBatch.Draw(Resources.SprWhite,
|
|||
|
new Rectangle(_currentPosition.X, _currentPosition.Y, 1, 1), Color.Red * 0.5f);
|
|||
|
|
|||
|
for (var i = 0; i < _sourceData.Count; i++)
|
|||
|
{
|
|||
|
spriteBatch.Draw(Resources.SprWhite, new Rectangle(
|
|||
|
_sourceData[i].SourceRectangle.X, _sourceData[i].SourceRectangle.Y,
|
|||
|
_sourceData[i].SourceRectangle.Width, _sourceData[i].SourceRectangle.Height),
|
|||
|
_spriteIndex == i ? Color.Red * (0.5f + MathF.Sin((float)Game1.TotalTime / 100) * 0.125f) : Color.Red * 0.25f);
|
|||
|
}
|
|||
|
|
|||
|
spriteBatch.End();
|
|||
|
spriteBatch.Begin();
|
|||
|
|
|||
|
// draw the origin xy axis
|
|||
|
if (0 <= _spriteIndex && _spriteIndex <= _sourceData.Count)
|
|||
|
{
|
|||
|
var originPosition = new Vector2(
|
|||
|
_camera.Location.X + (_sourceData[_spriteIndex].SourceRectangle.X + _sourceData[_spriteIndex].Origin.X) * _camera.Scale,
|
|||
|
_camera.Location.Y + (_sourceData[_spriteIndex].SourceRectangle.Y + _sourceData[_spriteIndex].Origin.Y) * _camera.Scale);
|
|||
|
spriteBatch.Draw(Resources.SprWhite, new Vector2(originPosition.X - 1, originPosition.Y - 10), new Rectangle(0, 0, 2, 20), Color.Green);
|
|||
|
spriteBatch.Draw(Resources.SprWhite, new Vector2(originPosition.X - 10, originPosition.Y - 1), new Rectangle(0, 0, 20, 2), Color.Red);
|
|||
|
}
|
|||
|
|
|||
|
spriteBatch.End();
|
|||
|
}
|
|||
|
|
|||
|
private void MoveSelection(Point offset)
|
|||
|
{
|
|||
|
foreach (var entryIndex in _moveEntries)
|
|||
|
{
|
|||
|
_sourceData[entryIndex].SourceRectangle.X += offset.X;
|
|||
|
_sourceData[entryIndex].SourceRectangle.Y += offset.Y;
|
|||
|
}
|
|||
|
|
|||
|
_sourceData[_spriteIndex].SourceRectangle.X += offset.X;
|
|||
|
_sourceData[_spriteIndex].SourceRectangle.Y += offset.Y;
|
|||
|
}
|
|||
|
|
|||
|
private void FixSelectedPart()
|
|||
|
{
|
|||
|
if (_sprSelectionTexture == null)
|
|||
|
return;
|
|||
|
|
|||
|
_imageWasEdited = true;
|
|||
|
|
|||
|
var selectionSource = _sourceData[_spriteIndex].SourceRectangle;
|
|||
|
|
|||
|
// need to resize the texture?
|
|||
|
var offset = Point.Zero;
|
|||
|
if (selectionSource.X < 0)
|
|||
|
offset.X = -selectionSource.X;
|
|||
|
if (selectionSource.Y < 0)
|
|||
|
offset.Y = -selectionSource.Y;
|
|||
|
|
|||
|
var newSizeX = Math.Max(offset.X + selectionSource.Right, offset.X + _sprTexture.Width);
|
|||
|
var newSizeY = Math.Max(offset.Y + selectionSource.Bottom, offset.Y + _sprTexture.Height);
|
|||
|
|
|||
|
// clamp the max texture size
|
|||
|
var maxSize = 4096;
|
|||
|
if (newSizeX > maxSize)
|
|||
|
{
|
|||
|
if (offset.X > 0)
|
|||
|
offset.X = Math.Clamp(offset.X, 0, maxSize - _sprTexture.Width);
|
|||
|
newSizeX = maxSize;
|
|||
|
}
|
|||
|
if (newSizeY > maxSize)
|
|||
|
{
|
|||
|
if (offset.Y > 0)
|
|||
|
offset.X = Math.Clamp(offset.Y, 0, maxSize - _sprTexture.Height);
|
|||
|
newSizeY = maxSize;
|
|||
|
}
|
|||
|
|
|||
|
if (newSizeX != _sprTexture.Width || newSizeY != _sprTexture.Height)
|
|||
|
{
|
|||
|
var newColorData = new Color[newSizeX * newSizeY];
|
|||
|
for (var y = 0; y < _sprTexture.Height; y++)
|
|||
|
for (var x = 0; x < _sprTexture.Width; x++)
|
|||
|
{
|
|||
|
newColorData[(x + offset.X) + (y + offset.Y) * newSizeX] = _colorData[x + y * _sprTexture.Width];
|
|||
|
}
|
|||
|
|
|||
|
_colorData = newColorData;
|
|||
|
_sprTexture = new Texture2D(Game1.Graphics.GraphicsDevice, newSizeX, newSizeY);
|
|||
|
_sprTexture.SetData(_colorData);
|
|||
|
|
|||
|
_currentPosition += offset;
|
|||
|
_camera.Location -= new Point((int)(offset.X * _camera.Scale), (int)(offset.Y * _camera.Scale));
|
|||
|
}
|
|||
|
|
|||
|
// fill the color data array
|
|||
|
for (var y = 0; y < _sprSelectionTexture.Height; y++)
|
|||
|
for (var x = 0; x < _sprSelectionTexture.Width; x++)
|
|||
|
{
|
|||
|
var originX = selectionSource.X + x + offset.X;
|
|||
|
var originY = selectionSource.Y + y + offset.Y;
|
|||
|
|
|||
|
if (0 <= originX && originX < _sprTexture.Width &&
|
|||
|
0 <= originY && originY < _sprTexture.Height &&
|
|||
|
_colorDataSelection[x + y * selectionSource.Width] != Color.Transparent)
|
|||
|
_colorData[originX + originY * _sprTexture.Width] = _colorDataSelection[x + y * selectionSource.Width];
|
|||
|
}
|
|||
|
_sprTexture.SetData(_colorData);
|
|||
|
|
|||
|
_sprSelectionTexture = null;
|
|||
|
|
|||
|
// move the source data if the texture gets expanded on the left or top
|
|||
|
foreach (var source in _sourceData)
|
|||
|
{
|
|||
|
source.SourceRectangle.X += offset.X;
|
|||
|
source.SourceRectangle.Y += offset.Y;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void SelectSprite(Point position)
|
|||
|
{
|
|||
|
for (var i = 0; i < _sourceData.Count; i++)
|
|||
|
if (_sourceData[i].SourceRectangle.Contains(position))
|
|||
|
{
|
|||
|
_spriteIndex = i;
|
|||
|
_selectedEntry = _sourceData[_spriteIndex];
|
|||
|
_spriteAtlasList.SelectedEntry = _spriteIndex;
|
|||
|
UpdateInputUi();
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void UpdateInputUi()
|
|||
|
{
|
|||
|
_inputSourceX.Value = _sourceData[_spriteIndex].SourceRectangle.X;
|
|||
|
_inputSourceY.Value = _sourceData[_spriteIndex].SourceRectangle.Y;
|
|||
|
_inputSourceWidth.Value = _sourceData[_spriteIndex].SourceRectangle.Width;
|
|||
|
_inputSourceHeight.Value = _sourceData[_spriteIndex].SourceRectangle.Height;
|
|||
|
_inputSourceOriginX.Value = _sourceData[_spriteIndex].Origin.X;
|
|||
|
_inputSourceOriginY.Value = _sourceData[_spriteIndex].Origin.Y;
|
|||
|
_inputEntryName.StrValue = _sourceData[_spriteIndex].EntryId;
|
|||
|
}
|
|||
|
|
|||
|
private void AddAtlasEntry()
|
|||
|
{
|
|||
|
_sourceData.Add(new SpriteAtlasSerialization.AtlasEntry { EntryId = "" });
|
|||
|
}
|
|||
|
|
|||
|
private void RemoveAtlasEntry()
|
|||
|
{
|
|||
|
if (_spriteIndex >= 0 && _sourceData.Count > _spriteIndex)
|
|||
|
_sourceData.RemoveAt(_spriteIndex);
|
|||
|
|
|||
|
// move the selection up if we are at the bottom
|
|||
|
if (_spriteIndex >= _sourceData.Count)
|
|||
|
{
|
|||
|
_spriteIndex--;
|
|||
|
_spriteAtlasList.SelectedEntry = _spriteIndex;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void SortAtlasEntry()
|
|||
|
{
|
|||
|
_sourceData.Sort((x, y) => x.EntryId.CompareTo(y.EntryId));
|
|||
|
}
|
|||
|
|
|||
|
private void LoadSprite()
|
|||
|
{
|
|||
|
#if WINDOWS
|
|||
|
var openFileDialog = new OpenFileDialog()
|
|||
|
{
|
|||
|
Filter = "sprite file (*.png)|*.png"
|
|||
|
};
|
|||
|
|
|||
|
if (openFileDialog.ShowDialog() != DialogResult.OK)
|
|||
|
return;
|
|||
|
|
|||
|
LoadSpriteEditor(openFileDialog.FileName);
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
public void LoadSpriteEditor(string filePath)
|
|||
|
{
|
|||
|
_imageWasEdited = false;
|
|||
|
|
|||
|
// load the sprite
|
|||
|
using var stream = File.OpenRead(filePath);
|
|||
|
_sprTexture = Texture2D.FromStream(Game1.Graphics.GraphicsDevice, stream);
|
|||
|
_lastFileName = filePath;
|
|||
|
|
|||
|
_colorData = new Color[_sprTexture.Width * _sprTexture.Height];
|
|||
|
_sprTexture.GetData(_colorData);
|
|||
|
|
|||
|
_spriteAtlas.Scale = 1;
|
|||
|
_sourceData.Clear();
|
|||
|
|
|||
|
// load the sprite atlas if there is one
|
|||
|
var atlasFileName = _lastFileName.Replace(".png", ".atlas");
|
|||
|
if (!SpriteAtlasSerialization.LoadSpriteAtlas(atlasFileName, _spriteAtlas))
|
|||
|
AddAtlasEntry();
|
|||
|
|
|||
|
// need to be scaled to actually have the source rectangle
|
|||
|
foreach (var entry in _spriteAtlas.Data)
|
|||
|
{
|
|||
|
entry.SourceRectangle.X *= _spriteAtlas.Scale;
|
|||
|
entry.SourceRectangle.Y *= _spriteAtlas.Scale;
|
|||
|
entry.SourceRectangle.Width *= _spriteAtlas.Scale;
|
|||
|
entry.SourceRectangle.Height *= _spriteAtlas.Scale;
|
|||
|
entry.Origin.X *= _spriteAtlas.Scale;
|
|||
|
entry.Origin.Y *= _spriteAtlas.Scale;
|
|||
|
}
|
|||
|
|
|||
|
_atlasScale.Value = _spriteAtlas.Scale;
|
|||
|
|
|||
|
_spriteIndex = 0;
|
|||
|
_selectedEntry = _sourceData[_spriteIndex];
|
|||
|
UpdateInputUi();
|
|||
|
}
|
|||
|
|
|||
|
private void SaveSpriteAtlasDialog()
|
|||
|
{
|
|||
|
#if WINDOWS
|
|||
|
var saveFileDialog = new SaveFileDialog()
|
|||
|
{
|
|||
|
RestoreDirectory = true,
|
|||
|
Filter = "sprite file (*.png)|*.png",
|
|||
|
};
|
|||
|
|
|||
|
if (_lastFileName != null)
|
|||
|
{
|
|||
|
saveFileDialog.FileName = Path.GetFileName(_lastFileName);
|
|||
|
saveFileDialog.InitialDirectory = Path.GetFullPath(Path.GetDirectoryName(_lastFileName));
|
|||
|
}
|
|||
|
|
|||
|
if (saveFileDialog.ShowDialog() == DialogResult.OK)
|
|||
|
SaveSpriteAtlas(saveFileDialog.FileName);
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
private void SaveSpriteAtlas(string filePath)
|
|||
|
{
|
|||
|
FixSelectedPart();
|
|||
|
|
|||
|
// save the texture?
|
|||
|
if (_imageWasEdited)
|
|||
|
{
|
|||
|
using Stream stream = File.Create(filePath);
|
|||
|
_sprTexture.SaveAsPng(stream, _sprTexture.Width, _sprTexture.Height);
|
|||
|
}
|
|||
|
|
|||
|
// save the sprite atlas
|
|||
|
var atlasFileName = filePath.Replace(".png", ".atlas");
|
|||
|
SpriteAtlasSerialization.SaveSpriteAtlas(atlasFileName, _spriteAtlas);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|