diff --git a/GLWidget.sln b/GLWidget.sln index cf6713c..35aefcf 100644 --- a/GLWidget.sln +++ b/GLWidget.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 16.0.29709.97 MinimumVisualStudioVersion = 15.0.26124.0 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GLWidget", "GLWidget\GLWidget.csproj", "{2F1DF6C0-67E9-4ADF-B2DA-F3E1504CF57B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GLWidgetTestGTK3", "GLWidgetTestGTK3\GLWidgetTestGTK3.csproj", "{227DE1F9-A40A-440A-9A06-4AA5532DBA17}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,6 +29,18 @@ Global {2F1DF6C0-67E9-4ADF-B2DA-F3E1504CF57B}.Release|x64.Build.0 = Release|Any CPU {2F1DF6C0-67E9-4ADF-B2DA-F3E1504CF57B}.Release|x86.ActiveCfg = Release|Any CPU {2F1DF6C0-67E9-4ADF-B2DA-F3E1504CF57B}.Release|x86.Build.0 = Release|Any CPU + {227DE1F9-A40A-440A-9A06-4AA5532DBA17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {227DE1F9-A40A-440A-9A06-4AA5532DBA17}.Debug|Any CPU.Build.0 = Debug|Any CPU + {227DE1F9-A40A-440A-9A06-4AA5532DBA17}.Debug|x64.ActiveCfg = Debug|Any CPU + {227DE1F9-A40A-440A-9A06-4AA5532DBA17}.Debug|x64.Build.0 = Debug|Any CPU + {227DE1F9-A40A-440A-9A06-4AA5532DBA17}.Debug|x86.ActiveCfg = Debug|Any CPU + {227DE1F9-A40A-440A-9A06-4AA5532DBA17}.Debug|x86.Build.0 = Debug|Any CPU + {227DE1F9-A40A-440A-9A06-4AA5532DBA17}.Release|Any CPU.ActiveCfg = Debug|Any CPU + {227DE1F9-A40A-440A-9A06-4AA5532DBA17}.Release|Any CPU.Build.0 = Debug|Any CPU + {227DE1F9-A40A-440A-9A06-4AA5532DBA17}.Release|x64.ActiveCfg = Debug|Any CPU + {227DE1F9-A40A-440A-9A06-4AA5532DBA17}.Release|x64.Build.0 = Debug|Any CPU + {227DE1F9-A40A-440A-9A06-4AA5532DBA17}.Release|x86.ActiveCfg = Debug|Any CPU + {227DE1F9-A40A-440A-9A06-4AA5532DBA17}.Release|x86.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/GLWidget/GLWidget.cs b/GLWidget/GLWidget.cs index 17936e6..22d5bb3 100644 --- a/GLWidget/GLWidget.cs +++ b/GLWidget/GLWidget.cs @@ -35,9 +35,11 @@ using System.ComponentModel; using OpenTK.Graphics; -using OpenTK.Platform; +using OpenTK.Graphics.OpenGL; + using Gtk; +using Cairo; namespace OpenTK { @@ -50,85 +52,48 @@ namespace OpenTK private static int _graphicsContextCount; private static bool _sharedContextInitialized; - public bool IsRenderHandler { get; set; } = false; + public bool IsRenderHandler { get; set; } = false; #endregion #region Attributes - private IGraphicsContext _graphicsContext; - private IWindowInfo _windowInfo; private bool _initialized; #endregion #region Properties - /// Use a single buffer versus a double buffer. - [Browsable(true)] - public bool SingleBuffer { get; set; } - - /// Color Buffer Bits-Per-Pixel - public int ColorBPP { get; set; } - - /// Accumulation Buffer Bits-Per-Pixel - public int AccumulatorBPP { get; set; } - - /// Depth Buffer Bits-Per-Pixel - public int DepthBPP { get; set; } - - /// Stencil Buffer Bits-Per-Pixel - public int StencilBPP { get; set; } - - /// Number of samples - public int Samples { get; set; } - - /// Indicates if steropic renderering is enabled - public bool Stereo { get; set; } - /// The major version of OpenGL to use. public int GLVersionMajor { get; set; } /// The minor version of OpenGL to use. public int GLVersionMinor { get; set; } - public GraphicsContextFlags GraphicsContextFlags + private int _framebuffer; + private int _renderbuffer; + private int _stencilbuffer; + + public Gdk.GLContext GraphicsContext { get; set; } + public bool ForwardCompatible { get; } + + #endregion + + #region Construction/Destruction + + /// Constructs a new GLWidget + public GLWidget() : this(new Version(4, 0), true) + { + + } + + /// Constructs a new GLWidget + public GLWidget(Version apiVersion, bool forwardCompatible) { - get; - set; - } - - #endregion - - #region Construction/Destruction - - /// Constructs a new GLWidget. - public GLWidget() - : this(GraphicsMode.Default) - { - } - - /// Constructs a new GLWidget using a given GraphicsMode - public GLWidget(GraphicsMode graphicsMode) - : this(graphicsMode, 1, 0, GraphicsContextFlags.Default) - { - } - - /// Constructs a new GLWidget - public GLWidget(GraphicsMode graphicsMode, int glVersionMajor, int glVersionMinor, GraphicsContextFlags graphicsContextFlags) - { - SingleBuffer = graphicsMode.Buffers == 1; - ColorBPP = graphicsMode.ColorFormat.BitsPerPixel; - AccumulatorBPP = graphicsMode.AccumulatorFormat.BitsPerPixel; - DepthBPP = graphicsMode.Depth; - StencilBPP = graphicsMode.Stencil; - Samples = graphicsMode.Samples; - Stereo = graphicsMode.Stereo; - - GLVersionMajor = glVersionMajor; - GLVersionMinor = glVersionMinor; - GraphicsContextFlags = graphicsContextFlags; - } + GLVersionMajor = apiVersion.Major; + GLVersionMinor = apiVersion.Minor; + ForwardCompatible = forwardCompatible; + } ~GLWidget() { @@ -139,22 +104,8 @@ namespace OpenTK { if (disposing) { - try - { - GraphicsContext.MakeCurrent(WindowInfo); - }catch(Exception ex) - { - - } - OnShuttingDown(); - if (OpenTK.Graphics.GraphicsContext.ShareContexts && (Interlocked.Decrement(ref _graphicsContextCount) == 0)) - { - OnGraphicsContextShuttingDown(); - _sharedContextInitialized = false; - } - GraphicsContext.Dispose(); } } @@ -201,11 +152,8 @@ namespace OpenTK protected virtual void OnRenderFrame() { - if (RenderFrame != null) - { - RenderFrame(this, EventArgs.Empty); - } - } + RenderFrame?.Invoke(this, EventArgs.Empty); + } // Called when this GLWidget is being Disposed public event EventHandler ShuttingDown; @@ -220,334 +168,113 @@ namespace OpenTK #endregion - // Called when a widget is realized. (window handles and such are valid) - // protected override void OnRealized() { base.OnRealized(); } - // Called when the widget needs to be (fully or partially) redrawn. protected override bool OnDrawn(Cairo.Context cr) { if (!_initialized) Initialize(); - else if(!IsRenderHandler) - GraphicsContext.MakeCurrent(WindowInfo); + else if (!IsRenderHandler) + { + MakeCurrent(); + } - return true; + cr.SetSourceColor(new Color(0, 0, 0, 1)); + cr.Paint(); + + var scale = this.ScaleFactor; + + Gdk.CairoHelper.DrawFromGl(cr, this.Window, _renderbuffer, (int)ObjectLabelIdentifier.Renderbuffer, scale, 0, 0, AllocatedWidth, AllocatedHeight); + + return true; } - // Called on Resize - protected override bool OnConfigureEvent(Gdk.EventConfigure evnt) + public void Swapbuffers() + { + GL.Flush(); + + QueueDraw(); + } + + public void MakeCurrent() + { + GraphicsContext?.MakeCurrent(); + GL.BindFramebuffer(FramebufferTarget.Framebuffer, _framebuffer); + } + + public void ClearCurrent() + { + Gdk.GLContext.ClearCurrent(); + } + + // Called on Resize + protected override bool OnConfigureEvent(Gdk.EventConfigure evnt) { if (GraphicsContext != null) { - GraphicsContext.Update(WindowInfo); - } + MakeCurrent(); + + GraphicsContext.Window.Resize(evnt.X, evnt.Y); + + DeleteBuffers(); + + CreateFramebuffer(); + } return true; } - private void Initialize() + private void DeleteBuffers() + { + if (_framebuffer != 0) + { + GL.DeleteFramebuffer(_framebuffer); + GL.DeleteRenderbuffer(_renderbuffer); + } + } + + private void CreateFramebuffer() + { + _framebuffer = GL.GenFramebuffer(); + GL.BindFramebuffer(FramebufferTarget.Framebuffer, _framebuffer); + + _renderbuffer = GL.GenRenderbuffer(); + GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, _renderbuffer); + GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, RenderbufferStorage.Rgba8, AllocatedWidth, AllocatedHeight); + + GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, RenderbufferTarget.Renderbuffer, _renderbuffer); + + _stencilbuffer = GL.GenRenderbuffer(); + GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, _stencilbuffer); + GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, RenderbufferStorage.Depth24Stencil8, AllocatedWidth, AllocatedHeight); + + GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferAttachment.DepthStencilAttachment, RenderbufferTarget.Renderbuffer, _stencilbuffer); + + GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, 0); + var state = GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer); + } + + private void Initialize() { _initialized = true; - // If this looks uninitialized... initialize. - if (ColorBPP == 0) - { - ColorBPP = 32; - - if (DepthBPP == 0) - { - DepthBPP = 16; - } - } - - ColorFormat colorBufferColorFormat = new ColorFormat(ColorBPP); - - ColorFormat accumulationColorFormat = new ColorFormat(AccumulatorBPP); - - int buffers = 2; - if (SingleBuffer) - { - buffers--; - } - - GraphicsMode graphicsMode = new GraphicsMode(colorBufferColorFormat, DepthBPP, StencilBPP, Samples, accumulationColorFormat, buffers, Stereo); - this.Window.EnsureNative(); - // IWindowInfo - if (OpenTK.Configuration.RunningOnWindows) - { - WindowInfo = InitializeWindows(); - } - else if (OpenTK.Configuration.RunningOnMacOS) - { - WindowInfo = InitializeOSX(); - } - else - { - WindowInfo = InitializeX(graphicsMode); - } + GraphicsContext = Window.CreateGlContext(); - // GraphicsContext - GraphicsContext = new GraphicsContext(graphicsMode, WindowInfo, GLVersionMajor, GLVersionMinor, GraphicsContextFlags); - GraphicsContext.MakeCurrent(WindowInfo); + GraphicsContext.SetRequiredVersion(GLVersionMajor, GLVersionMinor); - if (OpenTK.Graphics.GraphicsContext.ShareContexts) - { - Interlocked.Increment(ref _graphicsContextCount); + GraphicsContext.SetUseEs(0); - if (!_sharedContextInitialized) - { - _sharedContextInitialized = true; - ((IGraphicsContextInternal)GraphicsContext).LoadAll(); - OnGraphicsContextInitialized(); - } - } - else - { - ((IGraphicsContextInternal)GraphicsContext).LoadAll(); - OnGraphicsContextInitialized(); - } + GraphicsContext.ForwardCompatible = ForwardCompatible; - OnInitialized(); + GraphicsContext.Realize(); + + GraphicsContext.MakeCurrent(); + + CreateFramebuffer(); + + OnInitialized(); } - - #region Windows Specific initalization - - IWindowInfo InitializeWindows() - { - IntPtr windowHandle = gdk_win32_window_get_handle(this.Window.Handle); - return Utilities.CreateWindowsWindowInfo(windowHandle); - } - - [SuppressUnmanagedCodeSecurity, DllImport("libgdk-3-0.dll")] - public static extern IntPtr gdk_win32_window_get_handle(IntPtr d); - - #endregion - - #region OSX Specific Initialization - - IWindowInfo InitializeOSX() - { - IntPtr windowHandle = gdk_quartz_window_get_nswindow(this.Window.Handle); - //IntPtr viewHandle = gdk_quartz_window_get_nsview(this.GdkWindow.Handle); - return Utilities.CreateMacOSWindowInfo(windowHandle); - } - - [SuppressUnmanagedCodeSecurity, DllImport("libgdk-3.0.dylib")] - static extern IntPtr gdk_quartz_window_get_nswindow(IntPtr handle); - - [SuppressUnmanagedCodeSecurity, DllImport("libgdk-3.0.dylib")] - static extern IntPtr gdk_quartz_window_get_nsview(IntPtr handle); - - #endregion - - #region X Specific Initialization - - const string UnixLibGdkName = "libgdk-3.so.0"; - - const string UnixLibX11Name = "libX11.so.6"; - const string UnixLibGLName = "libGL.so.1"; - - const int GLX_NONE = 0; - const int GLX_USE_GL = 1; - const int GLX_BUFFER_SIZE = 2; - const int GLX_LEVEL = 3; - const int GLX_RGBA = 4; - const int GLX_DOUBLEBUFFER = 5; - const int GLX_STEREO = 6; - const int GLX_AUX_BUFFERS = 7; - const int GLX_RED_SIZE = 8; - const int GLX_GREEN_SIZE = 9; - const int GLX_BLUE_SIZE = 10; - const int GLX_ALPHA_SIZE = 11; - const int GLX_DEPTH_SIZE = 12; - const int GLX_STENCIL_SIZE = 13; - const int GLX_ACCUM_RED_SIZE = 14; - const int GLX_ACCUM_GREEN_SIZE = 15; - const int GLX_ACCUM_BLUE_SIZE = 16; - const int GLX_ACCUM_ALPHA_SIZE = 17; - - public enum XVisualClass - { - StaticGray = 0, - GrayScale = 1, - StaticColor = 2, - PseudoColor = 3, - TrueColor = 4, - DirectColor = 5, - } - - [StructLayout(LayoutKind.Sequential)] - struct XVisualInfo - { - public IntPtr Visual; - public IntPtr VisualID; - public int Screen; - public int Depth; - public XVisualClass Class; - public long RedMask; - public long GreenMask; - public long blueMask; - public int ColormapSize; - public int BitsPerRgb; - - public override string ToString() - { - return $"id ({VisualID}), screen ({Screen}), depth ({Depth}), class ({Class})"; - } - } - - [Flags] - internal enum XVisualInfoMask - { - No = 0x0, - ID = 0x1, - Screen = 0x2, - Depth = 0x4, - Class = 0x8, - Red = 0x10, - Green = 0x20, - Blue = 0x40, - ColormapSize = 0x80, - BitsPerRGB = 0x100, - All = 0x1FF, - } - - private IWindowInfo InitializeX(GraphicsMode mode) - { - IntPtr display = gdk_x11_display_get_xdisplay(Display.Handle); - int screen = Screen.Number; - - IntPtr windowHandle = gdk_x11_window_get_xid(Window.Handle); - IntPtr rootWindow = gdk_x11_window_get_xid(RootWindow.Handle); - - IntPtr visualInfo; - - if (mode.Index.HasValue) - { - XVisualInfo info = new XVisualInfo(); - info.VisualID = mode.Index.Value; - int dummy; - visualInfo = XGetVisualInfo(display, XVisualInfoMask.ID, ref info, out dummy); - } - else - { - visualInfo = GetVisualInfo(display); - } - - IWindowInfo retval = Utilities.CreateX11WindowInfo(display, screen, windowHandle, rootWindow, visualInfo); - XFree(visualInfo); - - return retval; - } - - private static IntPtr XGetVisualInfo(IntPtr display, XVisualInfoMask vinfo_mask, ref XVisualInfo template, out int nitems) - { - return XGetVisualInfoInternal(display, (IntPtr)(int)vinfo_mask, ref template, out nitems); - } - - private IntPtr GetVisualInfo(IntPtr display) - { - try - { - int[] attributes = AttributeList.ToArray(); - return glXChooseVisual(display, Screen.Number, attributes); - } - catch (DllNotFoundException e) - { - throw new DllNotFoundException("OpenGL dll not found!", e); - } - catch (EntryPointNotFoundException enf) - { - throw new EntryPointNotFoundException("Glx entry point not found!", enf); - } - } - - private List AttributeList - { - get - { - List attributeList = new List(24); - - attributeList.Add(GLX_RGBA); - - if (!SingleBuffer) - attributeList.Add(GLX_DOUBLEBUFFER); - - if (Stereo) - attributeList.Add(GLX_STEREO); - - attributeList.Add(GLX_RED_SIZE); - attributeList.Add(ColorBPP / 4); // TODO support 16-bit - - attributeList.Add(GLX_GREEN_SIZE); - attributeList.Add(ColorBPP / 4); // TODO support 16-bit - - attributeList.Add(GLX_BLUE_SIZE); - attributeList.Add(ColorBPP / 4); // TODO support 16-bit - - attributeList.Add(GLX_ALPHA_SIZE); - attributeList.Add(ColorBPP / 4); // TODO support 16-bit - - attributeList.Add(GLX_DEPTH_SIZE); - attributeList.Add(DepthBPP); - - attributeList.Add(GLX_STENCIL_SIZE); - attributeList.Add(StencilBPP); - - attributeList.Add(GLX_ACCUM_RED_SIZE); - attributeList.Add(AccumulatorBPP / 4);// TODO support 16-bit - - attributeList.Add(GLX_ACCUM_GREEN_SIZE); - attributeList.Add(AccumulatorBPP / 4);// TODO support 16-bit - - attributeList.Add(GLX_ACCUM_BLUE_SIZE); - attributeList.Add(AccumulatorBPP / 4);// TODO support 16-bit - - attributeList.Add(GLX_ACCUM_ALPHA_SIZE); - attributeList.Add(AccumulatorBPP / 4);// TODO support 16-bit - - attributeList.Add(GLX_NONE); - - return attributeList; - } - } - - public IGraphicsContext GraphicsContext { get => _graphicsContext; set => _graphicsContext = value; } - public IWindowInfo WindowInfo { get => _windowInfo; set => _windowInfo = value; } - - [DllImport(UnixLibX11Name, EntryPoint = "XGetVisualInfo")] - private static extern IntPtr XGetVisualInfoInternal(IntPtr display, IntPtr vinfo_mask, ref XVisualInfo template, out int nitems); - - [SuppressUnmanagedCodeSecurity, DllImport(UnixLibX11Name)] - private static extern void XFree(IntPtr handle); - - /// Returns the X resource (window or pixmap) belonging to a GdkDrawable. - /// XID gdk_x11_drawable_get_xid(GdkDrawable *drawable); - /// The GdkDrawable. - /// The ID of drawable's X resource. - [SuppressUnmanagedCodeSecurity, DllImport(UnixLibGdkName)] - private static extern IntPtr gdk_x11_drawable_get_xid(IntPtr gdkDisplay); - - /// Returns the X resource (window or pixmap) belonging to a GdkDrawable. - /// XID gdk_x11_drawable_get_xid(GdkDrawable *drawable); - /// The GdkDrawable. - /// The ID of drawable's X resource. - [SuppressUnmanagedCodeSecurity, DllImport(UnixLibGdkName)] - private static extern IntPtr gdk_x11_window_get_xid(IntPtr gdkDisplay); - - /// Returns the X display of a GdkDisplay. - /// Display* gdk_x11_display_get_xdisplay(GdkDisplay *display); - /// The GdkDrawable. - /// The X Display of the GdkDisplay. - [SuppressUnmanagedCodeSecurity, DllImport(UnixLibGdkName)] - private static extern IntPtr gdk_x11_display_get_xdisplay(IntPtr gdkDisplay); - - [SuppressUnmanagedCodeSecurity, DllImport(UnixLibGLName)] - private static extern IntPtr glXChooseVisual(IntPtr display, int screen, int[] attr); - - #endregion } } \ No newline at end of file diff --git a/GLWidget/GLWidget.csproj b/GLWidget/GLWidget.csproj index 8b59567..c31add6 100644 --- a/GLWidget/GLWidget.csproj +++ b/GLWidget/GLWidget.csproj @@ -1,5 +1,4 @@ - netstandard2.1 true @@ -7,10 +6,9 @@ 1.0.1 https://github.com/Ryujinx/GLWidget - - + + - - + \ No newline at end of file diff --git a/GLWidgetTestGTK3/Data/RGB.cs b/GLWidgetTestGTK3/Data/RGB.cs new file mode 100644 index 0000000..c66d90a --- /dev/null +++ b/GLWidgetTestGTK3/Data/RGB.cs @@ -0,0 +1,49 @@ +// +// Vertex.cs +// +// Author: +// Jarl Gullberg +// +// Copyright (c) 2016 Jarl Gullberg +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +namespace GLWidgetTestGTK3.Data +{ + public class RGB + { + public float R + { + get; set; + } + + public float G + { + get; set; + } + + public float B + { + get; set; + } + + public RGB(float R, float G, float B) + { + this.R = R; + this.G = G; + this.B = B; + } + } +} \ No newline at end of file diff --git a/GLWidgetTestGTK3/Data/Transform.cs b/GLWidgetTestGTK3/Data/Transform.cs new file mode 100644 index 0000000..77e9f6e --- /dev/null +++ b/GLWidgetTestGTK3/Data/Transform.cs @@ -0,0 +1,61 @@ +// +// Transform.cs +// +// Author: +// Jarl Gullberg +// +// Copyright (c) 2016 Jarl Gullberg +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +//using System.Net; + + +using System; +using OpenTK.Mathematics; + +namespace GLWidgetTestGTK3.Data +{ + public class Transform + { + public Vector3 Translation + { + get; + set; + } + + public Quaternion Rotation + { + get; + set; + } + + public Vector3 Scale + { + get; + set; + } + + public Transform(Vector3 Translation) + : this(Translation, Quaternion.FromAxisAngle(Vector3.UnitX, 0.0f), Vector3.One) + { + } + + public Transform(Vector3 Translation, Quaternion Rotation, Vector3 Scale) + { + this.Translation = Translation; + this.Rotation = Rotation; + this.Scale = Scale; + } + } +} \ No newline at end of file diff --git a/GLWidgetTestGTK3/Data/UVCoordinate.cs b/GLWidgetTestGTK3/Data/UVCoordinate.cs new file mode 100644 index 0000000..6690028 --- /dev/null +++ b/GLWidgetTestGTK3/Data/UVCoordinate.cs @@ -0,0 +1,47 @@ +// +// UVCoordinate.cs +// +// Author: +// Jarl Gullberg +// +// Copyright (c) 2016 Jarl Gullberg +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; + +namespace GLWidgetTestGTK3.Data +{ + public sealed class UVCoordinate + { + public float U + { + get; + set; + } + + public float V + { + get; + set; + } + + public UVCoordinate(float U, float V) + { + this.U = U; + this.V = V; + } + } +} \ No newline at end of file diff --git a/GLWidgetTestGTK3/Data/Vertex.cs b/GLWidgetTestGTK3/Data/Vertex.cs new file mode 100644 index 0000000..efcdb60 --- /dev/null +++ b/GLWidgetTestGTK3/Data/Vertex.cs @@ -0,0 +1,92 @@ +// +// Vertex.cs +// +// Author: +// Jarl Gullberg +// +// Copyright (c) 2016 Jarl Gullberg +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using OpenTK.Mathematics; + +namespace GLWidgetTestGTK3.Data +{ + public sealed class Vertex + { + public Vector3 Position + { + get; + private set; + } + + public Vector3 Normal + { + get; + private set; + } + + public UVCoordinate UVCoordinate + { + get; + private set; + } + + public RGB VertexColour + { + get; + private set; + } + + public Vertex(Vector3 Position) + :this(Position, Vector3.Zero, new UVCoordinate(0.0f, 0.0f), new RGB(1.0f, 1.0f, 1.0f)) + { + } + + public Vertex(Vector3 Position, Vector3 Normal) + :this(Position, Normal, new UVCoordinate(0.0f, 0.0f), new RGB(1.0f, 1.0f, 1.0f)) + { + } + + + public Vertex(Vector3 Position, RGB VertexColour) + :this(Position, Vector3.Zero, new UVCoordinate(0.0f, 0.0f), VertexColour) + { + } + + public Vertex(Vector3 Position, Vector3 Normal, RGB VertexColour) + :this(Position, Normal, new UVCoordinate(0.0f, 0.0f), VertexColour) + { + } + + public Vertex(Vector3 Position, UVCoordinate UVCoordinate) + :this(Position, Vector3.Zero, UVCoordinate, new RGB(1.0f, 1.0f, 1.0f)) + { + } + + public Vertex(Vector3 Position, Vector3 Normal, UVCoordinate UVCoordinate) + :this(Position, Normal, UVCoordinate, new RGB(1.0f, 1.0f, 1.0f)) + { + } + + public Vertex(Vector3 Position, Vector3 Normal, UVCoordinate UVCoordinate, RGB VertexColour) + { + this.Position = Position; + this.Normal = Normal; + this.UVCoordinate = UVCoordinate; + this.VertexColour = VertexColour; + } + } +} \ No newline at end of file diff --git a/GLWidgetTestGTK3/Extensions/ExtensionMethods.cs b/GLWidgetTestGTK3/Extensions/ExtensionMethods.cs new file mode 100644 index 0000000..2d49382 --- /dev/null +++ b/GLWidgetTestGTK3/Extensions/ExtensionMethods.cs @@ -0,0 +1,41 @@ +// +// ExtensionMethods.cs +// +// Author: +// Jarl Gullberg +// +// Copyright (c) 2016 Jarl Gullberg +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using OpenTK.Mathematics; + +namespace GLWidgetTestGTK3.Extensions +{ + public static class ExtensionMethods + { + public static Quaternion QuaternionFromEuler(float Yaw, float Pitch, float Roll) + { + Quaternion XRotation = Quaternion.FromAxisAngle(Vector3.UnitX, Yaw); + Quaternion YRotation = Quaternion.FromAxisAngle(Vector3.UnitY, Pitch); + Quaternion ZRotation = Quaternion.FromAxisAngle(Vector3.UnitZ, Roll); + + Quaternion final = Quaternion.Multiply(ZRotation, YRotation); + final = Quaternion.Multiply(XRotation, final); + + return final; + } + } +} \ No newline at end of file diff --git a/GLWidgetTestGTK3/GLWidgetTestGTK3.csproj b/GLWidgetTestGTK3/GLWidgetTestGTK3.csproj new file mode 100644 index 0000000..60dcc92 --- /dev/null +++ b/GLWidgetTestGTK3/GLWidgetTestGTK3.csproj @@ -0,0 +1,39 @@ + + + netcoreapp3.1 + 8.0 + win-x64;osx-x64;linux-x64 + Exe + true + Debug;Release + GLWidgetTestGTK3 + GLWidgetTestGTK3.MainClass + GLWidgetTestGTK3 + 1.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/GLWidgetTestGTK3/GTKBindingContext.cs b/GLWidgetTestGTK3/GTKBindingContext.cs new file mode 100644 index 0000000..5775da8 --- /dev/null +++ b/GLWidgetTestGTK3/GTKBindingContext.cs @@ -0,0 +1,140 @@ +using System; +using OpenTK; +using OpenGL; +using System.Runtime.InteropServices; +using Khronos; +using System.Collections.Generic; + +namespace GLWidgetTestGTK3 +{ + public class GTKBindingContext : IBindingsContext + { + private static bool _loaded; + + private const string GlxLibrary = "libGL.so.1"; + private const string WglLibrary = "opengl32.dll"; + private const string OSXLibrary = "libdl.dylib"; + + + /// + /// Currently loaded libraries. + /// + private static readonly Dictionary _LibraryHandles = new Dictionary(); + + public IntPtr GetProcAddress(string procName) + { + switch (Platform.CurrentPlatformId) + { + case Platform.Id.WindowsNT: + return GetProcAddressWgl(procName); + case Platform.Id.Linux: + return GetProcAddressGlx(procName); + case Platform.Id.MacOS: + return !Glx.IsRequired ? GetProcAddressOSX(procName) : GetProcAddressGlx(procName); + default: + throw new NotSupportedException(); + } + } + + private static IntPtr GetProcAddressWgl(string function) + { + return UnsafeNativeMethods.wglGetProcAddress(function); + } + + private static void LoadLibraries() + { + if (_loaded) + { + return; + } + + string function = "glXGetProcAddress"; + + IntPtr handle = GetLibraryHandle(GlxLibrary, true); + + if (handle == IntPtr.Zero) + throw new ArgumentNullException(nameof(handle)); + + IntPtr functionPtr = UnsafeNativeMethods.dlsym(handle, function); + + if (functionPtr != IntPtr.Zero) + Delegates.pglXGetProcAddress = (Delegates.glXGetProcAddress)Marshal.GetDelegateForFunctionPointer(functionPtr, typeof(Delegates.glXGetProcAddress)); + + _loaded = true; + } + + internal static IntPtr GetLibraryHandle(string libraryPath, bool throws) + { + IntPtr libraryHandle; + + if (_LibraryHandles.TryGetValue(libraryPath, out libraryHandle) == false) + { + if ((libraryHandle = UnsafeNativeMethods.dlopen(libraryPath, UnsafeNativeMethods.RTLD_LAZY)) == IntPtr.Zero) + { + if (throws) + throw new InvalidOperationException($"unable to load library at {libraryPath}", new InvalidOperationException(UnsafeNativeMethods.dlerror())); + } + + _LibraryHandles.Add(libraryPath, libraryHandle); + } + + return libraryHandle; + } + + private static IntPtr GetProcAddressGlx(string function) + { + LoadLibraries(); + + return Delegates.pglXGetProcAddress != null ? Delegates.pglXGetProcAddress(function) : IntPtr.Zero; + } + + private static IntPtr GetProcAddressOSX(string function) + { + string fname = "_" + function; + if (!UnsafeNativeMethods.NSIsSymbolNameDefined(fname)) + return IntPtr.Zero; + + IntPtr symbol = UnsafeNativeMethods.NSLookupAndBindSymbol(fname); + + if (symbol != IntPtr.Zero) + symbol = UnsafeNativeMethods.NSAddressOfSymbol(symbol); + + return symbol; + } + + private static class UnsafeNativeMethods + { + [DllImport(WglLibrary, EntryPoint = "wglGetProcAddress", ExactSpelling = true, SetLastError = true)] + public static extern IntPtr wglGetProcAddress(string lpszProc); + + [DllImport(OSXLibrary, EntryPoint = "NSIsSymbolNameDefined")] + public static extern bool NSIsSymbolNameDefined(string s); + + [DllImport(OSXLibrary, EntryPoint = "NSLookupAndBindSymbol")] + public static extern IntPtr NSLookupAndBindSymbol(string s); + + [DllImport(OSXLibrary, EntryPoint = "NSAddressOfSymbol")] + public static extern IntPtr NSAddressOfSymbol(IntPtr symbol); + + public const int RTLD_LAZY = 1; + + public const int RTLD_NOW = 2; + + [DllImport("dl")] + public static extern IntPtr dlopen(string filename, int flags); + + [DllImport("dl")] + public static extern IntPtr dlsym(IntPtr handle, string symbol); + + [DllImport("dl")] + public static extern string dlerror(); + } + + private static class Delegates + { + public delegate IntPtr glXGetProcAddress(string procName); + + public static glXGetProcAddress pglXGetProcAddress; + } + } +} \ No newline at end of file diff --git a/GLWidgetTestGTK3/Geometry/Shapes.cs b/GLWidgetTestGTK3/Geometry/Shapes.cs new file mode 100644 index 0000000..d544806 --- /dev/null +++ b/GLWidgetTestGTK3/Geometry/Shapes.cs @@ -0,0 +1,76 @@ +// +// Mesh.cs +// +// Author: +// Jarl Gullberg +// +// Copyright (c) 2016 Jarl Gullberg +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; + +namespace GLWidgetTestGTK3.Debug +{ + public static class Shapes + { + public static readonly float[] UnindexedTriangle = + { + -1.0f, -1.0f, 0.0f, + 1.0f, -1.0f, 0.0f, + 0.0f, 1.0f, 0.0f + }; + + public static readonly float[] UnindexedCube = + { + -1.0f,-1.0f,-1.0f, + -1.0f,-1.0f, 1.0f, + -1.0f, 1.0f, 1.0f, + 1.0f, 1.0f,-1.0f, + -1.0f,-1.0f,-1.0f, + -1.0f, 1.0f,-1.0f, + 1.0f,-1.0f, 1.0f, + -1.0f,-1.0f,-1.0f, + 1.0f,-1.0f,-1.0f, + 1.0f, 1.0f,-1.0f, + 1.0f,-1.0f,-1.0f, + -1.0f,-1.0f,-1.0f, + -1.0f,-1.0f,-1.0f, + -1.0f, 1.0f, 1.0f, + -1.0f, 1.0f,-1.0f, + 1.0f,-1.0f, 1.0f, + -1.0f,-1.0f, 1.0f, + -1.0f,-1.0f,-1.0f, + -1.0f, 1.0f, 1.0f, + -1.0f,-1.0f, 1.0f, + 1.0f,-1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f,-1.0f,-1.0f, + 1.0f, 1.0f,-1.0f, + 1.0f,-1.0f,-1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f,-1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f,-1.0f, + -1.0f, 1.0f,-1.0f, + 1.0f, 1.0f, 1.0f, + -1.0f, 1.0f,-1.0f, + -1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + -1.0f, 1.0f, 1.0f, + 1.0f,-1.0f, 1.0f + }; + } +} \ No newline at end of file diff --git a/GLWidgetTestGTK3/MainWindow.cs b/GLWidgetTestGTK3/MainWindow.cs new file mode 100644 index 0000000..176ccba --- /dev/null +++ b/GLWidgetTestGTK3/MainWindow.cs @@ -0,0 +1,578 @@ +// +// MainWindow.cs +// +// Author: +// Jarl Gullberg +// +// Copyright (c) 2016 Jarl Gullberg +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using Gdk; +using GLWidgetTestGTK3.Data; +using GLWidgetTestGTK3.Debug; +using GLWidgetTestGTK3.World; +using Gtk; +using UI = Gtk.Builder.ObjectAttribute; +using OpenTK; +using OpenTK.Mathematics; +using OpenTK.Graphics.OpenGL; +using KeyPressEventArgs = Gtk.KeyPressEventArgs; + +namespace GLWidgetTestGTK3 +{ + public partial class MainWindow : Gtk.Window + { + [UI] readonly Box MainBox; + [UI] readonly MenuBar MainMenuBar; + + [UI] readonly Alignment GLWidgetAlignment; + [UI] readonly GLWidget MainGLWidget; + + private bool GLInit; + + private Scene Scene; + + private uint VertexArrayID; + private uint ColourBufferID; + + private int ShaderProgramID; + + /* Default camera positions */ + private Vector3 cameraPosition; + private Vector3 cameraLookDirection; + private Vector3 cameraRightVector; + + private Vector3 cameraUpVector; + private float horizontalViewAngle; + private float verticalViewAngle; + private const float defaultFOV = 45.0f; + + private const float defaultMovementSpeed = 10.0f; + private const float defaultCameraSpeed = 0.5f; + + // Other variables + private bool wantsToMove = false; + private float deltaTime; + + private int mouseXLastFrame; + private int mouseYLastFrame; + + private float rightAxis; + private float forwardAxis; + + private static readonly float[] cubeColourBufferData = + { + 0.583f, 0.771f, 0.014f, + 0.609f, 0.115f, 0.436f, + 0.327f, 0.483f, 0.844f, + 0.822f, 0.569f, 0.201f, + 0.435f, 0.602f, 0.223f, + 0.310f, 0.747f, 0.185f, + 0.597f, 0.770f, 0.761f, + 0.559f, 0.436f, 0.730f, + 0.359f, 0.583f, 0.152f, + 0.483f, 0.596f, 0.789f, + 0.559f, 0.861f, 0.639f, + 0.195f, 0.548f, 0.859f, + 0.014f, 0.184f, 0.576f, + 0.771f, 0.328f, 0.970f, + 0.406f, 0.615f, 0.116f, + 0.676f, 0.977f, 0.133f, + 0.971f, 0.572f, 0.833f, + 0.140f, 0.616f, 0.489f, + 0.997f, 0.513f, 0.064f, + 0.945f, 0.719f, 0.592f, + 0.543f, 0.021f, 0.978f, + 0.279f, 0.317f, 0.505f, + 0.167f, 0.620f, 0.077f, + 0.347f, 0.857f, 0.137f, + 0.055f, 0.953f, 0.042f, + 0.714f, 0.505f, 0.345f, + 0.783f, 0.290f, 0.734f, + 0.722f, 0.645f, 0.174f, + 0.302f, 0.455f, 0.848f, + 0.225f, 0.587f, 0.040f, + 0.517f, 0.713f, 0.338f, + 0.053f, 0.959f, 0.120f, + 0.393f, 0.621f, 0.362f, + 0.673f, 0.211f, 0.457f, + 0.820f, 0.883f, 0.371f, + 0.982f, 0.099f, 0.879f + }; + + public static MainWindow Create() + { + Builder builder = new Builder(null, "GLWidgetTestGTK3.interfaces.MainWindow.glade", null); + return new MainWindow(builder, builder.GetObject("MainWindow").Handle); + } + + protected MainWindow(Builder builder, IntPtr handle) + : base(handle) + { + builder.Autoconnect(this); + DeleteEvent += OnDeleteEvent; + + this.GLInit = false; + ResetCamera(); + + this.MainGLWidget = new GLWidget() + { + GLVersionMajor = 3, + GLVersionMinor = 3, + }; + + this.MainGLWidget.Events |= + EventMask.ButtonPressMask | + EventMask.ButtonReleaseMask | + EventMask.KeyPressMask | + EventMask.KeyReleaseMask; + + this.MainGLWidget.Initialized += OnViewportInitialized; + this.MainGLWidget.ButtonPressEvent += OnViewportButtonPressed; + this.MainGLWidget.ButtonReleaseEvent += OnViewportButtonReleased; + this.MainGLWidget.KeyPressEvent += OnViewportKeyPressed; + this.MainGLWidget.KeyReleaseEvent += OnViewportKeyReleased; + + // Add the GL widget to the UI + this.GLWidgetAlignment.Add(this.MainGLWidget); + this.GLWidgetAlignment.ShowAll(); + + } + + private List FloatArrayToVertexList(float[] vertexPositions) + { + if ((vertexPositions.Length % 3) != 0) + { + throw new ArgumentException("The input array must be of a 3-multiple length. Incomplete entries are not allowed.", nameof(vertexPositions)); + } + + List convertedVertices = new List(); + + for (int i = 0; i < vertexPositions.Length; i += 3) + { + Vector3 Position = new Vector3(vertexPositions[i], vertexPositions[i + 1], vertexPositions[i + 2]); + Vertex vertex = new Vertex(Position); + + convertedVertices.Add(vertex); + } + + return convertedVertices; + } + + private void OnViewportKeyReleased(object o, KeyReleaseEventArgs args) + { + if (args.Event.Type == EventType.KeyRelease) + { + if( args.Event.Key == Gdk.Key.w || args.Event.Key == Gdk.Key.W) + { + forwardAxis = 0.0f; + } + else if( args.Event.Key == Gdk.Key.s || args.Event.Key == Gdk.Key.S) + { + forwardAxis = 0.0f; + } + + if( args.Event.Key == Gdk.Key.d || args.Event.Key == Gdk.Key.D) + { + rightAxis = 0.0f; + } + else if( args.Event.Key == Gdk.Key.a || args.Event.Key == Gdk.Key.A) + { + rightAxis = 0.0f; + } + } + } + + private void OnViewportKeyPressed(object o, KeyPressEventArgs args) + { + if (args.Event.Type == EventType.KeyPress) + { + if( args.Event.Key == Gdk.Key.w || args.Event.Key == Gdk.Key.W) + { + forwardAxis = 1.0f; + } + else if( args.Event.Key == Gdk.Key.s || args.Event.Key == Gdk.Key.S) + { + forwardAxis = -1.0f; + } + + if( args.Event.Key == Gdk.Key.d || args.Event.Key == Gdk.Key.D) + { + rightAxis = 1.0f; + } + else if( args.Event.Key == Gdk.Key.a || args.Event.Key == Gdk.Key.A) + { + rightAxis = -1.0f; + } + + if( args.Event.Key == Gdk.Key.r || args.Event.Key == Gdk.Key.R) + { + if (wantsToMove) + { + ResetCamera(); + } + } + + if (args.Event.Key == Gdk.Key.Escape) + { + Application.Quit(); + } + } + } + + private void ResetCamera() + { + this.cameraPosition = new Vector3(0.0f, 0.0f, 5.0f); + this.horizontalViewAngle = MathHelper.DegreesToRadians(180.0f); + this.verticalViewAngle = MathHelper.DegreesToRadians(0.0f); + + this.cameraLookDirection = new Vector3( + (float)(Math.Cos(this.verticalViewAngle) * Math.Sin(this.horizontalViewAngle)), + (float)Math.Sin(this.verticalViewAngle), + (float)(Math.Cos(this.verticalViewAngle) * Math.Cos(this.horizontalViewAngle))); + + this.cameraRightVector = new Vector3( + (float)Math.Sin(horizontalViewAngle - MathHelper.PiOver2), + 0, + (float)Math.Cos(horizontalViewAngle - MathHelper.PiOver2)); + + this.cameraUpVector = Vector3.Cross(cameraRightVector, cameraLookDirection); + } + + [GLib.ConnectBefore] + private void OnViewportButtonReleased(object o, ButtonReleaseEventArgs args) + { + // Right click is released + if (args.Event.Type == EventType.ButtonRelease && args.Event.Button == 3) + { + // Return the mouse pointer + this.Window.Cursor = new Cursor(CursorType.Arrow); + + this.GrabFocus(); + this.wantsToMove = false; + } + } + + [GLib.ConnectBefore] + private void OnViewportButtonPressed(object o, ButtonPressEventArgs args) + { + // Right click is pressed + if (args.Event.Type == EventType.ButtonPress && args.Event.Button == 3) + { + // Hide the mouse pointer + this.Window.Cursor = new Cursor(CursorType.BlankCursor); + + this.MainGLWidget.GrabFocus(); + this.wantsToMove = true; + this.MainGLWidget.GetPointer(out this.mouseXLastFrame, out this.mouseYLastFrame); + } + } + + protected virtual void OnViewportInitialized(object sender, EventArgs e) + { + var version = GL.GetString(StringName.Version); + + this.Scene = new Scene(); + + // Create the cube actor + Actor cubeActor = new Actor(new Mesh(FloatArrayToVertexList(Shapes.UnindexedCube))); + Actor cubeActor1 = new Actor(new Mesh(FloatArrayToVertexList(Shapes.UnindexedCube))); + Actor cubeActor2 = new Actor(new Mesh(FloatArrayToVertexList(Shapes.UnindexedCube))); + Actor cubeActor3 = new Actor(new Mesh(FloatArrayToVertexList(Shapes.UnindexedCube))); + + // Translate the cube actor + cubeActor.Transform.Translation = new Vector3(4.0f, 0.0f, 0.0f); + cubeActor1.Transform.Translation = new Vector3(0.0f, 4.0f, 0.0f); + cubeActor2.Transform.Translation = new Vector3(0.0f, -4.0f, 0.0f); + cubeActor3.Transform.Translation = new Vector3(-4.0f, 0.0f, 0.0f); + + Actor triangleActor = new Actor(new Mesh(FloatArrayToVertexList(Shapes.UnindexedTriangle))); + + this.Scene.Actors.Add(cubeActor); + this.Scene.Actors.Add(cubeActor1); + this.Scene.Actors.Add(cubeActor2); + this.Scene.Actors.Add(cubeActor3); + this.Scene.Actors.Add(triangleActor); + + // Generate the colour buffer + GL.GenBuffers(1, out ColourBufferID); + + // Upload the colour data + GL.BindBuffer(BufferTarget.ArrayBuffer, ColourBufferID); + GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(cubeColourBufferData.Length * sizeof(float)), cubeColourBufferData, BufferUsageHint.StaticDraw); + + // Make sure we use the depth buffer when drawing + GL.Enable(EnableCap.DepthTest); + //GL.DepthFunc(DepthFunction.Less); + + // Enable backface culling for performance reasons + //GL.Enable(EnableCap.CullFace); + + // Render wireframe + //GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line); + + // Initialize the viewport + int widgetWidth = this.GLWidgetAlignment.AllocatedWidth; + int widgetHeight = this.GLWidgetAlignment.AllocatedHeight; + + GL.Viewport(0, 0, widgetWidth, widgetHeight); + GL.ClearColor(0.522f, 0.573f, 0.678f, 1.0f); + GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); + + // Load the shaders + ShaderProgramID = LoadShaders(); + + // Add idle event handler to process rendering whenever and as long as time is available. + GLInit = true; + GLib.Idle.Add(OnIdleProcessMain); + } + + protected void RenderFrame() + { + MainGLWidget.MakeCurrent(); + + // Make sure the viewport is accurate for the current widget size on screen + int widgetWidth = this.GLWidgetAlignment.AllocatedWidth; + int widgetHeight = this.GLWidgetAlignment.AllocatedHeight; + var error = GL.GetError(); + GL.Viewport(0, 0, widgetWidth, widgetHeight); + GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); + + // Activate the shaders + GL.UseProgram(ShaderProgramID); + + // See if there's any movement to compute + if (wantsToMove) + { + int mouseX; + int mouseY; + this.MainGLWidget.GetPointer(out mouseX, out mouseY); + + this.horizontalViewAngle += defaultCameraSpeed * this.deltaTime * (float)(mouseXLastFrame - mouseX); + this.verticalViewAngle += defaultCameraSpeed * this.deltaTime * (float)(mouseYLastFrame - mouseY); + + if (verticalViewAngle > MathHelper.DegreesToRadians(90.0f)) + { + verticalViewAngle = MathHelper.DegreesToRadians(90.0f); + } + else if (verticalViewAngle < MathHelper.DegreesToRadians(-90.0f)) + { + verticalViewAngle = MathHelper.DegreesToRadians(-90.0f); + } + + mouseXLastFrame = mouseX; + mouseYLastFrame = mouseY; + + // Compute the look direction + this.cameraLookDirection = new Vector3( + (float)(Math.Cos(this.verticalViewAngle) * Math.Sin(this.horizontalViewAngle)), + (float)Math.Sin(this.verticalViewAngle), + (float)(Math.Cos(this.verticalViewAngle) * Math.Cos(this.horizontalViewAngle))); + + this.cameraRightVector = new Vector3( + (float)Math.Sin(this.horizontalViewAngle - MathHelper.PiOver2), + 0, + (float)Math.Cos(this.horizontalViewAngle - MathHelper.PiOver2)); + + this.cameraUpVector = Vector3.Cross(this.cameraRightVector, this.cameraLookDirection); + + // Perform any movement + if (forwardAxis > 0) + { + this.cameraPosition += this.cameraLookDirection * deltaTime * defaultMovementSpeed; + } + + if (forwardAxis < 0) + { + this.cameraPosition -= this.cameraLookDirection * deltaTime * defaultMovementSpeed; + } + + if (rightAxis > 0) + { + this.cameraPosition += this.cameraRightVector * deltaTime * defaultMovementSpeed; + } + + if (rightAxis < 0) + { + this.cameraPosition -= this.cameraRightVector * deltaTime * defaultMovementSpeed; + } + } + + // Calculate the relative viewpoint + float aspectRatio = (float)widgetWidth / (float)widgetHeight; + Matrix4 Projection = Matrix4.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(defaultFOV), aspectRatio, 0.1f, 1000.0f); + Matrix4 View = Matrix4.LookAt( + cameraPosition, + cameraPosition + cameraLookDirection, + cameraUpVector + ); + + // Enable the colour array + GL.EnableVertexAttribArray(1); + GL.BindBuffer(BufferTarget.ArrayBuffer, ColourBufferID); + GL.VertexAttribPointer( + 1, + 3, + VertexAttribPointerType.Float, + false, + 0, + 0); + + // Tick the actors before any rendering is done + this.Scene.Tick(deltaTime); + + foreach (Actor actor in this.Scene.Actors) + { + actor.Render(ShaderProgramID, View, Projection); + } + + // Release the arrays + GL.DisableVertexAttribArray(1); + + //swap + MainGLWidget.Swapbuffers(); + } + + protected bool OnIdleProcessMain() + { + if (!GLInit) + return false; + else + { + // Start deltaTime calculation + Stopwatch deltaTimeWatcher = new Stopwatch(); + deltaTimeWatcher.Start(); + + System.Threading.Tasks.Task.Run(RenderFrame).Wait(); + + //RenderFrame(); + + MainGLWidget.ClearCurrent(); + + // End delta time calculation + deltaTimeWatcher.Stop(); + this.deltaTime = (float)(deltaTimeWatcher.ElapsedMilliseconds * 0.001f); + return true; + } + } + + private int LoadShaders() + { + int VertexShaderID = GL.CreateShader(ShaderType.VertexShader); + int FragmentShaderID = GL.CreateShader(ShaderType.FragmentShader); + + string vertexShaderSourceCode; + using (Stream shaderStream = + Assembly.GetExecutingAssembly().GetManifestResourceStream("GLWidgetTestGTK3.Shaders.VertexShader.glsl")) + { + using (StreamReader sr = new StreamReader(shaderStream)) + { + vertexShaderSourceCode = sr.ReadToEnd(); + } + } + + string fragmentShaderSourceCode; + using (Stream shaderStream = + Assembly.GetExecutingAssembly().GetManifestResourceStream("GLWidgetTestGTK3.Shaders.FragmentShader.glsl")) + { + using (StreamReader sr = new StreamReader(shaderStream)) + { + fragmentShaderSourceCode = sr.ReadToEnd(); + } + } + + + int result = 0; + int compilationLogLength; + + Console.WriteLine("Compiling vertex shader..."); + GL.ShaderSource(VertexShaderID, vertexShaderSourceCode); + GL.CompileShader(VertexShaderID); + + GL.GetShader(VertexShaderID, ShaderParameter.CompileStatus, out result); + GL.GetShader(VertexShaderID, ShaderParameter.InfoLogLength, out compilationLogLength); + + if (compilationLogLength > 0) + { + string compilationLog; + GL.GetShaderInfoLog(VertexShaderID, out compilationLog); + + Console.WriteLine(compilationLog); + } + + Console.WriteLine("Compiling fragment shader..."); + GL.ShaderSource(FragmentShaderID, fragmentShaderSourceCode); + GL.CompileShader(FragmentShaderID); + + GL.GetShader(FragmentShaderID, ShaderParameter.CompileStatus, out result); + GL.GetShader(FragmentShaderID, ShaderParameter.InfoLogLength, out compilationLogLength); + + if (compilationLogLength > 0) + { + string compilationLog; + GL.GetShaderInfoLog(FragmentShaderID, out compilationLog); + + Console.WriteLine(compilationLog); + } + + + Console.WriteLine("Linking shader program..."); + int shaderProgramID = GL.CreateProgram(); + + GL.AttachShader(shaderProgramID, VertexShaderID); + GL.AttachShader(shaderProgramID, FragmentShaderID); + GL.LinkProgram(shaderProgramID); + + GL.GetProgram(shaderProgramID, ProgramParameter.LinkStatus, out result); + GL.GetProgram(shaderProgramID, ProgramParameter.InfoLogLength, out compilationLogLength); + + if (compilationLogLength > 0) + { + string compilationLog; + GL.GetProgramInfoLog(shaderProgramID, out compilationLog); + + Console.WriteLine(compilationLog); + } + + // Clean up the shader source code and unlinked object files from graphics memory + GL.DetachShader(shaderProgramID, VertexShaderID); + GL.DetachShader(shaderProgramID, FragmentShaderID); + + GL.DeleteShader(VertexShaderID); + GL.DeleteShader(FragmentShaderID); + + return shaderProgramID; + } + + /// + /// Handles application shutdown procedures - terminating render threads, cleaning + /// up the UI, etc. + /// + /// Sender. + /// The deletion arguments. + protected void OnDeleteEvent(object sender, DeleteEventArgs a) + { + Application.Quit(); + a.RetVal = true; + } + } +} + diff --git a/GLWidgetTestGTK3/Program.cs b/GLWidgetTestGTK3/Program.cs new file mode 100644 index 0000000..dc9e588 --- /dev/null +++ b/GLWidgetTestGTK3/Program.cs @@ -0,0 +1,61 @@ +using System; +using System.Reflection; +using Gtk; +using OpenTK; + +namespace GLWidgetTestGTK3 +{ + class MainClass + { + public static void Main(string[] args) + { + InitializeGlBindings(); + + // GTK + Application.Init(); + MainWindow win = MainWindow.Create(); + win.Show(); + Application.Run(); + } + + private static void InitializeGlBindings() + { + // We don't put a hard dependency on OpenTK.Graphics here. + // So we need to use reflection to initialize the GL bindings, so users don't have to. + + // Try to load OpenTK.Graphics assembly. + Assembly assembly; + try + { + assembly = Assembly.Load("OpenTK.Graphics"); + } + catch + { + // Failed to load graphics, oh well. + // Up to the user I guess? + // TODO: Should we expose this load failure to the user better? + return; + } + + var provider = new GTKBindingContext(); + + void LoadBindings(string typeNamespace) + { + var type = assembly.GetType($"OpenTK.Graphics.{typeNamespace}.GL"); + if (type == null) + { + return; + } + + var load = type.GetMethod("LoadBindings"); + load.Invoke(null, new object[] { provider }); + } + + LoadBindings("ES11"); + LoadBindings("ES20"); + LoadBindings("ES30"); + LoadBindings("OpenGL"); + LoadBindings("OpenGL4"); + } + } +} \ No newline at end of file diff --git a/GLWidgetTestGTK3/Shaders/FragmentShader.glsl b/GLWidgetTestGTK3/Shaders/FragmentShader.glsl new file mode 100644 index 0000000..8569e51 --- /dev/null +++ b/GLWidgetTestGTK3/Shaders/FragmentShader.glsl @@ -0,0 +1,12 @@ +#version 330 core + +in vec3 fragmentColour; + +out vec3 color; + +void main() +{ + //color = vec3(0.18, 0.204, 0.212); + color = fragmentColour; + //color = vec3(gl_FragCoord.z); +} \ No newline at end of file diff --git a/GLWidgetTestGTK3/Shaders/VertexShader.glsl b/GLWidgetTestGTK3/Shaders/VertexShader.glsl new file mode 100644 index 0000000..8ed2a72 --- /dev/null +++ b/GLWidgetTestGTK3/Shaders/VertexShader.glsl @@ -0,0 +1,14 @@ +#version 330 core +layout(location = 0) in vec3 vertexPosition_modelSpace; +layout(location = 1) in vec3 vertexColour; + +uniform mat4 ModelViewProjection; + +out vec3 fragmentColour; + +void main() +{ + gl_Position = ModelViewProjection * vec4(vertexPosition_modelSpace, 1.0); + + fragmentColour = vertexColour; +} \ No newline at end of file diff --git a/GLWidgetTestGTK3/World/Actor.cs b/GLWidgetTestGTK3/World/Actor.cs new file mode 100644 index 0000000..7657bf6 --- /dev/null +++ b/GLWidgetTestGTK3/World/Actor.cs @@ -0,0 +1,104 @@ +// +// Actor.cs +// +// Author: +// Jarl Gullberg +// +// Copyright (c) 2016 Jarl Gullberg +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; +using GLib; +using GLWidgetTestGTK3.Data; +using OpenTK.Mathematics; +using OpenTK.Graphics.OpenGL; + +namespace GLWidgetTestGTK3.World +{ + public class Actor + { + public Transform Transform; + private readonly Mesh Mesh; + + /// + /// Creates a new instance of the class. + /// + /// The mesh bound to the actor. + public Actor(Mesh Mesh) + { + this.Mesh = Mesh; + this.Transform = new Transform(new Vector3(0.0f, 0.0f, 0.0f)); + } + + /// + /// Performs any arbitrary actions needed every frame, such as animations, texture manipulations or state + /// updates. + /// + /// The time (in thousands of a second) taken to render the previous frame. + public void Tick(float deltaTime) + { + + } + + /// + /// Renders this actor within the current OpenGL context. + /// + /// The ID of the currently active shader. + /// The camera view matrix, calulcated from the camera position. + /// The perspective projection currently in use. + public void Render(int ShaderProgramID, Matrix4 ViewMatrix, Matrix4 ProjectionMatrix) + { + // Enable the mesh vertex array + GL.BindBuffer(BufferTarget.ArrayBuffer, Mesh.VertexBufferID); + + GL.EnableVertexAttribArray(0); + GL.VertexAttribPointer( + 0, + 3, + VertexAttribPointerType.Float, + false, + 0, + 0); + + // Enable the normal attributes + GL.EnableVertexAttribArray(3); + GL.VertexAttribPointer( + 2, + 3, + VertexAttribPointerType.Float, + false, + 0, + 0); + + Matrix4 modelTranslation = Matrix4.CreateTranslation(Transform.Translation); + Matrix4 modelScale = Matrix4.CreateScale(Transform.Scale); + Matrix4 modelRotation = Matrix4.CreateFromQuaternion(Transform.Rotation); + + Matrix4 modelViewProjection = modelScale * modelRotation * modelTranslation * ViewMatrix * ProjectionMatrix; + + // Send the model matrix to the shader + int projectionShaderVariableHandle = GL.GetUniformLocation(ShaderProgramID, "ModelViewProjection"); + GL.UniformMatrix4(projectionShaderVariableHandle, false, ref modelViewProjection); + + // Draw the model + GL.DrawArrays(BeginMode.Triangles, 0, Mesh.GetVertexCount()); + + // Release the attribute arrays + GL.DisableVertexAttribArray(0); + GL.DisableVertexAttribArray(3); + } + } +} \ No newline at end of file diff --git a/GLWidgetTestGTK3/World/Mesh.cs b/GLWidgetTestGTK3/World/Mesh.cs new file mode 100644 index 0000000..9a61c7c --- /dev/null +++ b/GLWidgetTestGTK3/World/Mesh.cs @@ -0,0 +1,135 @@ +// +// Mesh.cs +// +// Author: +// Jarl Gullberg +// +// Copyright (c) 2016 Jarl Gullberg +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; +using System.Collections.Generic; +using GLWidgetTestGTK3.Data; +using OpenTK.Graphics.OpenGL; + +namespace GLWidgetTestGTK3.World +{ + public class Mesh + { + private List Vertices; + + private int vertexBufferID = -1; + public int VertexBufferID + { + get { return vertexBufferID; } + } + + private int normalBufferID = -1; + public int NormalBufferID + { + get { return normalBufferID; } + } + + private bool cullFaces; + + public bool CullFaces + { + get; + set; + } + + public Mesh(List Vertices) + { + this.Vertices = Vertices; + + this.vertexBufferID = UploadVertexPositions(); + this.normalBufferID = UploadVertexNormals(); + } + + public int GetVertexCount() + { + return Vertices.Count; + } + + private int UploadVertexPositions() + { + if (vertexBufferID > 0) + { + return vertexBufferID; + } + + // Generate a buffer + GL.GenBuffers(1, out vertexBufferID); + + // Get the vertex positions + float[] vertexPositions = GetVertexPositions(); + + // Upload the vertices to the GPU + GL.BindBuffer(BufferTarget.ArrayBuffer, vertexBufferID); + GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vertexPositions.Length * sizeof(float)), vertexPositions, BufferUsageHint.StaticDraw); + + return vertexBufferID; + } + + private int UploadVertexNormals() + { + if (normalBufferID > 0) + { + return normalBufferID; + } + + // Generate a buffer + GL.GenBuffers(1, out normalBufferID); + + // Get the vertex positions + float[] vertexNormals = GetVertexNormals(); + + // Upload the vertices to the GPU + GL.BindBuffer(BufferTarget.ArrayBuffer, normalBufferID); + GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vertexNormals.Length * sizeof(float)), vertexNormals, BufferUsageHint.StaticDraw); + + return vertexBufferID; + } + + private float[] GetVertexPositions() + { + List vertexPositions = new List(); + + foreach (Vertex StoredVertex in Vertices) + { + vertexPositions.Add(StoredVertex.Position.X); + vertexPositions.Add(StoredVertex.Position.Y); + vertexPositions.Add(StoredVertex.Position.Z); + } + + return vertexPositions.ToArray(); + } + + private float[] GetVertexNormals() + { + List vertexNormals = new List(); + + foreach (Vertex StoredVertex in Vertices) + { + vertexNormals.Add(StoredVertex.Normal.X); + vertexNormals.Add(StoredVertex.Normal.Y); + vertexNormals.Add(StoredVertex.Normal.Z); + } + + return vertexNormals.ToArray(); + } + } +} \ No newline at end of file diff --git a/GLWidgetTestGTK3/World/Scene.cs b/GLWidgetTestGTK3/World/Scene.cs new file mode 100644 index 0000000..e5afbac --- /dev/null +++ b/GLWidgetTestGTK3/World/Scene.cs @@ -0,0 +1,60 @@ +// +// Scene.cs +// +// Author: +// Jarl Gullberg +// +// Copyright (c) 2016 Jarl Gullberg +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +using System; +using System.Collections.Generic; +using OpenTK.Graphics.OpenGL; + +namespace GLWidgetTestGTK3.World +{ + public class Scene + { + public readonly List Actors = new List(); + + private readonly int vertexArrayID = -1; + public int VertexArrayID + { + get { return vertexArrayID; } + } + + public Scene() + { + // Generate the vertex array + GL.GenVertexArrays(1, out vertexArrayID); + GL.BindVertexArray(VertexArrayID); + } + + /// + /// Runs the function on all instances + /// in the scene. Actors can define arbitrary behaviour in their ticks, but in most + /// cases it's used for animation. + /// + /// The time (in thousands of a second) taken to render the previous frame. + public void Tick(float deltaTime) + { + for (int i = 0; i < Actors.Count; ++i) + { + Actors[i].Tick(deltaTime); + } + } + } +} \ No newline at end of file diff --git a/GLWidgetTestGTK3/interfaces/MainWindow.glade b/GLWidgetTestGTK3/interfaces/MainWindow.glade new file mode 100644 index 0000000..f2cab33 --- /dev/null +++ b/GLWidgetTestGTK3/interfaces/MainWindow.glade @@ -0,0 +1,194 @@ + + + + + + False + + + True + False + vertical + + + True + False + + + True + False + + + True + False + _File + True + + + True + False + + + gtk-new + True + False + True + True + + + + + gtk-open + True + False + True + True + + + + + gtk-save + True + False + True + True + + + + + gtk-save-as + True + False + True + True + + + + + True + False + + + + + gtk-quit + True + False + True + True + + + + + + + + + True + False + _Edit + True + + + True + False + + + gtk-cut + True + False + True + True + + + + + gtk-copy + True + False + True + True + + + + + gtk-paste + True + False + True + True + + + + + gtk-delete + True + False + True + True + + + + + + + + + True + False + _View + True + + + + + True + False + _Help + True + + + True + False + + + gtk-about + True + False + True + True + + + + + + + + + + + False + True + 0 + + + + + 500 + 400 + True + False + + + + + + True + True + 1 + + + + + +