flopoloco's picture

Model Loaders

Hello, do you know if someone programmer made an OpenTK model loader available to public? Currently I could use one "already-made" because I am developing some game examples. ;-)

P.S. Is there any general planning for OpenTK supporting model loaders? I would like to get my hands dirty with "Collada".


Comments

Comment viewing options

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

I wrote a model loader that supports milkshape files with skeletal animation, but I haven't finished the animation controller yet, so you can't do anything more advanced then loop the animation.

flopoloco's picture

Cool, I hope you finish it soon, making OpenTK even better. ;-)

Inertia's picture

There's a couple of heavy problems when it comes to loading models, which is why there's no solution for this in OpenTK atm.

  • Some formats store the data suitable to glDrawArrays, some to glDrawElements or even worse: bezier patches.
  • Since the introduction of GLSL a Vertex can be pretty much anything:
    struct Vertex
    {
      public byte X;
      public byte Y;
    }
     
    struct Vertex
    {
      public Vector3 Position;
      public Vector3 Normal;
      public Vector3 Tangent;
      public Vector3 Color;
      public Vector2 TexCoord1;
      public Vector2 TexCoord2;
      public Vector4 BoneIndex;
      public Vector4 Weights;
    }

    ...to give some extreme examples.

  • Animation can also be achieved by using a skeleton and storing bone transformations, or by storing every single vertex at a low framerate. Both has it's uses.
  • The file format might contain editor-specific information such as names for bones or groups, which are not needed for rendering, but needed for modelling tools.

Long story short, we didn't find a common denominator that would please everyone. With the upcoming OpenGL 3 the issue of the Vertex declaration might be not a problem anymore, so at very least a plugin architecture for loading static models could become reality.
Playing back skeletal animation - especially modes for mixing several animations with each other - is a fairly complex topic, and might never be part of OpenTK.

Kamujin's picture

If you want to see where the Milkshape loader is right now, check out http://www.robotechmmo.com/Video/beta1.ogg or http://www.robotechmmo.com/Video/beta1.avi

the Fiddler's picture

@Inertia: I have been experimenting with a possible solution for the represantation of abstract "Vertex" types, as part of an api abstraction for my diploma thesis. The basic idea is that structs can implement interfaces (so in the above example, both structs are instances of an IVertex type). You can then have generic IVertexBuffers of IVertex structs, which can be drawn as-is (DrawArrays) or using IElementBuffers (DrawElements).

I will make a post with source code soon, as soon as I work out some basic implementation details.

(Yeah, that was a bit off-topic :) )

Kamujin's picture

Here is my vertex struct and the code to render it. Personally, I've never really liked the various vertex definition systems that I've seen. Anyone who can understand them can understand this and this has more efficiency and flexibility.

namespace OOGL.Animation
{
	[StructLayout (LayoutKind.Sequential)]
	public struct VertexStruct : IComparable<VertexStruct>
	{
		public float x;
		public float y;
		public float z;
		public float nX;
		public float nY;
		public float nZ;
		public int	boneIndex;
		public float s;
		public float t;
 
		public const int elementSize = sizeof(float);
		public const int strideElements = 9;
		public const int stride = strideElements * elementSize;
 
		public VertexStruct(float x, float y, float z, float nX, float nY, float nZ,  float s, float t, int boneIndex)
		{
			this.x = x;
			this.y = y;
			this.z = z;
			this.nX = nX;
			this.nY = nY;
			this.nZ = nZ;
			this.s = s;
			this.t = t;
			this.boneIndex = boneIndex;
		}
 
		public VertexStruct(Vector3 vertex, Vector3 normal,  float s, float t, int boneIndex)
		{
			this.x = vertex.X;
			this.y = vertex.Y;
			this.z = vertex.Z;
			this.nX = normal.X;
			this.nY = normal.Y;
			this.nZ = normal.Z;
			this.s = s;
			this.t = t;
			this.boneIndex = boneIndex;
		}
 
		public Vector3 Position
		{
			get
			{
				return new Vector3(x, y, z);
			}
			set
			{
				this.x = value.X;
				this.y = value.Y;
				this.z = value.Z;
			}
		}
 
		public Vector3 Normal
		{
			get
			{
				return new Vector3(nX, nY, nZ);
			}
			set
			{
				this.nX = value.X;
				this.nY = value.Y;
				this.nZ = value.Z;
			}
		}
 
		public Vector2 TextureCoordinate
		{
			get
			{
				return new Vector2(s, t);
			}
			set
			{
				this.s = value.X;
				this.t = value.Y;
			}
		}
 
		public int BoneIndex
		{
			get
			{
				return boneIndex;
			}
			set
			{
				boneIndex = value;
			}
		}
 
