chandragon's picture

Load and draw an animated model (.md2)

Hello,

I would like to render an animated 3d model that would be stored in a file.
I know two ways of doing it: with bones and with frames.
I would like to be able to render a lot of times the animated model (but not at the same frame) and I don't need a very accurate render so I think the best thing is to use frames.
To make the animated model I use Blender with bones and then I would like to export the frames of the animation to a file.
I searched the right file format and I think MD2 could do, then I searched a way of loading a MD2 file and I found this topic where TheKrokodil posted a MD2 loader:
http://www.opentk.com/node/450
The problem I encounter is that each triangle not only contains vertex indices but also texcoord indices.
I also looked at the .obj file format, and I saw that each face could even have normal indices.
But the only ways I know of rendering 3D models are those explained here:
http://www.opentk.com/doc/chapter/2/opengl/geometry/drawing
And as far as I know, we can use GL.DrawElements to precise the vertex indices, but there is no way method that uses texcoord indices, or even normal indices.

So once I have loaded my model from a md2 or obj file, how do I render it (except using immediate mode) ?
And if we can do it with vertex arrays and VBO, what would be the faster ? (knowing that the vertex positions would always change).

Thank you


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
sgsrules's picture
chandragon wrote:

Hello,

I would like to render an animated 3d model that would be stored in a file.
I know two ways of doing it: with bones and with frames.
I would like to be able to render a lot of times the animated model (but not at the same frame) and I don't need a very accurate render so I think the best thing is to use frames.
To make the animated model I use Blender with bones and then I would like to export the frames of the animation to a file.
I searched the right file format and I think MD2 could do, then I searched a way of loading a MD2 file and I found this topic where TheKrokodil posted a MD2 loader:
http://www.opentk.com/node/450
The problem I encounter is that each triangle not only contains vertex indices but also texcoord indices.
I also looked at the .obj file format, and I saw that each face could even have normal indices.
But the only ways I know of rendering 3D models are those explained here:
http://www.opentk.com/doc/chapter/2/opengl/geometry/drawing
And as far as I know, we can use GL.DrawElements to precise the vertex indices, but there is no way method that uses texcoord indices, or even normal indices.

OpenGL only accepts one set of indexes. This index is used to reference the vertex data, normals, tex coordinates and any other vertex attributes. So this means that your tex coordinates and normal array have to be in the same order as your vertex arrays. The Obj format and most other have separate indexs for the normals etc. You'll have to reorganize your arrays so that they all match. Create new arrays for each of the different vertex attributes (these should all be the same size) and then use the indexes to populate them.

Quote:

So once I have loaded my model from a md2 or obj file, how do I render it (except using immediate mode) ?
And if we can do it with vertex arrays and VBO, what would be the faster ? (knowing that the vertex positions would always change).
Thank you

Don't use immediate mode, vbos and vaos will be a lot faster.

chandragon's picture

Okay, but what if one vertex is used with different texcoords/normals, I have to create a new vertex at the same position ?

Thanks

JTalton's picture

Yes.

chandragon's picture

Okay, Thank you !
And what is the best to use to render animations, Vertex arrays or VBO ?

JTalton's picture

I would start with Vertex Arrays... a VAO is just an addition to that that stores the enabled arrays so that it can be rendered in a single call...

If you plan to interpolate between frames in the MD2, I would put the TexCoords in one Array and the Positions & Normals in another since the Position and Normals will need to be updated every render.

Below is prototype code as an example.

using System;
using System.Runtime.InteropServices;
using OpenTK;
using OpenTK.Graphics.OpenGL;
 
namespace OpenGL
{
    public class Model
    {
        [StructLayout(LayoutKind.Sequential)]
        struct Triangle
        {
            public uint Index0;
            public uint Index1;
            public uint Index2;
        }
 
        [StructLayout(LayoutKind.Sequential)]
        struct Vertex
        {
            public Vector3 Position;
            public Vector3 Normal;
        }
 
        uint elementArrayID;
        uint texCoordArrayID;
        uint vertexArrayID;
 
        Vertex[] vertices;
        int triangleCount;
 
        void Load(Triangle[] triangles, Vector2[] texCoords, Vertex[] vertices)
        {
            this.vertices = new Vertex[vertices.Length];
            vertices.CopyTo(this.vertices, 0);
 
            triangleCount = triangles.Length;
 
            GL.GenBuffers(1, out elementArrayID);
            GL.BindBuffer(BufferTarget.ElementArrayBuffer, elementArrayID);
            GL.BufferData<Triangle>(BufferTarget.ElementArrayBuffer, (IntPtr)(triangles.Length * Marshal.SizeOf(typeof(Triangle))), triangles, BufferUsageHint.StaticDraw);
 
            GL.GenBuffers(1, out texCoordArrayID);
            GL.BindBuffer(BufferTarget.ArrayBuffer, texCoordArrayID);
            GL.BufferData<Vector2>(BufferTarget.ArrayBuffer, (IntPtr)(texCoords.Length * Vector2.SizeInBytes), texCoords, BufferUsageHint.StaticDraw);
 
            GL.GenBuffers(1, out vertexArrayID);
            GL.BindBuffer(BufferTarget.ArrayBuffer, vertexArrayID);
            GL.BufferData<Vertex>(BufferTarget.ArrayBuffer, (IntPtr)(vertices.Length * Marshal.SizeOf(typeof(Vertex))), vertices, BufferUsageHint.DynamicDraw);
        }
 
        void Update(Vertex[] vertices, float percent)
        {
            // TODO - Interpolate this.vertices to vertices based on percentage instead of copy...
            vertices.CopyTo(this.vertices, 0);
 
            // Update buffer in video memory...
            GL.BindBuffer(BufferTarget.ArrayBuffer, vertexArrayID);
            GL.BufferSubData(BufferTarget.ArrayBuffer, (IntPtr)0, (IntPtr)(vertices.Length * Marshal.SizeOf(typeof(Vertex))), vertices);
        }
 
        void Render()
        {
            GL.PushClientAttrib(ClientAttribMask.ClientVertexArrayBit);
 
            GL.BindBuffer(BufferTarget.ArrayBuffer, texCoordArrayID);
            GL.EnableClientState(ArrayCap.TextureCoordArray);
            GL.TexCoordPointer(2, TexCoordPointerType.Float, 0, (IntPtr)0);
 
            GL.BindBuffer(BufferTarget.ArrayBuffer, vertexArrayID);
            GL.EnableClientState(ArrayCap.VertexArray);
            GL.VertexPointer(3, VertexPointerType.Float, Marshal.SizeOf(typeof(Vertex)), (IntPtr)0);
            GL.EnableClientState(ArrayCap.NormalArray);
            GL.NormalPointer(NormalPointerType.Float, Marshal.SizeOf(typeof(Vertex)), (IntPtr)Vector3.SizeInBytes);
 
            GL.BindBuffer(BufferTarget.ElementArrayBuffer, elementArrayID);
            GL.DrawElements(BeginMode.Triangles, triangleCount * 3, DrawElementsType.UnsignedInt, IntPtr.Zero);
 
            GL.PopClientAttrib();
        }
    }
}
chandragon's picture

Allright !
Thanks a lot !