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 :
But when I use VBO, rendering of object is incomplete. like this :
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?