[Solved] OpenTK Static VBO Help

Whether you're a newbie or an experienced programmer, any questions, help, or just talk of any language will be welcomed here.

Moderator: Coders of Rage

Post Reply
User avatar
Odin
Chaos Rift Newbie
Chaos Rift Newbie
Posts: 5
Joined: Fri Aug 10, 2012 3:15 am
Favorite Gaming Platforms: PC, NES, SNES, PSX/1, GBA
Programming Language of Choice: C/C++/C#/AngelScript

[Solved] OpenTK Static VBO Help

Post by Odin »

I'm having a problem learning to use VBO's mainly drawing, Hoping that someone with alot more experience with OpenGL/TK can help.

This is the working code (pseudo C#) i used the OpenTK static vbo example for a template to VBO's
but didn't want to use interleaved arrays or a vertex struct

Code: Select all

float[] vertices =
{ 
    -1.0f, -1.0f, 0.0f,
     1.0f, -1.0f, 0.0f,
     1.0f,  1.0f, 0.0f,
    -1.0f,  1.0f, 0.0f 
};

float[] textureCoords =
{
    1.0f, 1.0f,
    1.0f, 0.0f,
    0.0f, 0.0f,
    0.0f, 1.0f
};

short[] indices = { 0, 1, 2, 0, 2, 3 };

int vertexBuffer;
int textureBuffer;
int indexBuffer;

void Initialize()
{
    //48 bytes
    GL.GenBuffers(1, out vertexBuffer);
    GL.BindBuffer(BufferTarget.ArrayBuffer, vertexBuffer);
    GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vertices.Length * BlittableValueType.StrideOf(vertices)), vertices, BufferUsageHint.StaticDraw);
    //32 bytes
    GL.GenBuffers(1, out textureBuffer);
    GL.BindBuffer(BufferTarget.TextureBuffer, textureBuffer);
    GL.BufferData(BufferTarget.TextureBuffer, (IntPtr)(textureCoords.Length * BlittableValueType.StrideOf(textureCoords)), textureCoords, BufferUsageHint.StaticDraw);
    //16 bytes
    GL.GenBuffers(1, out indexBuffer);
    GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBuffer);
    GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(indices.Length * sizeof(short)), indices, BufferUsageHint.StaticDraw);
}

void Draw()
{
    GL.VertexPointer(3, VertexPointerType.Float, 3 * sizeof(float), new IntPtr(0));
    GL.EnableClientState(ArrayCap.VertexArray);

    GL.Enable(EnableCap.Texture2D);
    GL.BindTexture(TextureTarget.Texture2D, textureID);
    GL.TexCoordPointer(2, TexCoordPointerType.Float, 4 * sizeof(float), new IntPtr(8));
    GL.EnableClientState(ArrayCap.TextureCoordArray);

    GL.DrawElements(BeginMode.Triangles, indices.Length, DrawElementsType.UnsignedShort, new IntPtr(0));
}
now i dont get how that works because if its pointing by bytes
a stride of 8 starting at a point of 8 would still be in the vertices array

GL.VertexPointer(3, VertexPointerType.Float, 3 * sizeof(float), new IntPtr(0));
GL.TexCoordPointer(2, TexCoordPointerType.Float, 4 * sizeof(float), new IntPtr(8));

would be wrong and the correct way would be

GL.VertexPointer(3, VertexPointerType.Float, 3 * sizeof(float), new IntPtr(0));
GL.TexCoordPointer(2, TexCoordPointerType.Float, 2 * sizeof(float), new IntPtr(48));

and i have tried GL.BindBuffer before each pointer but the textureCoords still read from vertices
and using an array as a parameter in the pointers doesn't work either
I'm hoping i just messed up on how it all works and its not a problem with OpenTK :P
Last edited by Odin on Sat Aug 18, 2012 2:36 am, edited 1 time in total.
User avatar
Falco Girgis
Elysian Shadows Team
Elysian Shadows Team
Posts: 10294
Joined: Thu May 20, 2004 2:04 pm
Current Project: Elysian Shadows
Favorite Gaming Platforms: Dreamcast, SNES, NES
Programming Language of Choice: C/++
Location: Studio Vorbis, AL
Contact:

Re: OpenTK Static VBO Help

Post by Falco Girgis »

Is that the actual code? I am both curious as to how that works in C# (the pointers in particular), and I would like to help clarify for you, but I can't without being able to analyze the actual C# in brooding detail, because I am not very fluent in it these days. :)
User avatar
Odin
Chaos Rift Newbie
Chaos Rift Newbie
Posts: 5
Joined: Fri Aug 10, 2012 3:15 am
Favorite Gaming Platforms: PC, NES, SNES, PSX/1, GBA
Programming Language of Choice: C/C++/C#/AngelScript

Re: OpenTK Static VBO Help

Post by Odin »

Well here is my actual class i tried cutting it down in the first post for convenience with the problem :P

Code: Select all

public class e2Rect
{
    private float[] vertices =
	    { 
		    -1.0f, -1.0f, 0.0f,
		     1.0f, -1.0f, 0.0f,
		     1.0f,  1.0f, 0.0f,
		    -1.0f,  1.0f, 0.0f 
	    };

    private float[] textureCoords =
	    {
		    1.0f, 1.0f,
		    1.0f, 0.0f,
		    0.0f, 0.0f,
		    0.0f, 1.0f
        };

    public e2Vec3 Position;
    public e2Vec3 Rotation;

    public static int textureID;

    private short[] indices = { 0, 1, 2, 0, 2, 3 };

    private int vertexBuffer;
    private int textureBuffer;
    private int indexBuffer;

    private static bool hasTexture;

    public e2Rect()
    {
        Position = new e2Vec3();
        Rotation = new e2Vec3();

        int size;

        GL.GenBuffers(1, out vertexBuffer);
        GL.BindBuffer(BufferTarget.ArrayBuffer, vertexBuffer);
        GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vertices.Length * BlittableValueType.StrideOf(vertices)), vertices, BufferUsageHint.StaticDraw);
        GL.GetBufferParameter(BufferTarget.ArrayBuffer, BufferParameterName.BufferSize, out size);
        if (vertices.Length * BlittableValueType.StrideOf(vertices) != size)
            throw new ApplicationException("Vertex data not uploaded correctly");

        GL.GenBuffers(1, out textureBuffer);
        GL.BindBuffer(BufferTarget.TextureBuffer, textureBuffer);
        GL.BufferData(BufferTarget.TextureBuffer, (IntPtr)(textureCoords.Length * BlittableValueType.StrideOf(textureCoords)), textureCoords, BufferUsageHint.StaticDraw);
        GL.GetBufferParameter(BufferTarget.TextureBuffer, BufferParameterName.BufferSize, out size);
        if (textureCoords.Length * BlittableValueType.StrideOf(textureCoords) != size)
            throw new ApplicationException("Vertex data not uploaded correctly");

        GL.GenBuffers(1, out indexBuffer);
        GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBuffer);
        GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(indices.Length * sizeof(short)), indices, BufferUsageHint.StaticDraw);
        GL.GetBufferParameter(BufferTarget.ElementArrayBuffer, BufferParameterName.BufferSize, out size);
        if (indices.Length * sizeof(short) != size)
            throw new ApplicationException("Element data not uploaded correctly");
    }

    public void Draw()
    {
        GL.PushMatrix();
        GL.Color3(System.Drawing.Color.White);
        GL.FrontFace(FrontFaceDirection.Ccw);
        GL.Enable(EnableCap.CullFace);
        GL.CullFace(CullFaceMode.Back);

        GL.Enable(EnableCap.AlphaTest);
        GL.AlphaFunc(AlphaFunction.Greater, 0.1f);

        GL.Translate(Position.X, Position.Y, Position.Z);
        GL.Rotate(Rotation.X, 1f, 0.0f, 0.0f);
        GL.Rotate(Rotation.Y, 0.0f, 1f, 0.0f);
        GL.Rotate(Rotation.Z, 0.0f, 0.0f, 1f);

        //GL.BindBuffer(BufferTarget.ArrayBuffer, vertexBuffer);
        GL.VertexPointer(3, VertexPointerType.Float, 3 * sizeof(float), IntPtr.Zero);
        GL.EnableClientState(ArrayCap.VertexArray);

        GL.Enable(EnableCap.Blend);
        GL.BlendFunc(BlendingFactorSrc.One, BlendingFactorDest.OneMinusSrcAlpha);

        if (hasTexture)
        {
            GL.Enable(EnableCap.Texture2D);
            GL.BindTexture(TextureTarget.Texture2D, textureID);
            //GL.BindBuffer(BufferTarget.TextureBuffer, textureBuffer);
            //GL.TexCoordPointer(2, TexCoordPointerType.Float, 2 * sizeof(float), new IntPtr(0));
            GL.TexCoordPointer(2, TexCoordPointerType.Float, 4 * sizeof(float), new IntPtr(8));
            GL.EnableClientState(ArrayCap.TextureCoordArray);
        }

        GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBuffer);
        GL.DrawElements(BeginMode.Triangles, indices.Length, DrawElementsType.UnsignedShort, IntPtr.Zero);

        if (hasTexture)
            GL.DisableClientState(ArrayCap.TextureCoordArray);
        GL.DisableClientState(ArrayCap.VertexArray);

        GL.Disable(EnableCap.Texture2D);
        GL.Disable(EnableCap.CullFace);
        GL.Disable(EnableCap.Blend);

        GL.PopMatrix();
    }

    public static void loadTexture()
    {
        textureID = e2Loader.LoadImage(e2Textures.Odin3);
        hasTexture = true;
    }
}
Now this is from my knowledge of drawing in OpenTK like this so far, So i don't know if it is correct or not.

