openecho's picture

Creating a VBO Cube (Vertex and Element Array Buffers)

I have been messing with C# and OpenTK for a few weeks now and I have spent the last few days attempting to get a cube rendering that is VBO based with elements that are also stored in the graphics card. I quickly got this to work on linux (my development platform) but it was not showing correctly on OSX. I did not see any errors, but my screen contained no geometry.

This version of the code works across Windows, OSX and Linux and is based on the current (opentk-2010-10-06.zip) release of OpenTK where it compiles without obsolete warnings. This code was largely based on and acknowledges http://www.opentk.com/node/425.

Hopefully it is useful for someone else.

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Xml.Serialization;
 
using OpenTK;
using OpenTK.Graphics.OpenGL;
 
namespace VBOCube
{
	/// <summary>
	/// Struct to hold the Vector Data. You can use the standard Vector3 struct
	/// that comes with OpenTK. 
	/// 
	/// If your app supports other graphics libraries then it may be appropriate to
	/// have your own vector storage.
	/// </summary>
	[Serializable]
	[StructLayout(LayoutKind.Sequential)]
	public struct Vector3f : IEquatable<Vector3f>
	{
		public float x, y, z;
 
		public static readonly int ByteSize = Marshal.SizeOf (new Vector3f ());
 
		public Vector3f (float x, float y, float z)
		{
			this.x = x;
			this.y = y;
			this.z = z;
		}
 
		public bool Equals (Vector3f other)
		{
			return x == other.x && y == other.y && z == other.z;
		}
	}
 
	class VBOCube : GameWindow
	{
		// Variables for the cube data
		Vector3f[] cubeVertexData = null;
		Vector3f[] cubeNormalData = null;
		int[] colorData = null;
		uint[] indicesVboData = null;
 
		// Variables for the graphics card handles
		int colorBufferID;
		int normalBufferID;
		int vertexBufferID;
		int indiciesBufferID;
 
		// Count of tri elements to be drawn
		int elementCount = 0;
 
		// Storage for the simple rotation angle to make it a bit more interesting
		double rotationAngle = 0;
 
		protected override void OnLoad (EventArgs e)
		{
			Title = "VBO Cube";
			GL.ClearColor (Color.DarkBlue);
 
			// Vertex Data
			cubeVertexData = new Vector3f[] { 
				new Vector3f (-1.0f, -1.0f, 1.0f), 
				new Vector3f (1.0f, -1.0f, 1.0f), 
				new Vector3f (1.0f, 1.0f, 1.0f), 
				new Vector3f (-1.0f, 1.0f, 1.0f), 
				new Vector3f (-1.0f, -1.0f, -1.0f), 
				new Vector3f (1.0f, -1.0f, -1.0f), 
				new Vector3f (1.0f, 1.0f, -1.0f), 
				new Vector3f (-1.0f, 1.0f, -1.0f) 
			};
 
			// Normal Data for the Cube Verticies
			cubeNormalData = new Vector3f[] { 
				new Vector3f (-1.0f, -1.0f, 1.0f), 
				new Vector3f (1.0f, -1.0f, 1.0f), 
				new Vector3f (1.0f, 1.0f, 1.0f), 
				new Vector3f (-1.0f, 1.0f, 1.0f), 
				new Vector3f (-1.0f, -1.0f, -1.0f), 
				new Vector3f (1.0f, -1.0f, -1.0f), 
				new Vector3f (1.0f, 1.0f, -1.0f), 
				new Vector3f (-1.0f, 1.0f, -1.0f) 
			};
 
			// Color Data for the Cube Verticies
			colorData = new int[] { 
				ColorToRgba32 (Color.Cyan), 
				ColorToRgba32 (Color.Cyan), 
				ColorToRgba32 (Color.DarkCyan), 
				ColorToRgba32 (Color.DarkCyan), 
				ColorToRgba32 (Color.Cyan), 
				ColorToRgba32 (Color.Cyan), 
				ColorToRgba32 (Color.DarkCyan), 
				ColorToRgba32 (Color.DarkCyan) 
			};
 
			// Element Indicies for the Cube
			indicesVboData = new uint[] { 
				0, 1, 2, 2, 3, 0, 
				3, 2, 6, 6, 7, 3, 
				7, 6, 5, 5, 4, 7, 
				4, 0, 3, 3, 7, 4, 
				0, 1, 5, 5, 4, 0,
				1, 5, 6, 6, 2, 1 
			};
 
			int bufferSize;
 
			// Color Array Buffer
			if (colorData != null) {
				// Generate Array Buffer Id
				GL.GenBuffers (1, out colorBufferID);
 
				// Bind current context to Array Buffer ID
				GL.BindBuffer (BufferTarget.ArrayBuffer, colorBufferID);
 
				// Send data to buffer
				GL.BufferData (BufferTarget.ArrayBuffer, (IntPtr)(colorData.Length * sizeof(int)), colorData, BufferUsageHint.StaticDraw);
 
				// Validate that the buffer is the correct size
				GL.GetBufferParameter (BufferTarget.ArrayBuffer, BufferParameterName.BufferSize, out bufferSize);
 
				if (colorData.Length * sizeof(int) != bufferSize)
					throw new ApplicationException ("Vertex array not uploaded correctly");
 
				// Clear the buffer Binding
				GL.BindBuffer (BufferTarget.ArrayBuffer, 0);
			}
 
			// Normal Array Buffer
			if (cubeNormalData != null) {
				// Generate Array Buffer Id
				GL.GenBuffers (1, out normalBufferID);
 
				// Bind current context to Array Buffer ID
				GL.BindBuffer (BufferTarget.ArrayBuffer, normalBufferID);
 
				// Send data to buffer
				GL.BufferData (BufferTarget.ArrayBuffer, (IntPtr)(cubeNormalData.Length * Vector3f.ByteSize), cubeNormalData, BufferUsageHint.StaticDraw);
 
				// Validate that the buffer is the correct size
				GL.GetBufferParameter (BufferTarget.ArrayBuffer, BufferParameterName.BufferSize, out bufferSize);
				if (cubeNormalData.Length * Vector3f.ByteSize != bufferSize)
					throw new ApplicationException ("Normal array not uploaded correctly");
 
				// Clear the buffer Binding
				GL.BindBuffer (BufferTarget.ArrayBuffer, 0);
			}
 
			// Vertex Array Buffer
			{
				// Generate Array Buffer Id
				GL.GenBuffers (1, out vertexBufferID);
 
				// Bind current context to Array Buffer ID
				GL.BindBuffer (BufferTarget.ArrayBuffer, vertexBufferID);
 
				// Send data to buffer
				GL.BufferData (BufferTarget.ArrayBuffer, (IntPtr)(cubeVertexData.Length * Vector3f.ByteSize), cubeVertexData, BufferUsageHint.DynamicDraw);
 
				// Validate that the buffer is the correct size
				GL.GetBufferParameter (BufferTarget.ArrayBuffer, BufferParameterName.BufferSize, out bufferSize);
				if (cubeVertexData.Length * Vector3f.ByteSize != bufferSize)
					throw new ApplicationException ("Vertex array not uploaded correctly");
 
				// Clear the buffer Binding
				GL.BindBuffer (BufferTarget.ArrayBuffer, 0);
			}
 
			// Element Array Buffer
			{
				// Generate Array Buffer Id
				GL.GenBuffers (1, out indiciesBufferID);
 
				// Bind current context to Array Buffer ID
				GL.BindBuffer (BufferTarget.ElementArrayBuffer, indiciesBufferID);
 
				// Send data to buffer
				GL.BufferData (BufferTarget.ElementArrayBuffer, (IntPtr)(indicesVboData.Length * sizeof(int)), indicesVboData, BufferUsageHint.StaticDraw);
 
				// Validate that the buffer is the correct size
				GL.GetBufferParameter (BufferTarget.ElementArrayBuffer, BufferParameterName.BufferSize, out bufferSize);
				if (indicesVboData.Length * sizeof(int) != bufferSize)
					throw new ApplicationException ("Element array not uploaded correctly");
 
				// Clear the buffer Binding
				GL.BindBuffer (BufferTarget.ElementArrayBuffer, 0);
			}
 
			elementCount = indicesVboData.Length;
		}
 
