attempt opentk4

This commit is contained in:
Emmanuel 2020-09-03 16:25:24 +00:00
parent 0b409d641a
commit f7b22649c6
19 changed files with 1831 additions and 389 deletions

View file

@ -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

View file

@ -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
/// <summary>Use a single buffer versus a double buffer.</summary>
[Browsable(true)]
public bool SingleBuffer { get; set; }
/// <summary>Color Buffer Bits-Per-Pixel</summary>
public int ColorBPP { get; set; }
/// <summary>Accumulation Buffer Bits-Per-Pixel</summary>
public int AccumulatorBPP { get; set; }
/// <summary>Depth Buffer Bits-Per-Pixel</summary>
public int DepthBPP { get; set; }
/// <summary>Stencil Buffer Bits-Per-Pixel</summary>
public int StencilBPP { get; set; }
/// <summary>Number of samples</summary>
public int Samples { get; set; }
/// <summary>Indicates if steropic renderering is enabled</summary>
public bool Stereo { get; set; }
/// <summary>The major version of OpenGL to use.</summary>
public int GLVersionMajor { get; set; }
/// <summary>The minor version of OpenGL to use.</summary>
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
/// <summary>Constructs a new GLWidget</summary>
public GLWidget() : this(new Version(4, 0), true)
{
}
/// <summary>Constructs a new GLWidget</summary>
public GLWidget(Version apiVersion, bool forwardCompatible)
{
get;
set;
}
#endregion
#region Construction/Destruction
/// <summary>Constructs a new GLWidget.</summary>
public GLWidget()
: this(GraphicsMode.Default)
{
}
/// <summary>Constructs a new GLWidget using a given GraphicsMode</summary>
public GLWidget(GraphicsMode graphicsMode)
: this(graphicsMode, 1, 0, GraphicsContextFlags.Default)
{
}
/// <summary>Constructs a new GLWidget</summary>
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<int> AttributeList
{
get
{
List<int> attributeList = new List<int>(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);
/// <summary> Returns the X resource (window or pixmap) belonging to a GdkDrawable. </summary>
/// <remarks> XID gdk_x11_drawable_get_xid(GdkDrawable *drawable); </remarks>
/// <param name="gdkDisplay"> The GdkDrawable. </param>
/// <returns> The ID of drawable's X resource. </returns>
[SuppressUnmanagedCodeSecurity, DllImport(UnixLibGdkName)]
private static extern IntPtr gdk_x11_drawable_get_xid(IntPtr gdkDisplay);
/// <summary> Returns the X resource (window or pixmap) belonging to a GdkDrawable. </summary>
/// <remarks> XID gdk_x11_drawable_get_xid(GdkDrawable *drawable); </remarks>
/// <param name="gdkDisplay"> The GdkDrawable. </param>
/// <returns> The ID of drawable's X resource. </returns>
[SuppressUnmanagedCodeSecurity, DllImport(UnixLibGdkName)]
private static extern IntPtr gdk_x11_window_get_xid(IntPtr gdkDisplay);
/// <summary> Returns the X display of a GdkDisplay. </summary>
/// <remarks> Display* gdk_x11_display_get_xdisplay(GdkDisplay *display); </remarks>
/// <param name="gdkDisplay"> The GdkDrawable. </param>
/// <returns> The X Display of the GdkDisplay. </returns>
[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
}
}

View file

@ -1,5 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
@ -7,10 +6,9 @@
<Version>1.0.1</Version>
<RepositoryUrl>https://github.com/Ryujinx/GLWidget</RepositoryUrl>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="GtkSharp" Version="3.22.25.56" />
<PackageReference Include="OpenTK.NetStandard" Version="1.0.5.12" />
<PackageReference Include="OpenTK.Graphics" Version="4.0.0-pre9.4" />
<PackageReference Include="OpenTK.Windowing.Common" Version="4.0.0-pre9.4" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,49 @@
//
// Vertex.cs
//
// Author:
// Jarl Gullberg <jarl.gullberg@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
//
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;
}
}
}

View file

@ -0,0 +1,61 @@
//
// Transform.cs
//
// Author:
// Jarl Gullberg <jarl.gullberg@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
//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;
}
}
}

View file

@ -0,0 +1,47 @@
//
// UVCoordinate.cs
//
// Author:
// Jarl Gullberg <jarl.gullberg@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
//
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;
}
}
}

View file

@ -0,0 +1,92 @@
//
// Vertex.cs
//
// Author:
// Jarl Gullberg <jarl.gullberg@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
//
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;
}
}
}

View file

@ -0,0 +1,41 @@
//
// ExtensionMethods.cs
//
// Author:
// Jarl Gullberg <jarl.gullberg@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
//
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;
}
}
}

View file

@ -0,0 +1,39 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<LangVersion>8.0</LangVersion>
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
<OutputType>Exe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Configurations>Debug;Release</Configurations>
<RootNamespace>GLWidgetTestGTK3</RootNamespace>
<StartupObject>GLWidgetTestGTK3.MainClass</StartupObject>
<AssemblyName>GLWidgetTestGTK3</AssemblyName>
<ReleaseVersion>1.1</ReleaseVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="GtkSharp" Version="3.22.25.56"/>
<PackageReference Include="OpenTK" Version="4.0.0-pre9.4"/>
<PackageReference Include="OpenGL.Net" Version="1.0.304"/>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="interfaces\MainWindow.glade"/>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Shaders\FragmentShader.glsl"/>
<EmbeddedResource Include="Shaders\VertexShader.glsl"/>
</ItemGroup>
<ItemGroup>
<None Include="packages.config"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GLWidget\GLWidget.csproj"/>
</ItemGroup>
<ProjectExtensions>
<MonoDevelop>
<Properties>
<GtkDesignInfo generateGettext="False"/>
</Properties>
</MonoDevelop>
</ProjectExtensions>
</Project>

