Editor/Tileset/TilesetEditor/Preview/RenderingWidget.cs
using Editor;
using Sandbox;
using System;
using System.Collections.Generic;
using System.Linq;
namespace SpriteTools.TilesetEditor.Preview;
public class RenderingWidget : SpriteRenderingWidget
{
MainWindow MainWindow;
float planeWidth;
float planeHeight;
float startX;
float startY;
float frameWidth;
float frameHeight;
float xSeparation;
float ySeparation;
Vector3 startMovePosition;
Dictionary<Vector2, TilesetResource.Tile> tileDict;
RealTimeSince timeSinceLastCornerHover = 0;
public RenderingWidget ( MainWindow window, Widget parent ) : base( parent )
{
MainWindow = window;
AcceptDrops = false;
IsDraggable = false;
}
[EditorEvent.Frame]
public void Frame ()
{
UpdateInputs();
if ( timeSinceLastCornerHover > 0.025f )
{
Cursor = CursorShape.Arrow;
}
tileDict = new();
foreach ( var tile in MainWindow.Tileset.Tiles )
{
if ( tile is null ) continue;
for ( int i = 0; i < tile.Size.x; i++ )
{
for ( int j = 0; j < tile.Size.y; j++ )
{
var realTile = ( i == 0 && j == 0 ) ? tile : null;
tileDict[tile.Position + new Vector2( i, j )] = realTile;
}
}
}
using ( GizmoInstance.Push() )
{
var hasTiles = ( MainWindow?.Tileset?.Tiles?.Count ?? 0 ) > 0;
planeWidth = 100f * TextureRect.Transform.Scale.y;
planeHeight = 100f * TextureRect.Transform.Scale.x;
startX = -( planeWidth / 2f );
startY = -( planeHeight / 2f );
frameWidth = MainWindow.Tileset.TileSize.x / TextureSize.x * planeWidth;
frameHeight = MainWindow.Tileset.TileSize.y / TextureSize.y * planeHeight;
xSeparation = MainWindow.Tileset.TileSeparation.x / TextureSize.x * planeWidth;
ySeparation = MainWindow.Tileset.TileSeparation.y / TextureSize.y * planeHeight;
if ( Gizmo.WasLeftMouseReleased )
{
AutotileTileReferenceControl.DragId = Guid.Empty;
}
{
int framesPerRow = MainWindow.Tileset.CurrentTextureSize.x / MainWindow.Tileset.CurrentTileSize.x;
int framesPerHeight = MainWindow.Tileset.CurrentTextureSize.y / MainWindow.Tileset.CurrentTileSize.y;
if ( !hasTiles )
{
framesPerRow = (int)TextureSize.x / MainWindow.Tileset.TileSize.x;
framesPerHeight = (int)TextureSize.y / MainWindow.Tileset.TileSize.y;
}
using ( Gizmo.Scope( "tiles" ) )
{
Gizmo.Draw.Color = new Color( 0.1f, 0.4f, 1f );
Gizmo.Draw.LineThickness = 3f;
int xi = 0;
int yi = 0;
if ( framesPerRow * framesPerHeight < 2048 )
{
while ( yi < framesPerHeight )
{
while ( xi < framesPerRow )
{
if ( tileDict.TryGetValue( new Vector2( xi, yi ), out var tile ) )
{
if ( tile is not null )
{
TileControl( xi, yi, tileDict[new Vector2( xi, yi )] );
}
}
else
{
EmptyTileControl( xi, yi );
}
xi++;
}
xi = 0;
yi++;
}
}
}
}
if ( !hasTiles || MainWindow.inspector.btnRegenerate.IsUnderMouse )
{
int framesPerRow = (int)TextureSize.x / MainWindow.Tileset.TileSize.x;
int framesPerHeight = (int)TextureSize.y / MainWindow.Tileset.TileSize.y;
using ( Gizmo.Scope( "setup" ) )
{
Gizmo.Draw.Color = Color.White.WithAlpha( 0.4f );
Gizmo.Draw.LineThickness = 1f;
int xi = 0;
int yi = 0;
if ( framesPerRow * framesPerHeight < 2048 )
{
while ( yi < framesPerHeight )
{
while ( xi < framesPerRow )
{
var x = startX + ( xi * frameWidth ) + ( xi * xSeparation );
var y = startY + ( yi * frameHeight ) + ( yi * ySeparation );
DrawBox( x, y, frameWidth, frameHeight );
xi++;
}
xi = 0;
yi++;
}
}
}
}
}
}
void TileControl ( int xi, int yi, TilesetResource.Tile tile )
{
if ( tile is null ) return;
bool isSelected = MainWindow?.inspector?.tileList?.Selected?.Any( x => x.Tile == tile ) ?? false;
using ( Gizmo.Scope( $"tile_{tile.Id}", Transform.Zero.WithPosition( isSelected ? ( Vector3.Up * 5f ) : Vector3.Zero ) ) )
{
float sizeX = tile.Size.x;
float sizeY = tile.Size.y;
var x = startX + ( xi * frameWidth + xi * xSeparation );
var y = startY + ( yi * frameHeight + yi * ySeparation );
var width = frameWidth * sizeX;
var height = frameHeight * sizeY;
var bbox = BBox.FromPositionAndSize( new Vector3( y + height / 2f, x + width / 2f, 1f ), new Vector3( height, width, 1f ) );
Gizmo.Hitbox.BBox( bbox );
if ( MainWindow.inspector.SelectedTab == 1 )
{
if ( isSelected || Gizmo.Pressed.This )
{
Gizmo.Draw.LineThickness = 4;
Gizmo.Draw.Color = Color.Yellow;
}
if ( Gizmo.WasLeftMousePressed ) startMovePosition = Gizmo.CurrentRay.Position;
if ( Gizmo.Pressed.This )
{
Cursor = CursorShape.SizeAll;
timeSinceLastCornerHover = 0f;
var preDelta = startMovePosition - Gizmo.CurrentRay.Position;
var deltaf = new Vector2( -preDelta.y, -preDelta.x );
if ( Math.Abs( deltaf.x ) >= frameWidth / 2f )
{
int xx = Math.Sign( deltaf.x );
if ( xx != 0 && CanExpand( tile, xx, 0 ) )
{
startMovePosition += new Vector3( 0, xx * frameWidth );
tile.Position += new Vector2Int( xx, 0 );
}
}
if ( Math.Abs( deltaf.y ) >= frameHeight / 2f )
{
int yy = Math.Sign( deltaf.y );
if ( yy != 0 && CanExpand( tile, 0, yy ) )
{
startMovePosition += new Vector3( yy * frameHeight, 0 );
tile.Position += new Vector2Int( 0, yy );
}
}
}
if ( Gizmo.IsHovered )
{
Cursor = CursorShape.Finger;
timeSinceLastCornerHover = 0f;
using ( Gizmo.Scope( "hover" ) )
{
Gizmo.Draw.Color = Gizmo.Draw.Color.WithAlpha( 0.5f );
Gizmo.Draw.SolidBox( bbox );
}
if ( Gizmo.WasLeftMousePressed )
{
MainWindow.SelectTile( tile, Gizmo.IsCtrlPressed || Gizmo.IsShiftPressed );
}
else if ( Gizmo.WasRightMousePressed )
{
MainWindow.DeleteTile( tile );
}
}
if ( isSelected )
{
using ( Gizmo.Scope( "selected" ) )
{
Gizmo.Draw.Color = Color.Orange;
Gizmo.Draw.LineThickness = 3;
// Draggable Corners
for ( int i = -1; i <= 1; i++ )
{
for ( int j = -1; j <= 1; j++ )
{
if ( i == 0 && j == 0 ) continue;
DraggableCorner( tile, i, j, x + width * ( i + 1 ) / 2f, y + height * ( j + 1 ) / 2f );
}
}
}
}
}
else if ( MainWindow.inspector.SelectedTab == 2 )
{
if ( Gizmo.IsHovered )
{
Cursor = CursorShape.Finger;
timeSinceLastCornerHover = 0f;
using ( Gizmo.Scope( "hover" ) )
{
Gizmo.Draw.Color = Gizmo.Draw.Color.WithAlpha( 0.5f );
Gizmo.Draw.SolidBox( bbox );
}
if ( Gizmo.WasLeftMouseReleased )
{
if ( MainWindow?.inspector?.autotileBrushList?.SelectedTile is not null )
{
MainWindow.PushUndo( "Set Autotile Tile" );
var currentTileCount = MainWindow?.inspector?.autotileBrushList?.SelectedTile?.Tiles?.Count ?? 0;
if ( currentTileCount > 0 && ( MainWindow?.inspector?.autotileBrushList?.SelectedTile?.Tiles?.LastOrDefault()?.Id ?? new Guid() ) == Guid.Empty )
{
MainWindow.inspector.autotileBrushList.SelectedTile.Tiles.RemoveAt( currentTileCount - 1 );
}
var hadNone = currentTileCount == 0;
var reference = new AutotileBrush.TileReference( tile.Id );
reference.Tileset = MainWindow.Tileset;
if ( currentTileCount < 2 )
{
MainWindow.inspector.autotileBrushList.SelectedTile.Tiles ??= new();
MainWindow.inspector.autotileBrushList.SelectedTile.Tiles.Clear();
}
if ( !MainWindow.inspector.autotileBrushList.SelectedTile.Tiles.Contains( reference ) )
MainWindow.inspector.autotileBrushList.SelectedTile.Tiles.Add( reference );
MainWindow.inspector?.UpdateSelectedAutotileSheet();
MainWindow.SetDirty();
if ( hadNone )
{
var selectedIndex = Array.IndexOf( MainWindow.inspector.autotileBrushList.SelectedBrush.Brush.Tiles, MainWindow.inspector.autotileBrushList.SelectedTile );
selectedIndex = ( selectedIndex + 1 ) % MainWindow.inspector.autotileBrushList.SelectedBrush.Brush.Tiles.Count();
MainWindow.inspector.autotileBrushList.SelectedTile = MainWindow.inspector.autotileBrushList.SelectedBrush.Brush.Tiles[selectedIndex];
}
MainWindow.PushRedo();
}
}
else if ( Gizmo.WasLeftMousePressed )
{
AutotileTileReferenceControl.DragId = tile.Id;
}
}
}
DrawBox( x, y, width, height );
}
}
void DraggableCorner ( TilesetResource.Tile tile, int x, int y, float xx, float yy )
{
int currentX = (int)tile.Position.x;
int currentY = (int)tile.Position.y;
float xi = currentX + x / 2f;
float yi = currentY + y / 2f;
float width = frameWidth * (int)tile.Size.x;
float height = frameHeight * (int)tile.Size.y;
// Can Expand Logic
bool canExpandX = CanExpand( tile, x, 0 );
bool canExpandY = CanExpand( tile, 0, y );
// Can Shrink Logic
bool canShrinkX = !( x != 0 && tile.Size.x == 1 );
bool canShrinkY = !( y != 0 && tile.Size.y == 1 );
bool canDrag = ( canExpandX && x != 0 ) || ( canExpandY && y != 0 ) || ( canShrinkX && x != 0 ) || ( canShrinkY && y != 0 );
using ( Gizmo.Scope( $"corner_{x}_{y}" ) )
{
if ( !canDrag )
{
Gizmo.Draw.LineThickness = 1;
Gizmo.Draw.Color = Gizmo.Draw.Color.WithAlpha( 0.2f );
}
if ( canDrag )
{
var bbox = BBox.FromPositionAndSize( new Vector3( yy, xx, 1f ), new Vector3( 2, 2, 1f ) );
Gizmo.Hitbox.BBox( bbox );
if ( Gizmo.Pressed.This )
{
Gizmo.Draw.Color = Color.Lerp( Gizmo.Draw.Color, Color.Red, 0.3f );
var preDelta = bbox.Center - Gizmo.CurrentRay.Position;
var delta = new Vector2( -preDelta.y, -preDelta.x );//Gizmo.Pressed.CursorDelta;
var position = tile.Position;
var size = tile.Size;
// Horizontal check
if ( x != 0 )
{
if ( Math.Abs( delta.x ) > frameWidth / 2f )
{
// Expanding
if ( Math.Sign( delta.x ) == Math.Sign( x ) )
{
if ( canExpandX )
{
// Expanding Backwards
if ( delta.x < 0 )
{
position -= new Vector2Int( 1, 0 );
size += new Vector2Int( 1, 0 );
}
else
{
size += new Vector2Int( 1, 0 );
}
}
}
// Shinking
else if ( canShrinkX )
{
// Shrinking Backwards
if ( delta.x > 0 )
{
size -= new Vector2Int( 1, 0 );
position += new Vector2Int( 1, 0 );
}
else
{
size -= new Vector2Int( 1, 0 );
}
}
}
}
// Vertical check
if ( y != 0 )
{
if ( Math.Abs( delta.y ) > frameHeight / 2f )
{
if ( Math.Sign( delta.y ) == Math.Sign( y ) )
{
if ( canExpandY )
{
// Expanding
if ( delta.y < 0 )
{
position -= new Vector2Int( 0, 1 );
size += new Vector2Int( 0, 1 );
}
else
{
size += new Vector2Int( 0, 1 );
}
}
}
else if ( canShrinkY )
{
// Shrink
if ( delta.y > 0 )
{
size -= new Vector2Int( 0, 1 );
position += new Vector2Int( 0, 1 );
}
else
{
size -= new Vector2Int( 0, 1 );
}
}
}
}
if ( tile.Position != position || tile.Size != size )
{
tile.Position = position;
tile.Size = size;
}
}
}
if ( canDrag && Gizmo.IsHovered )
{
Gizmo.Draw.SolidSphere( new Vector3( yy, xx, 10f ), 0.5f, 2, 4 );
Cursor = (x, y) switch
{
(-1, -1 ) => CursorShape.SizeFDiag,
(-1, 0 ) => CursorShape.SizeH,
(-1, 1 ) => CursorShape.SizeBDiag,
(0, -1 ) => CursorShape.SizeV,
(0, 1 ) => CursorShape.SizeV,
(1, -1 ) => CursorShape.SizeBDiag,
(1, 0 ) => CursorShape.SizeH,
(1, 1 ) => CursorShape.SizeFDiag,
_ => CursorShape.Arrow
};
timeSinceLastCornerHover = 0f;
}
else
{
Gizmo.Draw.LineCircle( new Vector3( yy, xx, 10f ), Vector3.Up, 0.5f, 0, 360, 8 );
}
}
}
void EmptyTileControl ( int xi, int yi )
{
using ( Gizmo.Scope( $"tile_{xi}_{yi}", Transform.Zero ) )
{
Gizmo.Draw.Color = Gizmo.Draw.Color.WithAlpha( 0.04f );
var x = startX + ( xi * frameWidth + xi * xSeparation );
var y = startY + ( yi * frameHeight + yi * ySeparation );
var width = frameWidth;
var height = frameHeight;
if ( MainWindow.inspector.SelectedTab == 1 )
{
var bbox = BBox.FromPositionAndSize( new Vector3( y + height / 2f, x + width / 2f, 1f ), new Vector3( height, width, 1f ) );
Gizmo.Hitbox.BBox( bbox );
if ( Gizmo.IsHovered )
{
using ( Gizmo.Scope( "hover" ) )
{
Gizmo.Draw.Color = Gizmo.Draw.Color.WithAlpha( 0.2f );
Gizmo.Draw.SolidBox( bbox );
}
if ( Gizmo.WasLeftMousePressed )
{
MainWindow.CreateTile( xi, yi, Gizmo.IsCtrlPressed || Gizmo.IsShiftPressed );
}
}
}
}
}
void DrawBox ( float x, float y, float width, float height )
{
Gizmo.Draw.Line( new Vector3( y, x, 0 ), new Vector3( y, x + width, 0 ) );
Gizmo.Draw.Line( new Vector3( y, x, 0 ), new Vector3( y + height, x, 0 ) );
Gizmo.Draw.Line( new Vector3( y + height, x, 0 ), new Vector3( y + height, x + width, 0 ) );
Gizmo.Draw.Line( new Vector3( y + height, x + width, 0 ), new Vector3( y, x + width, 0 ) );
}
bool CanExpand ( TilesetResource.Tile tile, int x, int y )
{
int currentX = tile.Position.x;
int currentY = tile.Position.y;
if ( x != 0 )
{
int nextX = currentX + ( x > 0 ? ( x * (int)tile.Size.x ) : x );
if ( nextX < 0 || nextX >= ( TextureSize.x / MainWindow.Tileset.CurrentTileSize.x ) ) return false;
else
{
for ( int i = 0; i < tile.Size.y; i++ )
{
int nextY = currentY + i;
if ( tileDict.ContainsKey( new Vector2( nextX, nextY ) ) )
{
return false;
}
}
}
}
if ( y != 0 )
{
int nextY = currentY + ( y > 0 ? ( y * (int)tile.Size.y ) : y );
if ( nextY < 0 || nextY >= ( TextureSize.y / MainWindow.Tileset.CurrentTileSize.y ) ) return false;
else
{
for ( int i = 0; i < tile.Size.x; i++ )
{
int nextX = currentX + i;
if ( tileDict.ContainsKey( new Vector2( nextX, nextY ) ) )
{
return false;
}
}
}
}
return true;
}
}