Pages

OpenTK Textures

So you want to know how to use textures? Well i don't know what to say other than read on :]

First off is just the prefix, Loading a texture in its simplest form.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
private Bitmap texture_source;
private int texture;

protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);

    GL.ClearColor(0.0f, 0.0f, 0.0f, 0.0f);

    //Load texture from file
    texture_source = new Bitmap("texture.png");

    //Generate empty texture
    texture = GL.GenTexture();

    //Link empty texture to texture2d
    GL.BindTexture(TextureTarget.Texture2D, texture);

    //Must be set else the texture will show glColor
    GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Linear);
    GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear);

    //Describe to gl what we want the bound texture to look like
    GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, texture_source.Width, texture_source.Height, 0, PixelFormat.Bgra, PixelType.UnsignedByte, IntPtr.Zero);
    
    //Lock pixel data to memory and prepare for pass through
    System.Drawing.Imaging.BitmapData bitmap_data = texture_source.LockBits(new Rectangle(0, 0, texture_source.Width, texture_source.Height),
        System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

    //Tell gl to write the data from are bitmap image/data to the bound texture
    GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, texture_source.Width, texture_source.Height, PixelFormat.Bgra, PixelType.UnsignedByte, bitmap_data.Scan0);
    
    //Release from memory
    texture_source.UnlockBits(bitmap_data);

    //Release texture
    GL.BindTexture(TextureTarget.Texture2D, 0);

    //Enable textures from texture2d target
    GL.Enable(EnableCap.Texture2D);
}

Method
For convenience

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
private int LoadTexture(string path, int quality = 0, bool repeat = true, bool flip_y = false)
{
    Bitmap bitmap = new Bitmap(path);

    //Flip the image
    if (flip_y)
        bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);

    //Generate a new texture target in gl
    int texture = GL.GenTexture();

    //Will bind the texture newly/empty created with GL.GenTexture
    //All gl texture methods targeting Texture2D will relate to this texture
    GL.BindTexture(TextureTarget.Texture2D, texture);

    //The reason why your texture will show up glColor without setting these parameters is actually
    //TextureMinFilters fault as its default is NearestMipmapLinear but we have not established mipmapping
    //We are only using one texture at the moment since mipmapping is a collection of textures pre filtered
    //I'm assuming it stops after not having a collection to check.
    switch (quality)
    {
        case 0:
        default://Low quality
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Linear);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear);
            break;
        case 1://High quality
            //This is in my opinion the best since it doesnt average the result and not blurred to shit
            //but most consider this low quality...
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Nearest);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Nearest);
            break;
    }

    if (repeat)
    {
        //This will repeat the texture past its bounds set by TexImage2D
        GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)All.Repeat);
        GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)All.Repeat);
    }
    else
    {
        //This will clamp the texture to the edge, so manipulation will result in skewing
        //It can also be useful for getting rid of repeating texture bits at the borders
        GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)All.ClampToEdge);
        GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)All.ClampToEdge);
    }

    //Creates a definition of a texture object in opengl
    /* Parameters
     * Target - Since we are using a 2D image we specify the target Texture2D
     * MipMap Count / LOD - 0 as we are not using mipmapping at the moment
     * InternalFormat - The format of the gl texture, Rgba is a base format it works all around
     * Width;
     * Height;
     * Border - must be 0;
     * 
     * Format - this is the images format not gl's the format Bgra i believe is only language specific
     *          C# uses little-endian so you have ARGB on the image A 24 R 16 G 8 B, B is the lowest
     *          So it gets counted first, as with a language like Java it would be PixelFormat.Rgba
     *          since Java is big-endian default meaning A is counted first.
     *          but i could be wrong here it could be cpu specific :P
     *          
     * PixelType - The type we are using, eh in short UnsignedByte will just fill each 8 bit till the pixelformat is full
     *             (don't quote me on that...)
     *             you can be more specific and say for are RGBA to little-endian BGRA -> PixelType.UnsignedInt8888Reversed
     *             this will mimic are 32bit uint in little-endian.
     *             
     * Data - No data at the moment it will be written with TexSubImage2D
     */
    GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, bitmap.Width, bitmap.Height, 0, PixelFormat.Bgra, PixelType.UnsignedByte, IntPtr.Zero);

    //Load the data from are loaded image into virtual memory so it can be read at runtime
    System.Drawing.Imaging.BitmapData bitmap_data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
        System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

    //Writes data to are texture target
    /* Target;
     * MipMap;
     * X Offset - Offset of the data on the x axis
     * Y Offset - Offset of the data on the y axis
     * Width;
     * Height;
     * Format;
     * Type;
     * Data - Now we have data from the loaded bitmap image we can load it into are texture data
     */
    GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, bitmap.Width, bitmap.Height, PixelFormat.Bgra, PixelType.UnsignedByte, bitmap_data.Scan0);

    //Release from memory
    bitmap.UnlockBits(bitmap_data);

    //get rid of bitmap object its no longer needed in this method
    bitmap.Dispose();

    /*Binding to 0 is telling gl to use the default or null texture target
    *This is useful to remember as you may forget that a texture is targeted
    *And may overflow to functions that you dont necessarily want to
    *Say you bind a texture
    *
    * Bind(Texture);
    * DrawObject1();
    *                <-- Insert Bind(NewTexture) or Bind(0)
    * DrawObject2();
    * 
    * Object2 will use Texture if not set to 0 or another.
    */
    GL.BindTexture(TextureTarget.Texture2D, 0);

    return texture;
}