View file

@ -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";
/// <summary>
/// Currently loaded libraries.
/// </summary>
private static readonly Dictionary<string, IntPtr> _LibraryHandles = new Dictionary<string, IntPtr>();
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;
}
}
}

View file

@ -0,0 +1,76 @@
//
// Mesh.cs
//
// Author:
// Jarl Gullberg <jarl.gullberg@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
//
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
};
}
}

View file

@ -0,0 +1,578 @@
//
// MainWindow.cs
//
// Author:
// Jarl Gullberg <jarl.gullberg@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
//
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<Vertex> 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<Vertex> convertedVertices = new List<Vertex>();
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;
}
/// <summary>
/// Handles application shutdown procedures - terminating render threads, cleaning
/// up the UI, etc.
/// </summary>
/// <param name="sender">Sender.</param>
/// <param name="a">The deletion arguments.</param>
protected void OnDeleteEvent(object sender, DeleteEventArgs a)
{
Application.Quit();
a.RetVal = true;
}
}
}

View file

@ -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");
}
}
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -0,0 +1,104 @@
//
// Actor.cs
//
// Author:
// Jarl Gullberg <jarl.gullberg@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
//
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;
/// <summary>
/// Creates a new instance of the <see cref="Actor"/> class.
/// </summary>
/// <param name="Mesh">The mesh bound to the actor.</param>
public Actor(Mesh Mesh)
{
this.Mesh = Mesh;
this.Transform = new Transform(new Vector3(0.0f, 0.0f, 0.0f));
}
/// <summary>
/// Performs any arbitrary actions needed every frame, such as animations, texture manipulations or state
/// updates.
/// </summary>
/// <param name="deltaTime">The time (in thousands of a second) taken to render the previous frame.</param>
public void Tick(float deltaTime)
{
}
/// <summary>
/// Renders this actor within the current OpenGL context.
/// </summary>
/// <param name="ShaderProgramID">The ID of the currently active shader.</param>
/// <param name="ViewMatrix">The camera view matrix, calulcated from the camera position.</param>
/// <param name="ProjectionMatrix">The perspective projection currently in use.</param>
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);
}
}
}

View file

@ -0,0 +1,135 @@
//
// Mesh.cs
//
// Author:
// Jarl Gullberg <jarl.gullberg@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
//
using System;
using System.Collections.Generic;
using GLWidgetTestGTK3.Data;
using OpenTK.Graphics.OpenGL;
namespace GLWidgetTestGTK3.World
{
public class Mesh
{
private List<Vertex> 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<Vertex> 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<float> vertexPositions = new List<float>();
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<float> vertexNormals = new List<float>();
foreach (Vertex StoredVertex in Vertices)
{
vertexNormals.Add(StoredVertex.Normal.X);
vertexNormals.Add(StoredVertex.Normal.Y);
vertexNormals.Add(StoredVertex.Normal.Z);
}
return vertexNormals.ToArray();
}
}
}

View file

@ -0,0 +1,60 @@
//
// Scene.cs
//
// Author:
// Jarl Gullberg <jarl.gullberg@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
//
using System;
using System.Collections.Generic;
using OpenTK.Graphics.OpenGL;
namespace GLWidgetTestGTK3.World
{
public class Scene
{
public readonly List<Actor> Actors = new List<Actor>();
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);
}
/// <summary>
/// Runs the <see cref="Actor.Tick"/> function on all <see cref="Actor"/> instances
/// in the scene. Actors can define arbitrary behaviour in their ticks, but in most
/// cases it's used for animation.
/// </summary>
/// <param name="deltaTime">The time (in thousands of a second) taken to render the previous frame.</param>
public void Tick(float deltaTime)
{
for (int i = 0; i < Actors.Count; ++i)
{
Actors[i].Tick(deltaTime);
}
}
}
}

View file

@ -0,0 +1,194 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.16.1 -->
<interface>
<requires lib="gtk+" version="3.10"/>
<object class="GtkWindow" id="MainWindow">
<property name="can_focus">False</property>
<child>
<object class="GtkBox" id="MainBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuBar" id="MainMenuBar">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem" id="menuitem1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_File</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu" id="menu1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkImageMenuItem" id="imagemenuitem1">
<property name="label">gtk-new</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
</child>
<child>
<object class="GtkImageMenuItem" id="imagemenuitem2">
<property name="label">gtk-open</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
</child>
<child>
<object class="GtkImageMenuItem" id="imagemenuitem3">
<property name="label">gtk-save</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
</child>
<child>
<object class="GtkImageMenuItem" id="imagemenuitem4">
<property name="label">gtk-save-as</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem" id="separatormenuitem1">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkImageMenuItem" id="imagemenuitem5">
<property name="label">gtk-quit</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem" id="menuitem2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Edit</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu" id="menu2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkImageMenuItem" id="imagemenuitem6">
<property name="label">gtk-cut</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
</child>
<child>
<object class="GtkImageMenuItem" id="imagemenuitem7">
<property name="label">gtk-copy</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
</child>
<child>
<object class="GtkImageMenuItem" id="imagemenuitem8">
<property name="label">gtk-paste</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
</child>
<child>
<object class="GtkImageMenuItem" id="imagemenuitem9">
<property name="label">gtk-delete</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem" id="menuitem3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_View</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="menuitem4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Help</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu" id="menu3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkImageMenuItem" id="imagemenuitem10">
<property name="label">gtk-about</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="GLWidgetAlignment">
<property name="width_request">500</property>
<property name="height_request">400</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</interface>