#region --- License ---
/* Copyright (c) 2006, 2007 Stefanos Apostolopoulos
* Contributions by Andy Gill.
* See license.txt for license info
*/
#endregion
#region --- Using Directives ---
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Diagnostics;
using System.Reflection.Emit;
using OpenTK.Math;
#endregion
[assembly: CLSCompliant(true)]
namespace OpenTK.OpenGL
{
///
/// OpenGL binding for .NET, implementing OpenGL 2.1, plus extensions.
///
///
///
/// OpenTK.OpenGL.GL contains all OpenGL enums and functions defined in the 2.1 specification.
/// The official .spec files can be found at: http://opengl.org/registry/.
///
///
/// OpenTK.OpenGL.GL relies on static initialization to obtain the entry points for OpenGL functions.
/// Please ensure that a valid OpenGL context has been made current in the pertinent thread before
/// any OpenGL functions are called (toolkits like GLUT, SDL or GLFW will automatically take care of
/// the context initialization process). Without a valid OpenGL context, OpenTK.OpenGL.GL will only be able
/// to retrieve statically exported entry points (typically corresponding to OpenGL version 1.1 under Windows,
/// 1.3 under Linux and 1.4 under Windows Vista), and extension methods will need to be loaded manually.
///
///
/// If you prefer not to rely on static initialisation for the Gl class, you can use the
/// ReloadFunctions or ReloadFunction methods to manually force the initialisation of OpenGL entry points.
/// The ReloadFunctions method should be called whenever you change an existing visual or pixelformat. This
/// generally happens when you change the color/stencil/depth buffer associated with a window (but probably
/// not the resolution). This may or may not be necessary under Linux/MacOS, but is generally required for
/// Windows.
///
///
/// You can use the Gl.IsExtensionSupported method to check whether any given category of extension functions
/// exists in the current OpenGL context. The results can be cached to speed up future searches.
/// Keep in mind that different OpenGL contexts may support different extensions, and under different entry
/// points. Always check if all required extensions are still supported when changing visuals or pixel
/// formats.
///
///
/// You may retrieve the entry point for an OpenGL function using the GL.GetDelegate method.
///
///
///
///
///
///
///
///
///
public static partial class GL
{
static GL()
{
assembly = Assembly.GetExecutingAssembly();//Assembly.Load("OpenTK.OpenGL");
glClass = assembly.GetType("OpenTK.OpenGL.GL");
delegatesClass = glClass.GetNestedType("Delegates", BindingFlags.Static | BindingFlags.NonPublic);
importsClass = glClass.GetNestedType("Imports", BindingFlags.Static | BindingFlags.NonPublic);
}
internal static partial class Imports
{
internal static SortedList import; // This is faster than either Dictionary or SortedDictionary
static Imports()
{
MethodInfo[] methods = importsClass.GetMethods(BindingFlags.Static | BindingFlags.NonPublic);
import = new SortedList(methods.Length);
foreach (MethodInfo m in methods)
{
import.Add(m.Name, m);
}
}
}
#region --- Fields ---
internal const string Library = "opengl32.dll";
//private static Dictionary AvailableExtensions = new Dictionary();
private static SortedList AvailableExtensions = new SortedList();
private static bool rebuildExtensionList;
private static Assembly assembly;
private static Type glClass;
private static Type delegatesClass;
private static Type importsClass;
private static FieldInfo[] delegates;
#endregion
#region public static bool SupportsExtension(string name)
///
/// Determines whether the specified OpenGL extension category is available in
/// the current OpenGL context. Equivalent to IsExtensionSupported(name, true)
///
/// The string for the OpenGL extension category (eg. "GL_ARB_multitexture")
/// True if the specified extension is available, false otherwise.
public static bool SupportsExtension(string name)
{
if (rebuildExtensionList)
{
BuildExtensionList();
}
// Search the cache for the string. Note that the cache substitutes
// strings "1.0" to "2.1" with "GL_VERSION_1_0" to "GL_VERSION_2_1"
if (AvailableExtensions.ContainsKey(name))
{
//return AvailableExtensions[name];
return true;
}
return false;
}
#endregion
#region private static void BuildExtensionList()
///
/// Builds a cache of the supported extensions to speed up searches.
///
private static void BuildExtensionList()
{
// Assumes there is an opengl context current.
AvailableExtensions.Clear();
string version_string = GL.GetString(OpenTK.OpenGL.GL.Enums.StringName.VERSION);
if (String.IsNullOrEmpty(version_string))
{
throw new ApplicationException("Failed to build extension list. Is there an opengl context current?");
}
string version = version_string.Trim(' ');
if (version.StartsWith("1.2"))
{
AvailableExtensions.Add("VERSION_1_2", true);
}
else if (version.StartsWith("1.3"))
{
AvailableExtensions.Add("VERSION_1_2", true);
AvailableExtensions.Add("VERSION_1_3", true);
}
else if (version.StartsWith("1.4"))
{
AvailableExtensions.Add("VERSION_1_2", true);
AvailableExtensions.Add("VERSION_1_3", true);
AvailableExtensions.Add("VERSION_1_4", true);
}
else if (version.StartsWith("1.5"))
{
AvailableExtensions.Add("VERSION_1_2", true);
AvailableExtensions.Add("VERSION_1_3", true);
AvailableExtensions.Add("VERSION_1_4", true);
AvailableExtensions.Add("VERSION_1_5", true);
}
else if (version.StartsWith("2.0"))
{
AvailableExtensions.Add("VERSION_1_2", true);
AvailableExtensions.Add("VERSION_1_3", true);
AvailableExtensions.Add("VERSION_1_4", true);
AvailableExtensions.Add("VERSION_1_5", true);
AvailableExtensions.Add("VERSION_2_0", true);
}
else if (version.StartsWith("2.1"))
{
AvailableExtensions.Add("VERSION_1_2", true);
AvailableExtensions.Add("VERSION_1_3", true);
AvailableExtensions.Add("VERSION_1_4", true);
AvailableExtensions.Add("VERSION_1_5", true);
AvailableExtensions.Add("VERSION_2_0", true);
AvailableExtensions.Add("VERSION_2_1", true);
}
string extension_string = GL.GetString(OpenTK.OpenGL.GL.Enums.StringName.EXTENSIONS);
if (String.IsNullOrEmpty(extension_string))
return; // no extensions are available
string[] extensions = extension_string.Split(' ');
foreach (string ext in extensions)
{
AvailableExtensions.Add(ext, true);
}
rebuildExtensionList = false;
}
#endregion
#region public static Delegate GetDelegate(string name, Type signature)
///
/// Creates a System.Delegate that can be used to call an OpenGL function, core or extension.
///
/// The name of the OpenGL function (eg. "glNewList")
/// The signature of the OpenGL function.
///
/// A System.Delegate that can be used to call this OpenGL function, or null if the specified
/// function name did not correspond to an OpenGL function.
///
public static Delegate GetDelegate(string name, Type signature)
{
MethodInfo m;
return
GetExtensionDelegate(name, signature) ??
/*((m = importsClass.GetMethod(name.Substring(2), BindingFlags.Static | BindingFlags.NonPublic)) != null ?*/
(Imports.import.TryGetValue((name.Substring(2)), out m) ?
Delegate.CreateDelegate(signature, m) : null);
}
#endregion
#region public static void LoadAll()
///
/// Loads all OpenGL functions (core and extensions).
///
///
///
/// This function will be automatically called the first time you use any opengl function. There is
///
///
/// Call this function manually whenever you need to update OpenGL entry points.
/// This need may arise if you change the pixelformat/visual, or in case you cannot
/// (or do not want) to use the automatic initialization of the GL class.
///
///
public static void LoadAll()
{
// Using reflection is more than 3 times faster than directly loading delegates on the first
// run, probably due to code generation overhead. Subsequent runs are faster with direct loading
// than with reflection, but the first time is more significant.
int supported = 0;
if (delegates == null)
{
delegates = delegatesClass.GetFields(BindingFlags.Static | BindingFlags.NonPublic);
}
Trace.Write("GL.LoadAll(): ");
System.Diagnostics.Stopwatch time = new System.Diagnostics.Stopwatch();
time.Reset();
time.Start();
foreach (FieldInfo f in delegates)
{
Delegate d = GetDelegate(f.Name, f.FieldType);
if (d != null)
{
++supported;
}
f.SetValue(null, d);
}
time.Stop();
Trace.WriteLine(String.Format("{0} OpenGL extensions loaded in {1} milliseconds.", supported, time.ElapsedMilliseconds));
time.Reset();
rebuildExtensionList = true;
}
static void set(object d, Delegate value)
{
d = value;
}
#endregion
#region public static bool Load(string function)
///
/// Tries to reload the given OpenGL function (core or extension).
///
/// The name of the OpenGL function (i.e. glShaderSource)
/// True if the function was found and reloaded, false otherwise.
///
///
/// Use this function if you require greater granularity when loading OpenGL entry points.
///
///
/// While the automatic initialisation will load all OpenGL entry points, in some cases
/// the initialisation can take place before an OpenGL Context has been established.
/// In this case, use this function to load the entry points for the OpenGL functions
/// you will need, or use ReloadFunctions() to load all available entry points.
///
///
/// This function returns true if the given OpenGL function is supported, false otherwise.
///
///
/// To query for supported extensions use the IsExtensionSupported() function instead.
///
///
public static bool Load(string function)
{
FieldInfo f = delegatesClass.GetField(function, BindingFlags.Static | BindingFlags.NonPublic);
if (f == null)
return false;
Delegate old = f.GetValue(null) as Delegate;
Delegate @new = GetDelegate(f.Name, f.FieldType);
if (old.Target != @new.Target)
{
f.SetValue(null, @new);
rebuildExtensionList = true;
}
return @new != null;
}
#endregion
#region --- GetProcAddress ---
private static IGetProcAddress getProcAddress;
internal interface IGetProcAddress
{
IntPtr GetProcAddress(string function);
}
internal class GetProcAddressWindows : IGetProcAddress
{
[System.Runtime.InteropServices.DllImport(Library, EntryPoint = "wglGetProcAddress", ExactSpelling = true)]
private static extern IntPtr wglGetProcAddress(String lpszProc);
public IntPtr GetProcAddress(string function)
{
return wglGetProcAddress(function);
}
}
internal class GetProcAddressX11 : IGetProcAddress
{
[DllImport(Library, EntryPoint = "glXGetProcAddress")]
private static extern IntPtr glxGetProcAddress([MarshalAs(UnmanagedType.LPTStr)] string procName);
public IntPtr GetProcAddress(string function)
{
return glxGetProcAddress(function);
}
}
internal class GetProcAddressOSX : IGetProcAddress
{
private const string Library = "libdl.dylib";
[DllImport(Library, EntryPoint = "NSIsSymbolNameDefined")]
private static extern bool NSIsSymbolNameDefined(string s);
[DllImport(Library, EntryPoint = "NSLookupAndBindSymbol")]
private static extern IntPtr NSLookupAndBindSymbol(string s);
[DllImport(Library, EntryPoint = "NSAddressOfSymbol")]
private static extern IntPtr NSAddressOfSymbol(IntPtr symbol);
public IntPtr GetProcAddress(string function)
{
string fname = "_" + function;
if (!NSIsSymbolNameDefined(fname))
return IntPtr.Zero;
IntPtr symbol = NSLookupAndBindSymbol(fname);
if (symbol != IntPtr.Zero)
symbol = NSAddressOfSymbol(symbol);
return symbol;
}
}
#region private static IntPtr GetAddress(string function)
///
/// Retrieves the entry point for a dynamically exported OpenGL function.
///
/// The function string for the OpenGL function (eg. "glNewList")
///
/// An IntPtr contaning the address for the entry point, or IntPtr.Zero if the specified
/// OpenGL function is not dynamically exported.
///
///
///
/// The Marshal.GetDelegateForFunctionPointer method can be used to turn the return value
/// into a call-able delegate.
///
///
/// This function is cross-platform. It determines the underlying platform and uses the
/// correct wgl, glx or agl GetAddress function to retrieve the function pointer.
///
///
///
///
private static IntPtr GetAddress(string function)
{
if (getProcAddress == null)
{
if (System.Environment.OSVersion.Platform == PlatformID.Win32NT ||
System.Environment.OSVersion.Platform == PlatformID.Win32S ||
System.Environment.OSVersion.Platform == PlatformID.Win32Windows ||
System.Environment.OSVersion.Platform == PlatformID.WinCE)
{
getProcAddress = new GetProcAddressWindows();
}
else if (System.Environment.OSVersion.Platform == PlatformID.Unix)
{
getProcAddress = new GetProcAddressX11();
}
else
{
throw new PlatformNotSupportedException(
"Extension loading is only supported under X11 and Windows. We are sorry for the inconvience.");
}
}
return getProcAddress.GetProcAddress(function);
}
#endregion
#region internal static Delegate GetExtensionDelegate(string name, Type signature)
///
/// Creates a System.Delegate that can be used to call a dynamically exported OpenGL function.
///
/// The name of the OpenGL function (eg. "glNewList")
/// The signature of the OpenGL function.
///
/// A System.Delegate that can be used to call this OpenGL function or null
/// if the function is not available in the current OpenGL context.
///
internal static Delegate GetExtensionDelegate(string name, Type signature)
{
IntPtr address = GetAddress(name);
if (address == IntPtr.Zero ||
address == new IntPtr(1) || // Workaround for buggy nvidia drivers which return
address == new IntPtr(2)) // 1 or 2 instead of IntPtr.Zero for some extensions.
{
return null;
}
else
{
return Marshal.GetDelegateForFunctionPointer(address, signature);
}
}
#endregion
#endregion
#region --- GL Overloads ---
#region public static void Color[34]() overloads
public static void Color4(System.Drawing.Color color)
{
GL.Color4(color.R, color.G, color.B, color.A);
}
public static void Color3(System.Drawing.Color color)
{
GL.Color3(color.R, color.G, color.B);
}
#endregion
#region public static void ClearColor() overloads
public static void ClearColor(System.Drawing.Color color)
{
GL.ClearColor(color.R/255.0f, color.G/255.0f, color.B/255.0f, color.A/255.0f);
}
#endregion
#region overloads using OpenTK.Math classes
public static
void Normal3(Vector3 normal)
{
Delegates.glNormal3f(normal.X, normal.Y, normal.Z);
}
public static
void RasterPos2(Vector2 pos)
{
Delegates.glRasterPos2f(pos.X, pos.Y);
}
public static
void RasterPos3(Vector3 pos)
{
Delegates.glRasterPos3f(pos.X, pos.Y, pos.Z);
}
public static
void RasterPos4(Vector4 pos)
{
Delegates.glRasterPos4f(pos.X, pos.Y, pos.Z, pos.W);
}
public static
void Vertex2(Vector2 v)
{
Delegates.glVertex2f(v.X, v.Y);
}
public static
void Vertex3(Vector3 v)
{
Delegates.glVertex3f(v.X, v.Y, v.Z);
}
public static
void Vertex4(Vector4 v)
{
Delegates.glVertex4f(v.X, v.Y, v.Z, v.W);
}
public static
void Rotate(Single angle, Vector3 axis)
{
Delegates.glRotatef((Single)angle, axis.X, axis.Y, axis.Z);
}
public static
void Scale(Vector3 scale)
{
Delegates.glScalef(scale.X, scale.Y, scale.Z);
}
public static
void Translate(Vector3 trans)
{
Delegates.glTranslatef(trans.X, trans.Y, trans.Z);
}
#endregion
#endregion
}
}