Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Runtime.InteropServices;
- using System.Text;
- using OpenTK;
- using OpenTK.Graphics.OpenGL;
- using TopHat.Graphics;
- namespace TopHat.IO
- {
- public static class IqmParser
- {
- private enum IqmAttributeType
- {
- Position = 0,
- TexCoord = 1,
- Normal = 2,
- Tangent = 3,
- BlendIndexes = 4,
- BlendWeights = 5,
- Color = 6,
- Custom = 0x10
- }
- private enum IqmDataFormat
- {
- Byte = 0,
- UnsignedByte = 1,
- Short = 2,
- UnsignedShort = 3,
- Int = 4,
- UnsignedInt = 5,
- Half = 6,
- Float = 7,
- Double = 8
- }
- private enum IqmAnimationType
- {
- Loop = 1 << 0
- }
- /// <summary>
- /// The header at the top of every IQM file.
- /// </summary>
- [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
- private struct IqmHeader
- {
- /// <summary>
- /// The identifier for an IQM file - "INTERQUAKEMODEL\0".
- /// </summary>
- [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
- public string Magic;
- /// <summary>
- /// The version of the file. Must be at version 2 to work.
- /// </summary>
- public uint Version;
- /// <summary>
- /// Size of the file.
- /// </summary>
- public uint FileSize;
- /// <summary>
- /// File flags. There's nothing in the specification about the meaning of this field.
- /// </summary>
- public uint Flags;
- public uint TextCount;
- public uint TextOffset;
- public uint MeshesCount;
- public uint MeshesOffset;
- public uint VertexArraysCount;
- public uint VertexesCount;
- public uint VertexArraysOffset;
- public uint TrianglesCount;
- public uint TrianglesOffset;
- public uint AdjacencyOffset;
- public uint JointsCount;
- public uint JointsOffset;
- public uint PosesCount;
- public uint PosesOffset;
- public uint AnimationsCount;
- public uint AnimationsOffset;
- public uint FramesCount;
- public uint FrameChannelsCount;
- public uint FramesOffset;
- public uint BoundsOffset;
- public uint CommentCount;
- public uint CommentOffset;
- public uint ExtensionsCount;
- public uint ExtensionsOffset;
- public static int SizeInBytes { get { return Marshal.SizeOf(typeof(IqmHeader)); } }
- }
- [StructLayout(LayoutKind.Sequential, Pack = 1)]
- private struct IqmMesh
- {
- public uint Name;
- public uint Material;
- public uint FirstVertex;
- public uint VertexesCount;
- public uint FirstTriangle;
- public uint TrianglesCount;
- public static int SizeInBytes { get { return Marshal.SizeOf(typeof(IqmMesh)); } }
- }
- [StructLayout(LayoutKind.Sequential, Pack = 1)]
- private struct IqmTriangle
- {
- public uint Vertex0;
- public uint Vertex1;
- public uint Vertex2;
- public static int SizeInBytes { get { return Marshal.SizeOf(typeof(IqmTriangle)); } }
- }
- [StructLayout(LayoutKind.Sequential, Pack = 1)]
- private struct IqmAdjacency
- {
- public uint Triangle0;
- public uint Triangle1;
- public uint Triangle2;
- public static int SizeInBytes { get { return Marshal.SizeOf(typeof(IqmAdjacency)); } }
- }
- [StructLayout(LayoutKind.Sequential, Pack = 1)]
- private struct IqmJoint
- {
- public uint Name;
- public int Parent;
- public Vector3 Translate;
- public Quaternion Rotate;
- public Vector3 Scale;
- public static int SizeInBytes { get { return Marshal.SizeOf(typeof(IqmJoint)); } }
- }
- [StructLayout(LayoutKind.Sequential, Pack = 1)]
- private struct IqmPose
- {
- public int Parent;
- public uint Mask;
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
- public float[] ChannelOffset;
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
- public float[] ChannelScale;
- public static int SizeInBytes { get { return Marshal.SizeOf(typeof(IqmPose)); } }
- }
- [StructLayout(LayoutKind.Sequential, Pack = 1)]
- private struct IqmAnimation
- {
- public uint Name;
- public uint FirstFrame;
- public uint FramesCount;
- public float Framerate;
- public uint Flags;
- public static int SizeInBytes { get { return Marshal.SizeOf(typeof(IqmAnimation)); } }
- }
- [StructLayout(LayoutKind.Sequential, Pack = 1)]
- private struct IqmVertexArray
- {
- public IqmAttributeType Type;
- public uint Flags;
- public IqmDataFormat Format;
- public uint Size;
- public uint Offset;
- public static int SizeInBytes { get { return Marshal.SizeOf(typeof(IqmVertexArray)); } }
- }
- [StructLayout(LayoutKind.Sequential, Pack = 1)]
- private struct IqmBounds
- {
- public Vector3 BBMin;
- public Vector3 BBMax;
- public float XYRadius;
- public float Radius;
- public static int SizeInBytes { get { return Marshal.SizeOf(typeof(IqmBounds)); } }
- }
- public static Drawable Parse(string path)
- {
- if (!File.Exists(path))
- throw new ArgumentException("The file \"" + path + "\" does not exist.", "path");
- List<string> text;
- List<IqmMesh> meshes;
- List<IqmVertexArray> vertexArrays;
- List<IqmTriangle> triangles;
- List<IqmAdjacency> adjacency;
- List<IqmJoint> joints;
- List<IqmPose> poses;
- List<IqmAnimation> animations;
- //TODO matrix3x4 for frames.
- IqmBounds bounds;
- List<VertexAttribute> attributes = new List<VertexAttribute>();
- using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
- {
- using (BinaryReader reader = new BinaryReader(fs))
- {
- //make sure the file is long enough to parse a header before we check the other requirements.
- if (fs.Length < IqmHeader.SizeInBytes)
- throw new ArgumentException("The file \"" + path + "\" is not an IQM file.", "path");
- //parse the header and check for magic number match, file size match, and version match.
- IqmHeader header = ParseHeader(reader);
- if (header.Magic != "INTERQUAKEMODEL")
- throw new ArgumentException("The file \"" + path + "\" is not an IQM file.", "path");
- if (header.FileSize != fs.Length)
- throw new DataCorruptionException("The file \"" + path + "\" is invalid or corrupted. The file size doesn't match the size reported in the header.");
- if (header.Version != 2)
- throw new OutdatedClientException("The file \"" + path + "\" is using a different version of the IQM specification. Make sure to compile the model as version 2.");
- //Parse text
- if (header.TextOffset != 0)
- text = ParseText(reader, (int)header.TextOffset, (int)header.TextCount);
- else
- text = new List<string>();
- //Parse meshes
- if (header.MeshesOffset != 0)
- meshes = ParseMeshes(reader, (int)header.MeshesOffset, (int)header.MeshesCount);
- else
- meshes = new List<IqmMesh>();
- //Parse vertexarrays
- if (header.VertexArraysOffset != 0)
- vertexArrays = ParseVertexArrays(reader, (int)header.VertexArraysOffset, (int)header.VertexArraysCount);
- else
- vertexArrays = new List<IqmVertexArray>();
- //Parse triangles
- if (header.TrianglesOffset != 0)
- triangles = ParseTriangles(reader, (int)header.TrianglesOffset, (int)header.TrianglesCount);
- else
- triangles = new List<IqmTriangle>();
- //Parse adjacency
- if (header.AdjacencyOffset != 0)
- adjacency = ParseAdjacency(reader, (int)header.AdjacencyOffset, (int)header.TrianglesCount);
- else
- adjacency = new List<IqmAdjacency>();
- //Parse joints
- if (header.JointsOffset != 0)
- joints = ParseJoints(reader, (int)header.JointsOffset, (int)header.JointsCount);
- else
- joints = new List<IqmJoint>();
- //Parse poses
- if (header.PosesOffset != 0)
- poses = ParsePoses(reader, (int)header.PosesOffset, (int)header.PosesCount);
- else
- poses = new List<IqmPose>();
- //Parse animations
- if (header.AnimationsOffset != 0)
- animations = ParseAnimations(reader, (int)header.AnimationsOffset, (int)header.AnimationsCount);
- else
- animations = new List<IqmAnimation>();
- //Parse frames
- //Parse bounds
- if (header.BoundsOffset != 0)
- bounds = ParseBounds(reader, (int)header.BoundsOffset);
- //Parse vertices
- for (int i = 0; i < header.VertexArraysCount; i++)
- {
- IqmVertexArray va = vertexArrays[i];
- if ((int)va.Type > 3) //HACK no animation stuff set up yet.
- continue;
- IBuffer buf = Resources.CreateBuffer();
- reader.BaseStream.Position = va.Offset;
- buf.SetData(reader.ReadBytes((int)va.Size * SizeOfIqmFormat(va.Format) * (int)header.VertexesCount), BufferUsageHint.StaticDraw);
- attributes.Add(new VertexAttribute(buf, (int)va.Size, 0, 0, ConvertIqmFormat(va.Format), ConvertIqmAttribute(va.Type)));
- }
- Drawable d = new Drawable(attributes);
- d.DrawMode = BeginMode.Triangles;
- //Parse indices
- IBuffer indBuf = Resources.CreateBuffer();
- indBuf.SetData(triangles.ToArray(), BufferUsageHint.StaticDraw);
- d.IndexBuffer = indBuf;
- d.IndexType = DrawElementsType.UnsignedInt;
- d.IndexCount = triangles.Count * 3;
- return d;
- }
- }
- }
- private unsafe static IqmHeader ParseHeader(BinaryReader reader)
- {
- byte[] headerData = reader.ReadBytes(IqmHeader.SizeInBytes);
- fixed (byte* headerPtr = headerData)
- return PtrToStructure<IqmHeader>(headerPtr);
- }
- private static List<string> ParseText(BinaryReader reader, int position, int count)
- {
- List<string> text = new List<string>();
- StringBuilder builder = new StringBuilder();
- reader.BaseStream.Position = position;
- for (int i = 0; i < count; i++)
- {
- byte nextByte = reader.ReadByte();
- builder.Append((char)nextByte);
- if (nextByte == 0)
- {
- text.Add(builder.ToString());
- builder.Clear();
- }
- }
- return text;
- }
- private unsafe static List<IqmMesh> ParseMeshes(BinaryReader reader, int position, int count)
- {
- List<IqmMesh> meshes = new List<IqmMesh>(count);
- reader.BaseStream.Position = position;
- for (int i = 0; i < count; i++)
- fixed (byte* meshPtr = reader.ReadBytes(IqmMesh.SizeInBytes))
- meshes.Add(PtrToStructure<IqmMesh>(meshPtr));
- return meshes;
- }
- private unsafe static List<IqmVertexArray> ParseVertexArrays(BinaryReader reader, int position, int count)
- {
- List<IqmVertexArray> vertexArrays = new List<IqmVertexArray>(count);
- reader.BaseStream.Position = position;
- for (int i = 0; i < count; i++)
- fixed (byte* vertexArrayPtr = reader.ReadBytes(IqmVertexArray.SizeInBytes))
- vertexArrays.Add(PtrToStructure<IqmVertexArray>(vertexArrayPtr));
- return vertexArrays;
- }
- private unsafe static List<IqmTriangle> ParseTriangles(BinaryReader reader, int position, int count)
- {
- List<IqmTriangle> triangles = new List<IqmTriangle>(count);
- reader.BaseStream.Position = position;
- for (int i = 0; i < count; i++)
- fixed (byte* trianglePtr = reader.ReadBytes(IqmTriangle.SizeInBytes))
- triangles.Add(PtrToStructure<IqmTriangle>(trianglePtr));
- return triangles;
- }
- private unsafe static List<IqmAdjacency> ParseAdjacency(BinaryReader reader, int position, int count)
- {
- List<IqmAdjacency> adjacency = new List<IqmAdjacency>(count);
- reader.BaseStream.Position = position;
- for (int i = 0; i < count; i++)
- fixed (byte* adjacencyPtr = reader.ReadBytes(IqmAdjacency.SizeInBytes))
- adjacency.Add(PtrToStructure<IqmAdjacency>(adjacencyPtr));
- return adjacency;
- }
- private unsafe static List<IqmJoint> ParseJoints(BinaryReader reader, int position, int count)
- {
- List<IqmJoint> joints = new List<IqmJoint>(count);
- reader.BaseStream.Position = position;
- for (int i = 0; i < count; i++)
- fixed (byte* jointPtr = reader.ReadBytes(IqmJoint.SizeInBytes))
- joints.Add(PtrToStructure<IqmJoint>(jointPtr));
- return joints;
- }
- private unsafe static List<IqmPose> ParsePoses(BinaryReader reader, int position, int count)
- {
- List<IqmPose> poses = new List<IqmPose>(count);
- reader.BaseStream.Position = position;
- for (int i = 0; i < count; i++)
- fixed (byte* posePtr = reader.ReadBytes(IqmPose.SizeInBytes))
- poses.Add(PtrToStructure<IqmPose>(posePtr));
- return poses;
- }
- private unsafe static List<IqmAnimation> ParseAnimations(BinaryReader reader, int position, int count)
- {
- List<IqmAnimation> animations = new List<IqmAnimation>(count);
- reader.BaseStream.Position = position;
- for (int i = 0; i < count; i++)
- fixed (byte* animationPtr = reader.ReadBytes(IqmAnimation.SizeInBytes))
- animations.Add(PtrToStructure<IqmAnimation>(animationPtr));
- return animations;
- }
- private unsafe static IqmBounds ParseBounds(BinaryReader reader, int position)
- {
- reader.BaseStream.Position = position;
- fixed (byte* boundsPtr = reader.ReadBytes(IqmBounds.SizeInBytes))
- return PtrToStructure<IqmBounds>(boundsPtr);
- }
- private unsafe static T PtrToStructure<T>(byte* data)
- where T : struct
- {
- return (T)Marshal.PtrToStructure((IntPtr)data, typeof(T));
- }
- private static AttributeType ConvertIqmAttribute(IqmAttributeType type)
- {
- switch (type)
- {
- case IqmAttributeType.Position:
- return AttributeType.Position;
- case IqmAttributeType.TexCoord:
- return AttributeType.TexCoord;
- case IqmAttributeType.Normal:
- return AttributeType.Normal;
- case IqmAttributeType.Tangent:
- return AttributeType.Tangent;
- default:
- return AttributeType.Position; //return 0.
- }
- }
- private static VertexAttribPointerType ConvertIqmFormat(IqmDataFormat format)
- {
- switch (format)
- {
- case IqmDataFormat.Byte:
- return VertexAttribPointerType.Byte;
- case IqmDataFormat.Double:
- return VertexAttribPointerType.Double;
- case IqmDataFormat.Float:
- return VertexAttribPointerType.Float;
- case IqmDataFormat.Half:
- return VertexAttribPointerType.HalfFloat;
- case IqmDataFormat.Int:
- return VertexAttribPointerType.Int;
- case IqmDataFormat.Short:
- return VertexAttribPointerType.Short;
- case IqmDataFormat.UnsignedByte:
- return VertexAttribPointerType.UnsignedByte;
- case IqmDataFormat.UnsignedInt:
- return VertexAttribPointerType.UnsignedInt;
- case IqmDataFormat.UnsignedShort:
- return VertexAttribPointerType.UnsignedShort;
- default:
- return VertexAttribPointerType.Float;
- }
- }
- private static int SizeOfIqmFormat(IqmDataFormat format)
- {
- switch (format)
- {
- case IqmDataFormat.Byte:
- case IqmDataFormat.UnsignedByte:
- return 1;
- case IqmDataFormat.Half:
- case IqmDataFormat.Short:
- case IqmDataFormat.UnsignedShort:
- return 2;
- case IqmDataFormat.Float:
- case IqmDataFormat.Int:
- case IqmDataFormat.UnsignedInt:
- return 4;
- case IqmDataFormat.Double:
- return 8;
- default:
- return 0;
- }
- }
- }
- }
Add Comment
Please, Sign In to add comment