		protected override void OnUpdateFrame (FrameEventArgs e)
		{
			base.OnUpdateFrame (e);
			rotationAngle += System.Math.PI / 16;
		}
 
		protected override void OnRenderFrame (FrameEventArgs e)
		{
			base.OnRenderFrame (e);
 
			// Clear the buffers
			GL.Clear (ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
 
			// Enable depth testing so our cube draws correctly
			GL.Enable (EnableCap.DepthTest);
 
			// Set the viewport
			GL.Viewport (0, 0, Width, Height);
 
			// Load a perspective matrix view
			GL.MatrixMode (MatrixMode.Projection);
			GL.LoadIdentity ();
			OpenTK.Matrix4 perspective = OpenTK.Matrix4.CreatePerspectiveFieldOfView ((float)(System.Math.PI / 4f), (float)Width / Height, 1f, 200f);
			GL.LoadMatrix (ref perspective);
 
			// Translate a little into the z-axis so we can see the cube
			GL.Translate (0, 0, -5f);
 
			// Rotate by the current angle
			GL.Rotate (rotationAngle, Vector3d.UnitY);
 
			if (vertexBufferID == 0)
				return;
			if (indiciesBufferID == 0)
				return;
 
			// Color Array Buffer (Colors not used when lighting is enabled)
			if (colorBufferID != 0) {
				// Bind to the Array Buffer ID
				GL.BindBuffer (BufferTarget.ArrayBuffer, colorBufferID);
 
				// Set the Pointer to the current bound array describing how the data ia stored
				GL.ColorPointer (4, ColorPointerType.UnsignedByte, sizeof(int), IntPtr.Zero);
 
				// Enable the client state so it will use this array buffer pointer
				GL.EnableClientState (ArrayCap.ColorArray);
			}
 
			// Vertex Array Buffer
			{
				// Bind to the Array Buffer ID
				GL.BindBuffer (BufferTarget.ArrayBuffer, vertexBufferID);
 
				// Set the Pointer to the current bound array describing how the data ia stored
				GL.VertexPointer (3, VertexPointerType.Float, Vector3f.ByteSize, IntPtr.Zero);
 
				// Enable the client state so it will use this array buffer pointer
				GL.EnableClientState (ArrayCap.VertexArray);
			}
 
			// Element Array Buffer
			{
				// Bind to the Array Buffer ID
				GL.BindBuffer (BufferTarget.ElementArrayBuffer, indiciesBufferID);
 
				// Draw the elements in the element array buffer
				// Draws up items in the Color, Vertex, TexCoordinate, and Normal Buffers using indices in the ElementArrayBuffer
				GL.DrawElements (BeginMode.Triangles, elementCount, DrawElementsType.UnsignedInt, IntPtr.Zero);
			}
 
			SwapBuffers ();
		}
 
		/// <summary>
		/// Converts a Color instance into an int representation
		/// </summary>
		/// <param name="c">
		/// A <see cref="Color"/> instance to be converted
		/// </param>
		/// <returns>
		/// A <see cref="System.Int32"/>
		/// </returns>
		public static int ColorToRgba32 (Color c)
		{
			return (int)((c.A << 24) | (c.B << 16) | (c.G << 8) | c.R);
		}
 
		public static void Main (string[] args)
		{
			using (VBOCube p = new VBOCube ()) {
				p.Run (60);
			}
		}
	}
}

If there is anything that is obsolete or should be corrected then I am very interested.

Images
OSX Cube
Linux Cube

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Tal's picture

Nice! It's very good for OpenGL/TK new programmers!

flopoloco's picture

Yep, it works perfectly in Windows7.

Do you know how can I display 10 cubes instead of only 1?

Tal's picture

I've just look again at your code, and I have some tips:
About the "Vector3f" struct:
It's good to use the StructLayout attribute, but you should use it that way(yes, it have a different):
[StructLayout(LayoutKind.Sequential, Pack = 1)]

About the general code:
Use "class VBOCube" as a separated item, instead of a "GameWindow", so the developer can access many of them as pointed by flopoloco.
Again, it's good you write how to do it.

openecho's picture

Hey Gents,

Okay, over the past few days I have been moving through teaching myself how to use OpenTK and OpenGL.

The following example is a lot more sophisticated (has a texture thats mapped to each face of the cube). It shows how to make many instances of VBO. It also uses different matrix modes which took me a little while to understand.

I agree the cube object should be on its own.. but these are justs tests for me to understand the API then I move to the next. Anyway, here you go.

John

P.S. Just make a square jpg called texture.jpg and throw it in with the project.

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Xml.Serialization;
 
using OpenTK;
using OpenTK.Graphics.OpenGL;
 
namespace VBOTexturedRotatedCube
{
	/// <summary>
	/// Struct to hold the Vector Data. You can use the standard Vector3 struct
	/// that comes with OpenTK. 
	/// 
	/// If your app supports other graphics libraries then it may be appropriate to
	/// have your own vector storage.
	/// </summary>
	[Serializable]
	[StructLayout(LayoutKind.Sequential, Pack = 1)]
	public struct Vector2f : IEquatable<Vector2f>
	{
		public float x, y;
 
