Merge pull request #9 from Artfunkel/master

More math features and other cleanup from Artfunkel.
This commit is contained in:
Andy Korth 2013-03-20 07:36:54 -07:00
commit a665122202
24 changed files with 841 additions and 227 deletions

1
.gitignore vendored
View file

@ -3,6 +3,7 @@ Binaries/
OpenTK.userprefs OpenTK.userprefs
Source/GlobalAssemblyInfo.cs Source/GlobalAssemblyInfo.cs
Version.txt Version.txt
Source/OpenTK/OpenTK.xml
# OpenTK Resource files that seem like they should be ignored: # OpenTK Resource files that seem like they should be ignored:
Source/Compatibility/Properties/Resources.resources Source/Compatibility/Properties/Resources.resources

View file

@ -52,6 +52,7 @@ namespace Examples
// The ExampleBrowser works pretty poorly on some platforms, so you may want to start examples directly. // The ExampleBrowser works pretty poorly on some platforms, so you may want to start examples directly.
// for example: Examples.Tutorial.T12_GLSL_Parallax.Main (); // for example: Examples.Tutorial.T12_GLSL_Parallax.Main ();
// Examples.Tutorial.T10_GLSL_Cube.Main (); // Examples.Tutorial.T10_GLSL_Cube.Main ();
Examples.Tests.BasicMouseInput.Main ();
using (Form browser = new ExampleBrowser()) using (Form browser = new ExampleBrowser())
{ {

View file

@ -25,7 +25,7 @@ namespace Examples.Tests
{ {
public BasicMouseInput() public BasicMouseInput()
: base(800, 600, GraphicsMode.Default) : base(800, 600)
{ } { }
protected override void OnLoad(EventArgs e) protected override void OnLoad(EventArgs e)
@ -42,6 +42,8 @@ namespace Examples.Tests
protected override void OnUpdateFrame(FrameEventArgs e) protected override void OnUpdateFrame(FrameEventArgs e)
{ {
base.OnUpdateFrame(e);
// Here's the big test! // Here's the big test!
if(OpenTK.Input.Mouse.GetState()[MouseButton.Left]){ if(OpenTK.Input.Mouse.GetState()[MouseButton.Left]){
Console.WriteLine("The left mouse button is down!"); Console.WriteLine("The left mouse button is down!");
@ -66,7 +68,7 @@ namespace Examples.Tests
protected override void OnRenderFrame(FrameEventArgs e) protected override void OnRenderFrame(FrameEventArgs e)
{ {
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); GL.Clear(ClearBufferMask.ColorBufferBit);
SwapBuffers(); SwapBuffers();
} }
@ -78,7 +80,8 @@ namespace Examples.Tests
// Get the title and category of this example using reflection. // Get the title and category of this example using reflection.
ExampleAttribute info = ((ExampleAttribute)example.GetType().GetCustomAttributes(false)[0]); ExampleAttribute info = ((ExampleAttribute)example.GetType().GetCustomAttributes(false)[0]);
example.Title = String.Format("OpenTK | {0} {1}: {2}", info.Category, info.Difficulty, info.Title); example.Title = String.Format("OpenTK | {0} {1}: {2}", info.Category, info.Difficulty, info.Title);
example.Run(30.0, 0.0);
example.Run(30.0);
} }
} }

View file

@ -22,6 +22,7 @@ namespace OpenTK.Input
{ {
//private IKeyboard keyboard; //private IKeyboard keyboard;
private bool[] keys = new bool[Enum.GetValues(typeof(Key)).Length]; private bool[] keys = new bool[Enum.GetValues(typeof(Key)).Length];
private bool[] scancodes = new bool[256];
private string description; private string description;
private int numKeys, numFKeys, numLeds; private int numKeys, numFKeys, numLeds;
private IntPtr devID; private IntPtr devID;
@ -44,24 +45,16 @@ namespace OpenTK.Input
public bool this[Key key] public bool this[Key key]
{ {
get { return keys[(int)key]; } get { return keys[(int)key]; }
internal set }
{
if (keys[(int)key] != value || KeyRepeat)
{
keys[(int)key] = value;
if (value && KeyDown != null) /// <summary>
/// Gets a value indicating the status of the specified Key.
/// </summary>
/// <param name="scancode">The scancode to check.</param>
/// <returns>True if the scancode is pressed, false otherwise.</returns>
public bool this[uint scancode]
{ {
args.Key = key; get { return scancodes[scancode]; }
KeyDown(this, args);
}
else if (!value && KeyUp != null)
{
args.Key = key;
KeyUp(this, args);
}
}
}
} }
/// <summary> /// <summary>
@ -197,12 +190,34 @@ namespace OpenTK.Input
internal void ClearKeys() internal void ClearKeys()
{ {
for (int i = 0; i < keys.Length; i++) for (int i = 0; i < keys.Length; i++)
if (this[(Key)i]) // Make sure KeyUp events are *not* raised for keys that are up, even if key repeat is on. keys[i] = false;
this[(Key)i] = false; for (uint i = 0; i < scancodes.Length; i++)
scancodes[i] = false;
} }
#endregion #endregion
internal void SetKey(Key key, uint scancode, bool state)
{
if (keys[(int)key] != state || KeyRepeat)
{
keys[(int)key] = scancodes[scancode] = state;
if (state && KeyDown != null)
{
args.Key = key;
args.ScanCode = scancode;
KeyDown(this, args);
}
else if (!state && KeyUp != null)
{
args.Key = key;
args.ScanCode = scancode;
KeyUp(this, args);
}
}
}
#endregion #endregion
} }
} }

View file

@ -46,6 +46,7 @@ namespace OpenTK.Input
#region Fields #region Fields
Key key; Key key;
uint scancode;
#endregion #endregion
@ -63,6 +64,7 @@ namespace OpenTK.Input
public KeyboardKeyEventArgs(KeyboardKeyEventArgs args) public KeyboardKeyEventArgs(KeyboardKeyEventArgs args)
{ {
Key = args.Key; Key = args.Key;
ScanCode = args.ScanCode;
} }
#endregion #endregion
@ -78,6 +80,16 @@ namespace OpenTK.Input
internal set { key = value; } internal set { key = value; }
} }
/// <summary>
/// Gets the scancode which generated this event.
/// </summary>
public uint ScanCode
{
get { return scancode; }
internal set { scancode = value; }
}
#endregion #endregion
} }
} }