		public int CompareTo(VertexStruct other)
		{
			if(x > other.x) return 1;
			if(x < other.x) return -1;
			if(y > other.y) return 1;
			if(y < other.y) return -1;
			if(z > other.z) return 1;
			if(z < other.z) return -1;
 
			if(s > other.s) return 1;
			if(s < other.s) return -1;
			if(t > other.t) return 1;
			if(t < other.t) return -1;
 
			if(boneIndex > other.boneIndex) return 1;
			if(boneIndex < other.boneIndex) return -1;
 
			if(nX > other.nX) return 1;
			if(nX < other.nX) return -1;
			if(nY > other.nY) return 1;
			if(nY < other.nY) return -1;
			if(nZ > other.nZ) return 1;
			if(nZ < other.nZ) return -1;
 
			return 0;
		}
	}
}

Here is how I render it. Its not alot of code.

namespace OOGL.Animation
{
	public class VertexBuffer : IDisposable
	{
		private int vertexHandle;
		private int indexHandle;
		private int numVerts;
		private int numIndices;
 
		private BufferUsageHint bufferUsage = BufferUsageHint.StaticDraw;
 
		private	const int vertexOffset = 0;
		private	const int normalOffset = 3 * VertexStruct.elementSize;
		private	const int vertexAttribOffset = 6 * VertexStruct.elementSize;
		private	const int texCoord0Offset = 7 * VertexStruct.elementSize;
 
		public VertexBuffer(VertexStruct[] vertices, ushort[] indices)
		{
			if(vertices == null) throw new ArgumentException("Vertices may not be null");
 
			this.numVerts = vertices.Length;
			this.numIndices = indices.Length;
 
			int[] handles = new int[2];
			GL.GenBuffers(2, handles);
			this.vertexHandle = handles[0];
			this.indexHandle = handles[1];
 
			GL.BindBuffer(BufferTarget.ArrayBuffer, this.vertexHandle);            
			GL.BindBuffer(BufferTarget.ElementArrayBuffer, this.indexHandle);
 
			int size = numVerts * VertexStruct.strideElements * VertexStruct.elementSize;									
			unsafe
			{
				fixed (float* pVertices = &vertices[0].x)
				{
					GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)size, (IntPtr)pVertices, bufferUsage);
				}
				fixed (ushort* pIndices = &indices[0])
				{
					GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(numIndices * 2), (IntPtr)pIndices, bufferUsage);
				}
			}						
 
			GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
			GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
		}
 
		public void Draw(int boneAttribIndex)
		{						
			GL.BindBuffer(BufferTarget.ArrayBuffer, vertexHandle);
			GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexHandle);
 
			GL.ClientActiveTexture(TextureUnit.Texture0);
 
			GL.EnableClientState(EnableCap.VertexArray);
			GL.EnableClientState(EnableCap.NormalArray);
			GL.EnableClientState(EnableCap.TextureCoordArray);
			GL.EnableVertexAttribArray(boneAttribIndex);			
 
			GL.VertexPointer(3, VertexPointerType.Float, VertexStruct.stride, (IntPtr)vertexOffset);
			GL.NormalPointer(NormalPointerType.Float, VertexStruct.stride, (IntPtr)normalOffset);
			GL.TexCoordPointer(2, TexCoordPointerType.Float, VertexStruct.stride, (IntPtr)texCoord0Offset);
			GL.VertexAttribPointer(boneAttribIndex, 1, VertexAttribPointerType.Int, false, VertexStruct.stride, (IntPtr)vertexAttribOffset);
 
			GL.DrawElements(BeginMode.Triangles, numIndices, DrawElementsType.UnsignedShort, IntPtr.Zero); 
 
			GL.DisableClientState(EnableCap.VertexArray);
			GL.DisableClientState(EnableCap.NormalArray);
			GL.DisableClientState(EnableCap.TextureCoordArray);
			GL.DisableVertexAttribArray(boneAttribIndex);
 
			GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
			GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
		}
 
 
		public void Dispose()
		{
			int[] bufferHandles = new int[] { vertexHandle, indexHandle };
			GL.DeleteBuffers(2, bufferHandles);
		}
	}
}
flopoloco's picture

I am trying to port the 3DS loader to C# but I am stuck :(

Could anyone port this into C#?

http://www.flipcode.com/archives/3dsloader.cpp

Kamujin's picture

That looks like a pretty straight forward port, but it looks like it only contains vertex position and index data. I don't see the material info, texture coordinates, or any animation data.

Is that what you wanted?

flopoloco's picture

Yes, this is a simple and quick way for starting out.

I hope one example will "open my eyes" to make OpenTK full support 3DS files, specifically, how to make it work the C# way...;-)

Generally, if you have any resources on loaders for C# or Java please let me know.

Thanks.

Kamujin's picture

I have been writing some utility classes that consume OpenTK. The source is available at https://sourceforge.net/projects/oogl/.

Can you give me a sample file to parse and I will try to port the code you requested when I get a chance.