Code: Select all

//Generate a buffer...
GL.GenBuffers(1, out vertexBuffer);

//Bind said buffer...
GL.BindBuffer(BufferTarget.ArrayBuffer, vertexBuffer);

//Write data to the currently bound buffer,
//1st param is the buffer type.
//Same as a byte array, the second parameter is the length in bytes of the buffer data
//so i have 12 vertices all floats, float in bytes is 4 so 48 bytes in total, or vertices.Length * sizeof(float);
//last param is telling GL how to draw it, not that GL will always listen...
GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vertices.Length * BlittableValueType.StrideOf(vertices)), vertices, BufferUsageHint.StaticDraw);

//draw
//now point to the ArrayBuffer
//1st parameter is the size to read
//2nd is the type float in this case
//3rd is the stride in bytes so its reading 3 floats so 12 bytes
//4th is the Pointer in bytes where to start reading
GL.VertexPointer(3, VertexPointerType.Float, 3 * sizeof(float), IntPtr.Zero);
GL.EnableClientState(ArrayCap.VertexArray);
Now the thing is i wrote the data to the Buffers so it has the references to each array
now if its acting as my whole class is being read then
GL.TexCoordPointer(2, TexCoordPointerType.Float, 2 * sizeof(float), new IntPtr(48));
would work since both arrays are at the top and the textureCoords is 48 bytes (vertices) from the top
but soon found out after my last post that, that can't be right since my class is nullable and GL would throw an error since it only takes non-nullable parameters.
so in fact
GL.TexCoordPointer(2, TexCoordPointerType.Float, 2 * sizeof(float), new IntPtr(0));
should be the correct way since it was referenced to bind to the TextureBuffer on init of the class
but it's acting like the textureCoords are bound to the vertexBuffer and only reading those

GL.BindBuffer(BufferTarget.ArrayBuffer, vertexBuffer);
GL.TexCoordPointer(2, TexCoordPointerType.Float, 2 * sizeof(float), new IntPtr(0));

and i found this out by debugging ofcourse...
OdinScreeny1.png
OdinScreeny1.png (39.28 KiB) Viewed 3314 times
which is
GL.TexCoordPointer(2, TexCoordPointerType.Float, 2 * sizeof(float), new IntPtr(0));
and
OdinScreeny2.png
OdinScreeny2.png (40.84 KiB) Viewed 3314 times
is
GL.TexCoordPointer(3, TexCoordPointerType.Float, 3 * sizeof(float), new IntPtr(0));