		public static readonly int ByteSize = Marshal.SizeOf (new Vector2f ());
 
		public Vector2f (float x, float y)
		{
			this.x = x;
			this.y = y;
		}
 
		public bool Equals (Vector2f other)
		{
			return x == other.x && y == other.y;
		}
	}
 
	[Serializable]
	[StructLayout(LayoutKind.Sequential, Pack = 1)]
	public struct Vector3f : IEquatable<Vector3f>
	{
		public float x, y, z;
 
		public static readonly int ByteSize = Marshal.SizeOf (new Vector3f ());
 
		public Vector3f (float x, float y, float z)
		{
			this.x = x;
			this.y = y;
			this.z = z;
		}
 
		public bool Equals (Vector3f other)
		{
			return x == other.x && y == other.y && z == other.z;
		}
	}
 
	class VBOTexturedRotatedCube : GameWindow
	{
		// Variables for the cube data
		Vector3f[] cubeVertexData = null;
		Vector3f[] cubeNormalData = null;
		Vector2f[] cubeTextureData = null;
		uint[] indicesVboData = null;
 
		// Variables for the graphics card handles
		int textureID;
		int vertexBufferID;
		int normalBufferID;
		int indiciesBufferID;
		int textureBufferID;
 
		// Count of tri elements to be drawn
		int elementCount = 0;
 
		// Storage for the simple rotation angle to make it a bit more interesting
		double rotationAngle = 0;
 
		protected override void OnLoad (EventArgs e)
		{
			Title = "VBO Textured Rotated Cubes";
			GL.ClearColor (Color.Purple);
 
			GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest);
 
            GL.GenTextures(1, out textureID);
            GL.BindTexture(TextureTarget.Texture2D, textureID);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
 
            Bitmap bitmap = new Bitmap("texture.jpg");
            BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
                ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            {
                GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, data.Width, data.Height, 0, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);
            }
            bitmap.UnlockBits(data);
 
            GL.Enable(EnableCap.Texture2D);
 
			// Vertex Data
			cubeVertexData = new Vector3f[] { 
				// Front face
				new Vector3f (-1.0f, -1.0f, 1.0f), 
				new Vector3f (1.0f, -1.0f, 1.0f), 
				new Vector3f (1.0f, 1.0f, 1.0f), 
				new Vector3f (-1.0f, 1.0f, 1.0f),
				// Right face
				new Vector3f (1.0f, -1.0f, 1.0f), 
				new Vector3f (1.0f, -1.0f, -1.0f), 
				new Vector3f (1.0f, 1.0f, -1.0f), 
				new Vector3f (1.0f, 1.0f, 1.0f),
				// Back face
				new Vector3f (1.0f, -1.0f, -1.0f), 
				new Vector3f (-1.0f, -1.0f, -1.0f), 
				new Vector3f (-1.0f, 1.0f, -1.0f), 
				new Vector3f (1.0f, 1.0f, -1.0f),
				// Left face
				new Vector3f (-1.0f, -1.0f, -1.0f), 
				new Vector3f (-1.0f, -1.0f, 1.0f), 
				new Vector3f (-1.0f, 1.0f, 1.0f), 
				new Vector3f (-1.0f, 1.0f, -1.0f),
				// Top Face	
				new Vector3f (-1.0f, 1.0f, 1.0f), 
				new Vector3f (1.0f, 1.0f, 1.0f),
				new Vector3f (1.0f, 1.0f, -1.0f), 
				new Vector3f (-1.0f, 1.0f, -1.0f),
				// Bottom Face
				new Vector3f (1.0f, -1.0f, 1.0f), 
				new Vector3f (-1.0f, -1.0f, 1.0f),
				new Vector3f (-1.0f, -1.0f, -1.0f), 
				new Vector3f (1.0f, -1.0f, -1.0f),
			};
 
			// Normal Data for the Cube Verticies
			cubeNormalData = new Vector3f[] {
				// Front face
				new Vector3f ( 0f, 0f, 1f), 
				new Vector3f ( 0f, 0f, 1f),
				new Vector3f ( 0f, 0f, 1f),
				new Vector3f ( 0f, 0f, 1f), 
				// Right face
				new Vector3f ( 1f, 0f, 0f), 
				new Vector3f ( 1f, 0f, 0f), 
				new Vector3f ( 1f, 0f, 0f), 
				new Vector3f ( 1f, 0f, 0f),
				// Back face
				new Vector3f ( 0f, 0f, -1f), 
				new Vector3f ( 0f, 0f, -1f), 
				new Vector3f ( 0f, 0f, -1f),  
				new Vector3f ( 0f, 0f, -1f), 
				// Left face
				new Vector3f ( -1f, 0f, 0f),  
				new Vector3f ( -1f, 0f, 0f), 
				new Vector3f ( -1f, 0f, 0f),  
				new Vector3f ( -1f, 0f, 0f),
				// Top Face	
				new Vector3f ( 0f, 1f, 0f),  
				new Vector3f ( 0f, 1f, 0f), 
				new Vector3f ( 0f, 1f, 0f),  
				new Vector3f ( 0f, 1f, 0f),
				// Bottom Face
				new Vector3f ( 0f, -1f, 0f),  
				new Vector3f ( 0f, -1f, 0f), 
				new Vector3f ( 0f, -1f, 0f),  
				new Vector3f ( 0f, -1f, 0f)
			};
 