Usage Example

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
private int current_texture;

protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);

    GL.ClearColor(0.0f, 0.0f, 0.0f, 0.0f);

    current_texture = LoadTexture("texture.png", 1);

    GL.Enable(EnableCap.Texture2D);
    //Basically enables the alpha channel to be used in the color buffer
    GL.Enable(EnableCap.Blend);
    //The operation/order to blend
    GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
    //Use for pixel depth comparing before storing in the depth buffer
    GL.Enable(EnableCap.DepthTest);
}

protected override void OnRenderFrame(FrameEventArgs e)
{
    base.OnRenderFrame(e);

    GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
    GL.MatrixMode(MatrixMode.Modelview);
    GL.LoadIdentity();

    GL.PushMatrix();

    GL.Translate(0, 0, -5);

    GL.Color4(Color.White);

    GL.BindTexture(TextureTarget.Texture2D, current_texture);

    GL.Begin(BeginMode.Quads);

    //Bind texture coordinates to vertices in ccw order

    //Top-Right
    GL.TexCoord2(1.0f, 0.0f);
    GL.Vertex2(1.0f, 1.0f);

    //Top-Left
    GL.TexCoord2(0f, 0f);
    GL.Vertex2(-1.0f, 1.0f);

    //Bottom-Left
    GL.TexCoord2(0f, 1f);
    GL.Vertex2(-1.0f, -1.0f);

    //Bottom-Right
    GL.TexCoord2(1f, 1f);
    GL.Vertex2(1.0f, -1.0f);

    GL.End();

    GL.BindTexture(TextureTarget.Texture2D, 0);

    GL.PopMatrix();

    SwapBuffers();
}

AS3 Input - Mouse And Keyboard

Messing around with OpenTK i find using the Keyboard class very comfortable and thought i would do something similar in AS3, I've used input classes in all of my AS3 projects having to add the same event in a different class over and over to get input for different tasks is cumbersome and it's not very wise stacking events like that, So an input class is very useful.

Keyboard

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package  
{
 import flash.display.DisplayObject;
 import flash.events.Event;
 import flash.events.KeyboardEvent;
 
 /**
  * ...
  * @author Zero
  */
 public final class Keyboard extends Array
 {
  public static var KEY_DOWN:int = 1;
  public static var KEY_UP:int = 0;
  public static var KEY_PRESSED:int = 2;
  public static var KEY_RELEASED:int = -1;
  
  private static var initialized:Boolean;
  //a reasonable 127 or 255
  private static var char_count:int;
  
  public static function Initialize(main_class:DisplayObject, character_count:int = 127):void
  {
   if (initialized)
    return;
   
   initialized = true;
   
   char_count = character_count;
   
   main_class.stage.addEventListener(KeyboardEvent.KEY_DOWN, KeyDown);
   main_class.stage.addEventListener(KeyboardEvent.KEY_UP, KeyUp);
  }
  
  private static function KeyDown(e:KeyboardEvent):void
  {
   Keyboard[e.keyCode] = (Keyboard[e.keyCode] == 0 ? 2 : 1);
  }
  
  private static function KeyUp(e:KeyboardEvent):void
  {
   Keyboard[e.keyCode] = -1;
  }
  
  public static function Update():void
  {
   for (var i:int = 0; i < char_count; i++)
   {
    if (Keyboard[i] > 0)
     Keyboard[i] = 1;
    else
     Keyboard[i] = 0;
   }
  }
 }
}

