Skip to content

Commit 8835146

Browse files
committed
Proper support for RB3/DC1-era vertex compression
1 parent 15463b3 commit 8835146

2 files changed

Lines changed: 188 additions & 104 deletions

File tree

MiloLib/Assets/Rnd/RndMesh.cs

Lines changed: 87 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -236,9 +236,13 @@ public void ReadVertices(EndianReader reader, uint meshVersion, bool isNextGen,
236236
}
237237
else
238238
{
239-
newVert.uvCheck = reader.ReadInt32();
240-
if (newVert.uvCheck == -1)
239+
if (compressionType == 1)
241240
{
241+
uint value = reader.ReadUInt32();
242+
newVert.vertexColors.r = (byte)((value >> 24) & 0xFF);
243+
newVert.vertexColors.g = (byte)((value >> 16) & 0xFF);
244+
newVert.vertexColors.b = (byte)((value >> 8) & 0xFF);
245+
newVert.vertexColors.a = (byte)(value & 0xFF);
242246
newVert.u = reader.ReadHalfFloat();
243247
newVert.v = reader.ReadHalfFloat();
244248

@@ -270,56 +274,44 @@ public void ReadVertices(EndianReader reader, uint meshVersion, bool isNextGen,
270274
newVert.bone2 = reader.ReadByte();
271275
newVert.bone3 = reader.ReadByte();
272276
}
273-
else
277+
else if (compressionType == 2)
274278
{
275-
if (compressionType == 1)
276-
{
277-
reader.BaseStream.Position -= 4;
278-
newVert.u = reader.ReadHalfFloat();
279-
newVert.v = reader.ReadHalfFloat();
280-
281-
Vertex.QTangent tangents = new Vertex.QTangent();
282-
tangents.Read(reader);
283-
newVert.tangent0 = tangents.x.fValue;
284-
newVert.tangent1 = tangents.y.fValue;
285-
newVert.tangent2 = tangents.z.fValue;
286-
newVert.tangent3 = tangents.w.fValue;
287-
288-
newVert.weight0 = reader.ReadByte();
289-
newVert.weight1 = reader.ReadByte();
290-
newVert.weight2 = reader.ReadByte();
291-
newVert.weight3 = reader.ReadByte();
292-
293-
newVert.bone0 = reader.ReadUInt16();
294-
newVert.bone1 = reader.ReadUInt16();
295-
newVert.bone2 = reader.ReadUInt16();
296-
newVert.bone3 = reader.ReadUInt16();
297-
}
298-
else if (compressionType == 2)
299-
{
300-
reader.BaseStream.Position -= 4;
301-
newVert.u = reader.ReadHalfFloat();
302-
newVert.v = reader.ReadHalfFloat();
303-
304-
newVert.unknown2 = reader.ReadFloat();
305-
306-
Vertex.QTangent tangents = new Vertex.QTangent();
307-
tangents.Read(reader);
308-
newVert.tangent0 = tangents.x.fValue;
309-
newVert.tangent1 = tangents.y.fValue;
310-
newVert.tangent2 = tangents.z.fValue;
311-
newVert.tangent3 = tangents.w.fValue;
312-
313-
newVert.weight0 = reader.ReadByte();
314-
newVert.weight1 = reader.ReadByte();
315-
newVert.weight2 = reader.ReadByte();
316-
newVert.weight3 = reader.ReadByte();
317-
318-
newVert.bone0 = reader.ReadUInt16();
319-
newVert.bone1 = reader.ReadUInt16();
320-
newVert.bone2 = reader.ReadUInt16();
321-
newVert.bone3 = reader.ReadUInt16();
322-
}
279+
newVert.u = reader.ReadHalfFloat();
280+
newVert.v = reader.ReadHalfFloat();
281+
282+
Vertex.PS3SignedCompressedVec3 norms = new Vertex.PS3SignedCompressedVec3();
283+
norms.Read(reader);
284+
newVert.nx = norms.x;
285+
newVert.ny = norms.y;
286+
newVert.nz = norms.z;
287+
newVert.nw = norms.w;
288+
289+
Vertex.PS3SignedCompressedVec3 tangents = new Vertex.PS3SignedCompressedVec3();
290+
tangents.Read(reader);
291+
292+
newVert.tangent0 = tangents.x;
293+
newVert.tangent1 = tangents.y;
294+
newVert.tangent2 = tangents.z;
295+
newVert.tangent3 = tangents.w;
296+
297+
Vertex.PS3UnsignedCompressedVec3 weights = new Vertex.PS3UnsignedCompressedVec3();
298+
weights.Read(reader);
299+
300+
uint value = reader.ReadUInt32();
301+
newVert.vertexColors.a = (byte)((value >> 24) & 0xFF);
302+
newVert.vertexColors.r = (byte)((value >> 16) & 0xFF);
303+
newVert.vertexColors.g = (byte)((value >> 8) & 0xFF);
304+
newVert.vertexColors.b = (byte)(value & 0xFF);
305+
306+
newVert.weight0 = weights.x;
307+
newVert.weight1 = weights.y;
308+
newVert.weight2 = weights.z;
309+
newVert.weight3 = weights.w;
310+
311+
newVert.bone0 = reader.ReadUInt16();
312+
newVert.bone1 = reader.ReadUInt16();
313+
newVert.bone2 = reader.ReadUInt16();
314+
newVert.bone3 = reader.ReadUInt16();
323315
}
324316
}
325317

@@ -449,11 +441,11 @@ public void WriteVertices(EndianWriter writer, uint meshVersion, bool isNextGen,
449441
}
450442
else
451443
{
452-
// We assume we have stored an int 'UvIndicator' in vertex indicating the read 'uv' value:
453-
writer.WriteInt32(vertex.uvCheck);
454-
455-
if (vertex.uvCheck == -1)
444+
if (compressionType == 1)
456445
{
446+
// vertex colors in RGBA?
447+
uint value = ((uint)vertex.vertexColors.r << 24) | ((uint)vertex.vertexColors.g << 16) | ((uint)vertex.vertexColors.b << 8) | (uint)vertex.vertexColors.a;
448+
writer.WriteUInt32(value);
457449
writer.WriteHalfFloat(vertex.u);
458450
writer.WriteHalfFloat(vertex.v);
459451

@@ -489,58 +481,52 @@ public void WriteVertices(EndianWriter writer, uint meshVersion, bool isNextGen,
489481
writer.WriteByte((byte)vertex.bone2);
490482
writer.WriteByte((byte)vertex.bone3);
491483
}
492-
else
484+
else if (compressionType == 2)
493485
{
494-
if (compressionType == 1)
486+
writer.WriteHalfFloat(vertex.u);
487+
writer.WriteHalfFloat(vertex.v);
488+
489+
Vertex.PS3SignedCompressedVec3 norms = new Vertex.PS3SignedCompressedVec3
495490
{
496-
writer.BaseStream.Position -= 4;
497-
writer.WriteHalfFloat(vertex.u);
498-
writer.WriteHalfFloat(vertex.v);
491+
x = vertex.nx,
492+
y = vertex.ny,
493+
z = vertex.nz,
494+
w = vertex.nw
495+
};
496+
norms.Write(writer);
499497

500-
Vertex.QTangent tangents = new Vertex.QTangent
501-
{
502-
x = new Vertex.QTangent.SNorm((short)vertex.tangent0),
503-
y = new Vertex.QTangent.SNorm((short)vertex.tangent1),
504-
z = new Vertex.QTangent.SNorm((short)vertex.tangent2),
505-
w = new Vertex.QTangent.SNorm((short)vertex.tangent3)
506-
};
507-
508-
writer.WriteByte((byte)vertex.weight0);
509-
writer.WriteByte((byte)vertex.weight1);
510-
writer.WriteByte((byte)vertex.weight2);
511-
writer.WriteByte((byte)vertex.weight3);
512-
513-
writer.WriteUInt16(vertex.bone0);
514-
writer.WriteUInt16(vertex.bone1);
515-
writer.WriteUInt16(vertex.bone2);
516-
writer.WriteUInt16(vertex.bone3);
517-
}
518-
else if (compressionType == 2)
498+
Vertex.PS3SignedCompressedVec3 tangents = new Vertex.PS3SignedCompressedVec3
499+
{
500+
x = vertex.tangent0,
501+
y = vertex.tangent1,
502+
z = vertex.tangent2,
503+
w = vertex.tangent3
504+
};
505+
tangents.Write(writer);
506+
507+
Vertex.PS3UnsignedCompressedVec3 weights = new Vertex.PS3UnsignedCompressedVec3
519508
{
520-
writer.BaseStream.Position -= 4;
521-
writer.WriteHalfFloat(vertex.u);
522-
writer.WriteHalfFloat(vertex.v);
509+
x = vertex.weight0,
510+
y = vertex.weight1,
511+
z = vertex.weight2,
512+
w = vertex.weight3
513+
};
514+
weights.Write(writer);
523515

524-
writer.WriteFloat(vertex.unknown2);
516+
Vertex.PS3UnsignedCompressedVec3 unknown = new Vertex.PS3UnsignedCompressedVec3
517+
{
518+
x = vertex.weight0,
519+
y = vertex.weight1,
520+
z = vertex.weight2,
521+
w = vertex.weight3
522+
};
523+
uint value = ((uint)vertex.vertexColors.a << 24) | ((uint)vertex.vertexColors.r << 16) | ((uint)vertex.vertexColors.g << 8) | (uint)vertex.vertexColors.b;
524+
writer.WriteUInt32(value);
525525

526-
Vertex.QTangent tangents = new Vertex.QTangent
527-
{
528-
x = new Vertex.QTangent.SNorm((short)vertex.tangent0),
529-
y = new Vertex.QTangent.SNorm((short)vertex.tangent1),
530-
z = new Vertex.QTangent.SNorm((short)vertex.tangent2),
531-
w = new Vertex.QTangent.SNorm((short)vertex.tangent3)
532-
};
533-
534-
writer.WriteByte((byte)vertex.weight0);
535-
writer.WriteByte((byte)vertex.weight1);
536-
writer.WriteByte((byte)vertex.weight2);
537-
writer.WriteByte((byte)vertex.weight3);
538-
539-
writer.WriteUInt16(vertex.bone0);
540-
writer.WriteUInt16(vertex.bone1);
541-
writer.WriteUInt16(vertex.bone2);
542-
writer.WriteUInt16(vertex.bone3);
543-
}
526+
writer.WriteUInt16((byte)vertex.bone0);
527+
writer.WriteUInt16((byte)vertex.bone1);
528+
writer.WriteUInt16((byte)vertex.bone2);
529+
writer.WriteUInt16((byte)vertex.bone3);
544530
}
545531
}
546532
}

MiloLib/Assets/Rnd/Vertex.cs

Lines changed: 101 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using MiloLib.Utils;
1+
using MiloLib.Classes;
2+
using MiloLib.Utils;
23
using System;
34
using System.Collections.Generic;
45
using System.Linq;
@@ -55,13 +56,14 @@ public class Vertex
5556
public float neg1 { get; set; } = 0.0f;
5657
public float pos1 { get; set; } = 0.0f;
5758

58-
public int uvCheck { get; set; }
59+
public HmxColor4 vertexColors = new HmxColor4();
5960

6061
public override string ToString()
6162
{
6263
return $"<{x}, {y}, {z}>";
6364
}
6465

66+
// Xbox 360: 10-10-10-2 format
6567
public class SignedCompressedVec4
6668
{
6769
public uint origValue { get; private set; }
@@ -121,6 +123,64 @@ public void Write(EndianWriter writer)
121123
}
122124
}
123125

126+
// PS3: 11-11-10 format (signed, for normals)
127+
public class PS3SignedCompressedVec3
128+
{
129+
public uint origValue { get; private set; }
130+
public float x { get; set; } = 0.0f;
131+
public float y { get; set; } = 0.0f;
132+
public float z { get; set; } = 0.0f;
133+
public float w { get; set; } = 0.0f;
134+
135+
private static int ToSNormBits(float f, int n)
136+
{
137+
f = Math.Clamp(f, -1f, 1f);
138+
int max = (1 << (n - 1)) - 1;
139+
int s = (int)MathF.Truncate(f * max);
140+
if (s < 0) s += (1 << n);
141+
return s & ((1 << n) - 1);
142+
}
143+
144+
private static float FromSNormBits(int bits, int n)
145+
{
146+
int max = (1 << (n - 1)) - 1;
147+
int s = bits;
148+
if (s > max) s -= (1 << n);
149+
return Math.Max(s / (float)max, -1f);
150+
}
151+
152+
public PS3SignedCompressedVec3 Read(EndianReader reader)
153+
{
154+
uint value = reader.ReadUInt32();
155+
origValue = value;
156+
157+
int xb = (int)(value & 0x7FF); // 11 bits
158+
int yb = (int)((value >> 11) & 0x7FF); // 11 bits
159+
int zb = (int)((value >> 22) & 0x3FF); // 10 bits
160+
161+
x = FromSNormBits(xb, 11);
162+
y = FromSNormBits(yb, 11);
163+
z = FromSNormBits(zb, 10);
164+
w = 0.0f; // No W component
165+
166+
return this;
167+
}
168+
169+
public void Write(EndianWriter writer)
170+
{
171+
int xBits = ToSNormBits(x, 11);
172+
int yBits = ToSNormBits(y, 11);
173+
int zBits = ToSNormBits(z, 10);
174+
175+
uint value = (uint)xBits
176+
| (uint)(yBits << 11)
177+
| (uint)(zBits << 22);
178+
179+
writer.WriteUInt32(value);
180+
}
181+
}
182+
183+
// Xbox 360: 10-10-10-2 format (unsigned, for weights)
124184
public class UnsignedCompressedVec4
125185
{
126186
public float x { get; set; } = 0.0f;
@@ -161,6 +221,44 @@ public void Write(EndianWriter writer)
161221
}
162222
}
163223