View file

@ -43,6 +43,7 @@ namespace OpenTK.Input
const int NumInts = ((int)Key.LastKey + IntSize - 1) / IntSize; const int NumInts = ((int)Key.LastKey + IntSize - 1) / IntSize;
// The following line triggers bogus CS0214 in gmcs 2.0.1, sigh... // The following line triggers bogus CS0214 in gmcs 2.0.1, sigh...
unsafe fixed int Keys[NumInts]; unsafe fixed int Keys[NumInts];
unsafe fixed int Codes[256];
bool is_connected; bool is_connected;
#endregion #endregion
@ -58,13 +59,17 @@ namespace OpenTK.Input
public bool this[Key key] public bool this[Key key]
{ {
get { return IsKeyDown(key); } get { return IsKeyDown(key); }
internal set
{
if (value)
EnableBit((int)key);
else
DisableBit((int)key);
} }
/// <summary>
/// Gets a <see cref="System.Boolean"/> indicating whether the specified
/// <see cref="OpenTK.Input.Key"/> is pressed.
/// </summary>
/// <param name="key">The <see cref="OpenTK.Input.Key"/> to check.</param>
/// <returns>True if key is pressed; false otherwise.</returns>
public bool this[short code]
{
get { return IsKeyDown(code); }
} }
/// <summary> /// <summary>
@ -76,6 +81,15 @@ namespace OpenTK.Input
return ReadBit((int)key); return ReadBit((int)key);
} }
/// <summary>
/// Gets a <see cref="System.Boolean"/> indicating whether this scan code is down.
/// </summary>
/// <param name="code">The scan code to check.</param>
public bool IsKeyDown(short code)
{
return ReadBit(code,true);
}
/// <summary> /// <summary>
/// Gets a <see cref="System.Boolean"/> indicating whether this key is up. /// Gets a <see cref="System.Boolean"/> indicating whether this key is up.
/// </summary> /// </summary>
@ -85,6 +99,15 @@ namespace OpenTK.Input
return !ReadBit((int)key); return !ReadBit((int)key);
} }
/// <summary>
/// Gets a <see cref="System.Boolean"/> indicating whether this scan code is down.
/// </summary>
/// <param name="code">The scan code to check.</param>
public bool IsKeyUp(short code)
{
return !ReadBit(code,true);
}
/// <summary> /// <summary>
/// Gets a <see cref="System.Boolean"/> indicating whether this keyboard /// Gets a <see cref="System.Boolean"/> indicating whether this keyboard
/// is connected. /// is connected.
@ -187,48 +210,62 @@ namespace OpenTK.Input
#region Internal Members #region Internal Members
internal bool ReadBit(int offset) internal void SetKeyState(Key key, byte code, bool down)
{ {
ValidateOffset(offset); if (down)
{
EnableBit((int)key);
EnableBit(code,true);
}
else
{
DisableBit((int)key);
DisableBit(code, true);
}
}
internal bool ReadBit(int offset, bool ScanCode = false)
{
ValidateOffset(offset, ScanCode);
int int_offset = offset / 32; int int_offset = offset / 32;
int bit_offset = offset % 32; int bit_offset = offset % 32;
unsafe unsafe
{ {
fixed (int* k = Keys) if (ScanCode)
{ fixed (int* c = Codes) { return (*(c + int_offset) & (1 << bit_offset)) != 0u; }
return (*(k + int_offset) & (1 << bit_offset)) != 0u; else
} fixed (int* k = Keys) { return (*(k + int_offset) & (1 << bit_offset)) != 0u; }
} }
} }
internal void EnableBit(int offset) internal void EnableBit(int offset, bool ScanCode = false)
{ {
ValidateOffset(offset); ValidateOffset(offset, ScanCode);
int int_offset = offset / 32; int int_offset = offset / 32;
int bit_offset = offset % 32; int bit_offset = offset % 32;
unsafe unsafe
{ {
fixed (int* k = Keys) if (ScanCode)
{ fixed (int* c = Codes) { *(c + int_offset) |= 1 << bit_offset; }
*(k + int_offset) |= 1 << bit_offset; else
} fixed (int* k = Keys) { *(k + int_offset) |= 1 << bit_offset; }
} }
} }
internal void DisableBit(int offset) internal void DisableBit(int offset, bool ScanCode = false)
{ {
ValidateOffset(offset); ValidateOffset(offset, ScanCode);
int int_offset = offset / 32; int int_offset = offset / 32;
int bit_offset = offset % 32; int bit_offset = offset % 32;
unsafe unsafe
{ {
fixed (int* k = Keys) if (ScanCode)
{ fixed (int* c = Codes) { *(c + int_offset) &= ~(1 << bit_offset); }
*(k + int_offset) &= ~(1 << bit_offset); else
} fixed (int* k = Keys) { *(k + int_offset) &= ~(1 << bit_offset); }
} }
} }
@ -242,6 +279,12 @@ namespace OpenTK.Input
for (int i = 0; i < NumInts; i++) for (int i = 0; i < NumInts; i++)
*(k1 + i) |= *(k2 + i); *(k1 + i) |= *(k2 + i);
} }
int* c2 = other.Codes;
fixed (int* c1 = Codes)
{
for (int i = 0; i < short.MaxValue; i++)
*(c1 + i) |= *(c2 + i);
}
} }
IsConnected |= other.IsConnected; IsConnected |= other.IsConnected;
} }
@ -250,9 +293,9 @@ namespace OpenTK.Input
#region Private Members #region Private Members
static void ValidateOffset(int offset) static void ValidateOffset(int offset, bool ScanCode)
{ {
if (offset < 0 || offset >= NumInts * IntSize) if (offset < 0 || offset >= (ScanCode ? 256 : NumInts * IntSize))
throw new ArgumentOutOfRangeException("offset"); throw new ArgumentOutOfRangeException("offset");
} }

