Skip to content
Permalink
Browse files Browse the repository at this point in the history
Add support for int16 position / int8 normal values
  • Loading branch information
Norbyte committed Apr 19, 2021
1 parent 48892f2 commit feb8350
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 20 deletions.
2 changes: 1 addition & 1 deletion LSLib/Granny/Model/ColladaExporter.cs
Expand Up @@ -148,7 +148,7 @@ private void DetermineInputsFromComponentNames(List<string> componentNames)
private void DetermineInputsFromVertex(Vertex vertex)
{
var desc = vertex.Format;
if (!desc.HasPosition)
if (desc.PositionType == PositionType.None)
{
throw new NotImplementedException("Cannot import vertices without position");
}
Expand Down
4 changes: 2 additions & 2 deletions LSLib/Granny/Model/ColladaMesh.cs
Expand Up @@ -438,7 +438,7 @@ private void ImportSources()
private VertexDescriptor FindVertexFormat(bool isSkinned)
{
var desc = new VertexDescriptor();
desc.HasPosition = true;
desc.PositionType = PositionType.Float3;
if (isSkinned)
{
desc.HasBoneWeights = true;
Expand Down Expand Up @@ -490,9 +490,9 @@ public void ImportFromCollada(mesh mesh, VertexDescriptor vertexFormat, bool isS
InputVertexType = vertexFormat;
OutputVertexType = new VertexDescriptor
{
HasPosition = InputVertexType.HasPosition,
HasBoneWeights = InputVertexType.HasBoneWeights,
NumBoneInfluences = InputVertexType.NumBoneInfluences,
PositionType = InputVertexType.PositionType,
NormalType = InputVertexType.NormalType,
TangentType = InputVertexType.TangentType,
BinormalType = InputVertexType.BinormalType,
Expand Down
2 changes: 1 addition & 1 deletion LSLib/Granny/Model/DivinityMesh.cs
Expand Up @@ -94,7 +94,7 @@ private static DivinityFormatDesc Make(DivinityVertexUsage usage, DivinityVertex
public static List<DivinityFormatDesc> FromVertexFormat(VertexDescriptor format)
{
var formats = new List<DivinityFormatDesc>();
if (format.HasPosition)
if (format.PositionType != PositionType.None)
{
formats.Add(Make(DivinityVertexUsage.Position, DivinityVertexAttributeFormat.Real32, 3));
}
Expand Down
62 changes: 52 additions & 10 deletions LSLib/Granny/Model/Vertex.cs
Expand Up @@ -46,11 +46,19 @@ public override int GetHashCode()
}
}

public enum PositionType
{
None,
Float3,
Word4
};

public enum NormalType
{
None,
Float3,
Half4,
Byte4,
QTangent
};

Expand All @@ -73,9 +81,9 @@ public enum TextureCoordinateType
/// </summary>
public class VertexDescriptor
{
public bool HasPosition = true;
public bool HasBoneWeights = false;
public int NumBoneInfluences = 4;
public PositionType PositionType = PositionType.None;
public NormalType NormalType = NormalType.None;
public NormalType TangentType = NormalType.None;
public NormalType BinormalType = NormalType.None;
Expand All @@ -88,7 +96,7 @@ public class VertexDescriptor
public List<String> ComponentNames()
{
var names = new List<String>();
if (HasPosition)
if (PositionType != PositionType.None)
{
names.Add("Position");
}
Expand Down Expand Up @@ -145,8 +153,24 @@ public List<String> ComponentNames()
public String Name()
{
string vertexFormat;
vertexFormat = "P";
string attributeCounts = "3";
vertexFormat = "";
string attributeCounts = "";

switch (PositionType)
{
case PositionType.None:
break;

case PositionType.Float3:
vertexFormat += "P";
attributeCounts += "3";
break;

case PositionType.Word4:
vertexFormat += "PW";
attributeCounts += "4";
break;
}

if (HasBoneWeights)
{
Expand Down Expand Up @@ -404,12 +428,18 @@ public VertexDescriptor ConstructDescriptor(MemberDefinition memberDefn, StructD
switch (member.Name)
{
case "Position":
if (member.Type != MemberType.Real32
|| member.ArraySize != 3)
if (member.Type == MemberType.Real32 && member.ArraySize == 3)
{
desc.PositionType = PositionType.Float3;
}
else if (member.Type == MemberType.BinormalInt16 && member.ArraySize == 4)
{
throw new Exception("Vertex position must be a Vector3");
desc.PositionType = PositionType.Word4;
}
else
{
throw new Exception($"Unsupported position format: {member.Type}, {member.ArraySize}");
}
desc.HasPosition = true;
break;

case "BoneWeights":
Expand Down Expand Up @@ -443,6 +473,10 @@ public VertexDescriptor ConstructDescriptor(MemberDefinition memberDefn, StructD
{
desc.NormalType = NormalType.Half4;
}
else if (member.Type == MemberType.BinormalInt8 && member.ArraySize == 4)
{
desc.NormalType = NormalType.Byte4;
}
else
{
throw new Exception($"Unsupported normal format: {member.Type}, {member.ArraySize}");
Expand All @@ -469,7 +503,11 @@ public VertexDescriptor ConstructDescriptor(MemberDefinition memberDefn, StructD
}
else if (member.Type == MemberType.Real16 && member.ArraySize == 4)
{
desc.NormalType = NormalType.Half4;
desc.TangentType = NormalType.Half4;
}
else if (member.Type == MemberType.BinormalInt8 && member.ArraySize == 4)
{
desc.TangentType = NormalType.Byte4;
}
else
{
Expand All @@ -484,7 +522,11 @@ public VertexDescriptor ConstructDescriptor(MemberDefinition memberDefn, StructD
}
else if (member.Type == MemberType.Real16 && member.ArraySize == 4)
{
desc.NormalType = NormalType.Half4;
desc.BinormalType = NormalType.Half4;
}
else if (member.Type == MemberType.BinormalInt8 && member.ArraySize == 4)
{
desc.BinormalType = NormalType.Byte4;
}
else
{
Expand Down
61 changes: 55 additions & 6 deletions LSLib/Granny/Model/VertexSerialization.cs
Expand Up @@ -93,6 +93,26 @@ public static Vector4 ReadNormalByteVector4(GR2Reader reader)
return v;
}

public static Vector3 ReadNormalSWordVector4As3(GR2Reader reader)
{
Vector3 v;
v.X = reader.Reader.ReadInt16() / 32767.0f;
v.Y = reader.Reader.ReadInt16() / 32767.0f;
v.Z = reader.Reader.ReadInt16() / 32767.0f;
reader.Reader.ReadInt16(); // Unused word
return v;
}

public static Vector3 ReadNormalSByteVector4As3(GR2Reader reader)
{
Vector3 v;
v.X = reader.Reader.ReadSByte() / 127.0f;
v.Y = reader.Reader.ReadSByte() / 127.0f;
v.Z = reader.Reader.ReadSByte() / 127.0f;
reader.Reader.ReadSByte(); // Unused byte
return v;
}

public static Matrix3 ReadQTangent(GR2Reader reader)
{
Quaternion qTangent = ReadBinormalShortVector4(reader);
Expand Down Expand Up @@ -242,6 +262,20 @@ public static void WriteNormalByteVector4(WritableSection section, Vector4 v)
section.Writer.Write((byte)(v.Z * 255));
section.Writer.Write((byte)(v.W * 255));
}
public static void WriteNormalSWordVector3As4(WritableSection section, Vector3 v)
{
section.Writer.Write((Int16)(v.X * 32767));
section.Writer.Write((Int16)(v.Y * 32767));
section.Writer.Write((Int16)(v.Z * 32767));
section.Writer.Write(0);
}
public static void WriteNormalSByteVector3As4(WritableSection section, Vector3 v)
{
section.Writer.Write((sbyte)(v.X * 127));
section.Writer.Write((sbyte)(v.Y * 127));
section.Writer.Write((sbyte)(v.Z * 127));
section.Writer.Write(0);
}

public static void WriteInfluences2(WritableSection section, BoneWeight v)
{
Expand Down Expand Up @@ -270,9 +304,11 @@ public static void Serialize(WritableSection section, Vertex v)
{
var d = v.Format;

if (d.HasPosition)
switch (d.PositionType)
{
WriteVector3(section, v.Position);
case PositionType.None: break;
case PositionType.Float3: WriteVector3(section, v.Position); break;
case PositionType.Word4: WriteNormalSWordVector3As4(section, v.Position); break;
}

if (d.HasBoneWeights)
Expand All @@ -294,6 +330,7 @@ public static void Serialize(WritableSection section, Vertex v)
case NormalType.None: break;
case NormalType.Float3: WriteVector3(section, v.Normal); break;
case NormalType.Half4: WriteHalfVector3As4(section, v.Normal); break;
case NormalType.Byte4: WriteNormalSByteVector3As4(section, v.Normal); break;
case NormalType.QTangent: WriteQTangent(section, v.Normal, v.Tangent, v.Binormal); break;
}

Expand All @@ -302,6 +339,7 @@ public static void Serialize(WritableSection section, Vertex v)
case NormalType.None: break;
case NormalType.Float3: WriteVector3(section, v.Tangent); break;
case NormalType.Half4: WriteHalfVector3As4(section, v.Tangent); break;
case NormalType.Byte4: WriteNormalSByteVector3As4(section, v.Tangent); break;
case NormalType.QTangent: break; // Tangent saved into QTangent
}

Expand All @@ -310,6 +348,7 @@ public static void Serialize(WritableSection section, Vertex v)
case NormalType.None: break;
case NormalType.Float3: WriteVector3(section, v.Binormal); break;
case NormalType.Half4: WriteHalfVector3As4(section, v.Binormal); break;
case NormalType.Byte4: WriteNormalSByteVector3As4(section, v.Binormal); break;
case NormalType.QTangent: break; // Binormal saved into QTangent
}

Expand Down Expand Up @@ -346,9 +385,11 @@ public static void Unserialize(GR2Reader reader, Vertex v)
{
var d = v.Format;

if (d.HasPosition)
switch (d.PositionType)
{
v.Position = ReadVector3(reader);
case PositionType.None: break;
case PositionType.Float3: v.Position = ReadVector3(reader); break;
case PositionType.Word4: v.Position = ReadNormalSWordVector4As3(reader); break;
}

if (d.HasBoneWeights)
Expand All @@ -370,6 +411,7 @@ public static void Unserialize(GR2Reader reader, Vertex v)
case NormalType.None: break;
case NormalType.Float3: v.Normal = ReadVector3(reader); break;
case NormalType.Half4: v.Normal = ReadHalfVector4As3(reader); break;
case NormalType.Byte4: v.Normal = ReadNormalSByteVector4As3(reader); break;
case NormalType.QTangent:
{
var qTangent = ReadQTangent(reader);
Expand All @@ -385,6 +427,7 @@ public static void Unserialize(GR2Reader reader, Vertex v)
case NormalType.None: break;
case NormalType.Float3: v.Tangent = ReadVector3(reader); break;
case NormalType.Half4: v.Tangent = ReadHalfVector4As3(reader); break;
case NormalType.Byte4: v.Tangent = ReadNormalSByteVector4As3(reader); break;
case NormalType.QTangent: break; // Tangent read from QTangent
}

Expand All @@ -393,6 +436,7 @@ public static void Unserialize(GR2Reader reader, Vertex v)
case NormalType.None: break;
case NormalType.Float3: v.Binormal = ReadVector3(reader); break;
case NormalType.Half4: v.Binormal = ReadHalfVector4As3(reader); break;
case NormalType.Byte4: v.Binormal = ReadNormalSByteVector4As3(reader); break;
case NormalType.QTangent: break; // Binormal read from QTangent
}

Expand Down Expand Up @@ -502,9 +546,11 @@ public StructDefinition CreateStructDefinition(object instance)
Type = typeof(Vertex)
};

if (desc.HasPosition)
switch (desc.PositionType)
{
AddMember(defn, "Position", MemberType.Real32, 3);
case PositionType.None: break;
case PositionType.Float3: AddMember(defn, "Position", MemberType.Real32, 3); break;
case PositionType.Word4: AddMember(defn, "Position", MemberType.BinormalInt16, 4); break;
}

if (desc.HasBoneWeights)
Expand All @@ -518,6 +564,7 @@ public StructDefinition CreateStructDefinition(object instance)
case NormalType.None: break;
case NormalType.Float3: AddMember(defn, "Normal", MemberType.Real32, 3); break;
case NormalType.Half4: AddMember(defn, "Normal", MemberType.Real16, 4); break;
case NormalType.Byte4: AddMember(defn, "Normal", MemberType.BinormalInt8, 4); break;
case NormalType.QTangent: AddMember(defn, "QTangent", MemberType.BinormalInt16, 4); break;
}

Expand All @@ -526,6 +573,7 @@ public StructDefinition CreateStructDefinition(object instance)
case NormalType.None: break;
case NormalType.Float3: AddMember(defn, "Tangent", MemberType.Real32, 3); break;
case NormalType.Half4: AddMember(defn, "Tangent", MemberType.Real16, 4); break;
case NormalType.Byte4: AddMember(defn, "Tangent", MemberType.BinormalInt8, 4); break;
case NormalType.QTangent: break; // Tangent saved into QTangent
}

Expand All @@ -534,6 +582,7 @@ public StructDefinition CreateStructDefinition(object instance)
case NormalType.None: break;
case NormalType.Float3: AddMember(defn, "Binormal", MemberType.Real32, 3); break;
case NormalType.Half4: AddMember(defn, "Binormal", MemberType.Real16, 4); break;
case NormalType.Byte4: AddMember(defn, "Binormal", MemberType.BinormalInt8, 4); break;
case NormalType.QTangent: break; // Binormal saved into QTangent
}

Expand Down

0 comments on commit feb8350

Please sign in to comment.