			// Texture Data for the Cube Verticies 
			cubeTextureData = new Vector2f[] {
				// Font Face
                new Vector2f (0, 1),
                new Vector2f (1, 1),
                new Vector2f (1, 0),
                new Vector2f (0, 0),
				// Right Face
				new Vector2f (0, 1),
                new Vector2f (1, 1),
                new Vector2f (1, 0),
                new Vector2f (0, 0),
				// Back Face
                new Vector2f (0, 1),
                new Vector2f (1, 1),
                new Vector2f (1, 0),
                new Vector2f (0, 0),
				// Left Face
				new Vector2f (0, 1),
                new Vector2f (1, 1),
                new Vector2f (1, 0),
                new Vector2f (0, 0),
				// Top Face	
				new Vector2f (0, 1),
                new Vector2f (1, 1),
                new Vector2f (1, 0),
                new Vector2f (0, 0),
				// Bottom Face
				new Vector2f (0, 1),
                new Vector2f (1, 1),
                new Vector2f (1, 0),
                new Vector2f (0, 0)
			};
 
			// Element Indices for the Cube
			indicesVboData = new uint[] { 
				// Font face
				0, 1, 2, 2, 3, 0, 
				// Right face
				7, 6, 5, 5, 4, 7, 
				// Back face
				11, 10, 9, 9, 8, 11, 
				// Left face
				15, 14, 13, 13, 12, 15, 
				// Top Face	
				19, 18, 17, 17, 16, 19,
				// Bottom Face
				23, 22, 21, 21, 20, 23,
			};
 
			int bufferSize;
 
			// Normal Array Buffer
			if (cubeNormalData != null) {
				// Generate Array Buffer Id
				GL.GenBuffers (1, out normalBufferID);
 
				// Bind current context to Array Buffer ID
				GL.BindBuffer (BufferTarget.ArrayBuffer, normalBufferID);
 
				// Send data to buffer
				GL.BufferData (BufferTarget.ArrayBuffer, (IntPtr)(cubeNormalData.Length * Vector3f.ByteSize), cubeNormalData, BufferUsageHint.StaticDraw);
 
				// Validate that the buffer is the correct size
				GL.GetBufferParameter (BufferTarget.ArrayBuffer, BufferParameterName.BufferSize, out bufferSize);
				if (cubeNormalData.Length * Vector3f.ByteSize != bufferSize)
					throw new ApplicationException ("Normal array not uploaded correctly");
 
				// Clear the buffer Binding
				GL.BindBuffer (BufferTarget.ArrayBuffer, 0);
			}
 
			// TexCoord Array Buffer
            if (cubeTextureData != null)
            {
                // Generate Array Buffer Id
                GL.GenBuffers(1, out textureBufferID);
 
                // Bind current context to Array Buffer ID
                GL.BindBuffer(BufferTarget.ArrayBuffer, textureBufferID);
 
                // Send data to buffer
                GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(cubeTextureData.Length * 8), cubeTextureData, BufferUsageHint.StaticDraw);
 
                // Validate that the buffer is the correct size
                GL.GetBufferParameter(BufferTarget.ArrayBuffer, BufferParameterName.BufferSize, out bufferSize);
                if (cubeTextureData.Length * 8 != bufferSize)
                    throw new ApplicationException("TexCoord array not uploaded correctly");
 
                // Clear the buffer Binding
                GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
            }
 
            // Texture Array Buffer
            if (GL.IsEnabled(EnableCap.Texture2D))
            {
                if (textureBufferID != 0)
                {
                    // Bind to the Array Buffer ID
                    GL.BindBuffer(BufferTarget.ArrayBuffer, textureBufferID);
 
                    // Set the Pointer to the current bound array describing how the data ia stored
                    GL.TexCoordPointer(2, TexCoordPointerType.Float, 8, IntPtr.Zero);
 
                    // Enable the client state so it will use this array buffer pointer
					GL.EnableClientState(ArrayCap.TextureCoordArray);
                }
            }
 
			// Vertex Array Buffer
			{
				// Generate Array Buffer Id
				GL.GenBuffers (1, out vertexBufferID);
 
				// Bind current context to Array Buffer ID
				GL.BindBuffer (BufferTarget.ArrayBuffer, vertexBufferID);
 
				// Send data to buffer
				GL.BufferData (BufferTarget.ArrayBuffer, (IntPtr)(cubeVertexData.Length * Vector3f.ByteSize), cubeVertexData, BufferUsageHint.DynamicDraw);
 
				// Validate that the buffer is the correct size
				GL.GetBufferParameter (BufferTarget.ArrayBuffer, BufferParameterName.BufferSize, out bufferSize);
				if (cubeVertexData.Length * Vector3f.ByteSize != bufferSize)
					throw new ApplicationException ("Vertex array not uploaded correctly");
 
				// Clear the buffer Binding
				GL.BindBuffer (BufferTarget.ArrayBuffer, 0);
			}
 
			// Element Array Buffer
			{
				// Generate Array Buffer Id
				GL.GenBuffers (1, out indiciesBufferID);
 
				// Bind current context to Array Buffer ID
				GL.BindBuffer (BufferTarget.ElementArrayBuffer, indiciesBufferID);
 
				// Send data to buffer
				GL.BufferData (BufferTarget.ElementArrayBuffer, (IntPtr)(indicesVboData.Length * sizeof(int)), indicesVboData, BufferUsageHint.StaticDraw);
 
				// Validate that the buffer is the correct size
				GL.GetBufferParameter (BufferTarget.ElementArrayBuffer, BufferParameterName.BufferSize, out bufferSize);
				if (indicesVboData.Length * sizeof(int) != bufferSize)
					throw new ApplicationException ("Element array not uploaded correctly");
 
				// Clear the buffer Binding
				GL.BindBuffer (BufferTarget.ElementArrayBuffer, 0);
			}
 
