1
\$\begingroup\$

Using Assimp.net I did load obj file. assimp returns back Mesh object that contains vertexes, normals, faces, colors and texture. when I don't use VBO the object will load complete. like this :

enter image description here

But when I use VBO, rendering of object is incomplete. like this :

enter image description here

Here is my source code :

public class VBO : GameWindow
{
    public Mesh InputMesh { get; set; }
    Vbo vbo = new Vbo();
    int textureID;

    public VBO() : base(800, 600)
    {
        Keyboard.KeyDown += OnKeyDownInternal;
    }

    public VBO(Mesh mesh) : this()
    {
        this.InputMesh = mesh;
    }

    private void OnKeyDownInternal(object sender, KeyboardKeyEventArgs e) { OnKeyDown(e); }

    protected virtual void OnKeyDown(KeyboardKeyEventArgs e)
    {
        switch (e.Key)
        {
            // Lighting
            case OpenTK.Input.Key.L:
                if (GL.IsEnabled(EnableCap.Lighting)) GL.Disable(EnableCap.Lighting);
                else GL.Enable(EnableCap.Lighting);
                break;

            // Texture
            case OpenTK.Input.Key.T:
                if (GL.IsEnabled(EnableCap.Texture2D)) GL.Disable(EnableCap.Texture2D);
                else GL.Enable(EnableCap.Texture2D);
                break;

            // Dynamic Update
            case OpenTK.Input.Key.D:
                dynamicUpdate = !dynamicUpdate;
                break;

            // Exit
            case OpenTK.Input.Key.Escape:
                Exit();
                break;
        }
    }

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);

        GL.ClearColor(0.1f, 0.1f, 0.5f, 0.0f);
        GL.Enable(EnableCap.DepthTest);

        // Vertex Buffers
        vbo = LoadVBO(InputMesh);

        // Lighting
        GL.Enable(EnableCap.Light0);
        GL.Enable(EnableCap.Lighting);

        // Texture
        GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Fastest);

        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("..\\..\\Data\\logo.jpg");
        /*
        Bitmap bitmap = new Bitmap("3Pod_Lorna_001.png");
        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);
    }

    protected override void OnResize(EventArgs e)
    {
    }

    protected override void OnUpdateFrame(FrameEventArgs e)
    {
    }

    double counter = 0;
    protected override void OnRenderFrame(FrameEventArgs e)
    {
        base.OnRenderFrame(e);

        GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

        GL.PushAttrib(AttribMask.LightingBit);
        GL.Disable(EnableCap.Lighting);
        GL.Color3(Color.White);
        /*
        textPrinter.Begin();
        textPrinter.Draw((1.0 / e.Time).ToString("F2"), font);
        GL.Translate(150, 0, 0);
        textPrinter.Draw("(L)ighting  (T)exture  (D)ynamicUpdate", font);
        textPrinter.End();
        */
        GL.PopAttrib();

        GL.MatrixMode(MatrixMode.Modelview);
        GL.LoadIdentity();
        Matrix4 modelview = Matrix4.LookAt(0.0f, 3.5f, 3.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
        GL.LoadMatrix(ref modelview);

        counter += e.Time;
        GL.Rotate(counter * 50, 0, 1, 0);

        Draw(vbo);

        SwapBuffers();
    }

    #region Upload
    private void GenAndFillBuffer(out int outGlBufferId, List<Vector3D> dataBuffer)
    {
        GL.GenBuffers(1, out outGlBufferId);
        GL.BindBuffer(BufferTarget.ArrayBuffer, outGlBufferId);
        var byteCount = dataBuffer.Count * (sizeof(float) * 3);
        GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(byteCount), dataBuffer.ToArray(), BufferUsageHint.StaticDraw);

        VerifyBufferSize(byteCount);
        GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
    }

    private void VerifyBufferSize(int byteCount)
    {
        int bufferSize;
        GL.GetBufferParameter(BufferTarget.ArrayBuffer, BufferParameterName.BufferSize, out bufferSize);
        if (byteCount != bufferSize)
        {
            throw new Exception("Vertex data array not uploaded correctly - buffer size does not match upload size");
        }
    }

    private void UploadColors(out int colorBufferId, Mesh mesh)
    {
        GL.GenBuffers(1, out colorBufferId);
        GL.BindBuffer(BufferTarget.ArrayBuffer, colorBufferId);

        var colors = mesh.VertexColorChannels[0];
        // convert to 32Bit RGBA
        var byteCount = colors.Count * 4;
        var byteColors = new byte[byteCount];
        var n = 0;
        foreach (var c in colors)
        {
            byteColors[n++] = (byte)(c.R * 255);
            byteColors[n++] = (byte)(c.G * 255);
            byteColors[n++] = (byte)(c.B * 255);
            byteColors[n++] = (byte)(c.A * 255);
        }

        GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(byteCount), byteColors, BufferUsageHint.StaticDraw);

        int bufferSize;
        GL.GetBufferParameter(BufferTarget.ArrayBuffer, BufferParameterName.BufferSize, out bufferSize);
        if (byteCount != bufferSize)
        {
            throw new Exception("Vertex array not uploaded correctly");
        }

        GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
    }

    private void UploadTextureCoords(out int texCoordBufferId, Mesh mesh)
    {
        GL.GenBuffers(1, out texCoordBufferId);
        GL.BindBuffer(BufferTarget.ArrayBuffer, texCoordBufferId);

        var uvs = mesh.TextureCoordinateChannels[0];
        var floatCount = uvs.Count * 2;
        var temp = new float[floatCount];
        var n = 0;
        foreach (var uv in uvs)
        {
            temp[n++] = uv.X;
            temp[n++] = uv.Y;
        }

        var byteCount = floatCount * sizeof(float);
        GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(byteCount), temp, BufferUsageHint.StaticDraw);
        VerifyBufferSize(byteCount);
        GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
    }

    private void UploadFaces(out int elementBufferId, out int indicesCount, out bool is32Bit, Mesh mesh)
    {
        GL.GenBuffers(1, out elementBufferId);
        GL.BindBuffer(BufferTarget.ElementArrayBuffer, elementBufferId);

        var faces = mesh.Faces;

        // TODO account for other primitives than triangles
        var triCount = 0;
        int byteCount;
        is32Bit = false;
        foreach (var face in faces)
        {
            if (face.IndexCount != 3)
                continue;
            ++triCount;
            if (face.Indices.Any(idx => idx > 0xffff))
                is32Bit = true;
        }

        var intCount = triCount * 3;
        if (is32Bit)
        {
            var temp = new uint[intCount];
            byteCount = intCount * sizeof(uint);
            var n = 0;
            foreach (var idx in faces.Where(face => face.IndexCount == 3).SelectMany(face => face.Indices))
                temp[n++] = (uint)idx;

            GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)byteCount,
                temp, BufferUsageHint.StaticDraw);
        }
        else
        {
            var temp = new ushort[intCount];
            byteCount = intCount * sizeof(ushort);
            var n = 0;
            foreach (var idx in faces.Where(face => face.IndexCount == 3).SelectMany(face => face.Indices))
                temp[n++] = (ushort)idx;

            GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)byteCount, temp, BufferUsageHint.StaticDraw);
        }

        int bufferSize;
        GL.GetBufferParameter(BufferTarget.ElementArrayBuffer, BufferParameterName.BufferSize, out bufferSize);
        if (byteCount != bufferSize)
        {
            throw new Exception("Index data array not uploaded correctly - buffer size does not match upload size");
        }

        GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
        indicesCount = triCount * 3;
    }

    Vbo LoadVBO(Mesh mesh)
    {
        Vbo vbo = new Vbo();

        if (mesh.VertexCount == 0) return vbo;
        if (mesh.FaceCount == 0) return vbo;

        // Color Array Buffer
        if (mesh.HasVertexColors(0))
            UploadColors(out vbo.ColorBufferId, mesh);

        // Normal Array Buffer
        if (mesh.HasNormals)
            GenAndFillBuffer(out vbo.NormalBufferId, mesh.Normals);

        // TexCoord Array Buffer
        if (mesh.HasTextureCoords(0))
            UploadTextureCoords(out vbo.TexCoordBufferId, mesh);

        // Vertex Array Buffer
        if (mesh.HasVertices)
            GenAndFillBuffer(out vbo.VertexBufferId, mesh.Vertices);

        // Element Array Buffer
        if (mesh.HasFaces)
            UploadFaces(out vbo.ElementBufferId, out vbo.NumIndices, out vbo.Is32BitIndices, mesh);

        // Store the number of elements for the DrawElements call
        vbo.NumIndices = mesh.FaceCount;

        return vbo;
    }

    #endregion

    void Draw(Vbo vbo)
    {
        // Push current Array Buffer state so we can restore it later
        GL.PushClientAttrib(ClientAttribMask.ClientVertexArrayBit);

        if (vbo.VertexBufferId == 0) return;
        if (vbo.ElementBufferId == 0) return;

        if (GL.IsEnabled(EnableCap.Lighting))
        {
            // Normal Array Buffer
            if (vbo.NormalBufferId != 0)
            {
                GL.BindBuffer(BufferTarget.ArrayBuffer, vbo.NormalBufferId);
                GL.NormalPointer(NormalPointerType.Float, Vector3.SizeInBytes, IntPtr.Zero);
                GL.EnableClientState(ArrayCap.NormalArray);
            }
        }

        // Color Array Buffer (Colors not used when lighting is enabled)
        if (vbo.ColorBufferId != 0)
        {
            GL.BindBuffer(BufferTarget.ArrayBuffer, vbo.ColorBufferId);
            GL.ColorPointer(4, ColorPointerType.UnsignedByte, sizeof(int), IntPtr.Zero);
            GL.EnableClientState(ArrayCap.ColorArray);
        }

        // Texture Array Buffer
        if (GL.IsEnabled(EnableCap.Texture2D))
        {
            if (vbo.TexCoordBufferId != 0)
            {
                GL.BindBuffer(BufferTarget.ArrayBuffer, vbo.TexCoordBufferId);
                GL.TexCoordPointer(2, TexCoordPointerType.Float, 8, IntPtr.Zero);
                GL.EnableClientState(ArrayCap.TextureCoordArray);
            }
        }

        // Vertex Array Buffer
        GL.BindBuffer(BufferTarget.ArrayBuffer, vbo.VertexBufferId);
        GL.VertexPointer(3, VertexPointerType.Float, Vector3.SizeInBytes, IntPtr.Zero);
        GL.EnableClientState(ArrayCap.VertexArray);

        // Element Array Buffer
        GL.BindBuffer(BufferTarget.ElementArrayBuffer, vbo.ElementBufferId);
        GL.DrawElements(BeginMode.Triangles, vbo.NumIndices, vbo.Is32BitIndices ? DrawElementsType.UnsignedInt : DrawElementsType.UnsignedShort, IntPtr.Zero);

        // Restore the state
        GL.PopClientAttrib();
    }

    struct Vbo
    {
        public int VertexBufferId;
        public int ColorBufferId;
        public int TexCoordBufferId;
        public int NormalBufferId;
        public int ElementBufferId;
        public int NumIndices;
        public bool Is32BitIndices; 
    }
}

Where is my mistake?

\$\endgroup\$
2
  • \$\begingroup\$ I feel I should point out that you should have just kept your original question. AFAIK, I was the only downvoter, and now that this is a suitable question, I would be upvoting regardless. No harm done, though you do now have another "deleted negative question" in your records for no good reason. In the long run, a pattern like this can limit your asking privileges. \$\endgroup\$
    – Gnemlock
    Jan 9 '17 at 1:14
  • \$\begingroup\$ Thank you for upvoting. My previous question was held off! So I did decide to delete both (SO and GameDev) questions and ask a new question here! \$\endgroup\$ Jan 9 '17 at 7:56
0
\$\begingroup\$

Finally I found the answer!

I changed this line :

GL.DrawElements(BeginMode.Triangles, vbo.NumIndices, vbo.Is32BitIndices ? DrawElementsType.UnsignedInt : DrawElementsType.UnsignedShort, IntPtr.Zero);

to This :

GL.DrawElements(BeginMode.Triangles, vbo.NumIndices * 3, vbo.Is32BitIndices ? DrawElementsType.UnsignedInt : DrawElementsType.UnsignedShort, IntPtr.Zero);

and problem solved.

enter image description here

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.