View file

@ -28,7 +28,7 @@ using System.Runtime.InteropServices;
namespace OpenTK namespace OpenTK
{ {
/// <summary> /// <summary>
/// Represents a 3x3 Matrix /// Represents a 3x3 matrix containing 3D rotation and scale.
/// </summary> /// </summary>
[Serializable] [Serializable]
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
@ -255,6 +255,110 @@ namespace OpenTK
#endregion #endregion
/// <summary>
/// Returns a normalised copy of this instance.
/// </summary>
public Matrix3 Normalized()
{
Matrix3 m = this;
m.Normalize();
return m;
}
/// <summary>
/// Divides each element in the Matrix by the <see cref="Determinant"/>.
/// </summary>
public void Normalize()
{
var determinant = this.Determinant;
Row0 /= determinant;
Row1 /= determinant;
Row2 /= determinant;
}
/// <summary>
/// Returns an inverted copy of this instance.
/// </summary>
public Matrix3 Inverted()
{
Matrix3 m = this;
if (m.Determinant != 0)
m.Invert();
return m;
}
/// <summary>
/// Returns the scale component of this instance.
/// </summary>
public Vector3 ExtractScale() { return new Vector3(Row0.Length, Row1.Length, Row2.Length); }
/// <summary>
/// Returns the rotation component of this instance. Quite slow.
/// </summary>
/// <param name="row_normalise">Whether the method should row-normalise (i.e. remove scale from) the Matrix. Pass false if you know it's already normalised.</param>
public Quaternion ExtractRotation(bool row_normalise = true)
{
var row0 = Row0;
var row1 = Row1;
var row2 = Row2;
if (row_normalise)
{
row0 = row0.Normalized();
row1 = row1.Normalized();
row2 = row2.Normalized();
}
// code below adapted from Blender
Quaternion q = new Quaternion();
double trace = 0.25 * (row0[0] + row1[1] + row2[2] + 1.0);
if (trace > 0)
{
double sq = Math.Sqrt(trace);
q.W = (float)sq;
sq = 1.0 / (4.0 * sq);
q.X = (float)((row1[2] - row2[1]) * sq);
q.Y = (float)((row2[0] - row0[2]) * sq);
q.Z = (float)((row0[1] - row1[0]) * sq);
}
else if (row0[0] > row1[1] && row0[0] > row2[2])
{
double sq = 2.0 * Math.Sqrt(1.0 + row0[0] - row1[1] - row2[2]);
q.X = (float)(0.25 * sq);
sq = 1.0 / sq;
q.W = (float)((row2[1] - row1[2]) * sq);
q.Y = (float)((row1[0] + row0[1]) * sq);
q.Z = (float)((row2[0] + row0[2]) * sq);
}
else if (row1[1] > row2[2])
{
double sq = 2.0 * Math.Sqrt(1.0 + row1[1] - row0[0] - row2[2]);
q.Y = (float)(0.25 * sq);
sq = 1.0 / sq;
q.W = (float)((row2[0] - row0[2]) * sq);
q.X = (float)((row1[0] + row0[1]) * sq);
q.Z = (float)((row2[1] + row1[2]) * sq);
}
else
{
double sq = 2.0 * Math.Sqrt(1.0 + row2[2] - row0[0] - row1[1]);
q.Z = (float)(0.25 * sq);
sq = 1.0 / sq;
q.W = (float)((row1[0] - row0[1]) * sq);
q.X = (float)((row2[0] + row0[2]) * sq);
q.Y = (float)((row2[1] + row1[2]) * sq);
}
q.Normalize();
return q;
}
#endregion #endregion
#region Static #region Static

View file

@ -28,7 +28,7 @@ using System.Runtime.InteropServices;
namespace OpenTK namespace OpenTK
{ {
/// <summary> /// <summary>
/// Represents a 3x3 Matrix /// Represents a 3x3 matrix containing 3D rotation and scale with double-precision components.
/// </summary> /// </summary>
[Serializable] [Serializable]
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
@ -251,6 +251,110 @@ namespace OpenTK
#endregion #endregion
/// <summary>
/// Returns a normalised copy of this instance.
/// </summary>
public Matrix3d Normalized()
{
Matrix3d m = this;
m.Normalize();
return m;
}
/// <summary>
/// Divides each element in the Matrix by the <see cref="Determinant"/>.
/// </summary>
public void Normalize()
{
var determinant = this.Determinant;
Row0 /= determinant;
Row1 /= determinant;
Row2 /= determinant;
}
/// <summary>
/// Returns an inverted copy of this instance.
/// </summary>
public Matrix3d Inverted()
{
Matrix3d m = this;
if (m.Determinant != 0)
m.Invert();
return m;
}
/// <summary>
/// Returns the scale component of this instance.
/// </summary>
public Vector3d ExtractScale() { return new Vector3d(Row0.Length, Row1.Length, Row2.Length); }
/// <summary>
/// Returns the rotation component of this instance. Quite slow.
/// </summary>
/// <param name="row_normalise">Whether the method should row-normalise (i.e. remove scale from) the Matrix. Pass false if you know it's already normalised.</param>
public Quaterniond ExtractRotation(bool row_normalise = true)
{
var row0 = Row0;
var row1 = Row1;
var row2 = Row2;
if (row_normalise)
{
row0 = row0.Normalized();
row1 = row1.Normalized();
row2 = row2.Normalized();
}
// code below adapted from Blender
Quaterniond q = new Quaterniond();
double trace = 0.25 * (row0[0] + row1[1] + row2[2] + 1.0);
if (trace > 0)
{
double sq = Math.Sqrt(trace);
q.W = sq;
sq = 1.0 / (4.0 * sq);
q.X = (row1[2] - row2[1]) * sq;
q.Y = (row2[0] - row0[2]) * sq;
q.Z = (row0[1] - row1[0]) * sq;
}
else if (row0[0] > row1[1] && row0[0] > row2[2])
{
double sq = 2.0 * Math.Sqrt(1.0 + row0[0] - row1[1] - row2[2]);
q.X = 0.25 * sq;
sq = 1.0 / sq;
q.W = (row2[1] - row1[2]) * sq;
q.Y = (row1[0] + row0[1]) * sq;
q.Z = (row2[0] + row0[2]) * sq;
}
else if (row1[1] > row2[2])
{
double sq = 2.0 * Math.Sqrt(1.0 + row1[1] - row0[0] - row2[2]);
q.Y = 0.25 * sq;
sq = 1.0 / sq;
q.W = (row2[0] - row0[2]) * sq;
q.X = (row1[0] + row0[1]) * sq;
q.Z = (row2[1] + row1[2]) * sq;
}
else
{
double sq = 2.0 * Math.Sqrt(1.0 + row2[2] - row0[0] - row1[1]);
q.Z = 0.25 * sq;
sq = 1.0 / sq;
q.W = (row1[0] - row0[1]) * sq;
q.X = (row2[0] + row0[2]) * sq;
q.Y = (row2[1] + row1[2]) * sq;
}
q.Normalize();
return q;
}
#endregion #endregion
#region Static #region Static

View file

@ -28,8 +28,9 @@ using System.Runtime.InteropServices;
namespace OpenTK namespace OpenTK
{ {
/// <summary> /// <summary>
/// Represents a 4x4 Matrix /// Represents a 4x4 matrix containing 3D rotation, scale, transform, and projection.
/// </summary> /// </summary>
/// <seealso cref="Matrix4d"/>
[Serializable] [Serializable]
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct Matrix4 : IEquatable<Matrix4> public struct Matrix4 : IEquatable<Matrix4>
@ -315,6 +316,116 @@ namespace OpenTK
#endregion #endregion
/// <summary>
/// Returns a normalised copy of this instance.
/// </summary>
public Matrix4 Normalized()
{
Matrix4 m = this;
m.Normalize();
return m;
}
/// <summary>
/// Divides each element in the Matrix by the <see cref="Determinant"/>.
/// </summary>
public void Normalize()
{
var determinant = this.Determinant;
Row0 /= determinant;
Row1 /= determinant;
Row2 /= determinant;
Row3 /= determinant;
}
/// <summary>
/// Returns an inverted copy of this instance.
/// </summary>
public Matrix4 Inverted()
{
Matrix4 m = this;
if (m.Determinant != 0)
m.Invert();
return m;
}
/// <summary>
/// Returns the translation component of this instance.
/// </summary>
public Vector3 ExtractTranslation() { return Row3.Xyz; }
/// <summary>
/// Returns the scale component of this instance.
/// </summary>
public Vector3 ExtractScale() { return new Vector3(Row0.Length, Row1.Length, Row2.Length); }
/// <summary>
/// Returns the rotation component of this instance. Quite slow.
/// </summary>
/// <param name="row_normalise">Whether the method should row-normalise (i.e. remove scale from) the Matrix. Pass false if you know it's already normalised.</param>
public Quaternion ExtractRotation(bool row_normalise = true)
{
var row0 = Row0.Xyz;
var row1 = Row1.Xyz;
var row2 = Row2.Xyz;
if (row_normalise)
{
row0 = row0.Normalized();
row1 = row1.Normalized();
row2 = row2.Normalized();
}
// code below adapted from Blender
Quaternion q = new Quaternion();
double trace = 0.25 * (row0[0] + row1[1] + row2[2] + 1.0);
if (trace > 0)
{
double sq = Math.Sqrt(trace);
q.W = (float)sq;
sq = 1.0 / (4.0 * sq);
q.X = (float)((row1[2] - row2[1]) * sq);
q.Y = (float)((row2[0] - row0[2]) * sq);
q.Z = (float)((row0[1] - row1[0]) * sq);
}
else if (row0[0] > row1[1] && row0[0] > row2[2])
{
double sq = 2.0 * Math.Sqrt(1.0 + row0[0] - row1[1] - row2[2]);
q.X = (float)(0.25 * sq);
sq = 1.0 / sq;
q.W = (float)((row2[1] - row1[2]) * sq);
q.Y = (float)((row1[0] + row0[1]) * sq);
q.Z = (float)((row2[0] + row0[2]) * sq);
}
else if (row1[1] > row2[2])
{
double sq = 2.0 * Math.Sqrt(1.0 + row1[1] - row0[0] - row2[2]);
q.Y = (float)(0.25 * sq);
sq = 1.0 / sq;
q.W = (float)((row2[0] - row0[2]) * sq);
q.X = (float)((row1[0] + row0[1]) * sq);
q.Z = (float)((row2[1] + row1[2]) * sq);
}
else
{
double sq = 2.0 * Math.Sqrt(1.0 + row2[2] - row0[0] - row1[1]);
q.Z = (float)(0.25 * sq);
sq = 1.0 / sq;
q.W = (float)((row1[0] - row0[1]) * sq);
q.X = (float)((row2[0] + row0[2]) * sq);
q.Y = (float)((row2[1] + row1[2]) * sq);
}
q.Normalize();
return q;
}
#endregion #endregion
#region Static #region Static

View file

@ -28,8 +28,9 @@ using System.Runtime.InteropServices;
namespace OpenTK namespace OpenTK
{ {
/// <summary> /// <summary>
/// Represents a 4x4 Matrix with double-precision components. /// Represents a 4x4 matrix containing 3D rotation, scale, transform, and projection with double-precision components.
/// </summary> /// </summary>
/// <seealso cref="Matrix4"/>
[Serializable] [Serializable]
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct Matrix4d : IEquatable<Matrix4d> public struct Matrix4d : IEquatable<Matrix4d>
@ -298,6 +299,116 @@ namespace OpenTK
#endregion #endregion
/// <summary>
/// Returns a normalised copy of this instance.
/// </summary>
public Matrix4d Normalized()
{
Matrix4d m = this;
m.Normalize();
return m;
}
/// <summary>
/// Divides each element in the Matrix by the <see cref="Determinant"/>.
/// </summary>
public void Normalize()
{
var determinant = this.Determinant;
Row0 /= determinant;
Row1 /= determinant;
Row2 /= determinant;
Row3 /= determinant;
}
/// <summary>
/// Returns an inverted copy of this instance.
/// </summary>
public Matrix4d Inverted()
{
Matrix4d m = this;
if (m.Determinant != 0)
m.Invert();
return m;
}
/// <summary>
/// Returns the translation component of this instance.
/// </summary>
public Vector3d ExtractTranslation() { return Row3.Xyz; }
/// <summary>
/// Returns the scale component of this instance.
/// </summary>
public Vector3d ExtractScale() { return new Vector3d(Row0.Length, Row1.Length, Row2.Length); }
/// <summary>
/// Returns the rotation component of this instance. Quite slow.
/// </summary>
/// <param name="row_normalise">Whether the method should row-normalise (i.e. remove scale from) the Matrix. Pass false if you know it's already normalised.</param>
public Quaterniond ExtractRotation(bool row_normalise = true)
{
var row0 = Row0.Xyz;
var row1 = Row1.Xyz;
var row2 = Row2.Xyz;
if (row_normalise)
{
row0 = row0.Normalized();
row1 = row1.Normalized();
row2 = row2.Normalized();
}
// code below adapted from Blender
Quaterniond q = new Quaterniond();
double trace = 0.25 * (row0[0] + row1[1] + row2[2] + 1.0);
if (trace > 0)
{
double sq = Math.Sqrt(trace);
q.W = sq;
sq = 1.0 / (4.0 * sq);
q.X = (row1[2] - row2[1]) * sq;
q.Y = (row2[0] - row0[2]) * sq;
q.Z = (row0[1] - row1[0]) * sq;
}
else if (row0[0] > row1[1] && row0[0] > row2[2])
{
double sq = 2.0 * Math.Sqrt(1.0 + row0[0] - row1[1] - row2[2]);
q.X = 0.25 * sq;
sq = 1.0 / sq;
q.W = (row2[1] - row1[2]) * sq;
q.Y = (row1[0] + row0[1]) * sq;
q.Z = (row2[0] + row0[2]) * sq;
}
else if (row1[1] > row2[2])
{
double sq = 2.0 * Math.Sqrt(1.0 + row1[1] - row0[0] - row2[2]);
q.Y = 0.25 * sq;
sq = 1.0 / sq;
q.W = (row2[0] - row0[2]) * sq;
q.X = (row1[0] + row0[1]) * sq;
q.Z = (row2[1] + row1[2]) * sq;
}
else
{
double sq = 2.0 * Math.Sqrt(1.0 + row2[2] - row0[0] - row1[1]);
q.Z = 0.25 * sq;
sq = 1.0 / sq;
q.W = (row1[0] - row0[1]) * sq;
q.X = (row2[0] + row0[2]) * sq;
q.Y = (row2[1] + row1[2]) * sq;
}
q.Normalize();
return q;
}
#endregion #endregion
#region Static #region Static

View file

@ -189,6 +189,34 @@ namespace OpenTK
#endregion #endregion
/// <summary>
/// Returns a copy of the Quaternion scaled to unit length.
/// </summary>
public Quaternion Normalized()
{
Quaternion q = this;
q.Normalize();
return q;
}
/// <summary>
/// Reverses the rotation angle of this Quaterniond.
/// </summary>
public void Invert()
{
W = -W;
}
/// <summary>
/// Returns a copy of this Quaterniond with its rotation angle reversed.
/// </summary>
public Quaternion Inverted()
{
var q = this;
q.Invert();
return q;
}
#region public void Normalize() #region public void Normalize()
/// <summary> /// <summary>
@ -206,7 +234,7 @@ namespace OpenTK
#region public void Conjugate() #region public void Conjugate()
/// <summary> /// <summary>
/// Convert this quaternion to its conjugate /// Inverts the Vector3 component of this Quaternion.
/// </summary> /// </summary>
public void Conjugate() public void Conjugate()
{ {

View file

@ -189,6 +189,34 @@ namespace OpenTK
#endregion #endregion
/// <summary>
/// Returns a copy of the Quaterniond scaled to unit length.
/// </summary>
public Quaterniond Normalized()
{
Quaterniond q = this;
q.Normalize();
return q;
}
/// <summary>
/// Reverses the rotation angle of this Quaterniond.
/// </summary>
public void Invert()
{
W = -W;
}
/// <summary>
/// Returns a copy of this Quaterniond with its rotation angle reversed.
/// </summary>
public Quaterniond Inverted()
{
var q = this;
q.Invert();
return q;
}
#region public void Normalize() #region public void Normalize()
/// <summary> /// <summary>
@ -206,7 +234,7 @@ namespace OpenTK
#region public void Conjugate() #region public void Conjugate()
/// <summary> /// <summary>
/// Convert this Quaterniond to its conjugate /// Inverts the Vector3d component of this Quaterniond.
/// </summary> /// </summary>
public void Conjugate() public void Conjugate()
{ {

View file

@ -289,6 +289,16 @@ namespace OpenTK
#endregion #endregion
/// <summary>
/// Returns a copy of the Vector2 scaled to unit length.
/// </summary>
/// <returns></returns>
public Vector2 Normalized()
{
Vector2 v = this;
v.Normalize();
return v;
}
#region public void Normalize() #region public void Normalize()
/// <summary> /// <summary>

View file

@ -249,6 +249,17 @@ namespace OpenTK
#endregion #endregion
/// <summary>
/// Returns a copy of the Vector2d scaled to unit length.
/// </summary>
/// <returns></returns>
public Vector2d Normalized()
{
Vector2d v = this;
v.Normalize();
return v;
}
#region public void Normalize() #region public void Normalize()
/// <summary> /// <summary>

View file

@ -277,6 +277,16 @@ namespace OpenTK
#endregion #endregion
/// <summary>
/// Returns a copy of the Vector3 scaled to unit length.
/// </summary>
public Vector3 Normalized()
{
Vector3 v = this;
v.Normalize();
return v;
}
#region public void Normalize() #region public void Normalize()
/// <summary> /// <summary>

View file

@ -275,6 +275,17 @@ namespace OpenTK
#endregion #endregion
/// <summary>
/// Returns a copy of the Vector3d scaled to unit length.
/// </summary>
/// <returns></returns>
public Vector3d Normalized()
{
Vector3d v = this;
v.Normalize();
return v;
}
#region public void Normalize() #region public void Normalize()
/// <summary> /// <summary>

View file

@ -343,6 +343,16 @@ namespace OpenTK
#endregion #endregion
/// <summary>
/// Returns a copy of the Vector4 scaled to unit length.
/// </summary>
public Vector4 Normalized()
{
Vector4 v = this;
v.Normalize();
return v;
}
#region public void Normalize() #region public void Normalize()
/// <summary> /// <summary>

View file

@ -340,6 +340,16 @@ namespace OpenTK
#endregion #endregion
/// <summary>
/// Returns a copy of the Vector4d scaled to unit length.
/// </summary>
public Vector4d Normalized()
{
Vector4d v = this;
v.Normalize();
return v;
}
#region public void Normalize() #region public void Normalize()
/// <summary> /// <summary>

View file

@ -368,6 +368,7 @@ namespace OpenTK.Platform.MacOS
break; break;
} }
OpenTK.Input.Key key;
switch (evt.KeyboardEventKind) switch (evt.KeyboardEventKind)
{ {
case KeyboardEventKind.RawKeyRepeat: case KeyboardEventKind.RawKeyRepeat:
@ -376,25 +377,15 @@ namespace OpenTK.Platform.MacOS
break; break;
case KeyboardEventKind.RawKeyDown: case KeyboardEventKind.RawKeyDown:
{ Keymap.TryGetValue(code, out key);
OpenTK.Input.Key key; InputDriver.Keyboard[0].SetKey(key, (uint)code, true);
if (Keymap.TryGetValue(code, out key))
{
InputDriver.Keyboard[0][key] = true;
OnKeyPress(mKeyPressArgs); OnKeyPress(mKeyPressArgs);
}
return OSStatus.NoError; return OSStatus.NoError;
}
case KeyboardEventKind.RawKeyUp: case KeyboardEventKind.RawKeyUp:
{ Keymap.TryGetValue(code, out key);
OpenTK.Input.Key key; InputDriver.Keyboard[0].SetKey(key, (uint)code, false);
if (Keymap.TryGetValue(code, out key))
{
InputDriver.Keyboard[0][key] = false;
}
return OSStatus.NoError; return OSStatus.NoError;
}
case KeyboardEventKind.RawKeyModifiersChanged: case KeyboardEventKind.RawKeyModifiersChanged:
ProcessModifierKey(inEvent); ProcessModifierKey(inEvent);
@ -616,19 +607,19 @@ namespace OpenTK.Platform.MacOS
Input.KeyboardDevice keyboard = InputDriver.Keyboard[0]; Input.KeyboardDevice keyboard = InputDriver.Keyboard[0];
if (keyboard[OpenTK.Input.Key.AltLeft] ^ option) if (keyboard[OpenTK.Input.Key.AltLeft] ^ option)
keyboard[OpenTK.Input.Key.AltLeft] = option; keyboard.SetKey(OpenTK.Input.Key.AltLeft, (uint)MacOSKeyModifiers.Option, option);
if (keyboard[OpenTK.Input.Key.ShiftLeft] ^ shift) if (keyboard[OpenTK.Input.Key.ShiftLeft] ^ shift)
keyboard[OpenTK.Input.Key.ShiftLeft] = shift; keyboard.SetKey(OpenTK.Input.Key.ShiftLeft, (uint)MacOSKeyModifiers.Shift, shift);
if (keyboard[OpenTK.Input.Key.WinLeft] ^ command) if (keyboard[OpenTK.Input.Key.WinLeft] ^ command)
keyboard[OpenTK.Input.Key.WinLeft] = command; keyboard.SetKey(OpenTK.Input.Key.WinLeft, (uint)MacOSKeyModifiers.Command, command);
if (keyboard[OpenTK.Input.Key.ControlLeft] ^ control) if (keyboard[OpenTK.Input.Key.ControlLeft] ^ control)
keyboard[OpenTK.Input.Key.ControlLeft] = control; keyboard.SetKey(OpenTK.Input.Key.ControlLeft, (uint)MacOSKeyModifiers.Control, control);
if (keyboard[OpenTK.Input.Key.CapsLock] ^ caps) if (keyboard[OpenTK.Input.Key.CapsLock] ^ caps)
keyboard[OpenTK.Input.Key.CapsLock] = caps; keyboard.SetKey(OpenTK.Input.Key.CapsLock, (uint)MacOSKeyModifiers.CapsLock, caps);
} }

View file

@ -263,14 +263,12 @@ namespace OpenTK.Platform.MacOS
{ {
case HIDPage.GenericDesktop: case HIDPage.GenericDesktop:
case HIDPage.KeyboardOrKeypad: case HIDPage.KeyboardOrKeypad:
int raw = (int) usage; if (usage >= RawKeyMap.Length)
if (raw >= RawKeyMap.Length)
{ {
Debug.Print("[Warning] Key {0} not mapped.", raw); Debug.Print("[Warning] Key {0} not mapped.", usage);
return state; return state;
} }
Key key = RawKeyMap[raw]; state.SetKeyState(RawKeyMap[usage], (byte)usage, v_int != 0);
state[key] = v_int != 0;
break; break;
} }

View file

@ -70,14 +70,12 @@ namespace OpenTK.Platform.Windows
void UpdateKeyboard() void UpdateKeyboard()
{ {
for (int i = 0; i < 256; i++) for (byte i = 0; i < byte.MaxValue; i++)
{ {
VirtualKeys key = (VirtualKeys)i; bool pressed = (Functions.GetAsyncKeyState((VirtualKeys)i) >> 8) != 0;
bool pressed = (Functions.GetAsyncKeyState(key) >> 8) != 0; Key key;
if (KeyMap.ContainsKey(key)) KeyMap.TryGetValue((VirtualKeys)i,out key);
{ keyboard.SetKeyState(key, i, pressed);
keyboard[KeyMap[key]] = pressed;
}
} }
} }

View file

@ -89,7 +89,13 @@ namespace OpenTK.Platform.Windows
IList<KeyboardDevice> keyboards = new List<KeyboardDevice>(1); IList<KeyboardDevice> keyboards = new List<KeyboardDevice>(1);
IList<MouseDevice> mice = new List<MouseDevice>(1); IList<MouseDevice> mice = new List<MouseDevice>(1);
const long ExtendedBit = 1 << 24; // Used to distinguish left and right control, alt and enter keys. const long ExtendedBit = 1 << 24; // Used to distinguish left and right control, alt and enter keys.
static readonly uint ShiftRightScanCode = Functions.MapVirtualKey(VirtualKeys.RSHIFT, 0); // Used to distinguish left and right shift keys.
public static readonly uint ShiftLeftScanCode = Functions.MapVirtualKey(VirtualKeys.LSHIFT, 0);
public static readonly uint ShiftRightScanCode = Functions.MapVirtualKey(VirtualKeys.RSHIFT, 0);
public static readonly uint ControlLeftScanCode = Functions.MapVirtualKey(VirtualKeys.LCONTROL, 0);
public static readonly uint ControlRightScanCode = Functions.MapVirtualKey(VirtualKeys.RCONTROL, 0);
public static readonly uint AltLeftScanCode = Functions.MapVirtualKey(VirtualKeys.LMENU, 0);
public static readonly uint AltRightScanCode = Functions.MapVirtualKey(VirtualKeys.RMENU, 0);
KeyPressEventArgs key_press = new KeyPressEventArgs((char)0); KeyPressEventArgs key_press = new KeyPressEventArgs((char)0);
@ -369,6 +375,8 @@ namespace OpenTK.Platform.Windows
// In this case, both keys will be reported as pressed. // In this case, both keys will be reported as pressed.
bool extended = (lParam.ToInt64() & ExtendedBit) != 0; bool extended = (lParam.ToInt64() & ExtendedBit) != 0;
uint scancode = (uint)((lParam.ToInt64() >> 16) & 0xFF);
Key key = Key.Unknown;
switch ((VirtualKeys)wParam) switch ((VirtualKeys)wParam)
{ {
case VirtualKeys.SHIFT: case VirtualKeys.SHIFT:
@ -382,55 +390,50 @@ namespace OpenTK.Platform.Windows
// Otherwise, the state of one key might be stuck to pressed. // Otherwise, the state of one key might be stuck to pressed.
if (ShiftRightScanCode != 0 && pressed) if (ShiftRightScanCode != 0 && pressed)
{ {
unchecked if (scancode == ShiftRightScanCode)
{ key = Input.Key.ShiftRight;
if (((lParam.ToInt64() >> 16) & 0xFF) == ShiftRightScanCode)
keyboard[Input.Key.ShiftRight] = pressed;
else else
keyboard[Input.Key.ShiftLeft] = pressed; key = Input.Key.ShiftLeft;
}
} }
else else
{ {
// Windows 9x and NT4.0 or key release event. // Windows 9x and NT4.0 or key release event.
keyboard[Input.Key.ShiftLeft] = keyboard[Input.Key.ShiftRight] = pressed; keyboard.SetKey(Input.Key.ShiftLeft, ShiftLeftScanCode, pressed);
keyboard.SetKey(Input.Key.ShiftRight, ShiftRightScanCode, pressed);
} }
return IntPtr.Zero; break;
case VirtualKeys.CONTROL: case VirtualKeys.CONTROL:
if (extended) if (extended)
keyboard[Input.Key.ControlRight] = pressed; key = Input.Key.ControlRight;
else else
keyboard[Input.Key.ControlLeft] = pressed; key = Input.Key.ControlLeft;
return IntPtr.Zero; break;
case VirtualKeys.MENU: case VirtualKeys.MENU:
if (extended) if (extended)
keyboard[Input.Key.AltRight] = pressed; key = Input.Key.AltRight;
else else
keyboard[Input.Key.AltLeft] = pressed; key = Input.Key.AltLeft;
return IntPtr.Zero; break;
case VirtualKeys.RETURN: case VirtualKeys.RETURN:
if (extended) if (extended)
keyboard[Key.KeypadEnter] = pressed; key = Key.KeypadEnter;
else else
keyboard[Key.Enter] = pressed; key = Key.Enter;
return IntPtr.Zero; break;
default: default:
if (!KeyMap.ContainsKey((VirtualKeys)wParam)) if (!KeyMap.ContainsKey((VirtualKeys)wParam))
{
Debug.Print("Virtual key {0} ({1}) not mapped.", (VirtualKeys)wParam, (long)lParam); Debug.Print("Virtual key {0} ({1}) not mapped.", (VirtualKeys)wParam, (long)lParam);
break;
}
else else
{ key = KeyMap[(VirtualKeys)wParam];
keyboard[KeyMap[(VirtualKeys)wParam]] = pressed;
}
return IntPtr.Zero;
}
break; break;
}
keyboard.SetKey(key, scancode, pressed);
return IntPtr.Zero;
case WindowMessage.SYSCHAR: case WindowMessage.SYSCHAR:
return IntPtr.Zero; return IntPtr.Zero;

View file

@ -180,31 +180,30 @@ namespace OpenTK.Platform.Windows
switch (rin.Data.Keyboard.VKey) switch (rin.Data.Keyboard.VKey)
{ {
case VirtualKeys.SHIFT: case VirtualKeys.SHIFT:
keyboard[Input.Key.ShiftLeft] = keyboard[Input.Key.ShiftRight] = pressed; keyboard.SetKeyState(Key.ShiftLeft, (byte)WinGLNative.ShiftLeftScanCode, pressed);
keyboard.SetKeyState(Key.ShiftRight, (byte)WinGLNative.ShiftRightScanCode, pressed);
processed = true; processed = true;
break; break;
case VirtualKeys.CONTROL: case VirtualKeys.CONTROL:
keyboard[Input.Key.ControlLeft] = keyboard[Input.Key.ControlRight] = pressed; keyboard.SetKeyState(Key.ControlLeft, (byte)WinGLNative.ControlLeftScanCode, pressed);
keyboard.SetKeyState(Key.ControlRight, (byte)WinGLNative.ControlRightScanCode, pressed);
processed = true; processed = true;
break; break;
case VirtualKeys.MENU: case VirtualKeys.MENU:
keyboard[Input.Key.AltLeft] = keyboard[Input.Key.AltRight] = pressed; keyboard.SetKeyState(Key.AltLeft, (byte)WinGLNative.AltLeftScanCode, pressed);
keyboard.SetKeyState(Key.AltRight, (byte)WinGLNative.AltRightScanCode, pressed);
processed = true; processed = true;
break; break;
default: default:
if (!KeyMap.ContainsKey(rin.Data.Keyboard.VKey)) Key key;
{ KeyMap.TryGetValue(rin.Data.Keyboard.VKey, out key);
Debug.Print("Virtual key {0} ({1}) not mapped.", if (key == Key.Unknown)
rin.Data.Keyboard.VKey, (int)rin.Data.Keyboard.VKey); Debug.Print("Virtual key {0} ({1}) not mapped.", rin.Data.Keyboard.VKey, (int)rin.Data.Keyboard.VKey);
} keyboard.SetKeyState(key, BitConverter.GetBytes(rin.Data.Keyboard.MakeCode)[0], pressed);
else
{
keyboard[KeyMap[rin.Data.Keyboard.VKey]] = pressed;
processed = true; processed = true;
}
break; break;
} }

View file

@ -157,16 +157,18 @@ namespace OpenTK.Platform.X11
case XEventName.KeyPress: case XEventName.KeyPress:
case XEventName.KeyRelease: case XEventName.KeyRelease:
bool pressed = e.type == XEventName.KeyPress; bool pressed = e.type == XEventName.KeyPress;
XKey keysym = (XKey)API.LookupKeysym(ref e.KeyEvent, 0);
XKey keysym2 = (XKey)API.LookupKeysym(ref e.KeyEvent, 1);
Key key = Key.Unknown;
IntPtr keysym = API.LookupKeysym(ref e.KeyEvent, 0); if (keymap.ContainsKey(keysym))
IntPtr keysym2 = API.LookupKeysym(ref e.KeyEvent, 1); key = keymap[keysym];
else if (keymap.ContainsKey(keysym2))
if (keymap.ContainsKey((XKey)keysym)) key = keymap[keysym2];
keyboard[keymap[(XKey)keysym]] = pressed;
else if (keymap.ContainsKey((XKey)keysym2))
keyboard[keymap[(XKey)keysym2]] = pressed;
else else
Debug.Print("KeyCode {0} (Keysym: {1}, {2}) not mapped.", e.KeyEvent.keycode, (XKey)keysym, (XKey)keysym2); Debug.Print("KeyCode {0} (Keysym: {1}, {2}) not mapped.", e.KeyEvent.keycode, (XKey)keysym, (XKey)keysym2);
keyboard.SetKey(key, (uint)e.KeyEvent.keycode, pressed);
break; break;
case XEventName.ButtonPress: case XEventName.ButtonPress: