Sims 3:0x015A1849
From SimsWiki
Header
Start with the RCOL Header: Sims 3:RCOL
Then the GEOM chunk:
long 'GEOM' long Version // 5 long TailOffset long TailSectionSize // (0x14, 0x44 observed) long EmbeddedID //(0, 0x548394B9 observed) if(EmbeddedID!=0) { long ChunkSize byte[ChunkSize] // starts with 'MTNF' } long 0 //unknown purpose long 0 // unknown purpose long NumVerts long FCount // was named Version, but is number of vertex elements block VertexFormat[FCount] // long, long, byte; documented below 9 bytes/vertex-element-count struct VertexData[NumVerts] // follows vertex format block, documented below block FaceFmt // long ItemCount, byte BytesPerFacepoint long NumFacePoints // means faces*3 word [NumFacePoints][3] // standard face index (three unsigned words per face) for 2-byte FaceFmt ---------- Tail: long flags long count1 long bonehasharray[count1] // 32-bit hash of used bone names. long numtgi block references[numtgi] // each is a TGI[sub]64[/sub], a 16-byte quantity, so size is 16*numtgi // references are to DDS textures and BONE file
VertexFormat
For each vertex count:
long DataType long SubType byte BytesPerElement For DataType: 1 == Position (3 float == 12 bytes) 2 == Normal (3 float == 12 bytes) 3 == UV (2 float == 8 bytes) 4 == Bone Assignment (long == 4 bytes) 5 == Weights (4 float == 16 bytes) 6 == Tangent Normal (3 float == 12 bytes) 7 == TagVal (4 packed bytes) -- colour channel data 10 == VertexID (long == 4 bytes) For SubType 1 == floats 2 == bytes 4 == long
You calculate the offset of each element from the sum of the previous BytesPerElement
Reading Vertex Data Blocks
For each vertex, you need to loop through the VertexFormat as defined, and read the information in order.
The order can be completely arbitary, and varied in size.
Here is some example code in c# to do so:
for (int i = 0; i < numVerts; i++) { for (int j = 0; j < vertexFormats.Count; j++) { float x = 0; float y = 0; float z = 0; vertexFormat vf = (vertexFormat)vertexFormats[j]; switch (vf.dataType) { case 1: x = reader.ReadSingle(); z = reader.ReadSingle(); y = reader.ReadSingle(); sb.AppendLine(" XYZ: " + x.ToString() + " " + y.ToString() + " " + z.ToString() ); break; case 2: x = reader.ReadSingle(); z = reader.ReadSingle(); y = reader.ReadSingle(); sb.AppendLine(" Normal: " + x.ToString() + " " + y.ToString() + " " + z.ToString() ); break; case 3: float u = reader.ReadSingle(); float v = reader.ReadSingle(); sb.AppendLine(" UV: " + u.ToString() + " " + v.ToString() ); break; case 4: sb.AppendLine(" Bone: " + reader.ReadUInt32().ToString() ); break; case 5: float w1 = reader.ReadSingle(); float w2 = reader.ReadSingle(); float w3 = reader.ReadSingle(); float w4 = reader.ReadSingle(); sb.AppendLine(" Weights: " + w1.ToString() + " " + w2.ToString() + " " + w3.ToString() + " " + w4.ToString() ); break; case 6: x = reader.ReadSingle(); z = reader.ReadSingle(); y = reader.ReadSingle(); sb.AppendLine(" Tangent Normal: " + x.ToString() + " " + y.ToString() + " " + z.ToString() ); break; case 7: // Note, not splitting this up yet just reading an int sb.AppendLine(" TagVal: " + reader.ReadUInt32().ToString() ); break; case 10: sb.AppendLine(" VertexID: " + reader.ReadUInt32().ToString() ); break; } } }
Note that basically we just look through the VertexFormat, and read the data in order. There is no fixed "vertex format" version, it's all controlled via the vertexformat data.