307 lines
9.3 KiB
C#
307 lines
9.3 KiB
C#
using StgSharp.Controlling;
|
|
using StgSharp.Math;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Numerics;
|
|
using System.Reflection;
|
|
using System.Reflection.Emit;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Threading;
|
|
|
|
namespace StgSharp.Graphics
|
|
{
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public abstract partial class Form
|
|
{
|
|
internal LinkedList<Control> allControl;
|
|
internal Vector4 color;
|
|
internal Thread formThread;
|
|
internal int height;
|
|
internal IntPtr monitor;
|
|
internal string name;
|
|
internal Form sharedForm;
|
|
internal int width;
|
|
internal IntPtr windowID;
|
|
|
|
protected IglFunc GL;
|
|
internal FormUpdateBuffer UpdateBuffer;
|
|
internal FormRenderBuffer RenderBuffer;
|
|
|
|
internal FrameUpdateTimer timer = new FrameUpdateTimer();
|
|
|
|
private SemaphoreSlim ping = new SemaphoreSlim(1, 1);
|
|
private SemaphoreSlim pong = new SemaphoreSlim(0, 1);
|
|
internal readonly SemaphoreSlim ouetrPong = new SemaphoreSlim(0, 1);
|
|
|
|
private UpdateWhenRender updateWhenRender;
|
|
private DynamicMethod updateWhenRenderDM;
|
|
private ILGenerator updateWhenRenderIL;
|
|
|
|
private delegate void UpdateWhenRender();
|
|
|
|
/// <summary>
|
|
/// Height of this form, represent in <see cref="int"/>
|
|
/// </summary>
|
|
public int Height
|
|
{
|
|
get => height;
|
|
set => height = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Width of this form, represent in <see cref="int"/>
|
|
/// </summary>
|
|
public Vec2d Size
|
|
{
|
|
get { return new Vec2d(width, height); }
|
|
set
|
|
{
|
|
width = (int)value.X;
|
|
height = (int)value.Y;
|
|
}
|
|
}
|
|
|
|
public int Width
|
|
{
|
|
get => width;
|
|
set => width = value;
|
|
}
|
|
|
|
unsafe ~Form()
|
|
{
|
|
Marshal.FreeHGlobal((IntPtr)contextID);
|
|
RenderBuffer.GL.api = default;
|
|
}
|
|
|
|
#region form main
|
|
|
|
public abstract void Deinit(object[] args);
|
|
|
|
public abstract void Init(object[] args);
|
|
|
|
public abstract void OnRenderFrame(object[] args);
|
|
|
|
public abstract void OnUpdateFrame(object[] args);
|
|
|
|
/// <summary>
|
|
/// Init this form and render it on screen, and respond with operation
|
|
/// </summary>
|
|
public void Show(object[] args)
|
|
{
|
|
if (args == null)
|
|
{
|
|
args = new object[4] { null, null, null, null };
|
|
}
|
|
Operation.RegistForm(this);
|
|
if (!TimeLine.RenderStarted)
|
|
{
|
|
TimeLine.RenderAll();
|
|
}
|
|
try
|
|
{
|
|
Thread updateThread = new Thread(InternalUpdateFrame);
|
|
Thread renderThread = new Thread(InternalRenderFrame);
|
|
|
|
updateThread.Start(args[1]);
|
|
renderThread.Start(args[2]);
|
|
|
|
updateThread.Join();
|
|
renderThread.Join();
|
|
|
|
ping.Dispose();
|
|
pong.Dispose();
|
|
|
|
InternalDeinit(args[3]);
|
|
|
|
}
|
|
catch (Exception)
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
|
|
internal void InternalDeinit(object args)
|
|
{
|
|
Deinit((object[])args);
|
|
}
|
|
|
|
internal void InternalInit(object args)
|
|
{
|
|
updateWhenRenderDM = new DynamicMethod(
|
|
name: "updateWhenRender",
|
|
returnType: typeof(void),
|
|
parameterTypes: new Type[] { typeof(void) },
|
|
restrictedSkipVisibility: true
|
|
);
|
|
updateWhenRenderIL = updateWhenRenderDM.GetILGenerator();
|
|
|
|
Init((object[])args);
|
|
|
|
}
|
|
|
|
|
|
internal void InternalRenderFrame(object args)
|
|
{
|
|
while (InternalIO.glfw.WindowShouldClose(windowID) != 0)
|
|
{
|
|
ping.Wait();
|
|
try
|
|
{
|
|
OnRenderFrame((object[])args);
|
|
timer.WaitNextFrame();
|
|
|
|
pong.Release();
|
|
}
|
|
catch (NullReferenceException)
|
|
{
|
|
throw new Exception("Forget to reload GL apis");
|
|
pong.Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void InternalUpdateFrame(object args)
|
|
{
|
|
while (InternalIO.glfw.WindowShouldClose(windowID) != 0)
|
|
{
|
|
pong.Wait();
|
|
try
|
|
{
|
|
OnUpdateFrame((object[])args);
|
|
timer.WaitNextFrame();
|
|
throw new Exception("Forget to reload GL apis");
|
|
ping.Release();
|
|
}
|
|
catch (NullReferenceException)
|
|
{
|
|
throw new Exception("Forget to reload GL apis");
|
|
ping.Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Constructor of a Form, creats an instance of a form and
|
|
/// initilize all the customizing operations.
|
|
/// </summary>
|
|
public void InitForm(int width, int height, string name, IntPtr monitor, Form sharedForm)
|
|
{
|
|
this.width = width;
|
|
this.height = height;
|
|
this.name = name;
|
|
this.sharedForm = sharedForm;
|
|
this.monitor = monitor;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Init an OpenGl context if current form has not been binded to any context.
|
|
/// and set the context binded to current form as current OpenGL context.
|
|
/// </summary>
|
|
/// <exception cref="Exception">Something goes wrong and
|
|
/// StgSharp fails in creating a new OpenGL context. </exception>
|
|
public unsafe void MakeAsCurrentContext()
|
|
{
|
|
if (windowID == default)
|
|
{
|
|
IntPtr windowPtr;
|
|
byte[] tittleBytes = Encoding.UTF8.GetBytes(name);
|
|
if (sharedForm == null)
|
|
{
|
|
windowPtr = InternalIO.glfw.CreateWindow(
|
|
width, height, tittleBytes, monitor, IntPtr.Zero);
|
|
}
|
|
else
|
|
{
|
|
windowPtr = InternalIO.glfw.CreateWindow(
|
|
width, height, tittleBytes, monitor, sharedForm.windowID);
|
|
}
|
|
|
|
if ((long)windowPtr == -1 || (long)windowPtr == -2)
|
|
{
|
|
throw new Exception("Cannot create window instance!");
|
|
}
|
|
else
|
|
{
|
|
windowID = windowPtr;
|
|
}
|
|
glRender = new GLonRender();
|
|
glContext = new GLcontext();
|
|
IntPtr ptr = Marshal.AllocHGlobal(sizeof(GLcontextIO));
|
|
contextID = (GLcontextIO*)ptr;
|
|
*contextID = new GLcontextIO();
|
|
InternalIO.InternalWriteLog("Successfully create a context binded to current form. " +
|
|
$"Its ID is {(ulong)ptr}", LogType.Info);
|
|
}
|
|
InternalIO.InternalSetCurrentGLContext(windowID, (IntPtr)contextID);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Resize the current form
|
|
/// </summary>
|
|
/// <param name="width">New width of the window</param>
|
|
/// <param name="height">New height of the window</param>
|
|
public void SetSize(int width, int height)
|
|
{
|
|
this.Width = width;
|
|
this.Height = height;
|
|
InternalIO.glfw.SetWindowSize(this.windowID, width, height);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resize the current form
|
|
/// </summary>
|
|
/// <param name="size">
|
|
/// A <see cref="Vec2d"/> (width,height) representing the new size of current form
|
|
/// </param>
|
|
public void SetSize(Vec2d size)
|
|
{
|
|
this.Size = size;
|
|
InternalIO.glfw.SetWindowSize(this.windowID, (int)size.X, (int)size.Y);
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the size limit of current form
|
|
/// </summary>
|
|
/// <param name="minWidth">Minimum width of current form </param>
|
|
/// <param name="minHeight">Minimum height of current form</param>
|
|
/// <param name="maxWidth">Maximum width of curent form</param>
|
|
/// <param name="maxHeight">Maximum height of current form</param>
|
|
public void SetSizeLimit(int minWidth, int minHeight, int maxWidth, int maxHeight)
|
|
{
|
|
InternalIO.glfw.SetWindowSizeLimits(
|
|
this.windowID,
|
|
minWidth, minHeight, maxWidth, maxHeight);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the size limit of current form
|
|
/// </summary>
|
|
/// <param name="minSize">
|
|
/// Minimun size of current form. In form of a <see cref="Vec2d"/> of (width,height)
|
|
/// </param>
|
|
/// <param name="maxSize">
|
|
/// Maximum size of current form. In form of a <see cref="Vec2d"/> of (width,height)
|
|
/// </param>
|
|
public void SetSizeLimit(Vec2d minSize, Vec2d maxSize)
|
|
{
|
|
InternalIO.glfw.SetWindowSizeLimits(
|
|
this.windowID,
|
|
(int)minSize.X, (int)minSize.Y, (int)maxSize.X, (int)maxSize.Y);
|
|
}
|
|
|
|
|
|
}
|
|
}
|