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
Source/GlobalAssemblyInfo.cs
Version.txt
Source/OpenTK/OpenTK.xml
# OpenTK Resource files that seem like they should be ignored:
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.
// for example: Examples.Tutorial.T12_GLSL_Parallax.Main ();
// Examples.Tutorial.T10_GLSL_Cube.Main ();
Examples.Tests.BasicMouseInput.Main ();
using (Form browser = new ExampleBrowser())
{

View file

@ -25,7 +25,7 @@ namespace Examples.Tests
{
public BasicMouseInput()
: base(800, 600, GraphicsMode.Default)
: base(800, 600)
{ }
protected override void OnLoad(EventArgs e)
@ -42,6 +42,8 @@ namespace Examples.Tests
protected override void OnUpdateFrame(FrameEventArgs e)
{
base.OnUpdateFrame(e);
// Here's the big test!
if(OpenTK.Input.Mouse.GetState()[MouseButton.Left]){
Console.WriteLine("The left mouse button is down!");
@ -66,7 +68,7 @@ namespace Examples.Tests
protected override void OnRenderFrame(FrameEventArgs e)
{
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.Clear(ClearBufferMask.ColorBufferBit);
SwapBuffers();
}
@ -78,7 +80,8 @@ namespace Examples.Tests
// Get the title and category of this example using reflection.
ExampleAttribute info = ((ExampleAttribute)example.GetType().GetCustomAttributes(false)[0]);
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 bool[] keys = new bool[Enum.GetValues(typeof(Key)).Length];
private bool[] scancodes = new bool[256];
private string description;
private int numKeys, numFKeys, numLeds;
private IntPtr devID;
@ -44,24 +45,16 @@ namespace OpenTK.Input
public bool this[Key key]
{
get { return keys[(int)key]; }
internal set
{
if (keys[(int)key] != value || KeyRepeat)
{
keys[(int)key] = value;
}
if (value && KeyDown != null)
{
args.Key = key;
KeyDown(this, args);
}
else if (!value && KeyUp != null)
{
args.Key = key;
KeyUp(this, args);
}
}
}
/// <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]
{
get { return scancodes[scancode]; }
}
/// <summary>
@ -197,12 +190,34 @@ namespace OpenTK.Input
internal void ClearKeys()
{
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.
this[(Key)i] = false;
keys[i] = false;
for (uint i = 0; i < scancodes.Length; i++)
scancodes[i] = false;
}
#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
}
}

View file

@ -1,83 +1,95 @@
#region License
//
// The Open Toolkit Library License
//
// Copyright (c) 2006 - 2009 the Open Toolkit library.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
#endregion
using System;
using System.Collections.Generic;
using System.Text;
namespace OpenTK.Input
{
/// <summary>
/// Defines the event data for <see cref="KeyboardDevice"/> events.
/// </summary>
/// <remarks>
/// <para>
/// Do not cache instances of this type outside their event handler.
/// If necessary, you can clone a KeyboardEventArgs instance using the
/// <see cref="KeyboardKeyEventArgs(KeyboardKeyEventArgs)"/> constructor.
/// </para>
/// </remarks>
public class KeyboardKeyEventArgs : EventArgs
{
#region Fields
Key key;
#endregion
#region Constructors
/// <summary>
/// Constructs a new KeyboardEventArgs instance.
/// </summary>
public KeyboardKeyEventArgs() { }
/// <summary>
/// Constructs a new KeyboardEventArgs instance.
/// </summary>
/// <param name="args">An existing KeyboardEventArgs instance to clone.</param>
public KeyboardKeyEventArgs(KeyboardKeyEventArgs args)
{
Key = args.Key;
}
#endregion
#region Public Members
/// <summary>
/// Gets the <see cref="Key"/> that generated this event.
/// </summary>
public Key Key
{
get { return key; }
internal set { key = value; }
}
#endregion
}
}
#region License
//
// The Open Toolkit Library License
//
// Copyright (c) 2006 - 2009 the Open Toolkit library.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
#endregion
using System;
using System.Collections.Generic;
using System.Text;
namespace OpenTK.Input
{
/// <summary>
/// Defines the event data for <see cref="KeyboardDevice"/> events.
/// </summary>
/// <remarks>
/// <para>
/// Do not cache instances of this type outside their event handler.
/// If necessary, you can clone a KeyboardEventArgs instance using the
/// <see cref="KeyboardKeyEventArgs(KeyboardKeyEventArgs)"/> constructor.
/// </para>
/// </remarks>
public class KeyboardKeyEventArgs : EventArgs
{
#region Fields
Key key;
uint scancode;
#endregion
#region Constructors
/// <summary>
/// Constructs a new KeyboardEventArgs instance.
/// </summary>
public KeyboardKeyEventArgs() { }
/// <summary>
/// Constructs a new KeyboardEventArgs instance.
/// </summary>
/// <param name="args">An existing KeyboardEventArgs instance to clone.</param>
public KeyboardKeyEventArgs(KeyboardKeyEventArgs args)
{
Key = args.Key;
ScanCode = args.ScanCode;
}
#endregion
#region Public Members
/// <summary>
/// Gets the <see cref="Key"/> that generated this event.
/// </summary>
public Key Key
{
get { return key; }
internal set { key = value; }
}
/// <summary>
/// Gets the scancode which generated this event.
/// </summary>
public uint ScanCode
{
get { return scancode; }
internal set { scancode = value; }
}
#endregion
}
}

View file

@ -43,6 +43,7 @@ namespace OpenTK.Input
const int NumInts = ((int)Key.LastKey + IntSize - 1) / IntSize;
// The following line triggers bogus CS0214 in gmcs 2.0.1, sigh...
unsafe fixed int Keys[NumInts];
unsafe fixed int Codes[256];
bool is_connected;
#endregion
@ -58,13 +59,17 @@ namespace OpenTK.Input
public bool this[Key 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>
@ -76,6 +81,15 @@ namespace OpenTK.Input
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>
/// Gets a <see cref="System.Boolean"/> indicating whether this key is up.
/// </summary>
@ -85,6 +99,15 @@ namespace OpenTK.Input
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>
/// Gets a <see cref="System.Boolean"/> indicating whether this keyboard
/// is connected.
@ -187,48 +210,62 @@ namespace OpenTK.Input
#region Internal Members
internal bool ReadBit(int offset)
internal void SetKeyState(Key key, byte code, bool down)
{
ValidateOffset(offset);
int int_offset = offset / 32;
int bit_offset = offset % 32;
unsafe
if (down)
{
fixed (int* k = Keys)
{
return (*(k + int_offset) & (1 << bit_offset)) != 0u;
}
EnableBit((int)key);
EnableBit(code,true);
}
else
{
DisableBit((int)key);
DisableBit(code, true);
}
}
internal void EnableBit(int offset)
internal bool ReadBit(int offset, bool ScanCode = false)
{
ValidateOffset(offset);
ValidateOffset(offset, ScanCode);
int int_offset = offset / 32;
int bit_offset = offset % 32;
unsafe
{
fixed (int* k = Keys)
{
*(k + int_offset) |= 1 << bit_offset;
}
if (ScanCode)
fixed (int* c = Codes) { return (*(c + int_offset) & (1 << bit_offset)) != 0u; }
else
fixed (int* k = Keys) { return (*(k + int_offset) & (1 << bit_offset)) != 0u; }
}
}
internal void DisableBit(int offset)
internal void EnableBit(int offset, bool ScanCode = false)
{
ValidateOffset(offset);
ValidateOffset(offset, ScanCode);
int int_offset = offset / 32;
int bit_offset = offset % 32;
unsafe
{
fixed (int* k = Keys)
{
*(k + int_offset) &= ~(1 << bit_offset);
}
if (ScanCode)
fixed (int* c = Codes) { *(c + int_offset) |= 1 << bit_offset; }
else
fixed (int* k = Keys) { *(k + int_offset) |= 1 << bit_offset; }
}
}
internal void DisableBit(int offset, bool ScanCode = false)
{
ValidateOffset(offset, ScanCode);
int int_offset = offset / 32;
int bit_offset = offset % 32;
unsafe
{
if (ScanCode)
fixed (int* c = Codes) { *(c + 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++)
*(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;
}
@ -250,9 +293,9 @@ namespace OpenTK.Input
#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");
}

View file

@ -28,7 +28,7 @@ using System.Runtime.InteropServices;
namespace OpenTK
{
/// <summary>
/// Represents a 3x3 Matrix
/// Represents a 3x3 matrix containing 3D rotation and scale.
/// </summary>
[Serializable]
[StructLayout(LayoutKind.Sequential)]
@ -254,7 +254,111 @@ namespace OpenTK
}
#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
#region Static

View file

@ -28,7 +28,7 @@ using System.Runtime.InteropServices;
namespace OpenTK
{
/// <summary>
/// Represents a 3x3 Matrix
/// Represents a 3x3 matrix containing 3D rotation and scale with double-precision components.
/// </summary>
[Serializable]
[StructLayout(LayoutKind.Sequential)]
@ -250,7 +250,111 @@ namespace OpenTK
}
#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
#region Static

View file

@ -28,8 +28,9 @@ using System.Runtime.InteropServices;
namespace OpenTK
{
/// <summary>
/// Represents a 4x4 Matrix
/// Represents a 4x4 matrix containing 3D rotation, scale, transform, and projection.
/// </summary>
/// <seealso cref="Matrix4d"/>
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct Matrix4 : IEquatable<Matrix4>
@ -259,7 +260,7 @@ namespace OpenTK
/// Gets or sets the value at row 4, column 4 of this instance.
/// </summary>
public float M44 { get { return Row3.W; } set { Row3.W = value; } }
#endregion
#region Indexers
@ -315,6 +316,116 @@ namespace OpenTK
#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
#region Static

View file

@ -28,8 +28,9 @@ using System.Runtime.InteropServices;
namespace OpenTK
{
/// <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>
/// <seealso cref="Matrix4"/>
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct Matrix4d : IEquatable<Matrix4d>
@ -298,6 +299,116 @@ namespace OpenTK
#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
#region Static

View file

@ -189,6 +189,34 @@ namespace OpenTK
#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()
/// <summary>
@ -206,7 +234,7 @@ namespace OpenTK
#region public void Conjugate()
/// <summary>
/// Convert this quaternion to its conjugate
/// Inverts the Vector3 component of this Quaternion.
/// </summary>
public void Conjugate()
{

View file

@ -189,6 +189,34 @@ namespace OpenTK
#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()
/// <summary>
@ -206,7 +234,7 @@ namespace OpenTK
#region public void Conjugate()
/// <summary>
/// Convert this Quaterniond to its conjugate
/// Inverts the Vector3d component of this Quaterniond.
/// </summary>
public void Conjugate()
{

View file

@ -289,6 +289,16 @@ namespace OpenTK
#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()
/// <summary>

View file

@ -249,6 +249,17 @@ namespace OpenTK
#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()
/// <summary>

View file

@ -277,6 +277,16 @@ namespace OpenTK
#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()
/// <summary>

View file

@ -275,6 +275,17 @@ namespace OpenTK
#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()
/// <summary>

View file

@ -343,6 +343,16 @@ namespace OpenTK
#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()
/// <summary>

View file

@ -340,6 +340,16 @@ namespace OpenTK
#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()
/// <summary>

View file

@ -368,6 +368,7 @@ namespace OpenTK.Platform.MacOS
break;
}
OpenTK.Input.Key key;
switch (evt.KeyboardEventKind)
{
case KeyboardEventKind.RawKeyRepeat:
@ -376,25 +377,15 @@ namespace OpenTK.Platform.MacOS
break;
case KeyboardEventKind.RawKeyDown:
{
OpenTK.Input.Key key;
if (Keymap.TryGetValue(code, out key))
{
InputDriver.Keyboard[0][key] = true;
OnKeyPress(mKeyPressArgs);
}
Keymap.TryGetValue(code, out key);
InputDriver.Keyboard[0].SetKey(key, (uint)code, true);
OnKeyPress(mKeyPressArgs);
return OSStatus.NoError;
}
case KeyboardEventKind.RawKeyUp:
{
OpenTK.Input.Key key;
if (Keymap.TryGetValue(code, out key))
{
InputDriver.Keyboard[0][key] = false;
}
Keymap.TryGetValue(code, out key);
InputDriver.Keyboard[0].SetKey(key, (uint)code, false);
return OSStatus.NoError;
}
case KeyboardEventKind.RawKeyModifiersChanged:
ProcessModifierKey(inEvent);
@ -614,21 +605,21 @@ namespace OpenTK.Platform.MacOS
Debug.Print("Modifiers Changed: {0}", modifiers);
Input.KeyboardDevice keyboard = InputDriver.Keyboard[0];
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)
keyboard[OpenTK.Input.Key.ShiftLeft] = shift;
keyboard.SetKey(OpenTK.Input.Key.ShiftLeft, (uint)MacOSKeyModifiers.Shift, shift);
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)
keyboard[OpenTK.Input.Key.ControlLeft] = control;
keyboard.SetKey(OpenTK.Input.Key.ControlLeft, (uint)MacOSKeyModifiers.Control, control);
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.KeyboardOrKeypad:
int raw = (int) usage;
if (raw >= RawKeyMap.Length)
if (usage >= RawKeyMap.Length)
{
Debug.Print("[Warning] Key {0} not mapped.", raw);
Debug.Print("[Warning] Key {0} not mapped.", usage);
return state;
}
Key key = RawKeyMap[raw];
state[key] = v_int != 0;
state.SetKeyState(RawKeyMap[usage], (byte)usage, v_int != 0);
break;
}

View file

@ -70,14 +70,12 @@ namespace OpenTK.Platform.Windows
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(key) >> 8) != 0;
if (KeyMap.ContainsKey(key))
{
keyboard[KeyMap[key]] = pressed;
}
bool pressed = (Functions.GetAsyncKeyState((VirtualKeys)i) >> 8) != 0;
Key key;
KeyMap.TryGetValue((VirtualKeys)i,out key);
keyboard.SetKeyState(key, i, pressed);
}
}

View file

@ -89,7 +89,13 @@ namespace OpenTK.Platform.Windows
IList<KeyboardDevice> keyboards = new List<KeyboardDevice>(1);
IList<MouseDevice> mice = new List<MouseDevice>(1);
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);
@ -369,6 +375,8 @@ namespace OpenTK.Platform.Windows
// In this case, both keys will be reported as pressed.
bool extended = (lParam.ToInt64() & ExtendedBit) != 0;
uint scancode = (uint)((lParam.ToInt64() >> 16) & 0xFF);
Key key = Key.Unknown;
switch ((VirtualKeys)wParam)
{
case VirtualKeys.SHIFT:
@ -382,55 +390,50 @@ namespace OpenTK.Platform.Windows
// Otherwise, the state of one key might be stuck to pressed.
if (ShiftRightScanCode != 0 && pressed)
{
unchecked
{
if (((lParam.ToInt64() >> 16) & 0xFF) == ShiftRightScanCode)
keyboard[Input.Key.ShiftRight] = pressed;
else
keyboard[Input.Key.ShiftLeft] = pressed;
}
if (scancode == ShiftRightScanCode)
key = Input.Key.ShiftRight;
else
key = Input.Key.ShiftLeft;
}
else
{
// 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:
if (extended)
keyboard[Input.Key.ControlRight] = pressed;
key = Input.Key.ControlRight;
else
keyboard[Input.Key.ControlLeft] = pressed;
return IntPtr.Zero;
key = Input.Key.ControlLeft;
break;
case VirtualKeys.MENU:
if (extended)
keyboard[Input.Key.AltRight] = pressed;
key = Input.Key.AltRight;
else
keyboard[Input.Key.AltLeft] = pressed;
return IntPtr.Zero;
key = Input.Key.AltLeft;
break;
case VirtualKeys.RETURN:
if (extended)
keyboard[Key.KeypadEnter] = pressed;
key = Key.KeypadEnter;
else
keyboard[Key.Enter] = pressed;
return IntPtr.Zero;
key = Key.Enter;
break;
default:
if (!KeyMap.ContainsKey((VirtualKeys)wParam))
{
Debug.Print("Virtual key {0} ({1}) not mapped.", (VirtualKeys)wParam, (long)lParam);
break;
}
else
{
keyboard[KeyMap[(VirtualKeys)wParam]] = pressed;
}
return IntPtr.Zero;
key = KeyMap[(VirtualKeys)wParam];
break;
}
break;
keyboard.SetKey(key, scancode, pressed);
return IntPtr.Zero;
case WindowMessage.SYSCHAR:
return IntPtr.Zero;

View file

@ -180,31 +180,30 @@ namespace OpenTK.Platform.Windows
switch (rin.Data.Keyboard.VKey)
{
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;
break;
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;
break;
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;
break;
default:
if (!KeyMap.ContainsKey(rin.Data.Keyboard.VKey))
{
Debug.Print("Virtual key {0} ({1}) not mapped.",
rin.Data.Keyboard.VKey, (int)rin.Data.Keyboard.VKey);
}
else
{
keyboard[KeyMap[rin.Data.Keyboard.VKey]] = pressed;
processed = true;
}
Key key;
KeyMap.TryGetValue(rin.Data.Keyboard.VKey, out key);
if (key == Key.Unknown)
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);
processed = true;
break;
}

View file

@ -157,20 +157,22 @@ namespace OpenTK.Platform.X11
case XEventName.KeyPress:
case XEventName.KeyRelease:
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);
IntPtr keysym2 = API.LookupKeysym(ref e.KeyEvent, 1);
if (keymap.ContainsKey((XKey)keysym))
keyboard[keymap[(XKey)keysym]] = pressed;
else if (keymap.ContainsKey((XKey)keysym2))
keyboard[keymap[(XKey)keysym2]] = pressed;
if (keymap.ContainsKey(keysym))
key = keymap[keysym];
else if (keymap.ContainsKey(keysym2))
key = keymap[keysym2];
else
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;
case XEventName.ButtonPress:
if (e.ButtonEvent.button == 1) mouse[OpenTK.Input.MouseButton.Left] = true;
if (e.ButtonEvent.button == 1) mouse[OpenTK.Input.MouseButton.Left] = true;
else if (e.ButtonEvent.button == 2) mouse[OpenTK.Input.MouseButton.Middle] = true;
else if (e.ButtonEvent.button == 3) mouse[OpenTK.Input.MouseButton.Right] = true;
else if (e.ButtonEvent.button == 4) mouse.Wheel++;
@ -190,7 +192,7 @@ namespace OpenTK.Platform.X11
break;
case XEventName.ButtonRelease:
if (e.ButtonEvent.button == 1) mouse[OpenTK.Input.MouseButton.Left] = false;
if (e.ButtonEvent.button == 1) mouse[OpenTK.Input.MouseButton.Left] = false;
else if (e.ButtonEvent.button == 2) mouse[OpenTK.Input.MouseButton.Middle] = false;
else if (e.ButtonEvent.button == 3) mouse[OpenTK.Input.MouseButton.Right] = false;
else if (e.ButtonEvent.button == 6) mouse[OpenTK.Input.MouseButton.Button1] = false;