			elementCount = indicesVboData.Length;
		}
 
		protected override void OnUpdateFrame (FrameEventArgs e)
		{
			base.OnUpdateFrame (e);
			rotationAngle += System.Math.PI / 4;
		}
 
		protected override void OnRenderFrame (FrameEventArgs e)
		{
			base.OnRenderFrame (e);
 
			// Clear the buffers
			GL.Clear (ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
 
			// Enable depth testing so our cube draws correctly
			GL.Enable (EnableCap.DepthTest);
 
			// Set the viewport
			GL.Viewport (0, 0, Width, Height);
 
			// Load a perspective matrix view
			GL.MatrixMode (MatrixMode.Projection);
			GL.LoadIdentity ();
			OpenTK.Matrix4 perspective = OpenTK.Matrix4.CreatePerspectiveFieldOfView ((float)(System.Math.PI / 4f), (float)Width / Height, 1f, 600f);
			GL.LoadMatrix (ref perspective);
 
			if (vertexBufferID == 0)
				return;
			if (indiciesBufferID == 0)
				return;
 
			GL.MatrixMode (MatrixMode.Modelview);
			GL.LoadIdentity ();
			GL.Translate (0, 0, -10f);
			GL.Rotate (rotationAngle, Vector3d.UnitY);
 
			// Vertex Array Buffer
			{
				// Bind to the Array Buffer ID
				GL.BindBuffer (BufferTarget.ArrayBuffer, vertexBufferID);
 
				// Set the Pointer to the current bound array describing how the data ia stored
				GL.VertexPointer (3, VertexPointerType.Float, Vector3f.ByteSize, IntPtr.Zero);
 
				// Enable the client state so it will use this array buffer pointer
				GL.EnableClientState (ArrayCap.VertexArray);
			}
 
			// Element Array Buffer
			{
				// Bind to the Array Buffer ID
				GL.BindBuffer (BufferTarget.ElementArrayBuffer, indiciesBufferID);
 
				// Draw the elements in the element array buffer
				// Draws up items in the Color, Vertex, TexCoordinate, and Normal Buffers using indices in the ElementArrayBuffer
				GL.DrawElements (BeginMode.Triangles, elementCount, DrawElementsType.UnsignedInt, IntPtr.Zero);
			}
 
			for (float i = -16; i <= 16; i=i+4) {
				for (float j = -8; j <= 8; j=j+4) {
					for (float z = -32; z >= -38; z = z-4) {
						GL.MatrixMode (MatrixMode.Modelview);
						GL.LoadIdentity ();
						GL.Translate (i, j, z);
 
						// Just a mess of x y and z to make it interesting.
						int psudoRand = (int) ((i/3f+j/3f)*z/3f/(i+1f)) % 3;
 
						if(Math.Abs(psudoRand) == 0) {
							GL.Rotate (-rotationAngle, Vector3d.UnitX);
						} else if(Math.Abs(psudoRand) == 1) {
							GL.Rotate (rotationAngle, 6, 2, 1);
						} else if(Math.Abs(psudoRand) == 2) {
							GL.Rotate (-rotationAngle, 3, 4, 5);
						}
 
						GL.BindBuffer (BufferTarget.ArrayBuffer, vertexBufferID);
						GL.VertexPointer (3, VertexPointerType.Float, Vector3f.ByteSize, IntPtr.Zero);
						GL.EnableClientState (ArrayCap.VertexArray);
						GL.BindBuffer (BufferTarget.ElementArrayBuffer, indiciesBufferID);
						GL.DrawElements (BeginMode.Triangles, elementCount, DrawElementsType.UnsignedInt, IntPtr.Zero);
					}
				}
			}
 
 
 
			SwapBuffers ();
		}
 
		/// <summary>
		/// Converts a Color instance into an int representation
		/// </summary>
		/// <param name="c">
		/// A <see cref="Color"/> instance to be converted
		/// </param>
		/// <returns>
		/// A <see cref="System.Int32"/>
		/// </returns>
		public static int ColorToRgba32 (Color c)
		{
			return (int)((c.A << 24) | (c.B << 16) | (c.G << 8) | c.R);
		}
 
		public static void Main (string[] args)
		{
			using (VBOTexturedRotatedCube p = new VBOTexturedRotatedCube ()) {
				p.Run (60);
			}
		}
	}
}
flopoloco's picture

Hi there, I packed this functionality into a nice class to make it more reusable. I will work again on it by the 2nd of April, because I have to reach some deadlines in work. In the meantime if you can add texture or shader support for it then it would be great for all OpenTK users want to display graphics.

using System;
using System.Drawing;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
 
namespace OpenTKGraphicsKit
{
	// Todo
	class Shader
	{
		public int ProgramHandle { get; private set; }
		public int VertexHandle { get; private set; }
		public int FragmentHandle { get; private set; }
		public string VertexSource { get; set; }
		public string FragmentSource { get; set; }
 
		public void Create()
		{
			// Quit if there is not shader source
 
			if (VertexSource.Length == 0)
				return;
 
			if (FragmentSource.Length == 0)
				return;
 
			// Create shader objects
			VertexHandle = GL.CreateShader(ShaderType.VertexShader);
			FragmentHandle = GL.CreateShader(ShaderType.FragmentShader);
 
			// Set shader sources
			GL.ShaderSource(VertexHandle, VertexSource);
			GL.ShaderSource(FragmentHandle, FragmentSource);
 
			// Compile shaders
			GL.CompileShader(VertexHandle);
			GL.CompileShader(FragmentHandle);
 
			// Debug log shaders
			System.Diagnostics.Debug.WriteLine(GL.GetShaderInfoLog(VertexHandle));
			System.Diagnostics.Debug.WriteLine(GL.GetShaderInfoLog(FragmentHandle));
 
			// Create program
			ProgramHandle = GL.CreateProgram();
			GL.AttachShader(ProgramHandle, VertexHandle);
			GL.AttachShader(ProgramHandle, FragmentHandle);
			GL.LinkProgram(ProgramHandle);
			System.Diagnostics.Debug.WriteLine(GL.GetProgramInfoLog(ProgramHandle));
			GL.UseProgram(ProgramHandle);
		}
 
		public void SetUniform(string name)
		{
			int location = GL.GetUniformLocation(ProgramHandle, name);
		}
	}
 
	class MeshBuffers
	{
		public Vector3[] VertexData;
		public Vector3[] NormalData;
		public int[] ColorData;
		public uint[] IndicesData;
 
		public int VertexBufferID;
		public int NormalBufferID;
		public int ColorBufferID;
		public int IndicesBufferID;
 
