CommuSoft's picture

3DS File Format Specifications

Are there any resources on the web describing how the structure of the 3DS file in a formal way? I have already implemented an algorithm that can read vertices, triangles and calculate normals (because as far as I can find normals aren't saved in the 3ds file format). I've browsed the web for some time, but I only can find pages explaining parts of the formats: I want to include the following features:
- Material import
- Skeletal animation
-...

Here the partial loader I've written that far. I've been trying to optimize the rendering procedure for large models:

public class Model3DSLoader {
 
	public Model3DSLoader () {
	}
 
	public Model3DS[] Read3DSData (string filename) {
		FileStream fs = new FileStream(filename,FileMode.Open,FileAccess.Read);
		Model3DS[] result = Read3DSData(fs);
		fs.Close();
		return result;
	}
	public Model3DS[] Read3DSData (Stream stream) {
		BinaryReader br = new BinaryReader(stream);
		int i;
		ushort chunk_id;
		int chunk_length;
		Stack<Model3DS> models = new Stack<Model3DS>();
		ushort qty;
		byte data;
		string text;
		float[] flist;
		ushort[] uslist;
		int n;
		Model3DS model = null;
		while(stream.Position < stream.Length) {
			chunk_id = br.ReadUInt16();
			chunk_length = br.ReadInt32();
			switch(chunk_id) {
				case 0x4d4d : 
					break;
				case 0x3d3d :
					break;
				case 0x4000 :
					i = 0x00;
					text = string.Empty;
					for(i = 0x13; i >= 0x00; i--) {
						data = br.ReadByte();
						if(data != 0x00) {
							text += (char) data;
						}
						else {
							break;
						}
					}
					model = new Model3DS();
					model.Name = text;
					models.Push(model);
					break;
				case 0x4100 :
					break;
				case 0x4110 :
					qty = br.ReadUInt16();
					n = 0x03*qty;
					flist = new float[n];
					for(i = 0x00; i < n;) {
						flist[i++] = br.ReadSingle();
						flist[i++] = br.ReadSingle();
						flist[i++] = br.ReadSingle();
					}
					model.Vertices = flist;
					break;
				case 0x4120 :
					qty = br.ReadUInt16();
					n = 0x03*qty;
					uslist = new ushort[n];
					for(i = 0x00; i < n;) {
						uslist[i++] = br.ReadUInt16();
						uslist[i++] = br.ReadUInt16();
						uslist[i++] = br.ReadUInt16();
						br.ReadUInt16();
					}
					model.Triangles = uslist;
					break;
				case 0xafff :
					break;
				case 0xa000 :
					i = 0x00;
					text = string.Empty;
					for(i = 0x13; i >= 0x00; i--) {
						data = br.ReadByte();
						if(data != 0x00) {
							text += (char) data;
						}
						else {
							break;
						}
					}
					break;
				default :
					stream.Position += chunk_length-0x06;
					break;
			}
		}
		return models.ToArray();
	}
 
}
public class Model3DS : IRenderable {
 
	private string name;
	private Vector3[] vertices;
	private Vector3[] normals;
	private ushort[] triangles;
	private ushort faceFlags;
	private int glVertexRef;
	private int glTriangleRef;
	private int n;
 
	public string Name {
		get {
			return this.name;
		}
		internal set {
			this.name = value;
		}
	}
	internal float[] Vertices {
		set {
			float[] val = value;
			int n = value.Length/0x03;
			Vector3[] vs = new Vector3[n];
			float x, y, z;
			for(int i = 0x00, j = 0x00; i < n;) {
				x = val[j++]; y = val[j++]; z = val[j++];
				vs[i++] = new Vector3(x,y,z);
			}
			this.vertices = vs;
		}
	}
	public ushort[] Triangles {
		get {
			return this.triangles;
		}
		internal set {
			this.triangles = value;
			this.n = this.triangles.Length;
		}
	}
	public ushort FaceFlags {
		get {
			return this.faceFlags;
		}
		internal set {
			this.faceFlags = value;
		}
	}
 
	public Model3DS () {
 
	}
 
	public void CacheModel () {
		this.calculateNormals();
		Vector3[] vertices = this.vertices;
		Vector3[] normals = this.normals;
		int n = normals.Length<<0x01;
		Vector3[] vertexData = new Vector3[n];
		for(int i = 0x00, j = 0x00; i < n;) {
			vertexData[i++] = vertices[j];
			vertexData[i++] = normals[j++];
		}
		GL.GenBuffers(0x01, out this.glVertexRef);
		GL.BindBuffer(BufferTarget.ArrayBuffer,this.glVertexRef);
		GL.BufferData(BufferTarget.ArrayBuffer,new IntPtr(n*Vector3.SizeInBytes),vertexData,BufferUsageHint.StaticDraw);
		GL.GenBuffers(0x01, out this.glTriangleRef);
		GL.BindBuffer(BufferTarget.ElementArrayBuffer,this.glTriangleRef);
		GL.BufferData(BufferTarget.ElementArrayBuffer,new IntPtr(this.triangles.Length*sizeof(uint)),this.triangles,BufferUsageHint.StaticDraw);
	}
	private void calculateNormals () {
		ushort[] triangles = this.triangles;
		Vector3[] vertices = this.vertices;
		int nT = triangles.Length;
		int nV = vertices.Length;
		Vector3[] normals = new Vector3[nV];
		uint ia, ib, ic;
		Vector3 va, vb, vc;
		Vector3 normal;
		for(int i = 0x00; i < nT; ) {
			ia = triangles[i++];
			ib = triangles[i++];
			ic = triangles[i++];
 
			va = vertices[ia];
			vb = vertices[ib];
			vc = vertices[ic];
 
			normal = Vector3.Normalize(Vector3.Cross(vb-va,vc-va));
 
			normals[ia] += normal;
			normals[ib] += normal;
			normals[ic] += normal;
		}
		for(int i = nV-0x01; i >= 0x00; i--) {
			normals[i] = Vector3.Normalize(normals[i]);
		}
		this.normals = normals;
	}
	public void Render (FrameEventArgs e) {
		int stride = Vector3.SizeInBytes<<0x01;
		GL.PushClientAttrib(ClientAttribMask.ClientAllAttribBits);
		GL.EnableClientState(ArrayCap.VertexArray);
		GL.EnableClientState(ArrayCap.NormalArray);
		GL.EnableClientState(ArrayCap.IndexArray);
		GL.BindBuffer(BufferTarget.ArrayBuffer,this.glVertexRef);
		GL.BindBuffer(BufferTarget.ElementArrayBuffer,this.glTriangleRef);
		GL.VertexPointer(0x03,VertexPointerType.Float,stride,0x00);
		GL.NormalPointer(NormalPointerType.Float,stride,stride>>0x01);
		GL.IndexPointer(IndexPointerType.Short,0x03*sizeof(ushort),0x00);
		GL.DrawElements(BeginMode.Triangles,this.n,DrawElementsType.UnsignedShort,0x00);
		GL.PopClientAttrib();
	}
 
}

Comments

Comment viewing options

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

The only spec that I know of is the Martin Reddy's. This site also gives a lookup table in addition to this spec. After a quick searc i have found this site. But I personnaly recommend Asset Importer Library which has both C and C++ API. It can open a lot of formats and it should not be hard to convert their in memory format to yours.