Mouse

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package  
{
 import flash.display.DisplayObject;
 import flash.events.MouseEvent;
 
 /**
  * ...
  * @author Zero
  */
 public final class Mouse extends Array
 {
  public static const LEFT:int = 0;
  public static const MIDDLE:int = 1;
  public static const RIGHT:int = 2;
  
  public static const BUTTON_DOWN:int = 1;
  public static const BUTTON_UP:int = 0;
  public static const BUTTON_PRESSED:int = 2;
  public static const BUTTON_RELEASED:int = -1;
  
  public static var initialized:Boolean;
  
  private static var mouse_dX:int, mouse_dY:int, mouse_X:int, mouse_Y:int;
  private static var mouse_moving:Boolean, mouse_delta_sign:Boolean = true;
  
  public static function Initialize(main_class:DisplayObject):void
  {
   if (initialized)
    return;
    
   initialized = true;
   
   main_class.stage.addEventListener(MouseEvent.MOUSE_DOWN, MouseDown);
   main_class.stage.addEventListener(MouseEvent.MOUSE_UP, MouseUp);
   
   main_class.stage.addEventListener(MouseEvent.MIDDLE_MOUSE_DOWN, MouseDown);
   main_class.stage.addEventListener(MouseEvent.MIDDLE_MOUSE_UP, MouseUp);
   
   main_class.stage.addEventListener(MouseEvent.RIGHT_MOUSE_DOWN, MouseDown);
   main_class.stage.addEventListener(MouseEvent.RIGHT_MOUSE_UP, MouseUp);
   
   main_class.stage.addEventListener(MouseEvent.MOUSE_MOVE, MouseMove);
  }
  
  private static function MouseDown(e:MouseEvent):void
  {
   var button_code:int;
   
   if (e.type == MouseEvent.MOUSE_DOWN)
    button_code = LEFT;
   if (e.type == MouseEvent.MIDDLE_MOUSE_DOWN)
    button_code = MIDDLE;
   if (e.type == MouseEvent.RIGHT_MOUSE_DOWN)
    button_code = RIGHT;
    
   Mouse[button_code] = (Mouse[button_code] == 0 ? 2 : 1);
  }
  
  private static function MouseUp(e:MouseEvent):void
  {
   var button_code:int;
   
   if (e.type == MouseEvent.MOUSE_UP)
    button_code = LEFT;
   if (e.type == MouseEvent.MIDDLE_MOUSE_UP)
    button_code = MIDDLE;
   if (e.type == MouseEvent.RIGHT_MOUSE_UP)
    button_code = RIGHT;
   
   Mouse[button_code] = -1;
  }
  
  private static function MouseMove(e:MouseEvent):void
  {
   mouse_dX = (int)(e.localX) - mouse_X;
   mouse_dY = (int)(e.localY) - mouse_Y;
   
   mouse_X = e.localX;
   mouse_Y = e.localY;
   
   mouse_moving = true;
  }
  
  public static function get Moving():Boolean
  {
   return mouse_moving;
  }
  
  public static function get X():int
  {
   return mouse_X;
  }
  
  public static function get Y():int
  {
   return mouse_Y;
  }
  
  public static function SetDeltaSign(flag:Boolean):void
  {
   mouse_delta_sign = flag;
  }
  
  public static function get DeltaX():int
  {
   return (mouse_delta_sign ? (mouse_dX < -1 ? -1 : mouse_dX > 1 ? 1 : 0) : mouse_dX);
  }
  
  public static function get DeltaY():int
  {
   return (mouse_delta_sign ? (mouse_dY < -1 ? -1 : mouse_dY > 1 ? 1 : 0) : mouse_dY);
  }
  
  public static function Update():void
  {
   for (var i:int = 0; i < 3; i++)
   {
    if (Mouse[i] > 0)
     Mouse[i] = 1;
    else
     Mouse[i] = 0;
   }
   
   mouse_moving = false;
   
   //reset delta sign
   mouse_dX = 0;
   mouse_dY = 0;
  }
 }
}

