jjspierx's picture

Adding points to a GLControl Window without removing previous points

I am trying to add a series of points to a GLControl Window. I have an accelerometer that I am reading raw values into my program via serial port. I have successfully gotten the program to update the raw value X,Y,Z point on each iteration, but I can't figure out how to successfully have all the previous points show in the GLControl Window in each loop without creating an array and having a loop load each point upon each render. The problem with this method is after a few thousand points the loop becomes so large that video refresh slows to a crawl. Is there a way to add a new point upon each render without having to create a loop through the vector array on each frame? Here is the code I am using from the class I created to handle the GL control objects.

You will notice the commented out loop I was using to loop through the vector array to add all previous points on each render.

public void Render(GLControl glc3D, float accX, float accY, float accZ, int count, Vector3[]points)
        {
 
            accX = accX / 25000;
            accY = accY / 25000;
            accZ = accZ / 25000;
 
            points[counter] = new Vector3(accX, accY, accZ);
 
            //points = new Vector3(accX, accY, accZ);
            //points.add
 
            Matrix4 lookat = Matrix4.LookAt(eye, target, up);
            GL.MatrixMode(MatrixMode.Modelview);
            GL.LoadMatrix(ref lookat);
 
 
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
 
 
            DrawPoint(glc3D, accX, accY, accZ, points, count);
 
            glc3D.SwapBuffers();
            //glc3D.Update();
            //glc3D.Refresh();
        }
 
        public void DrawPoint(GLControl glc3D, float accX, float accY, float accZ, Vector3[] points, int count)
        {
 
            GL.Begin(BeginMode.Points);
 
            GL.Color3(Color.Red);
 
            //for (int i = 0; i < counter; i++ )
            //{
                GL.Vertex3(points[counter]);
            //}
 
            counter++;
 
            GL.End();
        }

Comments

Comment viewing options

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

Well, you have to render your scene entirely each time. But there are a couple of things you can do to speed it up.

Using immediate mode (GL.Begin ... GL.Vertex3 ... GL.End) is extremely slow. Instead, create a vertex buffer object (VBO) and update it each time you have a new point, and then use GL.DrawArrays to render your points. This should give you a pretty considerable speed up.

jjspierx's picture

Great, I will give that a try! I am brand new to OpenGL, so every thing I try is new to me. I haven't used VBO yet, but I will check out one of the many VBO examples and try to implement that. Thank you so much! I may have more questions later, but this should give me a start.

jjspierx's picture

I am still having issues with my code. This time I created a VBO and am trying to update it on each render using GL.DrawArrays. I created a vector array that stores 5000 points. When it initializes they are all set to 0,0,0 (i realize this isn't very efficient and I should probably use a dynamic array, but for a quick and dirty debugging I figured it would be ok). Anyway, on each iteration, I update an integer named counter, and store the newest data read in from the serial port. I have watched the code and I can see that the vector array called "pointAcc" is receiving the proper X,Y,Z coordinates. The following line of code I think should be drawing the entire array:

GL.DrawArrays(BeginMode.Points, 0, counter);

However, all I see is a single point rendered at what appears to be 0,0,0 and it never changes while I move the sensor around, when I should be seeing several points appearing every second. Any ideas on what I am doing wrong? Shouldn't the array indices from 0 to "counter" be displayed each render frame?

Thanks in advance.

 class glcObjects
    {
 
        int counter;
        int vbo;
 
        Vector3[] pointAcc = new Vector3[5000];
 
         public void CreateVertexBuffer()
        {
            GL.GenBuffers(1, out vbo);
            GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
            GL.BufferData<Vector3>(BufferTarget.ArrayBuffer, new IntPtr(pointAcc.Length *      Vector3.SizeInBytes), pointAcc, BufferUsageHint.StaticDraw);
 
        }
 
        public void Render(GLControl glc3D, float accX, float accY, float accZ)
        {
 
            accX = accX / 25000;
            accY = accY / 25000;
            accZ = accZ / 25000;
 
            pointAcc[counter].X = accX;
            pointAcc[counter].Y = accY;
            pointAcc[counter].Z = accZ;
 
            Matrix4 lookat = Matrix4.LookAt(eye, target, up);
            GL.MatrixMode(MatrixMode.Modelview);
            GL.LoadMatrix(ref lookat);
 
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
 
            DrawPoint(glc3D, accX, accY, accZ);
 
            glc3D.SwapBuffers();
 
        }
 
        public void DrawPoint(GLControl glc3D, float accX, float accY, float accZ)
        {
 
            //GL.Begin(BeginMode.Points);
            GL.Color3(Color.Red);
 
            GL.EnableVertexAttribArray(0);
            GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
            GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 0, 0);
 
            GL.DrawArrays(BeginMode.Points, 0, counter);
 
            GL.DisableVertexAttribArray(0);
 
            if (accX != 0)
            {
                counter++;
            }
 
        }
    }
}
jjspierx's picture

I figured it out! I didn't realize I had to call the "CreateVertexBuffer" function each time to load the new data. Thanks for pointing me in the right direction!

jjspierx's picture

Here is a quick video showing the 3D visualization of my sensors as they are being read in by the serial port and plotted using the VBO. Thanks again for the help.

http://www.youtube.com/watch?v=v3s1YNPIoqs

kanato's picture

Nice, glad to see you got it working.

But you shouldn't need to create a vertex buffer every time, you should be able to rewrite the data to the VBO by just calling GL.BindBuffer then GL.BufferData. If you call GL.GenBuffers each time you're creating a memory leak in your video unless you call GL.DeleteBuffer to destroy the old one.

jjspierx's picture

Thanks again, its working much better now that I'm not creating a VBO every render. Very smooth and fast even with thousands of points rendered on two different GLControl windows. The memory leak you mentioned was exactly what was happening, the program would slow to a crawl after a few minutes, now it works great!

I am still drawing the axis on the GLControl windows using Gl.Begin and GL.End. Is that fine, since there are only 3 lines I am drawing and they only move when I rotate the window, or should I think about throwing those vertexes in with the VBO as well?

kanato's picture

You could, depending on what you want to do with them.

I usually think of vertex buffers as being best used for homogeneous data, so in your situation I would make one VBO for the points and have a separate VBO for the lines. Especially since your lines are different colors, you could include the colors for each line in the VBO and draw them all with one call to DrawArrays.

But... there's only three lines. So you're probably not going to see much performance change either way. You may want to do it just for the practice of making a VBO for a different type of data. If you care about being all OpenGL 3.0-4.0 compatible, then you would want to eliminate calls to deprecated functions like GL.Begin. But this won't really improve the functionality of your software at all.