shp's picture

Example project of a VBO in a GLControl

Hi,
I'm just starting out with OpenTK and OpenGL. Is there a simple example project of a VBO in a GLControl? I don't want to use a Gamewindow.

Thanks
Shp


Comments

Comment viewing options

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

So this is what I have so far... it is based on these post
http://www.opentk.com/node/2302
and
http://www.opentk.com/node/2318
and other stuff I've copied of the internet. The code runs, but nothing is shown. I'm sure there is something simple i'm not doing right.
Thanks

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using OpenTK;
using OpenTK.Graphics.OpenGL;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
 
namespace Glyphs3d
{
    public partial class Form2a : Form
    {
        bool loaded = false;
        Stopwatch sw = new Stopwatch();
        double accumulator = 0;
        int idleCounter = 0;
        float rotation = 0;
        /// <summary>
        /// Struct to hold our VBO data.
        /// </summary>
        [Serializable]
        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct VBO
        {
            public int vertexBufferID;
            public int normalBufferID;
            public int indiciesBufferID;
            public int textureBufferID;
            public int elementCount;
        }
        // Variables for the VBO handles
        VBO cube = new VBO();
        VBO pyramid = new VBO();
 
        // Variables for the textures
        int cubeTextureID = 0;
        int pyramidTextureID = 0;
 
        // Storage for the simple rotation angle to make it a bit more interesting
        double rotationAngle = 0;
        double randomSeed1 = 0;
        double randomSeed2 = 0;
 
 
 
        public Form2a()
        {
            InitializeComponent();
        }
        // initialisation
        private void initorig()
        {
            // initialise Glut
            //Glut.glutInit();
 
            float[] mat_specular = { 1.0f, 1.0f, 1.0f, 1.0f };
            float[] mat_shininess = { 50.0f };
            float[] light_position = { 1.0f, 1.0f, 1.0f, 0.0f };
            float[] light_ambient = { 0.5f, 0.5f, 0.5f, 1.0f };
 
            GL.ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
            GL.ShadeModel(ShadingModel.Smooth);
 
            GL.Material(MaterialFace.Front, MaterialParameter.Specular, mat_specular);
            GL.Material(MaterialFace.Front, MaterialParameter.Shininess, mat_shininess);
            GL.Light(LightName.Light0, LightParameter.Position, light_position);
            GL.Light(LightName.Light0, LightParameter.Ambient, light_ambient);
            GL.Light(LightName.Light0, LightParameter.Diffuse, mat_specular);
 
            GL.Enable(EnableCap.Lighting);
            GL.Enable(EnableCap.Light0);
            GL.Enable(EnableCap.DepthTest);
            GL.Enable(EnableCap.ColorMaterial);
            GL.Enable(EnableCap.CullFace);
 
            GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest);
 
            // Vbo Data
            VBOData cubeData = new CubeVBOData();
            VBOData pyramidData = new PyramidVBOData();
 
            // Load the textures we are going to use
            LoadTexture("C:\\Code_hause\\__SynGlyphX\\Src_Code\\GBP\\Glyphs3d\\Glyphs3d\\Glyphs3d\\texture_01.jpg", out cubeTextureID);
            LoadTexture("C:\\Code_hause\\__SynGlyphX\\Src_Code\\GBP\\Glyphs3d\\Glyphs3d\\Glyphs3d\\texture_02.jpg", out pyramidTextureID);
 
            // Create the VBOs
            CreateVBO(ref cubeData, ref cube);
            CreateVBO(ref pyramidData, ref pyramid);
 
            // Set some random numbers for rendering different stuff in the background
            Random random = new Random();
            randomSeed1 = random.NextDouble();
            randomSeed2 = random.NextDouble();
 
            Application.Idle += Application_Idle;
            sw.Start(); // start at application boot
 
        }
 
        [Serializable]
        public class VBOData
        {
            public float[] VertexData;
            public float[] NormalData;
            public float[] TextureData;
            public uint[] IndicesData;
 
            public VBOData()
            {
                VertexData = null;
                NormalData = null;
                TextureData = null;
                IndicesData = null;
            }
        }
 
        public class CubeVBOData : VBOData
        {
            public CubeVBOData()
            {
                init();
            }
 