		public void GenerateBuffers()
		{
			if (VertexData != null)
			{
				GL.GenBuffers(1, out VertexBufferID);
				GL.BindBuffer(BufferTarget.ArrayBuffer, VertexBufferID);
				GL.BufferData<Vector3>(BufferTarget.ArrayBuffer, 
				                       new IntPtr(VertexData.Length * Vector3.SizeInBytes),
				                       VertexData, BufferUsageHint.StaticDraw);
			}
 
			if (NormalData != null)
			{
				GL.GenBuffers(1, out NormalBufferID);
				GL.BindBuffer(BufferTarget.ArrayBuffer, NormalBufferID);
				GL.BufferData<Vector3>(BufferTarget.ArrayBuffer, 
				                       new IntPtr(NormalData.Length * Vector3.SizeInBytes),
				                       NormalData, BufferUsageHint.StaticDraw);
			}
 
			if (ColorData != null)
			{
				GL.GenBuffers(1, out ColorBufferID);
				GL.BindBuffer(BufferTarget.ArrayBuffer, ColorBufferID);
				GL.BufferData<int>(BufferTarget.ArrayBuffer,
				                   new IntPtr(ColorData.Length * sizeof(int)),
				                   ColorData, BufferUsageHint.StaticDraw);
			}
 
			if (IndicesData != null)
			{
				GL.GenBuffers(1, out IndicesBufferID);
				GL.BindBuffer(BufferTarget.ElementArrayBuffer, IndicesBufferID);
				GL.BufferData<uint>(BufferTarget.ElementArrayBuffer,
				                    new IntPtr(IndicesData.Length * sizeof(uint)),
				                    IndicesData, BufferUsageHint.StaticDraw);
			}
 
			GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
			GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
		}
 
		public void DrawBuffers()
		{
			if (VertexBufferID != 0)
			{
				GL.BindBuffer(BufferTarget.ArrayBuffer, VertexBufferID);
				GL.VertexPointer(3, VertexPointerType.Float, Vector3.SizeInBytes, IntPtr.Zero);
				GL.EnableClientState(ArrayCap.VertexArray);
			}
 
			if (ColorBufferID != 0)
			{
				GL.BindBuffer(BufferTarget.ArrayBuffer, ColorBufferID);
				GL.ColorPointer(4, ColorPointerType.UnsignedByte, sizeof(int), IntPtr.Zero);
				GL.EnableClientState(ArrayCap.ColorArray);
			}
 
			if (IndicesBufferID != 0)
			{
				GL.BindBuffer(BufferTarget.ElementArrayBuffer, IndicesBufferID);
				GL.DrawElements(BeginMode.Triangles, IndicesData.Length,
				                DrawElementsType.UnsignedInt, IntPtr.Zero);
			}
		}
	}
 
	class Program : GameWindow
	{		
		MeshBuffers mb;
		float rotation = 0f;
 
		protected override void OnLoad(EventArgs e)
		{
			mb = new MeshBuffers();
 
			mb.VertexData = new Vector3[] { 
				new Vector3 (-1.0f, -1.0f, 1.0f), 
				new Vector3 (1.0f, -1.0f, 1.0f), 
				new Vector3 (1.0f, 1.0f, 1.0f), 
				new Vector3 (-1.0f, 1.0f, 1.0f), 
				new Vector3 (-1.0f, -1.0f, -1.0f), 
				new Vector3 (1.0f, -1.0f, -1.0f), 
				new Vector3 (1.0f, 1.0f, -1.0f), 
				new Vector3 (-1.0f, 1.0f, -1.0f) 
			};
 
			mb.NormalData = new Vector3[] { 
				new Vector3 (-1.0f, -1.0f, 1.0f), 
				new Vector3 (1.0f, -1.0f, 1.0f), 
				new Vector3 (1.0f, 1.0f, 1.0f), 
				new Vector3 (-1.0f, 1.0f, 1.0f), 
				new Vector3 (-1.0f, -1.0f, -1.0f), 
				new Vector3 (1.0f, -1.0f, -1.0f), 
				new Vector3 (1.0f, 1.0f, -1.0f), 
				new Vector3 (-1.0f, 1.0f, -1.0f) 
			};
 
			mb.ColorData = new int[] {
				ColorToRgba32 (Color.Cyan), 
				ColorToRgba32 (Color.Cyan), 
				ColorToRgba32 (Color.DarkCyan), 
				ColorToRgba32 (Color.DarkCyan), 
				ColorToRgba32 (Color.Cyan), 
				ColorToRgba32 (Color.Cyan), 
				ColorToRgba32 (Color.DarkCyan), 
				ColorToRgba32 (Color.DarkCyan) 
			};
 
			mb.IndicesData = new uint[] { 
				0, 1, 2, 2, 3, 0, 
				3, 2, 6, 6, 7, 3, 
				7, 6, 5, 5, 4, 7, 
				4, 0, 3, 3, 7, 4, 
				0, 1, 5, 5, 4, 0,
				1, 5, 6, 6, 2, 1 
			};
 
			mb.GenerateBuffers();
		}
 
		protected override void OnRenderFrame(FrameEventArgs e)
		{
			GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
			GL.Enable(EnableCap.DepthTest);
			GL.Viewport(0, 0, Width, Height);
 
			GL.MatrixMode(MatrixMode.Projection);
			GL.LoadIdentity();
			Matrix4 perspective = Matrix4.CreatePerspectiveFieldOfView(
				(float) Math.PI / 4f, (float)Width / Height, 1f, 1000f);
			GL.LoadMatrix(ref perspective);
 
			rotation += 20f * (float)e.Time;
			if (rotation >= 360f) rotation = 0f;
 
			GL.Translate(-2f, 0, -5f);
			GL.Rotate(rotation, 0.6f, 1f, 0.2f);	
			mb.DrawBuffers();
 
			// This is a bonus code block to show
			// reusability of vertex objects.
			GL.LoadMatrix(ref perspective);
			GL.Translate(2f, 0, -5f);
			GL.Rotate(rotation, 1.6f, 0.4f, 1.9f);		
			mb.DrawBuffers();
			// ----------------------------------
 
			SwapBuffers();
		}
 
		public static void Main(string[] args)
		{
			using (Program p = new Program())
			{
				p.Run();
			}
		}
 
		public static int ColorToRgba32 (Color c)
		{
			return (int)((c.A << 24) | (c.B << 16) | (c.G << 8) | c.R);
		}	
	}
}
openecho's picture
flopoloco wrote:

Hi there, I packed this functionality into a nice class to make it more reusable. I will work again on it by the 2nd of April, because I have to reach some deadlines in work. In the meantime if you can add texture or shader support for it then it would be great for all OpenTK users want to display graphics.

Hello,

I did one with multiple textures some time ago.

http://www.opentk.com/node/2318

No shader support or lighting. I am yet to teach myself about either of these things.

Regards,

John

tcsavage's picture

I tried to extend flopoloco's code by adding the texture code from openecho's example but now it crashes when GL.DrawElements is called. Can anyone see why? I'm still trying to understand VBOs and the like.

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
 