now i don't know how it get's to the vertex buffer when the texture pointer is to the texture buffer lol
i hope i explained that well enough :]
qpHalcy0n
Respected Programmer
Respected Programmer
Posts: 387
Joined: Fri Dec 19, 2008 3:33 pm
Location: Dallas
Contact:

Re: OpenTK Static VBO Help

Post by qpHalcy0n »

Did you just take bits and pieces from the tutorial they gave you so that you wouldn't have to deal w/ interleaved arrays? The code above is pretty incomplete.

Remember that when you bind an object as ElementArrayBuffer, it remains current until kicked out by binding a different handle to ElementArrayBuffer or until deleted. The issue is that if you've got two separate buffer assets, one containing position information, and the other containing texture addressing information, then you've got a problem right there. In order to render, you'd have to bind the position data, and the texture data both at the same time. You cannot do this. The texture array bind will kick out the position array bind. This is why we interleave data, not to mention it's more efficient.

Any time you are binding a "0" (NULL) ptr to a VertexPointer call, or whatever, you're telling it to use the currently bound buffer. In this case you cannot perform a render like you want without the two buffers mutually kicking themselves out.

Now remember interleaved arrays look thusly:
xyzuvxyzuvxyzuvxyzuv (Where each is a float)

Non-interleaved arrays look thusly:
xyzxyzxyzxyzxyzuvuvuvuvuvuvuv

Here you can see the difference. But you cannot have two separate buffers, simply because they can't both be bound at the same time as such.
User avatar
Odin
Chaos Rift Newbie
Chaos Rift Newbie
Posts: 5
Joined: Fri Aug 10, 2012 3:15 am
Favorite Gaming Platforms: PC, NES, SNES, PSX/1, GBA
Programming Language of Choice: C/C++/C#/AngelScript

Re: OpenTK Static VBO Help

Post by Odin »

I think i know what you mean, i fixed the problem.

i was binding textureCoords to the TextureBuffer
changed that to write to ArrayBuffer instead

Code: Select all

//load
GL.GenBuffers(1, out vertexBuffer);
GL.BindBuffer(BufferTarget.ArrayBuffer, vertexBuffer);
GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vertices.Length * BlittableValueType.StrideOf(vertices)), vertices, BufferUsageHint.StaticDraw);

GL.GenBuffers(1, out textureBuffer);
GL.BindBuffer(BufferTarget.ArrayBuffer, textureBuffer);
GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(textureCoords.Length * BlittableValueType.StrideOf(textureCoords)), textureCoords, BufferUsageHint.StaticDraw);

//draw
GL.BindBuffer(BufferTarget.ArrayBuffer, vertexBuffer);
GL.VertexPointer(3, VertexPointerType.Float, 3 * sizeof(float), IntPtr.Zero);
GL.EnableClientState(ArrayCap.VertexArray);

GL.Enable(EnableCap.Texture2D);
GL.BindTexture(TextureTarget.Texture2D, textureID);
GL.BindBuffer(BufferTarget.ArrayBuffer, textureBuffer);
GL.TexCoordPointer(2, TexCoordPointerType.Float, 2 * sizeof(float), IntPtr.Zero);
GL.EnableClientState(ArrayCap.TextureCoordArray);
now it draws correctly, from both vertices and texcoords :D

but i still don't know if what im thinking is the right way, is correct.
now i write my vertex and texcoord data to the ArrayBuffer,
and then it gets distributed to the correct buffer by pointers eh?
as in...

ArrayBuffer(vertices, textureCoords)
->TransformFeedbackBuffer(vertices)
->TextureBuffer(textureCoords)


:I i hope that's how it works, else this is going to be very confusing on my end lol
qpHalcy0n
Respected Programmer
Respected Programmer
Posts: 387
Joined: Fri Dec 19, 2008 3:33 pm
Location: Dallas
Contact:

Re: OpenTK Static VBO Help

Post by qpHalcy0n »