            public void init()
            {
                // Vertex Data
                VertexData = new float[] { 
					// Front face
					-1.0f, -1.0f, 1.0f, 
					1.0f, -1.0f, 1.0f, 
					1.0f, 1.0f, 1.0f, 
					-1.0f, 1.0f, 1.0f,
					// Right face
					1.0f, -1.0f, 1.0f, 
					1.0f, -1.0f, -1.0f, 
					1.0f, 1.0f, -1.0f, 
					1.0f, 1.0f, 1.0f,
					// Back face
					1.0f, -1.0f, -1.0f, 
					-1.0f, -1.0f, -1.0f, 
					-1.0f, 1.0f, -1.0f, 
					1.0f, 1.0f, -1.0f,
					// Left face
					-1.0f, -1.0f, -1.0f, 
					-1.0f, -1.0f, 1.0f, 
					-1.0f, 1.0f, 1.0f, 
					-1.0f, 1.0f, -1.0f,
					// Top Face	
					-1.0f, 1.0f, 1.0f, 
					1.0f, 1.0f, 1.0f,
					1.0f, 1.0f, -1.0f, 
					-1.0f, 1.0f, -1.0f,
					// Bottom Face
					1.0f, -1.0f, 1.0f, 
					-1.0f, -1.0f, 1.0f,
					-1.0f, -1.0f, -1.0f, 
					1.0f, -1.0f, -1.0f
				};
 
                // Normal Data for the Cube Verticies
                NormalData = new float[] {
					// Front face
					 0f, 0f, 1f, 
					 0f, 0f, 1f,
					 0f, 0f, 1f,
					 0f, 0f, 1f, 
					// Right face
					 1f, 0f, 0f, 
					 1f, 0f, 0f, 
					 1f, 0f, 0f, 
					 1f, 0f, 0f,
					// Back face
					 0f, 0f, -1f, 
					 0f, 0f, -1f, 
					 0f, 0f, -1f,  
					 0f, 0f, -1f, 
					// Left face
					 -1f, 0f, 0f,  
					 -1f, 0f, 0f, 
					 -1f, 0f, 0f,  
					 -1f, 0f, 0f,
					// Top Face	
					 0f, 1f, 0f,  
					 0f, 1f, 0f, 
					 0f, 1f, 0f,  
					 0f, 1f, 0f,
					// Bottom Face
					 0f, -1f, 0f,  
					 0f, -1f, 0f, 
					 0f, -1f, 0f,  
					 0f, -1f, 0f
				};
 
                // Texture Data for the Cube Verticies 
                TextureData = new float[] {
					// Font Face
	                0, 1,
	                1, 1,
	                1, 0,
	                0, 0,
					// Right Face
					0, 1,
	                1, 1,
	                1, 0,
	                0, 0,
					// Back Face
	                0, 1,
	                1, 1,
	                1, 0,
	                0, 0,
					// Left Face
					0, 1,
	                1, 1,
	                1, 0,
	                0, 0,
					// Top Face	
					0, 1,
	                1, 1,
	                1, 0,
	                0, 0,
					// Bottom Face
					0, 1,
	                1, 1,
	                1, 0,
	                0, 0
				};
 
                // Element Indices for the Cube
                IndicesData = 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,
				};
            }
        }
        public void CreateVBO(ref VBOData vboData, ref VBO vbo)
        {
            int bufferSize;
 
            // Normal Array Buffer
            if (vboData.NormalData != null)
            {
                // Generate Array Buffer Id
                GL.GenBuffers(1, out vbo.normalBufferID);
 
                // Bind current context to Array Buffer ID
                GL.BindBuffer(BufferTarget.ArrayBuffer, vbo.normalBufferID);
 
                // Send data to buffer
                GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vboData.NormalData.Length * sizeof(float)), vboData.NormalData, BufferUsageHint.StaticDraw);
 
                // Validate that the buffer is the correct size
                GL.GetBufferParameter(BufferTarget.ArrayBuffer, BufferParameterName.BufferSize, out bufferSize);
                if (vboData.NormalData.Length * sizeof(float) != bufferSize)
                    throw new ApplicationException("Normal array not uploaded correctly");
 
                // Clear the buffer Binding
                GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
            }
 
            // TexCoord Array Buffer
            if (vboData.TextureData != null)
            {
                // Generate Array Buffer Id
                GL.GenBuffers(1, out vbo.textureBufferID);
 
                // Bind current context to Array Buffer ID
                GL.BindBuffer(BufferTarget.ArrayBuffer, vbo.textureBufferID);
 
                // Send data to buffer
                GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vboData.TextureData.Length * sizeof(float)), vboData.TextureData, BufferUsageHint.StaticDraw);
 
                // Validate that the buffer is the correct size
                GL.GetBufferParameter(BufferTarget.ArrayBuffer, BufferParameterName.BufferSize, out bufferSize);
                if (vboData.TextureData.Length * sizeof(float) != bufferSize)
                    throw new ApplicationException("TexCoord array not uploaded correctly");
 
                // Clear the buffer Binding
                GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
            }
 
            // Vertex Array Buffer
            {
                // Generate Array Buffer Id
                GL.GenBuffers(1, out vbo.vertexBufferID);
 
                // Bind current context to Array Buffer ID
                GL.BindBuffer(BufferTarget.ArrayBuffer, vbo.vertexBufferID);
 
                // Send data to buffer
                GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vboData.VertexData.Length * sizeof(float)), vboData.VertexData, BufferUsageHint.DynamicDraw);
 
                // Validate that the buffer is the correct size
                GL.GetBufferParameter(BufferTarget.ArrayBuffer, BufferParameterName.BufferSize, out bufferSize);
                if (vboData.VertexData.Length * sizeof(float) != 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 vbo.indiciesBufferID);
 
                // Bind current context to Array Buffer ID
                GL.BindBuffer(BufferTarget.ElementArrayBuffer, vbo.indiciesBufferID);
 
                // Send data to buffer
                GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(vboData.IndicesData.Length * sizeof(int)), vboData.IndicesData, BufferUsageHint.StaticDraw);
 
                // Validate that the buffer is the correct size
                GL.GetBufferParameter(BufferTarget.ElementArrayBuffer, BufferParameterName.BufferSize, out bufferSize);
                if (vboData.IndicesData.Length * sizeof(int) != bufferSize)
                    throw new ApplicationException("Element array not uploaded correctly");
 
                // Clear the buffer Binding
                GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
            }
 
            vbo.elementCount = vboData.IndicesData.Length;
        }
 
        public class PyramidVBOData : VBOData
        {
            public PyramidVBOData()
            {
                init();
            }
 
            public void init()
            {
                // Vertex Data
                VertexData = new float[] { 
					// Front face
					-1.0f, -1.0f, 1.0f, 
					1.0f, -1.0f, 1.0f, 
					0f, 1.0f, 0f, 
 
					// Right face
					1.0f, -1.0f, 1.0f, 
					1.0f, -1.0f, -1.0f, 
					0f, 1.0f, 0f, 
 
					// Back face
					1.0f, -1.0f, -1.0f, 
					-1.0f, -1.0f, -1.0f, 
					0f, 1.0f, 0f, 
 
					// Left face
					-1.0f, -1.0f, -1.0f, 
					-1.0f, -1.0f, 1.0f, 
					0f, 1.0f, 0f, 
 
					// Bottom Face
					1.0f, -1.0f, 1.0f, 
					-1.0f, -1.0f, 1.0f,
					-1.0f, -1.0f, -1.0f, 
					1.0f, -1.0f, -1.0f
				};
 
                // Normal Data for the Pyramid Verticies
                NormalData = new float[] {
					// Front face
					 0f, 0f, 1f, 
					 0f, 0f, 1f,
					 0f, 0f, 1f, 
					// Right face
					 1f, 0f, 0f, 
					 1f, 0f, 0f, 
					 1f, 0f, 0f,
					// Back face
					 0f, 0f, -1f, 
					 0f, 0f, -1f, 
					 0f, 0f, -1f, 
					// Left face
					 -1f, 0f, 0f,  
					 -1f, 0f, 0f, 
					 -1f, 0f, 0f, 
					// Bottom Face
					 0f, -1f, 0f,  
					 0f, -1f, 0f, 
					 0f, -1f, 0f,  
					 0f, -1f, 0f
				};
 
                // Texture Data for the Pyramid Verticies 
                TextureData = new float[] {
					// Font Face
	                0, 1,
	                0.5f, 1,
	                0.25f, 0,
					// Right Face
	                0, 1,
	                0.5f, 1,
	                0.25f, 0,
					// Back Face
	                0, 1,
	                0.5f, 1,
	                0.25f, 0, 
					// Left Face
	                0, 1,
	                0.5f, 1,
	                0.25f, 0,
					// Bottom Face
					0.5f, 1f,
	                1f, 1f,
	                1f, 0f,
					0.5f, 0f
				};
 
                // Element Indices for the Pyramid
                IndicesData = new uint[] { 
					// Front face
					0, 1, 2, 
					// Right face
					3, 4, 5, 
					// Back face
					6, 7, 8, 
					// Left face
					9, 10, 11, 
					// Bottom Face
					15, 14, 13, 13, 12, 15,
				};
            }
        }
 
        public static void LoadTexture(string path, out int textureID)
        {
            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(path);
            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.BindTexture(TextureTarget.Texture2D, 0);
        }
        private void Accumulate(double milliseconds)
        {
            idleCounter++;
            accumulator += milliseconds;
            if (accumulator > 1000)
            {
                label1.Text = idleCounter.ToString();
                accumulator -= 1000;
                idleCounter = 0; // don't forget to reset the counter!
            }
        }
        private double ComputeTimeSlice()
        {
            sw.Stop();
            double timeslice = sw.Elapsed.TotalMilliseconds;
            sw.Reset();
            sw.Start();
            return timeslice;
        }
        void Application_Idle(object sender, EventArgs e)
        {
            // no guard needed -- we hooked into the event in Load handler
            double milliseconds = ComputeTimeSlice();
            Accumulate(milliseconds);
            Animate(milliseconds);
        }
        private void Animate(double milliseconds)
        {
            float deltaRotation = (float)milliseconds / 20.0f;
            rotation += deltaRotation;
 
            glControl2.Invalidate();
        }
        // draws the scene
        private void display()
        {
            // GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
 
            //GL.PushMatrix();
            //{
            //    GL.Rotate(10, 1, 0, 0);
            //    GL.Rotate(10, 0, 1, 0);
            //    GL.Rotate(rotation, OpenTK.Vector3.UnitZ);
            //    GL.Color3(0.0, 0.5, 1.0);
 
 
            //    // *** SET THIS TO FALSE TO CHANGE TO GLUTSOLIDCUBE ***
            //    bool drawQuads = true;
 
            //    if (drawQuads)
            //    {
            //        GL.Begin(BeginMode.Quads);
            //        {
            //            // front face
            //            GL.Normal3(0.0, 0.0, 1.0);
            //            GL.Vertex3(-0.5, -0.5, 0.5);
            //            GL.Vertex3(0.5, -0.5, 0.5);
            //            GL.Vertex3(0.5, 0.5, 0.5);
            //            GL.Vertex3(-0.5, 0.5, 0.5);
 
            //            // back face
            //            GL.Normal3(0.0, 0.0, -1.0);
            //            GL.Vertex3(-0.5, -0.5, -0.5);
            //            GL.Vertex3(-0.5, 0.5, -0.5);
            //            GL.Vertex3(0.5, 0.5, -0.5);
            //            GL.Vertex3(0.5, -0.5, -0.5);
 
            //            // top face
            //            GL.Normal3(0.0, 1.0, 0.0);
            //            GL.Vertex3(-0.5, 0.5, -0.5);
            //            GL.Vertex3(-0.5, 0.5, 0.5);
            //            GL.Vertex3(0.5, 0.5, 0.5);
            //            GL.Vertex3(0.5, 0.5, -0.5);
 
            //            // bottom face
            //            GL.Normal3(0.0, -1.0, 0.0);
            //            GL.Vertex3(-0.5, -0.5, -0.5);
            //            GL.Vertex3(0.5, -0.5, -0.5);
            //            GL.Vertex3(0.5, -0.5, 0.5);
            //            GL.Vertex3(-0.5, -0.5, 0.5);
 
            //            // right face
            //            GL.Normal3(1.0, 0.0, 0.0);
            //            GL.Vertex3(0.5, -0.5, -0.5);
            //            GL.Vertex3(0.5, 0.5, -0.5);
            //            GL.Vertex3(0.5, 0.5, 0.5);
            //            GL.Vertex3(0.5, -0.5, 0.5);
 
            //            // left face
            //            GL.Normal3(-1.0, 0.0, 0.0);
            //            GL.Vertex3(-0.5, -0.5, -0.5);
            //            GL.Vertex3(-0.5, -0.5, 0.5);
            //            GL.Vertex3(-0.5, 0.5, 0.5);
            //            GL.Vertex3(-0.5, 0.5, -0.5);
            //        }
            //        GL.End();
            //    }
            //    else
            //    {
            //        //Glut.glutSolidCube(1.0);
            //    }
            //}
            //GL.PopMatrix();
 
            //GL.Flush();
 
            // from onRenderFrame
            // 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, glControl2.Width, glControl2.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);
 
            // Draw the Cube
            GL.MatrixMode(MatrixMode.Modelview);
            GL.LoadIdentity();
            GL.Translate(-2f, 0, -10f);
            GL.Rotate(rotationAngle, Vector3d.UnitY);
 
            DrawVBO(ref cube, cubeTextureID);
 
            // Draw the Pyramid
            GL.MatrixMode(MatrixMode.Modelview);
            GL.LoadIdentity();
            GL.Translate(2f, 0, -10f);
            GL.Rotate(-rotationAngle, Vector3d.UnitY);
 
            DrawVBO(ref pyramid, pyramidTextureID);
 
            //glControl2.SwapBuffers();
            glControl2.Invalidate();
 
        }
        public void DrawVBO(ref VBO vbo, int textureID)
        {
            if (!loaded) return;
            if (vbo.vertexBufferID == 0)
                return;
            if (vbo.indiciesBufferID == 0)
                return;
 
            GL.Enable(EnableCap.Texture2D);
            GL.BindTexture(TextureTarget.Texture2D, textureID);
 
            // Texture Data Buffer Binding
            if (vbo.textureBufferID != 0)
            {
                // Bind to the Array Buffer ID
                GL.BindBuffer(BufferTarget.ArrayBuffer, vbo.textureBufferID);
 
                // Set the Pointer to the current bound array describing how the data ia stored
                GL.TexCoordPointer(2, TexCoordPointerType.Float, sizeof(float) * 2, IntPtr.Zero);
 
                // Enable the client state so it will use this array buffer pointer
                GL.EnableClientState(ArrayCap.TextureCoordArray);
            }
 
            // Vertex Array Buffer
            {
                // Bind to the Array Buffer ID
                GL.BindBuffer(BufferTarget.ArrayBuffer, vbo.vertexBufferID);
 
                // Set the Pointer to the current bound array describing how the data ia stored
                GL.VertexPointer(3, VertexPointerType.Float, sizeof(float) * 3, 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, vbo.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, cube.elementCount, DrawElementsType.UnsignedInt, IntPtr.Zero);
            }
            GL.Disable(EnableCap.Texture2D);
            //glControl2.Invalidate();
        }
        // resizes the viewport
        private void reshape(int w, int h)
        {
            if (!loaded) return;
            GL.Viewport(0, 0, w, h);
            GL.MatrixMode(MatrixMode.Projection);
            GL.LoadIdentity();
            if (w <= h)
            {
                GL.Ortho(-1.5, 1.5, -1.5 * (double)h / (double)w, 1.5 * (double)h / (double)w, -10.0, 10.0);
            }
            else
            {
                GL.Ortho(-1.5 * (double)w / (double)h, 1.5 * (double)w / (double)h, -1.5, 1.5, -10.0, 10.0);
            }
            GL.MatrixMode(MatrixMode.Modelview);
            GL.LoadIdentity();
        }
 
        // GLControl resize event handler
        private void glControl2_Resize(object sender, EventArgs e)
        {
            if (!loaded) return;
            reshape(glControl2.Width, glControl2.Height);
        }
 
        // GLControl paint event handler
        private void glControl2_Paint(object sender, PaintEventArgs e)
        {
            if (!loaded) return;
            display();
            //glControl2.SwapBuffers();
            //glControl2.Invalidate();
        }
 
        // Main form load event handler
        private void Form2_Load(object sender, EventArgs e)
        {
            loaded = true;
            initorig();
            reshape(glControl2.Width, glControl2.Height);
        }
    }
}
winterhell's picture

For a start can you change the backbuffer color of the GLControl? For example the color to be a variable parameter that changes frame to frame.

shp's picture

Yes I can do that now.

winterhell's picture

I'd recommend for you to see the code for GameWindow's example of rendering a triangle in immediate mode, make that work with glControl so you know the problem is not with the matrices or the states, and then add the VBO Draw Calls in place of the immediate mode.