namespace OpenTKGraphicsKit
{
    // Todo
    class Shader
    {
        public int ProgramHandle { get; private set; }
        public int VertexHandle { get; private set; }
        public int FragmentHandle { get; private set; }
        public string VertexSource { get; set; }
        public string FragmentSource { get; set; }
 
        public void Create()
        {
            // Quit if there is not shader source
 
            if (VertexSource.Length == 0)
                return;
 
            if (FragmentSource.Length == 0)
                return;
 
            // Create shader objects
            VertexHandle = GL.CreateShader(ShaderType.VertexShader);
            FragmentHandle = GL.CreateShader(ShaderType.FragmentShader);
 
            // Set shader sources
            GL.ShaderSource(VertexHandle, VertexSource);
            GL.ShaderSource(FragmentHandle, FragmentSource);
 
            // Compile shaders
            GL.CompileShader(VertexHandle);
            GL.CompileShader(FragmentHandle);
 
            // Debug log shaders
            System.Diagnostics.Debug.WriteLine(GL.GetShaderInfoLog(VertexHandle));
            System.Diagnostics.Debug.WriteLine(GL.GetShaderInfoLog(FragmentHandle));
 
            // Create program
            ProgramHandle = GL.CreateProgram();
            GL.AttachShader(ProgramHandle, VertexHandle);
            GL.AttachShader(ProgramHandle, FragmentHandle);
            GL.LinkProgram(ProgramHandle);
            System.Diagnostics.Debug.WriteLine(GL.GetProgramInfoLog(ProgramHandle));
            GL.UseProgram(ProgramHandle);
        }
 
        public void SetUniform(string name)
        {
            int location = GL.GetUniformLocation(ProgramHandle, name);
        }
    }
 
    [Serializable()]
    class MeshBuffers
    {
        public Vector3[] VertexData;
        public Vector3[] NormalData;
        public Vector2[] TextureData;
        public int[] ColorData;
        public uint[] IndicesData;
 
        [NonSerialized()]
        public int VertexBufferID;
        [NonSerialized()]
        public int NormalBufferID;
        [NonSerialized()]
        public int ColorBufferID;
        [NonSerialized()]
        public int IndicesBufferID;
        [NonSerialized()]
        int TextureBufferID;
        [NonSerialized()]
        int TextureID;
 
        public void GenerateTexture()
        {
            GL.GenTextures(1, out TextureID);
            GL.BindTexture(TextureTarget.Texture2D, TextureID);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
 
            Bitmap bitmap = new Bitmap("texture.jpg");
            BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
                ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            {
                GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, data.Width, data.Height, 0, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);
            }
            bitmap.UnlockBits(data);
 
            GL.Enable(EnableCap.Texture2D);
        }
 
        public void GenerateBuffers()
        {
            if (VertexData != null)
            {
                GL.GenBuffers(1, out VertexBufferID);
                GL.BindBuffer(BufferTarget.ArrayBuffer, VertexBufferID);
                GL.BufferData<Vector3>(BufferTarget.ArrayBuffer,
                                       new IntPtr(VertexData.Length * Vector3.SizeInBytes),
                                       VertexData, BufferUsageHint.StaticDraw);
            }
 
            if (NormalData != null)
            {
                GL.GenBuffers(1, out NormalBufferID);
                GL.BindBuffer(BufferTarget.ArrayBuffer, NormalBufferID);
                GL.BufferData<Vector3>(BufferTarget.ArrayBuffer,
                                       new IntPtr(NormalData.Length * Vector3.SizeInBytes),
                                       NormalData, BufferUsageHint.StaticDraw);
            }
 
            if (ColorData != null)
            {
                GL.GenBuffers(1, out ColorBufferID);
                GL.BindBuffer(BufferTarget.ArrayBuffer, ColorBufferID);
                GL.BufferData<int>(BufferTarget.ArrayBuffer,
                                   new IntPtr(ColorData.Length * sizeof(int)),
                                   ColorData, BufferUsageHint.StaticDraw);
            }
 
            if (TextureData != null)
            {
                GL.GenBuffers(1, out TextureBufferID);
                GL.BindBuffer(BufferTarget.ArrayBuffer, TextureBufferID);
                GL.BufferData<Vector2>(BufferTarget.ArrayBuffer,
                                    new IntPtr(TextureData.Length * Vector2.SizeInBytes),
                                    TextureData, BufferUsageHint.StaticDraw);
            }
 
            if (IndicesData != null)
            {
                GL.GenBuffers(1, out IndicesBufferID);
                GL.BindBuffer(BufferTarget.ElementArrayBuffer, IndicesBufferID);
                GL.BufferData<uint>(BufferTarget.ElementArrayBuffer,
                                    new IntPtr(IndicesData.Length * sizeof(uint)),
                                    IndicesData, BufferUsageHint.StaticDraw);
            }
 
            GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
            GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
        }
 