I'm not sure how OpenTK is organized, but it looks like this SHOULDN'T work. Essentially with a simple C implementation, the buffers are never bound at the SAME time. This is required for rendering. Because what would happen here is that the texture buffer would kick the vertex buffer out. You need all of the data streamed in for the render to work properly. OpenTK may have some other thing going on, but understand that OpenGL can't do this as such. The information would all have to be in the same buffer.

The pointers simply say "Here's where this information starts". Buffering the data up is simply feeding a buffer to the GL. So you can hop around the buffer for rendering. As such, you can only have one bound. Binding another handle will kick the other one out.

So this should not work, unless you move to a VAO implementation where you can encapsulate a ton of binds and render states inside one object. This also allows the GL to optimize the calls, which the driver may or may not do.
qpHalcy0n
Respected Programmer
Respected Programmer
Posts: 387
Joined: Fri Dec 19, 2008 3:33 pm
Location: Dallas
Contact:

Re: OpenTK Static VBO Help

Post by qpHalcy0n »

Try not to think of it as "I need a position buffer, a normal buffer, a texture buffer, etc..."

Back in the day we had static "vertex formats", which helps sortof bridge the gap. It used to be that you could not specify custom vertex formats. Now that you can, it's important to realize that a vertex can have very little information, or a ton of information. With the VBO implementation you're simply defining your own vertex format. They are not separate buffers, they are ONE buffer. A buffer of vertices. It's up to the "xxxPointer" to tell the GL what information is where in the buffer. It sets the "frame" so you don't get framing errors. Think of it like a camera snapshot. A roll of film (vertex stream) is sliding across a shutter of a fixed size. The pointers are saying "Start here and advance the film this far" so that you're not rendering from bad data (The frame sitting between two slides).
User avatar
Odin
Chaos Rift Newbie
Chaos Rift Newbie
Posts: 5
Joined: Fri Aug 10, 2012 3:15 am
Favorite Gaming Platforms: PC, NES, SNES, PSX/1, GBA
Programming Language of Choice: C/C++/C#/AngelScript

Re: OpenTK Static VBO Help

Post by Odin »

I just don't like handling all of the data in a vertex struct :/

Code: Select all

struct vertex
{
    public float x, y, z, s, t;
    public vertex(float _x, float _y, float _z, float _s, float _t)
    {
        x = _x;
        y = _y;
        z = _z;
        s = _s;
        t = _t;
    }
}

vertex[] _vertices = 
{
    new vertex(-1.0f, -1.0f, 0.0f, 0.0f, 1.0f),
    new vertex( 1.0f, -1.0f, 0.0f, 1.0f, 1.0f),
    new vertex( 1.0f,  1.0f, 0.0f, 1.0f, 0.0f),
    new vertex(-1.0f,  1.0f, 0.0f, 0.0f, 0.0f)
};

short[] indices = {0, 1, 2, 0, 2, 3};

int textureID, vertexBuffer, indexBuffer;

void init()
{
    GL.GenBuffers(1, out vertexBuffer);
    GL.BindBuffer(BufferTarget.ArrayBuffer, vertexBuffer);
    GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(_vertices.Length * 28), _vertices, BufferUsageHint.StaticDraw);
    
    GL.GenBuffers(1, out indexBuffer);
    GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBuffer);
    GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(indices.Length * sizeof(short)), indices, BufferUsageHint.StaticDraw);
}

void draw()
{
    GL.EnableClientState(ArrayCap.VertexArray);
    GL.EnableClientState(ArrayCap.TextureCoordArray);
    GL.BindBuffer(BufferTarget.ArrayBuffer, vertexBuffer);
    GL.VertexPointer(3, VertexPointerType.Float, 28, IntPtr.Zero);
            
    GL.Enable(EnableCap.Texture2D);
    GL.BindTexture(TextureTarget.Texture2D, textureID);
    GL.TexCoordPointer(2, TexCoordPointerType.Float, 28, new IntPtr(12));

    GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBuffer);
    GL.DrawElements(BeginMode.Triangles, indices.Length, DrawElementsType.UnsignedShort, IntPtr.Zero);
}
Kind of the point of me asking was to make it like in my last post :P
but i guess to get better i have to make some exceptions XD, thanks qpHalcy0n and Falco
Post Reply