224+
// PS3: 11-11-10 format (unsigned, for weights)
225+
public class PS3UnsignedCompressedVec3
226+
{
227+
public float x { get; set; } = 0.0f;
228+
public float y { get; set; } = 0.0f;
229+
public float z { get; set; } = 0.0f;
230+
public float w { get; set; } = 0.0f;
231+
232+
public PS3UnsignedCompressedVec3 Read(EndianReader reader)
233+
{
234+
uint value = reader.ReadUInt32();
235+
236+
uint xb = (value & 0x7FF); // 11 bits
237+
uint yb = ((value >> 11) & 0x7FF); // 11 bits
238+
uint zb = ((value >> 22) & 0x3FF); // 10 bits
239+
240+
x = xb / 1023f;
241+
y = yb / 1023f;
242+
z = zb / 511f;
243+
w = 0.0f; // No W component
244+
245+
return this;
246+
}
247+
248+
public void Write(EndianWriter writer)
249+
{
250+
int xBits = (int)MathF.Truncate(Math.Clamp(x, 0f, 1f) * 1023f) & 0x7FF;
251+
int yBits = (int)MathF.Truncate(Math.Clamp(y, 0f, 1f) * 1023f) & 0x7FF;
252+
int zBits = (int)MathF.Truncate(Math.Clamp(z, 0f, 1f) * 511f) & 0x3FF;
253+
254+
uint value = (uint)xBits
255+
| (uint)(yBits << 11)
256+
| (uint)(zBits << 22);
257+
258+
writer.WriteUInt32(value);
259+
}
260+
}
261+
164262
public class QTangent
165263
{
166264
public SNorm x { get; set; } = new SNorm();
@@ -199,4 +297,4 @@ public void Write(EndianWriter writer)
199297
}
200298
}
201299
}
202-
}
300+
}

0 commit comments

Comments
 (0)