        public void DrawBuffers()
        {
            if (VertexBufferID != 0)
            {
                GL.BindBuffer(BufferTarget.ArrayBuffer, VertexBufferID);
                GL.VertexPointer(3, VertexPointerType.Float, Vector3.SizeInBytes, IntPtr.Zero);
                GL.EnableClientState(ArrayCap.VertexArray);
            }
 
            if (NormalBufferID != 0)
            {
                GL.BindBuffer(BufferTarget.ArrayBuffer, NormalBufferID);
                GL.VertexPointer(3, VertexPointerType.Float, Vector3.SizeInBytes, IntPtr.Zero);
                GL.EnableClientState(ArrayCap.NormalArray);
            }
 
            if (TextureBufferID != 0)
            {
                GL.BindBuffer(BufferTarget.ArrayBuffer, TextureBufferID);
                GL.VertexPointer(2, VertexPointerType.Float, Vector2.SizeInBytes, IntPtr.Zero);
                GL.EnableClientState(ArrayCap.TextureCoordArray);
            }
 
            if (ColorBufferID != 0)
            {
                GL.BindBuffer(BufferTarget.ArrayBuffer, ColorBufferID);
                GL.ColorPointer(4, ColorPointerType.UnsignedByte, sizeof(int), IntPtr.Zero);
                GL.EnableClientState(ArrayCap.ColorArray);
            }
 
            if (IndicesBufferID != 0)
            {
                GL.BindBuffer(BufferTarget.ElementArrayBuffer, IndicesBufferID);
                GL.DrawElements(BeginMode.Triangles, IndicesData.Length,
                                DrawElementsType.UnsignedInt, IntPtr.Zero);
            }
        }
    }
 
    class Program : GameWindow
    {
        MeshBuffers mb;
        float rotation = 0f;
 
        protected override void OnLoad(EventArgs e)
        {
            mb = new MeshBuffers();
 
            mb.VertexData = new Vector3[] { 
				new Vector3 (-1.0f, -1.0f, 1.0f), 
				new Vector3 (1.0f, -1.0f, 1.0f), 
				new Vector3 (1.0f, 1.0f, 1.0f), 
				new Vector3 (-1.0f, 1.0f, 1.0f), 
				new Vector3 (-1.0f, -1.0f, -1.0f), 
				new Vector3 (1.0f, -1.0f, -1.0f), 
				new Vector3 (1.0f, 1.0f, -1.0f), 
				new Vector3 (-1.0f, 1.0f, -1.0f) 
			};
 
            mb.NormalData = new Vector3[] { 
				new Vector3 (-1.0f, -1.0f, 1.0f), 
				new Vector3 (1.0f, -1.0f, 1.0f), 
				new Vector3 (1.0f, 1.0f, 1.0f), 
				new Vector3 (-1.0f, 1.0f, 1.0f), 
				new Vector3 (-1.0f, -1.0f, -1.0f), 
				new Vector3 (1.0f, -1.0f, -1.0f), 
				new Vector3 (1.0f, 1.0f, -1.0f), 
				new Vector3 (-1.0f, 1.0f, -1.0f) 
			};
 
            mb.ColorData = new int[] {
				ColorToRgba32 (Color.Cyan), 
				ColorToRgba32 (Color.Cyan), 
				ColorToRgba32 (Color.DarkCyan), 
				ColorToRgba32 (Color.DarkCyan), 
				ColorToRgba32 (Color.Cyan), 
				ColorToRgba32 (Color.Cyan), 
				ColorToRgba32 (Color.DarkCyan), 
				ColorToRgba32 (Color.DarkCyan) 
			};
 
            mb.TextureData = new Vector2[] {
				// Font Face
                new Vector2 (0.0f, 1.0f),
                new Vector2 (1.0f, 1.0f),
                new Vector2 (1.0f, 0.0f),
                new Vector2 (0.0f, 0.0f),
				// Right Face
				new Vector2 (0.0f, 1.0f),
                new Vector2 (1.0f, 1.0f),
                new Vector2 (1.0f, 0.0f),
                new Vector2 (0.0f, 0.0f)
			};
 
            mb.IndicesData = new uint[] { 
				0, 1, 2, 2, 3, 0, 
				3, 2, 6, 6, 7, 3, 
				7, 6, 5, 5, 4, 7, 
				4, 0, 3, 3, 7, 4, 
				0, 1, 5, 5, 4, 0,
				1, 5, 6, 6, 2, 1 
			};
 
            mb.GenerateTexture();
            mb.GenerateBuffers();
 
            FileStream flStream = new FileStream("cube.dat", FileMode.OpenOrCreate, FileAccess.Write);
            try
            {
                BinaryFormatter binFormatter = new BinaryFormatter();
                binFormatter.Serialize(flStream, mb);
            }
            finally
            {
                flStream.Close();
            }
        }
 
        protected override void OnRenderFrame(FrameEventArgs e)
        {
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
            GL.Enable(EnableCap.DepthTest);
            GL.Viewport(0, 0, Width, Height);
 
            GL.MatrixMode(MatrixMode.Projection);
            GL.LoadIdentity();
            Matrix4 perspective = Matrix4.CreatePerspectiveFieldOfView(
                (float)Math.PI / 4f, (float)Width / Height, 1f, 1000f);
            GL.LoadMatrix(ref perspective);
 
            rotation += 20f * (float)e.Time;
            if (rotation >= 360f) rotation = 0f;
 
            GL.Translate(-2f, 0, -5f);
            GL.Rotate(rotation, 0.6f, 1f, 0.2f);
            mb.DrawBuffers();
 
            // This is a bonus code block to show
            // reusability of vertex objects.
            GL.LoadMatrix(ref perspective);
            GL.Translate(2f, 0, -5f);
            GL.Rotate(rotation, 1.6f, 0.4f, 1.9f);
            mb.DrawBuffers();
            // ----------------------------------
 
            SwapBuffers();
        }
 
        public static void Main(string[] args)
        {
            using (Program p = new Program())
            {
                p.Run();
            }
        }
 
        public static int ColorToRgba32(Color c)
        {
            return (int)((c.A << 24) | (c.B << 16) | (c.G << 8) | c.R);
        }
    }
}
Stupid_2D's picture

I have the problem that it draws me the textures 4 Times on each face:

2D Quad:

                        GL.Enable(EnableCap.Texture2D);
			LoadTexture("cross.png");  // look at tcSavage used his code here for testing
 
			VBO = new VertexBufferObject();
			VBO.VertexData = new Vector3[] { 
				new Vector3 (-1.0f, -1.0f, 0.0f), 
				new Vector3 (1.0f, -1.0f, 0.0f), 
				new Vector3 (1.0f, 1.0f, 0.0f), 
				new Vector3 (-1.0f, 1.0f, 0.0f)
			};
			VBO.TextureData=new Vector3[] { 
				new Vector3 (-1.0f, -1.0f, 0.0f), 
				new Vector3 (1.0f, -1.0f, 0.0f), 
				new Vector3 (1.0f, 1.0f, 0.0f), 
				new Vector3 (-1.0f, 1.0f, 0.0f)
			};
 
			VBO.IndicesData = new uint[] { 
				0, 1, 2, 3
			};
public void GenerateBuffers()
...
			if (TextureData != null)
			{
				GL.GenBuffers(1, out TextureBufferID);
				GL.BindBuffer(BufferTarget.TextureBuffer, TextureBufferID);
				GL.BufferData<Vector3>(BufferTarget.TextureBuffer, 
				                       new IntPtr(TextureData.Length * Vector3.SizeInBytes),
				                       TextureData, BufferUsageHint.StaticDraw);
			}
...
public void DrawBuffers()
...
			if(TextureBufferID!=0)
			{
				GL.BindBuffer(BufferTarget.TextureBuffer, TextureBufferID);
				GL.TexCoordPointer(3,TexCoordPointerType.Float, Vector3.SizeInBytes, IntPtr.Zero);
				GL.EnableClientState(ArrayCap.TextureCoordArray);
			}
...

Why is that?