Usage Example
I use FlashDevelop :]
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package 
{
 import flash.display.Sprite;
 import flash.events.Event;
 
 /**
  * ...
  * @author Zero
  */
 
 [SWF(backgroundColor='0x000000', frameRate='60')]
 public class Main extends Sprite 
 {
  public function Main():void 
  {
   if (stage) init();
   else addEventListener(Event.ADDED_TO_STAGE, init);
  }
  
  private function init(e:Event = null):void 
  {
   removeEventListener(Event.ADDED_TO_STAGE, init);
   // entry point

   Keyboard.Initialize(this);
   Mouse.Initialize(this);
   
   addEventListener(Event.ENTER_FRAME, Update);
  }
  
  private final function Update(e:Event):void
  {
   if (Keyboard[e2Key.D] == Keyboard.KEY_PRESSED)
    trace("Key Pressed");
   if (Keyboard[e2Key.D] == Keyboard.KEY_DOWN)
    trace("Key Down");
   if (Keyboard[e2Key.D] == Keyboard.KEY_RELEASED)
    trace("Key Released");
    
   //Get any key pressed
   if (Keyboard.Pressed())
   {
    trace(Keyboard.Pressed())
   }
    
   if (Mouse[0] == Mouse.BUTTON_PRESSED)
    trace("Left Mouse Pressed");
   if (Mouse[0] == Mouse.BUTTON_DOWN)
    trace("Left Mouse Down");
   if (Mouse[0] == Mouse.BUTTON_RELEASED)
    trace("Left Mouse Released");
    
   if (Mouse[1] == Mouse.BUTTON_PRESSED)
    trace("Middle Mouse Pressed");
   if (Mouse[1] == Mouse.BUTTON_DOWN)
    trace("Middle Mouse Down");
   if (Mouse[1] == Mouse.BUTTON_RELEASED)
    trace("Middle Mouse Released");
    
   if (Mouse[2] == Mouse.BUTTON_PRESSED)
    trace("Right Mouse Pressed");
   if (Mouse[2] == Mouse.BUTTON_DOWN)
    trace("Right Mouse Down");
   if (Mouse[2] == Mouse.BUTTON_RELEASED)
    trace("Right Mouse Released");
    
   if (Mouse.Moving)
   {
    trace("Mouse Delta X " + Mouse.DeltaX);
    trace("Mouse Delta Y " + Mouse.DeltaY);
   } 
   
   //update has to be after events else everything is reset.
   Keyboard.Update();
   Mouse.Update();
  }
 }
}

Drawing a Grid in OpenTK

A simple method to drawing a grid in GL with all the fixings
This can also be useful with the Camera2D/Camera3D classes

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public void DrawGrid(System.Drawing.Color color, float X, float Y, int cell_size = 16, int grid_size = 256)
{
    int dX = (int)Math.Round(X / cell_size) * cell_size;
    int dZ = (int)Math.Round(Z / cell_size) * cell_size;

    int ratio = grid_size / cell_size;

    GL.PushMatrix();

    GL.Translate(dX - grid_size / 2, 0, dZ - grid_size / 2);

    int i;

    GL.Color3(color);
    GL.Begin(BeginMode.Lines);

    for (i = 0; i < ratio + 1; i++)
    {
        int current = i * cell_size;

        GL.Vertex3(current, 0, 0);
        GL.Vertex3(current, 0, grid_size);

        GL.Vertex3(0, 0, current);
        GL.Vertex3(grid_size, 0, current);
    }

    GL.End();

    GL.PopMatrix();
}

-Update-
Made a little example of a grid using fog.



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
namespace Tutorial
{
    class Main : GameWindow
    {
        private Camera3D camera;
        private int cell_size = 32;
        private int grid_size = 4096;

        public Main()
            : base(800, 600)
        {
            Title = "Grid";
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            GL.ClearColor(0.0f, 0.0f, 0.0f, 0.0f);

            GL.Enable(EnableCap.Fog);
            GL.Fog(FogParameter.FogColor, new float[4] { 0.0f, 0.0f, 0.0f, 1.0f });//Same color as clear.
            GL.Fog(FogParameter.FogMode, (int)FogMode.Linear);
            GL.Fog(FogParameter.FogEnd, (float)(grid_size / 3.0f));

            camera = new Camera3D(0, 1, 0);

            this.Mouse.Move += new EventHandler<OpenTK.Input.MouseMoveEventArgs>(Mouse_Move);
        }

        private void Mouse_Move(object sender, OpenTK.Input.MouseMoveEventArgs e)
        {
            int dX = e.XDelta;
            int dY = e.YDelta;

            camera.RotationX += (float)dY / 5.0f;
            camera.RotationY += (float)dX / 5.0f;
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);

            GL.Viewport(0, 0, Width, Height);

            OpenTK.Matrix4 matrix = OpenTK.Matrix4.CreatePerspectiveFieldOfView(1.0f, (Width / (float)Height), 1.0f, 10000.0f);

            GL.MatrixMode(MatrixMode.Projection);
            GL.LoadMatrix(ref matrix);
        }

