Switch-Toolbox/File_Format_Library/GUI/BFLYT/LayoutViewer.cs
KillzXGaming 89d5b621b2 Giant layout update. Support BRLYT and BCLYT files!
BRLYT can now be edited and saved.
BCLYT can now be edited and saved.
BRLYT shaders greatly improved using ported shaders from WiiLayoutEditor. Plan to expand upon it for more accuacte shader rendering.
Add support for saving per character transforms.
Add support for BNR files.
Fixed flags so orientation can be edited properly.
Fix issues decoding some gamecube textures.
Fix animation timeline breaking at times for multi selecting animations.
2020-02-11 19:19:23 -05:00

1511 lines
56 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using OpenTK.Graphics.OpenGL;
using OpenTK;
using Toolbox.Library.Forms;
using Toolbox.Library;
using Toolbox.Library.Rendering;
using Toolbox.Library.IO;
namespace LayoutBXLYT
{
public partial class LayoutViewer : LayoutControlDocked
{
public LayoutUndoManager UndoManger = new LayoutUndoManager();
public List<BasePane> SelectedPanes = new List<BasePane>();
public Camera2D Camera = new Camera2D();
public class Camera2D
{
public Matrix4 ModelViewMatrix => ModelMatrix * ViewMatrix * ProjectionMatrix;
public Matrix4 ProjectionMatrix = Matrix4.Identity;
public Matrix4 ViewMatrix = Matrix4.Identity;
public Matrix4 ModelMatrix = Matrix4.Identity;
public float Zoom = 1;
public Vector2 Position;
}
private LayoutEditor ParentEditor;
private List<BasePane> CopiedPanes = new List<BasePane>();
private RenderableTex backgroundTex;
public BxlytHeader LayoutFile;
public List<BxlytHeader> LayoutFiles = new List<BxlytHeader>();
private Dictionary<string, STGenericTexture> Textures;
private void glControl1_Load(object sender, EventArgs e)
{
}
public void ResetCamera()
{
Camera = new Camera2D();
}
public void ResetLayout(BxlytHeader bxlyt)
{
LayoutFile = bxlyt;
UpdateViewport();
}
public Dictionary<string, STGenericTexture> GetTextures()
{
return Textures;
}
public GLControl GetGLControl() => glControl1;
public LayoutViewer(LayoutEditor editor, BxlytHeader bxlyt, Dictionary<string, STGenericTexture> textures)
{
InitializeComponent();
ParentEditor = editor;
Text = bxlyt.FileName;
Textures = textures;
LoadLayout(bxlyt);
}
public void LoadLayout(BxlytHeader bxlyt)
{
LayoutFile = bxlyt;
LayoutFiles.Add(bxlyt);
if (bxlyt.Textures.Count > 0)
{
var textures = bxlyt.GetTextures;
foreach (var tex in textures)
if (!Textures.ContainsKey(tex.Key))
Textures.Add(tex.Key, tex.Value);
}
}
public override void OnControlClosing()
{
foreach (var tex in LayoutFile.Textures)
{
if (Textures.ContainsKey(tex))
{
Textures[tex].DisposeRenderable();
Textures.Remove(tex);
}
}
}
public void UpdateViewport()
{
glControl1.Invalidate();
}
private void glControl1_Paint(object sender, PaintEventArgs e)
{
if (!Runtime.OpenTKInitialized)
return;
glControl1.Context.MakeCurrent(glControl1.WindowInfo);
OnRender();
}
private BxlytShader GlobalShader;
public bool GameWindow = false;
public bool UseOrtho => Runtime.LayoutEditor.UseOrthographicView;
private Color BackgroundColor => Runtime.LayoutEditor.BackgroundColor;
private void OnRender()
{
if (LayoutFile == null) return;
if (!GameWindow)
{
if (ParentEditor != null)
ParentEditor.GamePreviewWindow?.UpdateViewport();
}
if (GameWindow)
{
RenderGameWindow();
RenderScene();
}
else
{
RenderEditor();
RenderScene();
}
}
private void RenderGameWindow()
{
glControl1.MakeCurrent();
int WindowWidth = (int)LayoutFile.RootPane.Width;
int WindowHeight = (int)LayoutFile.RootPane.Height;
GL.Viewport(0, 0, glControl1.Width, glControl1.Height);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
if (UseOrtho)
{
float halfW = WindowWidth, halfH = WindowHeight;
var orthoMatrix = Matrix4.CreateOrthographic(halfW, halfH, -10000, 10000);
GL.LoadMatrix(ref orthoMatrix);
GL.MatrixMode(MatrixMode.Modelview);
Camera.ProjectionMatrix = orthoMatrix;
}
else
{
var cameraPosition = new Vector3(0, 0, -600);
var perspectiveMatrix = Matrix4.CreateTranslation(cameraPosition) * Matrix4.CreatePerspectiveFieldOfView(0.785398f, WindowWidth / WindowHeight, 0.01f, 100000);
GL.LoadMatrix(ref perspectiveMatrix);
GL.MatrixMode(MatrixMode.Modelview);
Camera.ProjectionMatrix = perspectiveMatrix;
}
GL.ClearColor(BackgroundColor);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
}
private void RenderEditor()
{
glControl1.MakeCurrent();
GL.Viewport(0, 0, glControl1.Width, glControl1.Height);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
if (UseOrtho)
{
float halfW = glControl1.Width / 2.0f, halfH = glControl1.Height / 2.0f;
var orthoMatrix = Matrix4.CreateOrthographic(halfW, halfH, -10000, 10000);
GL.LoadMatrix(ref orthoMatrix);
GL.MatrixMode(MatrixMode.Modelview);
Camera.ProjectionMatrix = orthoMatrix;
}
else
{
var cameraPosition = new Vector3(Camera.Position.X, Camera.Position.Y, -(Camera.Zoom * 500));
var perspectiveMatrix = Matrix4.CreateTranslation(cameraPosition) * Matrix4.CreatePerspectiveFieldOfView(1.3f, glControl1.Width / glControl1.Height, 0.01f, 100000);
GL.LoadMatrix(ref perspectiveMatrix);
GL.MatrixMode(MatrixMode.Modelview);
Camera.ProjectionMatrix = perspectiveMatrix;
}
GL.ClearColor(BackgroundColor);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
if (UseOrtho && !GameWindow)
{
GL.PushMatrix();
GL.Scale(Camera.Zoom, Camera.Zoom, 1);
GL.Translate(Camera.Position.X, Camera.Position.Y, 0);
Camera.ViewMatrix *= Matrix4.CreateScale(Camera.Zoom, Camera.Zoom, 1) *
Matrix4.CreateTranslation(Camera.Position.X, Camera.Position.Y, 0);
}
}
private void RenderScene(bool showSelectionBox = false)
{
// GL.Disable(EnableCap.CullFace);
GL.Enable(EnableCap.Blend);
GL.Enable(EnableCap.AlphaTest);
GL.AlphaFunc(AlphaFunction.Always, 0f);
GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
GL.Enable(EnableCap.ColorMaterial);
GL.Disable(EnableCap.Texture2D);
GL.BindTexture(TextureTarget.Texture2D, 0);
GL.BlendEquation(BlendEquationMode.FuncAdd);
if (!GameWindow)
{
DrawRootPane(LayoutFile.RootPane);
DrawGrid();
DrawXyLines();
}
GL.BindTexture(TextureTarget.Texture2D, 0);
GL.Disable(EnableCap.Texture2D);
if (GlobalShader == null)
{
GlobalShader = new BxlytShader();
GlobalShader.Compile();
}
bool PreviewHitbox = false;
if (PreviewHitbox)
{
foreach (var file in LayoutFiles)
{
foreach (var pane in file.PaneLookup.Values)
{
if (!pane.Visible || !pane.DisplayInEditor && !pane.IsRoot)
continue;
//Hitbox debug
var hitbox = pane.CreateRectangle();
hitbox = hitbox.GetTransformedRectangle(pane.Parent, pane.Translate, pane.Rotate, pane.Scale);
GL.Begin(PrimitiveType.Quads);
GL.Color4(Color.FromArgb(128, 255, 0, 0));
GL.Vertex2(hitbox.BottomLeftPoint);
GL.Vertex2(hitbox.BottomRightPoint);
GL.Vertex2(hitbox.TopRightPoint);
GL.Vertex2(hitbox.TopLeftPoint);
GL.End();
}
}
}
foreach (var layout in LayoutFiles)
RenderPanes(GlobalShader, layout.RootPane, true, 255, false, null, 0);
Vector2 TopLeft = new Vector2();
Vector2 BottomRight = new Vector2();
GL.Disable(EnableCap.Texture2D);
GL.Disable(EnableCap.AlphaTest);
GL.Disable(EnableCap.Blend);
GL.UseProgram(0);
foreach (var pane in SelectedPanes)
{
var rect = pane.CreateRectangle();
TopLeft.X = Math.Min(TopLeft.X, rect.LeftPoint);
TopLeft.Y = Math.Max(TopLeft.Y, rect.TopPoint);
BottomRight.X = Math.Max(BottomRight.X, rect.RightPoint);
BottomRight.Y = Math.Min(BottomRight.Y, rect.BottomPoint);
if (pickAxis == PickAxis.Y)
{
GL.Begin(PrimitiveType.Lines);
GL.Color4(Color.Green);
GL.Vertex2(pane.Translate.X, -999999);
GL.Vertex2(pane.Translate.X, 99999);
GL.End();
}
if (pickAxis == PickAxis.X)
{
GL.Begin(PrimitiveType.Lines);
GL.Color4(Color.Red);
GL.Vertex2(-999999, pane.Translate.Y);
GL.Vertex2(99999, pane.Translate.Y);
GL.End();
}
}
if (showSelectionBox)
{
GL.Begin(PrimitiveType.LineLoop);
GL.Color4(Color.Red);
GL.Vertex2(SelectionBox.BottomLeftPoint);
GL.Vertex2(SelectionBox.BottomRightPoint);
GL.Vertex2(SelectionBox.TopRightPoint);
GL.Vertex2(SelectionBox.TopLeftPoint);
GL.End();
}
//Create a bounding box for all selected panes
//This box will allow resizing of all selected panes
if (SelectedPanes.Count > 0)
DrawSelectionBox(SelectedPanes, true);
if (UseOrtho)
GL.PopMatrix();
GL.UseProgram(0);
glControl1.SwapBuffers();
}
private static CustomRectangle GetSelectedPanesBounding(List<BasePane> panes)
{
CustomRectangle rect = new CustomRectangle(0, 0, 0, 0);
List<Vector2> points = new List<Vector2>();
foreach (var pane in panes)
{
var paneRect = pane.CreateRectangle();
rect = paneRect.GetTransformedRectangle(pane.Parent,
pane.GetTranslation(), pane.GetRotation(), pane.GetScale());
points.AddRange(new Vector2[4]
{
rect.TopLeftPoint,
rect.TopRightPoint,
rect.BottomRightPoint,
rect.BottomLeftPoint,
});
var minX = (int)points.Min(p => p.X);
var maxX = (int)points.Max(p => p.X);
var minY = (int)points.Min(p => p.Y);
var maxY = (int)points.Max(p => p.Y);
rect = new CustomRectangle(minX, maxX, maxY, minY);
}
points.Clear();
return rect;
}
public static void DrawSelectionBox(List<BasePane> panes, bool isSelected)
{
//Create a rectangle with the largest points selected
CustomRectangle rect = GetSelectedPanesBounding(panes);
GL.Disable(EnableCap.Blend);
GL.Disable(EnableCap.AlphaTest);
GL.Disable(EnableCap.Texture2D);
GL.UseProgram(0);
GL.Begin(PrimitiveType.LineLoop);
GL.Color4(isSelected ? Color.Red : Color.Green);
GL.Vertex2(rect.BottomLeftPoint);
GL.Vertex2(rect.BottomRightPoint);
GL.Vertex2(rect.TopRightPoint);
GL.Vertex2(rect.TopLeftPoint);
GL.End();
if (isSelected)
{
var transformed = rect;
var leftTop = new Vector2(transformed.LeftPoint, transformed.TopPoint);
var left = new Vector2(transformed.LeftPoint, (transformed.BottomPoint + transformed.TopPoint) / 2);
var leftBottom = new Vector2(transformed.LeftPoint, transformed.BottomPoint);
var rightTop = new Vector2(transformed.RightPoint, transformed.TopPoint);
var right = new Vector2(transformed.RightPoint, (transformed.BottomPoint + transformed.TopPoint) / 2);
var rightBottom = new Vector2(transformed.RightPoint, transformed.BottomPoint);
var top = new Vector2((transformed.RightPoint + transformed.LeftPoint) / 2, transformed.TopPoint);
var bottom = new Vector2((transformed.RightPoint + transformed.LeftPoint) / 2, transformed.BottomPoint);
DrawEdgeSquare(leftTop);
DrawEdgeSquare(left);
DrawEdgeSquare(leftBottom);
DrawEdgeSquare(rightTop);
DrawEdgeSquare(right);
DrawEdgeSquare(rightBottom);
DrawEdgeSquare(top);
DrawEdgeSquare(bottom);
}
GL.Enable(EnableCap.Blend);
GL.Enable(EnableCap.AlphaTest);
GL.Enable(EnableCap.Texture2D);
}
private static void DrawEdgeSquare(Vector2 position)
{
float scale = 5;
GL.Begin(PrimitiveType.LineLoop);
GL.Color4(Color.Red);
GL.Vertex2(position.X + -1 * scale, position.Y + -1 * scale);
GL.Vertex2(position.X + 1 * scale, position.Y + -1 * scale);
GL.Vertex2(position.X + 1 * scale, position.Y + 1 * scale);
GL.Vertex2(position.X + -1 * scale, position.Y + 1 * scale);
GL.End();
}
private void RenderPanes(BxlytShader shader, BasePane pane, bool isRoot, byte parentAlpha, bool parentAlphaInfluence, BasePane partPane = null, int stage = 0)
{
if (!pane.DisplayInEditor)
return;
GL.PushMatrix();
//Check XY rotation and draw the pane before it was rotated
bool isRotatedXY = pane.Rotate.X != 0 || pane.Rotate.Y != 0;
if (isRotatedXY && SelectedPanes.Contains(pane))
{
GL.PushMatrix();
GL.Translate(pane.Translate.X, pane.Translate.Y, 0);
GL.Rotate(pane.Rotate.Z, 0, 0, 1);
GL.Scale(pane.Scale.X, pane.Scale.Y, 1);
DrawDefaultPane(shader, pane, false);
GL.PopMatrix();
}
var translate = pane.GetTranslation();
var rotate = pane.GetRotation();
var scale = pane.GetScale();
if (partPane != null)
{
translate = translate + pane.Translate;
scale = scale * pane.Scale;
rotate = rotate + pane.Rotate;
var prtPane = (Cafe.PRT1)partPane;
scale = new Syroot.Maths.Vector2F(
scale.X * prtPane.MagnifyX,
scale.Y * prtPane.MagnifyY);
}
//Note rotation matrix done by shaders
GL.Translate(translate.X, translate.Y, 0);
GL.Scale(scale.X, scale.Y, 1);
byte alpha = pane.Alpha;
if (pane.animController.PaneVertexColors.ContainsKey(LVCTarget.PaneAlpha))
alpha = (byte)pane.animController.PaneVertexColors[LVCTarget.PaneAlpha];
byte effectiveAlpha = (byte)(parentAlpha == 255 ? alpha : (alpha * parentAlpha) / 255);
if (!parentAlphaInfluence)
effectiveAlpha = alpha;
parentAlphaInfluence = parentAlphaInfluence || pane.InfluenceAlpha;
if (!isRoot)
{
bool isSelected = SelectedPanes.Contains(pane);
if (!pane.Visible && !pane.animController.Visibile)
DrawDefaultPane(shader, pane, isSelected);
else if (pane is IPicturePane)
BxlytToGL.DrawPictureBox(pane, Camera, GameWindow, effectiveAlpha, Textures, isSelected);
else if (pane is IWindowPane)
BxlytToGL.DrawWindowPane(pane, Camera, GameWindow, effectiveAlpha, Textures, isSelected);
else if (pane is IBoundryPane)
{
shader.Enable();
shader.SetBasic(pane, Color.White);
BxlytToGL.DrawBoundryPane(pane, GameWindow, effectiveAlpha, isSelected);
shader.Disable();
}
else if (pane is ITextPane && Runtime.LayoutEditor.DisplayTextPane)
{
var textPane = (ITextPane)pane;
Bitmap bitmap = null;
if (pane is Cafe.TXT1)
{
foreach (var fontFile in FirstPlugin.PluginRuntime.BxfntFiles)
{
if (Utils.CompareNoExtension(fontFile.Name, textPane.FontName))
bitmap = fontFile.GetBitmap(textPane.Text, false, pane);
}
}
if (bitmap != null)
BxlytToGL.DrawTextbox(pane, Camera, GameWindow, bitmap, effectiveAlpha,
Textures, SelectedPanes, textPane.RenderableFont == null, isSelected);
else
DrawDefaultPane(shader, pane, isSelected);
}
else if (pane is Cafe.SCR1)
BxlytToGL.DrawScissorPane(pane, GameWindow, effectiveAlpha, isSelected);
else if (pane is Cafe.ALI1)
BxlytToGL.DrawAlignmentPane(pane, GameWindow, effectiveAlpha, isSelected);
else if (pane is Cafe.PRT1)
DrawPartsPane(shader, (Cafe.PRT1)pane, effectiveAlpha, isSelected, parentAlphaInfluence);
else
DrawDefaultPane(shader, pane, isSelected);
}
else
isRoot = false;
byte childAlpha = pane.InfluenceAlpha || parentAlphaInfluence ? effectiveAlpha : byte.MaxValue;
foreach (var childPane in pane.Childern)
RenderPanes(shader, childPane, isRoot, childAlpha, parentAlphaInfluence, partPane);
GL.PopMatrix();
}
private void DrawRootPane(BasePane pane)
{
Color color = Color.Black;
if (SelectedPanes.Contains(pane))
color = Color.Red;
CustomRectangle rect = pane.CreateRectangle();
//Draw a quad which is the backcolor but lighter
GL.Begin(PrimitiveType.Quads);
GL.Color3(BackgroundColor.Lighten(10));
GL.Vertex2(rect.TopLeftPoint);
GL.Vertex2(rect.TopRightPoint);
GL.Vertex2(rect.BottomRightPoint);
GL.Vertex2(rect.BottomLeftPoint);
GL.End();
//Draw outline of root pane
GL.Begin(PrimitiveType.LineLoop);
GL.PolygonOffset(0.5f, 2);
GL.LineWidth(33);
GL.Color3(color);
GL.Vertex2(rect.TopLeftPoint);
GL.Vertex2(rect.TopRightPoint);
GL.Vertex2(rect.BottomRightPoint);
GL.Vertex2(rect.BottomLeftPoint);
GL.End();
}
private void DrawDefaultPane(BxlytShader shader, BasePane pane, bool isSelectionBox = false)
{
if (!Runtime.LayoutEditor.DisplayNullPane || GameWindow || Runtime.LayoutEditor.IsGamePreview)
return;
shader.Enable();
shader.SetBasic(pane, Color.Black);
Vector2[] TexCoords = new Vector2[] {
new Vector2(1,1),
new Vector2(0,1),
new Vector2(0,0),
new Vector2(1,0)
};
Color color = Color.Black;
if (SelectedPanes.Contains(pane))
color = Color.Red;
Color[] Colors = new Color[] {
color,
color,
color,
color,
};
BxlytToGL.DrawRectangle(pane, GameWindow, pane.Rectangle, TexCoords, Colors, true, 255, isSelectionBox);
shader.Disable();
}
private void DrawPartsPane(BxlytShader shader, Cafe.PRT1 pane, byte effectiveAlpha, bool isSelected, bool parentInfluenceAlpha)
{
if (Runtime.LayoutEditor.PartsAsNullPanes)
{
DrawDefaultPane(shader, pane, isSelected);
return;
}
pane.UpdateTextureData(this.Textures);
var partPane = pane.GetExternalPane();
if (partPane != null)
RenderPanes(shader, partPane, true, effectiveAlpha, parentInfluenceAlpha);
else
DrawDefaultPane(shader, pane, isSelected);
if (pane.Properties != null)
{
foreach (var prop in pane.Properties)
{
if (prop.Property != null)
{
RenderPanes(shader, prop.Property, false, effectiveAlpha, parentInfluenceAlpha || pane.InfluenceAlpha);
}
}
}
}
private void DrawBackground()
{
if (backgroundTex == null)
{
/* backgroundTex = RenderableTex.FromBitmap(Properties.Resources.GridBackground);
backgroundTex.TextureWrapR = TextureWrapMode.Repeat;
backgroundTex.TextureWrapT = TextureWrapMode.Repeat;
GL.Enable(EnableCap.Texture2D);
GL.BindTexture(TextureTarget.Texture2D, backgroundTex.TexID);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (float)backgroundTex.TextureWrapR);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (float)backgroundTex.TextureWrapT);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (float)backgroundTex.TextureMagFilter);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (float)backgroundTex.TextureMinFilter);
float UVscale = 15;
int PanelWidth = 9000;
int PanelWHeight = 9000;
Vector2 scaleCenter = new Vector2(0.5f, 0.5f);
Vector2[] TexCoords = new Vector2[] {
new Vector2(1,1),
new Vector2(0,1),
new Vector2(0,0),
new Vector2(1,0),
};
for (int i = 0; i < TexCoords.Length; i++)
TexCoords[i] = (TexCoords[i] - scaleCenter) * 20 + scaleCenter;
GL.MatrixMode(MatrixMode.Modelview);
GL.LoadIdentity();
GL.PushMatrix();
GL.Scale(1, 1, 1);
GL.Translate(0, 0, 0);
GL.Color4(Color.White);
GL.Begin(PrimitiveType.Quads);
GL.TexCoord2(TexCoords[0]);
GL.Vertex3(PanelWidth, PanelWHeight, 0);
GL.TexCoord2(TexCoords[1]);
GL.Vertex3(-PanelWidth, PanelWHeight, 0);
GL.TexCoord2(TexCoords[2]);
GL.Vertex3(-PanelWidth, -PanelWHeight, 0);
GL.TexCoord2(TexCoords[3]);
GL.Vertex3(PanelWidth, -PanelWHeight, 0);
GL.End();
GL.BindTexture(TextureTarget.Texture2D, 0);
GL.PopMatrix();*/
}
}
public void UpdateBackgroundColor(Color color)
{
Runtime.LayoutEditor.BackgroundColor = color;
glControl1.Invalidate();
Config.Save();
}
private void DrawXyLines()
{
if (GameWindow || Runtime.LayoutEditor.IsGamePreview)
return;
int lineLength = 20;
GL.Color3(Color.Green);
GL.Begin(PrimitiveType.Lines);
GL.Vertex2(0, 0);
GL.Vertex2(0, lineLength);
GL.End();
GL.Color3(Color.Red);
GL.Begin(PrimitiveType.Lines);
GL.Vertex2(0, 0);
GL.Vertex2(lineLength, 0);
GL.End();
}
private void DrawGrid()
{
if (!Runtime.LayoutEditor.DisplayGrid)
return;
var size = 40;
var amount = 300;
GL.LineWidth(0.001f);
GL.Color3(BackgroundColor.Darken(20));
GL.Begin(PrimitiveType.Lines);
int squareGridCounter = 0;
for (var i = -amount; i <= amount; i++)
{
if (squareGridCounter > 5)
{
squareGridCounter = 0;
GL.LineWidth(33f);
}
else
{
GL.LineWidth(0.001f);
}
GL.Vertex2(new Vector2(-amount * size, i * size));
GL.Vertex2(new Vector2(amount * size, i * size));
GL.Vertex2(new Vector2(i * size, -amount * size));
GL.Vertex2(new Vector2(i * size, amount * size));
squareGridCounter++;
}
GL.End();
GL.Color3(Color.Transparent);
GL.PopAttrib();
}
private bool mouseCameraDown = false;
private bool mouseDown = false;
private List<BasePane> SelectionBoxPanes = new List<BasePane>();
private bool showSelectionBox = false;
private bool isPicked = false;
private bool mouseMoving = false;
private Point originMouse;
private Point pickOriginMouse;
private Point pickMouse;
private Vector2 pickDistance;
private PickAction pickAction = PickAction.None;
private PickAxis pickAxis = PickAxis.All;
private bool snapToGrid = false;
private void glControl1_MouseDown(object sender, MouseEventArgs e)
{
if (GameWindow)
return;
pickAction = PickAction.None;
pickAxis = PickAxis.All;
if (Control.ModifierKeys == Keys.Shift && e.Button == MouseButtons.Left ||
e.Button == MouseButtons.Middle)
{
originMouse = e.Location;
mouseCameraDown = true;
glControl1.Invalidate();
}
//Pick an object for moving
else if (e.Button == MouseButtons.Left)
{
mouseDown = true;
RenderEditor();
var coords = OpenGLHelper.convertScreenToWorldCoords(e.Location.X, e.Location.Y);
GL.PopMatrix();
bool hasEdgeHit = false;
var rect = GetSelectedPanesBounding(SelectedPanes);
var edgePick = SearchEdgePicking(rect, coords.X, coords.Y);
if (edgePick != PickAction.None)
{
pickAction = edgePick;
isPicked = true;
hasEdgeHit = true;
}
if (hasEdgeHit)
{
UndoManger.AddToUndo(new LayoutUndoManager.UndoActionTransform(SelectedPanes));
pickOriginMouse = e.Location;
return;
}
BasePane hitPane = null;
SearchHit(LayoutFile.RootPane, coords.X, coords.Y, ref hitPane);
if (hitPane != null)
{
pickAction = PickAction.Translate;
if (!SelectedPanes.Contains(hitPane))
{
if (Control.ModifierKeys != Keys.Control)
SelectedPanes.Clear();
SelectedPanes.Add(hitPane);
}
var paneRect = GetSelectedPanesBounding(SelectedPanes);
var paneEdgePick = SearchEdgePicking(paneRect, coords.X, coords.Y);
if (paneEdgePick != PickAction.None)
pickAction = paneEdgePick;
UndoManger.AddToUndo(new LayoutUndoManager.UndoActionTransform(SelectedPanes));
ParentEditor.UpdateUndo();
ParentEditor.UpdateHiearchyNodeSelection(hitPane);
isPicked = true;
} //Check control key (multi selecting panes)
else if (Control.ModifierKeys != Keys.Control)
SelectedPanes.Clear();
pickOriginMouse = e.Location;
}
else if (e.Button == MouseButtons.Right)
{
RenderEditor();
var coords = OpenGLHelper.convertScreenToWorldCoords(e.Location.X, e.Location.Y);
pickOriginMouse = coords;
GL.PopMatrix();
//Add a content menu
var selectOverlapping = new STToolStripItem("Select Overlapping");
var createPanes = new STToolStripItem("Create Pane");
createPanes.DropDownItems.Add(new STToolStripItem("Null Pane", CreateNullPaneAction));
createPanes.DropDownItems.Add(new STToolStripItem("Picture Pane", CreatePicturePaneAction));
createPanes.DropDownItems.Add(new STToolStripItem("Part Pane", CreatePartPaneAction));
createPanes.DropDownItems.Add(new STToolStripItem("Text Box Pane", CreateTextPaneAction));
createPanes.DropDownItems.Add(new STToolStripItem("Window Pane", CreateWindowPaneAction));
createPanes.DropDownItems.Add(new STToolStripItem("Boundry Pane", CreateBoundryPaneAction));
var hitPanes = GetHitPanes(LayoutFile.RootPane, coords.X, coords.Y, new List<BasePane>());
for (int i = 0; i < hitPanes.Count; i++)
selectOverlapping.DropDownItems.Add(
new STToolStripItem(hitPanes[i].Name, SelectOverlappingAction));
stContextMenuStrip1.Items.Clear();
stContextMenuStrip1.Items.Add(createPanes);
stContextMenuStrip1.Items.Add(selectOverlapping);
stContextMenuStrip1.Items.Add(new STToolStripItem("Show All Hidden Panes", ShowAllPaneAction));
stContextMenuStrip1.Items.Add(new STToolStripItem("Paste (Experimental)", PastePaneAction) { Enabled = CopiedPanes.Count > 0 });
if (SelectedPanes.Count > 0)
{
stContextMenuStrip1.Items.Add(new STToolStripSeparator());
stContextMenuStrip1.Items.Add(new STToolStripItem("Copy (Experimental)", CopyPaneAction));
stContextMenuStrip1.Items.Add(new STToolStripItem("Edit Group"));
stContextMenuStrip1.Items.Add(new STToolStripItem("Delete Selected Panes", DeletePaneAction));
stContextMenuStrip1.Items.Add(new STToolStripItem("Hide Selected Panes", HidePaneAction));
}
stContextMenuStrip1.Show(Cursor.Position);
}
Console.WriteLine("SelectedPanes " + SelectedPanes.Count);
}
private void CreatePartPaneAction(object sender, EventArgs e)
{
var pane = ParentEditor.AddNewPartPane();
SetupNewPane(pane, pickOriginMouse);
}
private void CreateNullPaneAction(object sender, EventArgs e)
{
var pane = ParentEditor.AddNewNullPane();
SetupNewPane(pane, pickOriginMouse);
}
private void CreatePicturePaneAction(object sender, EventArgs e)
{
var pane = ParentEditor.AddNewPicturePane();
SetupNewPane(pane, pickOriginMouse);
}
private void CreateWindowPaneAction(object sender, EventArgs e)
{
var pane = ParentEditor.AddNewWindowPane();
SetupNewPane(pane, pickOriginMouse);
}
private void CreateTextPaneAction(object sender, EventArgs e)
{
var pane = ParentEditor.AddNewTextPane();
SetupNewPane(pane, pickOriginMouse);
if (pane is Cafe.TXT1)
((Cafe.TXT1)pane).UpdateTextRender();
}
private void CreateBoundryPaneAction(object sender, EventArgs e)
{
var pane = ParentEditor.AddNewBoundryPane();
SetupNewPane(pane, pickOriginMouse);
}
private void CopyPaneAction(object sender, EventArgs e)
{
CopyPanes();
}
private void PastePaneAction(object sender, EventArgs e)
{
PastePanes();
}
private void CopyPanes()
{
CopiedPanes.Clear();
foreach (var pane in SelectedPanes) {
BasePane copiedPane = (BasePane)pane.Clone();
CopiedPanes.Add(copiedPane);
}
}
private void PastePanes()
{
SelectedPanes.Clear();
foreach (var copiedPane in CopiedPanes)
{
//Copy again as the user may paste the same one multiple times
//This will make sure it's a completely new instance
BasePane pane = (BasePane)copiedPane.Clone();
ParentEditor.AddNewPastedPane(pane);
SelectedPanes.Add(pane);
}
glControl1.Invalidate();
}
private void SetupNewPane(BasePane pane, Point point)
{
if (pane == null) return;
SelectedPanes.Clear();
pane.Translate = new Syroot.Maths.Vector3F(point.X, point.Y, 0);
ParentEditor.LoadPaneEditorOnSelect(pane);
glControl1.Invalidate();
}
private void DeletePaneAction(object sender, EventArgs e)
{
DeleteSelectedPanes();
}
private void HidePaneAction(object sender, EventArgs e)
{
HideSelectedPanes();
}
private void ShowAllPaneAction(object sender, EventArgs e)
{
ShowHiddenPanes();
}
private void HideSelectedPanes()
{
UndoManger.AddToUndo(new LayoutUndoManager.UndoActionPaneHide(SelectedPanes));
ParentEditor?.UpdateHiearchyTree();
SelectedPanes.Clear();
glControl1.Invalidate();
}
private void ShowHiddenPanes()
{
UndoManger.AddToUndo(new LayoutUndoManager.UndoActionPaneHide(LayoutFile.PaneLookup.Values.ToList(), false));
ParentEditor?.UpdateHiearchyTree();
glControl1.Invalidate();
}
private void DeleteSelectedPanes()
{
if (SelectedPanes.Count == 0) return;
//Make sure to fill all the children in selected panes!
for (int i = 0; i < SelectedPanes.Count; i++)
SelectedPanes.AddRange(GetChildren(SelectedPanes, new List<BasePane>(), SelectedPanes[i]));
UndoManger.AddToUndo(new LayoutUndoManager.UndoActionPaneDelete(SelectedPanes, LayoutFile));
LayoutFile.RemovePanes(SelectedPanes, LayoutFile.RootPane);
SelectedPanes.Clear();
ParentEditor?.UpdateHiearchyTree();
glControl1.Invalidate();
}
private List<BasePane> GetChildren(List<BasePane> selectedPanes, List<BasePane> childrenPanes, BasePane parent)
{
if (!selectedPanes.Contains(parent))
childrenPanes.Add(parent);
foreach (var child in parent.Childern)
GetChildren(selectedPanes, childrenPanes, child);
return childrenPanes;
}
private void SelectOverlappingAction(object sender, EventArgs e)
{
var toolMenu = sender as STToolStripItem;
if (toolMenu != null)
{
string name = toolMenu.Text;
if (Control.ModifierKeys != Keys.Control)
SelectedPanes.Clear();
if (LayoutFile.PaneLookup.ContainsKey(name))
SelectedPanes.Add(LayoutFile.PaneLookup[name]);
glControl1.Invalidate();
}
}
private void SearchHit(BasePane pane, int X, int Y, ref BasePane SelectedPane)
{
bool isVisible = true;
if (!Runtime.LayoutEditor.DisplayPicturePane && pane is IPicturePane)
isVisible = false;
if (!Runtime.LayoutEditor.DisplayWindowPane && pane is IWindowPane)
isVisible = false;
if (!Runtime.LayoutEditor.DisplayBoundryPane && pane is IBoundryPane)
isVisible = false;
if (!Runtime.LayoutEditor.DisplayTextPane && pane is ITextPane)
isVisible = false;
if (!Runtime.LayoutEditor.DisplayNullPane && pane.IsNullPane)
isVisible = false;
if (isVisible && pane.DisplayInEditor && pane.IsHit(X, Y) && !pane.IsRoot)
{
//Select the first possible pane
//If the pane is selected already, pick that instead
//This is useful if the selected pane wants to be moved already
if (SelectedPane == null || SelectedPanes.Contains(pane))
SelectedPane = pane;
}
//Keep searching even if we found our pane so we can find any that's selected
foreach (var childPane in pane.Childern)
SearchHit(childPane, X, Y, ref SelectedPane);
}
private List<BasePane> GetHitPanes(BasePane pane, CustomRectangle rect, List<BasePane> SelectedPanes)
{
bool isVisible = pane.Visible;
if (!Runtime.LayoutEditor.DisplayPicturePane && pane is IPicturePane)
isVisible = false;
if (!Runtime.LayoutEditor.DisplayWindowPane && pane is IWindowPane)
isVisible = false;
if (!Runtime.LayoutEditor.DisplayBoundryPane && pane is IBoundryPane)
isVisible = false;
if (!Runtime.LayoutEditor.DisplayTextPane && pane is ITextPane)
isVisible = false;
if (!Runtime.LayoutEditor.DisplayNullPane && pane.IsNullPane)
isVisible = false;
if (isVisible && pane.DisplayInEditor && pane.IsHit(rect) && pane.Name != "RootPane")
if (!SelectedPanes.Contains(pane))
SelectedPanes.Add(pane);
foreach (var childPane in pane.Childern)
GetHitPanes(childPane, rect, SelectedPanes);
return SelectedPanes;
}
private List<BasePane> GetHitPanes(BasePane pane, int X, int Y, List<BasePane> SelectedPanes)
{
bool isVisible = pane.Visible;
if (!Runtime.LayoutEditor.DisplayPicturePane && pane is IPicturePane)
isVisible = false;
if (!Runtime.LayoutEditor.DisplayWindowPane && pane is IWindowPane)
isVisible = false;
if (!Runtime.LayoutEditor.DisplayBoundryPane && pane is IBoundryPane)
isVisible = false;
if (!Runtime.LayoutEditor.DisplayTextPane && pane is ITextPane)
isVisible = false;
if (!Runtime.LayoutEditor.DisplayNullPane && pane.IsNullPane)
isVisible = false;
if (isVisible && pane.DisplayInEditor && pane.IsHit(X, Y) && pane.Name != "RootPane")
if (!SelectedPanes.Contains(pane))
SelectedPanes.Add(pane);
foreach (var childPane in pane.Childern)
GetHitPanes(childPane, X, Y, SelectedPanes);
return SelectedPanes;
}
private PickAction SearchEdgePicking(CustomRectangle transformed, int X, int Y)
{
var leftTop = new Point(transformed.LeftPoint, transformed.TopPoint);
var left = new Point(transformed.LeftPoint, (transformed.BottomPoint + transformed.TopPoint) / 2);
var leftBottom = new Point(transformed.LeftPoint, transformed.BottomPoint);
var rightTop = new Point(transformed.RightPoint, transformed.TopPoint);
var right = new Point(transformed.RightPoint, (transformed.BottomPoint + transformed.TopPoint) / 2);
var rightBottom = new Point(transformed.RightPoint, transformed.BottomPoint);
var top = new Point((transformed.RightPoint + transformed.LeftPoint) / 2, transformed.TopPoint);
var bottom = new Point((transformed.RightPoint + transformed.LeftPoint) / 2, transformed.BottomPoint);
if (IsEdgeHit(leftTop, X, Y)) return PickAction.DragTopLeft;
else if (IsEdgeHit(left, X, Y)) return PickAction.DragLeft;
else if (IsEdgeHit(leftBottom, X, Y)) return PickAction.DragBottomLeft;
else if (IsEdgeHit(rightTop, X, Y)) return PickAction.DragTopRight;
else if (IsEdgeHit(rightBottom, X, Y)) return PickAction.DragBottomRight;
else if (IsEdgeHit(right, X, Y)) return PickAction.DragRight;
else if (IsEdgeHit(top, X, Y)) return PickAction.DragTop;
else if (IsEdgeHit(bottom, X, Y)) return PickAction.DragBottom;
return PickAction.None;
}
private bool IsEdgeHit(Point point, int X, int Y, int size = 10)
{
if ((X > point.X - size) && (X < point.X + size) &&
(Y > point.Y - size) && (Y < point.Y + size))
return true;
else
return false;
}
private void glControl1_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left || e.Button == MouseButtons.Middle)
{
pickAxis = PickAxis.All;
mouseCameraDown = false;
mouseDown = false;
isPicked = false;
mouseMoving = false;
showSelectionBox = false;
foreach (var pane in SelectionBoxPanes)
if (!SelectedPanes.Contains(pane))
SelectedPanes.Add(pane);
SelectionBoxPanes.Clear();
ParentEditor.RefreshEditors();
if (SelectedPanes.Count > 0)
ParentEditor.UpdateHiearchyNodeSelection(SelectedPanes.FirstOrDefault());
}
glControl1.Invalidate();
}
public enum PickAction
{
None,
DragTopRight,
DragTopLeft,
DragTop,
DragLeft,
DragRight,
DragBottom,
DragBottomLeft,
DragBottomRight,
Translate,
Scale,
Rotate
}
public enum PickAxis
{
All,
X,
Y,
Z,
}
private void glControl1_MouseMove(object sender, MouseEventArgs e)
{
if (GameWindow)
return;
if (UseOrtho)
GL.PopMatrix();
if (SelectedPanes.Count > 0 && !showSelectionBox)
{
RenderEditor();
var posWorld = OpenGLHelper.convertScreenToWorldCoords(e.Location.X, e.Location.Y);
GL.PopMatrix();
//Setup edge picking with move event
bool hasPick = false;
var paneRect = GetSelectedPanesBounding(SelectedPanes);
var pickState = SearchEdgePicking(paneRect, posWorld.X, posWorld.Y);
if (pickState != PickAction.None)
{
if (pickState == PickAction.DragTop)
Cursor.Current = Cursors.SizeNS;
if (pickState == PickAction.DragBottom)
Cursor.Current = Cursors.SizeNS;
if (pickState == PickAction.DragLeft)
Cursor.Current = Cursors.SizeWE;
if (pickState == PickAction.DragRight)
Cursor.Current = Cursors.SizeWE;
if (pickState == PickAction.DragBottomLeft)
Cursor.Current = Cursors.SizeNESW;
if (pickState == PickAction.DragBottomRight)
Cursor.Current = Cursors.SizeNWSE;
if (pickState == PickAction.DragTopLeft)
Cursor.Current = Cursors.SizeNWSE;
if (pickState == PickAction.DragTopRight)
Cursor.Current = Cursors.SizeNESW;
hasPick = true;
}
else if (isPicked && pickAction != PickAction.None)
{
if (pickAction == PickAction.Translate)
Cursor.Current = Cursors.SizeAll;
if (pickAction == PickAction.Rotate)
Cursor.Current = Cursors.SizeAll;
if (pickAction == PickAction.Scale)
Cursor.Current = Cursors.SizeAll;
hasPick = true;
}
if (!hasPick)
Cursor.Current = Cursors.Default;
}
if (isPicked && !showSelectionBox)
{
RenderEditor();
var temp = e.Location;
var curPos = OpenGLHelper.convertScreenToWorldCoords(temp.X, temp.Y);
var prevPos = OpenGLHelper.convertScreenToWorldCoords(pickOriginMouse.X, pickOriginMouse.Y);
var pickMouse = new Point((int)(prevPos.X - curPos.X), (int)(prevPos.Y - curPos.Y));
if (pickAction == PickAction.Translate)
{
foreach (var pane in SelectedPanes)
{
if (pickOriginMouse != Point.Empty)
{
float posX = pane.Translate.X;
float posY = pane.Translate.Y;
float posZ = pane.Translate.Z;
if (pickAxis == PickAxis.X)
posX = pane.Translate.X - pickMouse.X;
if (pickAxis == PickAxis.Y)
posY = pane.Translate.Y - pickMouse.Y;
if (pickAxis == PickAxis.All)
{
posX = pane.Translate.X - pickMouse.X;
posY = pane.Translate.Y - pickMouse.Y;
}
if (Runtime.LayoutEditor.AnimationEditMode)
{
}
else
{
if (!Runtime.LayoutEditor.TransformChidlren)
pane.KeepChildrenTransform(posX, posY);
if (snapToGrid)
{
int gridCubeWidth = 16, gridCubeHeight = 16;
pane.Translate = new Syroot.Maths.Vector3F(
(float)(Math.Round(posX / gridCubeWidth) * gridCubeWidth),
(float)(Math.Round(posY / gridCubeHeight) * gridCubeHeight),
posZ);
}
else
{
pane.Translate = new Syroot.Maths.Vector3F(posX, posY, posZ);
}
}
}
}
}
if (pickAction == PickAction.Scale)
{
foreach (var pane in SelectedPanes)
{
if (pickOriginMouse != Point.Empty)
{
float scaX = pane.Scale.X;
float scaY = pane.Scale.Y;
if (pickAxis == PickAxis.X)
scaX = pane.Scale.X - pickMouse.X;
if (pickAxis == PickAxis.Y)
scaY = pane.Scale.Y - pickMouse.Y;
if (pickAxis == PickAxis.All)
{
scaX = pane.Scale.X - pickMouse.X;
scaY = pane.Scale.Y - pickMouse.Y;
}
if (Runtime.LayoutEditor.AnimationEditMode)
{
}
else
{
if (snapToGrid)
{
int gridCubeWidth = 16, gridCubeHeight = 16;
pane.Scale = new Syroot.Maths.Vector2F(
(float)(Math.Round(scaX / gridCubeWidth) * gridCubeWidth),
(float)(Math.Round(scaY / gridCubeHeight) * gridCubeHeight));
}
else
{
pane.Scale = new Syroot.Maths.Vector2F(scaX, scaY);
}
}
}
}
}
else if (!showSelectionBox)
{
var selectionBox = GetSelectedPanesBounding(SelectedPanes);
//Setup edge picking with move event
foreach (var pane in SelectedPanes)
pane.TransformRectangle(pickAction, selectionBox, pickMouse.X, pickMouse.Y);
}
pickOriginMouse = temp;
RenderScene();
}
if (mouseDown && !isPicked)
{
RenderEditor();
var temp = e.Location;
var curPos = OpenGLHelper.convertScreenToWorldCoords(temp.X, temp.Y);
var prevPos = OpenGLHelper.convertScreenToWorldCoords(pickOriginMouse.X, pickOriginMouse.Y);
DrawSelectionBox(prevPos, curPos);
}
if (mouseCameraDown)
{
var pos = new Vector2(e.Location.X - originMouse.X, e.Location.Y - originMouse.Y);
Camera.Position.X += pos.X;
Camera.Position.Y -= pos.Y;
originMouse = e.Location;
glControl1.Invalidate();
}
}
private CustomRectangle SelectionBox;
private void DrawSelectionBox(Point point1, Point point2)
{
SelectionBoxPanes.Clear();
int left = point1.X;
int right = point2.X;
int top = point1.Y;
int bottom = point2.Y;
//Determine each point direction to see what is left/right/top/bottom
if (bottom > top)
{
top = point2.Y;
bottom = point1.Y;
}
if (left > right)
{
right = point1.X;
left = point2.X;
}
showSelectionBox = true;
SelectionBox = new CustomRectangle(left, right, top, bottom);
var hitPanes = GetHitPanes(LayoutFile.RootPane, SelectionBox, new List<BasePane>());
foreach (var pane in hitPanes)
if (!SelectionBoxPanes.Contains(pane))
SelectionBoxPanes.Add(pane);
RenderScene(true);
}
protected override void OnMouseWheel(MouseEventArgs e)
{
base.OnMouseWheel(e);
if (UseOrtho)
{
if (e.Delta > 0 && Camera.Zoom > 0)
Camera.Zoom += 0.1f;
if (e.Delta < 0 && Camera.Zoom < 100 && Camera.Zoom > 0.1)
Camera.Zoom -= 0.1f;
}
else
{
if (e.Delta > 0 && Camera.Zoom > 0.1)
Camera.Zoom -= 0.1f;
if (e.Delta < 0 && Camera.Zoom < 100 && Camera.Zoom > 0)
Camera.Zoom += 0.1f;
}
glControl1.Invalidate();
}
private void glControl1_Resize(object sender, EventArgs e)
{
glControl1.Invalidate();
}
private void glControl1_KeyDown(object sender, KeyEventArgs e)
{
if (isPicked && e.KeyCode == Keys.X)
{
pickAxis = PickAxis.X;
glControl1.Invalidate();
}
if (isPicked && e.KeyCode == Keys.Y)
{
pickAxis = PickAxis.Y;
glControl1.Invalidate();
}
else if (e.Control && e.KeyCode == Keys.Z) // Ctrl + Z undo
{
UndoManger.Undo();
ParentEditor.UpdateUndo();
glControl1.Invalidate();
}
else if (e.Control && e.KeyCode == Keys.R) // Ctrl + Z undo
{
UndoManger.Redo();
ParentEditor.UpdateUndo();
glControl1.Invalidate();
}
else if (e.Control && e.KeyCode == Keys.C) // Ctrl + C copy
{
CopyPanes();
glControl1.Invalidate();
}
else if (e.Control && e.KeyCode == Keys.V) // Ctrl + V paste
{
PastePanes();
glControl1.Invalidate();
}
else if (e.Control && e.KeyCode == Keys.A) // Ctrl + A select all
{
SelectedPanes.Clear();
foreach (var pane in LayoutFile.PaneLookup.Values)
if (!pane.IsRoot)
SelectedPanes.Add(pane);
glControl1.Invalidate();
}
else if (e.KeyCode == Keys.Delete)
{
DeleteSelectedPanes();
}
}
private void glControl1_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(ListViewItem)))
{
e.Effect = DragDropEffects.Move;
}
}
private void glControl1_DragDrop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(ListViewItem)))
{
var item = e.Data.GetData(typeof(ListViewItem)) as ListViewItem;
string texture = item.Text;
if (Textures.ContainsKey(texture))
{
var point = this.PointToClient(new Point(e.X, e.Y));
RenderEditor();
var coords = OpenGLHelper.convertScreenToWorldCoords(point.X, point.Y);
GL.PopMatrix();
var pane = ParentEditor.AddNewPicturePane();
pane.Width = Textures[texture].Width;
pane.Height = Textures[texture].Height;
((IPicturePane)pane).Material.AddTexture(texture);
SetupNewPane(pane, coords);
}
}
}
}
}