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.