# 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

// 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);
OpenTK.Matrix4 perspective = OpenTK.Matrix4.CreatePerspectiveFieldOfView ((float)(System.Math.PI / 4f), (float)Width / Height, 1f, 200f);

// 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

## Comment viewing options

### Re: Creating a VBO Cube (Vertex and Element Array Buffers)

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

### Re: Creating a VBO Cube (Vertex and Element Array Buffers)

Yep, it works perfectly in Windows7.

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

### Re: Creating a VBO Cube (Vertex and Element Array Buffers)

I've just look again at your code, and I have some tips:
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)]`

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.

### Re: Creating a VBO Cube (Vertex and Element Array Buffers)

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),
{
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

// 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);
OpenTK.Matrix4 perspective = OpenTK.Matrix4.CreatePerspectiveFieldOfView ((float)(System.Math.PI / 4f), (float)Width / Height, 1f, 600f);

if (vertexBufferID == 0)
return;
if (indiciesBufferID == 0)
return;

GL.MatrixMode (MatrixMode.Modelview);
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.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);
}
}
}
}```

### Re: Creating a VBO Cube (Vertex and Element Array Buffers)

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
{
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 program
ProgramHandle = GL.CreateProgram();
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;

{
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.Enable(EnableCap.DepthTest);
GL.Viewport(0, 0, Width, Height);

GL.MatrixMode(MatrixMode.Projection);
Matrix4 perspective = Matrix4.CreatePerspectiveFieldOfView(
(float) Math.PI / 4f, (float)Width / Height, 1f, 1000f);

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.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);
}
}
}```

### Re: Creating a VBO Cube (Vertex and Element Array Buffers)

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

### Re: Creating a VBO Cube (Vertex and Element Array Buffers)

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
{
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 program
ProgramHandle = GL.CreateProgram();
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),
{
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;

{
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.Enable(EnableCap.DepthTest);
GL.Viewport(0, 0, Width, Height);

GL.MatrixMode(MatrixMode.Projection);
Matrix4 perspective = Matrix4.CreatePerspectiveFieldOfView(
(float)Math.PI / 4f, (float)Width / Height, 1f, 1000f);

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.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);
}
}
}```

### Re: Creating a VBO Cube (Vertex and Element Array Buffers)

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

```                        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?