        protected override void OnUpdateFrame(FrameEventArgs e)
        {
            base.OnUpdateFrame(e);

            if (Keyboard[OpenTK.Input.Key.W])
                camera.Move(Camera3D.Direction.Forward);
            if (Keyboard[OpenTK.Input.Key.S])
                camera.Move(Camera3D.Direction.Backward);
            if (Keyboard[OpenTK.Input.Key.A])
                camera.Move(Camera3D.Direction.Left);
            if (Keyboard[OpenTK.Input.Key.D])
                camera.Move(Camera3D.Direction.Right);
            if (Keyboard[OpenTK.Input.Key.Q])
                camera.Move(Camera3D.Direction.Up);
            if (Keyboard[OpenTK.Input.Key.E])
                camera.Move(Camera3D.Direction.Down);

            if (Keyboard[OpenTK.Input.Key.Number1])
            {
                GL.Enable(EnableCap.Fog);
            }
            if (Keyboard[OpenTK.Input.Key.Number2])
            {
                GL.Disable(EnableCap.Fog);
            }
        }

        protected override void OnRenderFrame(FrameEventArgs e)
        {
            base.OnRenderFrame(e);

            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
            GL.MatrixMode(MatrixMode.Modelview);
            GL.LoadIdentity();

            camera.Update();
            camera.DrawGrid(System.Drawing.Color.LimeGreen, cell_size, grid_size);

            SwapBuffers();
        }
    }
}

View Change and 2D & 3D Camera Templates

Going to be lazy here, A method to change views and a camera class.
 
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
private void ChangeView(bool ortho = false)
{
    Matrix4 matrix;

    if (ortho)
    {
        matrix = Matrix4.CreateOrthographic(Width, Height, 1.0f, 100.0f);
    }
    else
    {
        matrix = Matrix4.CreatePerspectiveFieldOfView(1.0f, (float)(Width / (double)Height), 1.0f, 10000.0f);
    }

    GL.MatrixMode(MatrixMode.Projection);
    GL.LoadMatrix(ref matrix);
}


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class Camera2D
{
    public enum DrawMode
    {
        Normal,
        Wireframe
    }

    public float X { get; set; }
    public float Y { get; set; }

    public float Angle { get; set; }

    private DrawMode _mode;
    public DrawMode Mode
    {
        get { return _mode; }
        set
        { 
            _mode = value;
            UpdateDrawMode();
        }
    }

    public Camera2D(float x = 0.0f, float y = 0.0f)
    {
        X = x;
        Y = y;
        _mode = DrawMode.Normal;
    }

    private void UpdateDrawMode()
    {
        switch (_mode)
        {
            case DrawMode.Normal:
                GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill);
                break;
            case DrawMode.Wireframe:
                GL.PolygonMode(MaterialFace.Front, PolygonMode.Line);
                break;
        }
    }

    public void Update()
    {
        GL.Rotate(Angle, 0, 0, 1);
        GL.Translate(-X, -Y, 0);
    }
}

Also a 3D version RotationZ is ignored on movement, it is just a simple camera.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
class Camera3D
{
    public enum Direction
    {
        Forward,
        Backward,
        Left,
        Right,
        Up,
        Down
    }

    public float X { get; set; }
    public float Y { get; set; }
    public float Z { get; set; }

    public float RotationX { get; set; }
    public float RotationY { get; set; }
    public float RotationZ { get; set; }

    public Camera3D(float x = 0.0f, float y = 0.0f, float z = 0.0f)
    {
        X = x;
        Y = y;
        Z = z;
    }

    public void Update()
    {
        GL.Rotate(RotationX, 1, 0, 0);
        GL.Rotate(RotationY, 0, 1, 0);
        GL.Rotate(RotationZ, 0, 0, 1);
        GL.Translate(-X, -Y, -Z);
    }

    public void Move(Direction direction, float speed = 1.0f, bool flying = false)
    {
        float dX = RotationX * (float)(Math.PI / 180);
        float dY = RotationY * (float)(Math.PI / 180);

        switch (direction)
        {
            case Direction.Forward:
                X +=  (float)Math.Sin(dY) * speed;
                Z += -(float)Math.Cos(dY) * speed;
                if (flying) 
                    Y += -(float)Math.Sin(dX) * speed;
                break;
            case Direction.Backward:
                X += -(float)Math.Sin(dY) * speed;
                Z +=  (float)Math.Cos(dY) * speed;
                if (flying) 
                    Y += (float)Math.Sin(dX) * speed;
                break;
            case Direction.Left:
                X += -(float)Math.Cos(dY) * speed;
                Z += -(float)Math.Sin(dY) * speed;
                break;
            case Direction.Right:
                X += (float)Math.Cos(dY) * speed;
                Z += (float)Math.Sin(dY) * speed;
                break;
            case Direction.Up:
                Y += speed;
                break;
            case Direction.Down:
                Y -= speed;
                break;
        }
    }
}