AssetStudio/AssetStudio/Classes/Mesh.cs

1139 lines
51 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using SharpDX;
namespace AssetStudio
{
public sealed class Mesh : NamedObject
{
public List<SubMesh> m_SubMeshes = new List<SubMesh>();
public List<uint> m_Indices = new List<uint>(); //use a list because I don't always know the facecount for triangle strips
public List<int> m_materialIDs = new List<int>();
private uint[] m_IndexBuffer;
private ChannelInfo[] m_Channels;
private StreamInfo[] m_Streams;
public List<BoneInfluence>[] m_Skin;
public float[][,] m_BindPose;
public int m_VertexCount;
public float[] m_Vertices;
public float[] m_Normals;
public float[] m_Colors;
public float[] m_UV1;
public float[] m_UV2;
public float[] m_UV3;
public float[] m_UV4;
public float[] m_Tangents;
public uint[] m_BoneNameHashes;
public BlendShapeData m_Shapes;
public class SubMesh
{
public uint firstByte;
public uint indexCount;
public int topology;
public uint triangleCount;
public uint firstVertex;
public uint vertexCount;
}
public class BoneInfluence
{
public float weight;
public int boneIndex;
}
public class ChannelInfo
{
public byte stream;
public byte offset;
public byte format;
public byte dimension;
}
public class StreamInfo
{
public BitArray channelMask;
public int offset;
public int stride;
public uint align; //3.5.0 - 3.5.7
public byte dividerOp; //4.0.0 and later
public ushort frequency;
}
public class BlendShapeData
{
public class BlendShapeVertex
{
public Vector3 vertex { get; set; }
public Vector3 normal { get; set; }
public Vector3 tangent { get; set; }
public uint index { get; set; }
public BlendShapeVertex(ObjectReader reader)
{
vertex = reader.ReadVector3();
normal = reader.ReadVector3();
tangent = reader.ReadVector3();
index = reader.ReadUInt32();
}
}
public class MeshBlendShape
{
public uint firstVertex { get; set; }
public uint vertexCount { get; set; }
public bool hasNormals { get; set; }
public bool hasTangents { get; set; }
public MeshBlendShape(ObjectReader reader)
{
firstVertex = reader.ReadUInt32();
vertexCount = reader.ReadUInt32();
hasNormals = reader.ReadBoolean();
hasTangents = reader.ReadBoolean();
reader.AlignStream(4);
}
}
public class MeshBlendShapeChannel
{
public string name { get; set; }
public uint nameHash { get; set; }
public int frameIndex { get; set; }
public int frameCount { get; set; }
public MeshBlendShapeChannel(ObjectReader reader)
{
name = reader.ReadAlignedString();
nameHash = reader.ReadUInt32();
frameIndex = reader.ReadInt32();
frameCount = reader.ReadInt32();
}
}
public List<BlendShapeVertex> vertices { get; set; }
public List<MeshBlendShape> shapes { get; set; }
public List<MeshBlendShapeChannel> channels { get; set; }
public List<float> fullWeights { get; set; }
public BlendShapeData(ObjectReader reader)
{
int numVerts = reader.ReadInt32();
vertices = new List<BlendShapeVertex>(numVerts);
for (int i = 0; i < numVerts; i++)
{
vertices.Add(new BlendShapeVertex(reader));
}
int numShapes = reader.ReadInt32();
shapes = new List<MeshBlendShape>(numShapes);
for (int i = 0; i < numShapes; i++)
{
shapes.Add(new MeshBlendShape(reader));
}
int numChannels = reader.ReadInt32();
channels = new List<MeshBlendShapeChannel>(numChannels);
for (int i = 0; i < numChannels; i++)
{
channels.Add(new MeshBlendShapeChannel(reader));
}
int numWeights = reader.ReadInt32();
fullWeights = new List<float>(numWeights);
for (int i = 0; i < numWeights; i++)
{
fullWeights.Add(reader.ReadSingle());
}
}
}
private float BytesToFloat(byte[] inputBytes, EndianType endian)
{
float result = 0;
if (endian == EndianType.BigEndian)
{
Array.Reverse(inputBytes);
}
switch (inputBytes.Length)
{
case 1:
result = inputBytes[0] / 255.0f;
break;
case 2:
result = System.Half.ToHalf(inputBytes, 0);
break;
case 4:
result = BitConverter.ToSingle(inputBytes, 0);
break;
}
return result;
}
private static int GetChannelFormatSize(int format)
{
switch (format)
{
case 0: //kChannelFormatFloat
return 4;
case 1: //kChannelFormatFloat16
return 2;
case 2: //kChannelFormatColor, in 4.x is size 4
return 1;
case 3: //kChannelFormatByte
return 1;
case 11: //kChannelFormatInt32
return 4;
default:
return 0;
}
}
private static float[] BytesToFloatArray(byte[] inputBytes, int size)
{
var result = new float[inputBytes.Length / size];
for (int i = 0; i < inputBytes.Length / size; i++)
{
float value = 0f;
switch (size)
{
case 1:
value = inputBytes[i] / 255.0f;
break;
case 2:
value = System.Half.ToHalf(inputBytes, i * 2);
break;
case 4:
value = BitConverter.ToSingle(inputBytes, i * 4);
break;
}
result[i] = value;
}
return result;
}
private static int[] BytesToIntArray(byte[] inputBytes)
{
var result = new int[inputBytes.Length / 4];
for (int i = 0; i < inputBytes.Length / 4; i++)
{
result[i] = BitConverter.ToInt32(inputBytes, i * 4);
}
return result;
}
private void InitMSkin()
{
m_Skin = new List<BoneInfluence>[m_VertexCount];
for (int i = 0; i < m_VertexCount; i++)
{
m_Skin[i] = new List<BoneInfluence>(4);
for (int j = 0; j < 4; j++)
{
m_Skin[i].Add(new BoneInfluence());
}
}
}
public Mesh(ObjectReader reader) : base(reader)
{
bool m_Use16BitIndices = true; //3.5.0 and newer always uses 16bit indices
uint m_MeshCompression = 0;
if (version[0] < 3 || (version[0] == 3 && version[1] < 5))
{
m_Use16BitIndices = reader.ReadBoolean();
reader.Position += 3;
}
#region Index Buffer for 2.5.1 and earlier
if (version[0] == 2 && version[1] <= 5)
{
int m_IndexBuffer_size = reader.ReadInt32();
if (m_Use16BitIndices)
{
m_IndexBuffer = new uint[m_IndexBuffer_size / 2];
for (int i = 0; i < m_IndexBuffer_size / 2; i++) { m_IndexBuffer[i] = reader.ReadUInt16(); }
reader.AlignStream(4);
}
else
{
m_IndexBuffer = new uint[m_IndexBuffer_size / 4];
for (int i = 0; i < m_IndexBuffer_size / 4; i++) { m_IndexBuffer[i] = reader.ReadUInt32(); }
}
}
#endregion
#region subMeshes
int m_SubMeshes_size = reader.ReadInt32();
for (int s = 0; s < m_SubMeshes_size; s++)
{
m_SubMeshes.Add(new SubMesh());
m_SubMeshes[s].firstByte = reader.ReadUInt32();
m_SubMeshes[s].indexCount = reader.ReadUInt32(); //what is this in case of triangle strips?
m_SubMeshes[s].topology = reader.ReadInt32(); //isTriStrip
if (version[0] < 4)
{
m_SubMeshes[s].triangleCount = reader.ReadUInt32();
}
if (version[0] >= 3)
{
if (version[0] > 2017 || (version[0] == 2017 && version[1] >= 3))//2017.3 and up
{
var baseVertex = reader.ReadUInt32();
}
m_SubMeshes[s].firstVertex = reader.ReadUInt32();
m_SubMeshes[s].vertexCount = reader.ReadUInt32();
reader.Position += 24; //Axis-Aligned Bounding Box
}
}
#endregion
#region BlendShapeData for 4.1.0 to 4.2.x, excluding 4.1.0 alpha
if (version[0] == 4 && ((version[1] == 1 && buildType[0] != "a") || (version[1] > 1 && version[1] <= 2)))
{
int m_Shapes_size = reader.ReadInt32();
if (m_Shapes_size > 0)
{
//bool stop = true;
}
for (int s = 0; s < m_Shapes_size; s++) //untested
{
string shape_name = reader.ReadAlignedString();
reader.Position += 36; //uint firstVertex, vertexCount; Vector3f aabbMinDelta, aabbMaxDelta; bool hasNormals, hasTangents
}
int m_ShapeVertices_size = reader.ReadInt32();
reader.Position += m_ShapeVertices_size * 40; //vertex positions, normals, tangents & uint index
}
#endregion
#region BlendShapeData and BindPose for 4.3.0 and later
else if (version[0] >= 5 || (version[0] == 4 && version[1] >= 3))
{
m_Shapes = new BlendShapeData(reader);
m_BindPose = new float[reader.ReadInt32()][,];
for (int i = 0; i < m_BindPose.Length; i++)
{
m_BindPose[i] = new[,] {
{ reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle() },
{ reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle() },
{ reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle() },
{ reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle() } };
}
int m_BoneNameHashes_size = reader.ReadInt32();
m_BoneNameHashes = new uint[m_BoneNameHashes_size];
for (int i = 0; i < m_BoneNameHashes_size; i++)
{
m_BoneNameHashes[i] = reader.ReadUInt32();
}
uint m_RootBoneNameHash = reader.ReadUInt32();
}
#endregion
#region Index Buffer for 2.6.0 and later
if (version[0] >= 3 || (version[0] == 2 && version[1] >= 6))
{
m_MeshCompression = reader.ReadByte();
if (version[0] >= 4)
{
if (version[0] < 5)
{
uint m_StreamCompression = reader.ReadByte();
}
bool m_IsReadable = reader.ReadBoolean();
bool m_KeepVertices = reader.ReadBoolean();
bool m_KeepIndices = reader.ReadBoolean();
if (reader.HasStructMember("m_UsedForStaticMeshColliderOnly"))
{
var m_UsedForStaticMeshColliderOnly = reader.ReadBoolean();
}
}
reader.AlignStream(4);
//This is a bug fixed in 2017.3.1p1 and later versions
if ((version[0] > 2017 || (version[0] == 2017 && version[1] >= 4)) || //2017.4
((version[0] == 2017 && version[1] == 3 && version[2] == 1) && buildType[0] == "p") || //fixed after 2017.3.1px
((version[0] == 2017 && version[1] == 3) && m_MeshCompression == 0))//2017.3.xfx with no compression
{
var m_IndexFormat = reader.ReadInt32();
}
int m_IndexBuffer_size = reader.ReadInt32();
if (m_Use16BitIndices)
{
m_IndexBuffer = new uint[m_IndexBuffer_size / 2];
for (int i = 0; i < m_IndexBuffer_size / 2; i++) { m_IndexBuffer[i] = reader.ReadUInt16(); }
reader.AlignStream(4);
}
else
{
m_IndexBuffer = new uint[m_IndexBuffer_size / 4];
for (int i = 0; i < m_IndexBuffer_size / 4; i++) { m_IndexBuffer[i] = reader.ReadUInt32(); }
reader.AlignStream(4);//untested
}
}
#endregion
#region Vertex Buffer for 3.4.2 and earlier
if (version[0] < 3 || (version[0] == 3 && version[1] < 5))
{
m_VertexCount = reader.ReadInt32();
m_Vertices = new float[m_VertexCount * 3];
for (int v = 0; v < m_VertexCount * 3; v++) { m_Vertices[v] = reader.ReadSingle(); }
m_Skin = new List<BoneInfluence>[reader.ReadInt32()];
for (int s = 0; s < m_Skin.Length; s++)
{
m_Skin[s] = new List<BoneInfluence>();
for (int i = 0; i < 4; i++) { m_Skin[s].Add(new BoneInfluence() { weight = reader.ReadSingle() }); }
for (int i = 0; i < 4; i++) { m_Skin[s][i].boneIndex = reader.ReadInt32(); }
}
m_BindPose = new float[reader.ReadInt32()][,];
for (int i = 0; i < m_BindPose.Length; i++)
{
m_BindPose[i] = new[,] {
{ reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle() },
{ reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle() },
{ reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle() },
{ reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle() } };
}
int m_UV1_size = reader.ReadInt32();
m_UV1 = new float[m_UV1_size * 2];
for (int v = 0; v < m_UV1_size * 2; v++) { m_UV1[v] = reader.ReadSingle(); }
int m_UV2_size = reader.ReadInt32();
m_UV2 = new float[m_UV2_size * 2];
for (int v = 0; v < m_UV2_size * 2; v++) { m_UV2[v] = reader.ReadSingle(); }
if (version[0] == 2 && version[1] <= 5)
{
int m_TangentSpace_size = reader.ReadInt32();
m_Normals = new float[m_TangentSpace_size * 3];
for (int v = 0; v < m_TangentSpace_size; v++)
{
m_Normals[v * 3] = reader.ReadSingle();
m_Normals[v * 3 + 1] = reader.ReadSingle();
m_Normals[v * 3 + 2] = reader.ReadSingle();
reader.Position += 16; //Vector3f tangent & float handedness
}
}
else //2.6.0 and later
{
int m_Tangents_size = reader.ReadInt32();
reader.Position += m_Tangents_size * 16; //Vector4f
int m_Normals_size = reader.ReadInt32();
m_Normals = new float[m_Normals_size * 3];
for (int v = 0; v < m_Normals_size * 3; v++) { m_Normals[v] = reader.ReadSingle(); }
}
}
#endregion
#region Vertex Buffer for 3.5.0 and later
else
{
#region read vertex stream
if (version[0] < 2018 || (version[0] == 2018 && version[1] < 2)) //2018.2 down
{
m_Skin = new List<BoneInfluence>[reader.ReadInt32()];
for (int s = 0; s < m_Skin.Length; s++)
{
m_Skin[s] = new List<BoneInfluence>();
for (int i = 0; i < 4; i++)
{
m_Skin[s].Add(new BoneInfluence() { weight = reader.ReadSingle() });
}
for (int i = 0; i < 4; i++)
{
m_Skin[s][i].boneIndex = reader.ReadInt32();
}
}
}
if (version[0] == 3 || (version[0] == 4 && version[1] <= 2))
{
m_BindPose = new float[reader.ReadInt32()][,];
for (int i = 0; i < m_BindPose.Length; i++)
{
m_BindPose[i] = new[,] {
{ reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle() },
{ reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle() },
{ reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle() },
{ reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle() } };
}
}
if (version[0] < 2018)//2018 down
{
var m_CurrentChannels = reader.ReadInt32();
}
m_VertexCount = reader.ReadInt32();
int streamCount = 0;
#region streams for 3.5.0 - 3.5.7
if (version[0] < 4)
{
if (m_MeshCompression != 0 && version[2] == 0) //special case not just on platform 9
{
reader.Position += 12;
}
else
{
m_Streams = new StreamInfo[4];
for (int s = 0; s < 4; s++)
{
m_Streams[s] = new StreamInfo();
m_Streams[s].channelMask = new BitArray(new int[1] { reader.ReadInt32() });
m_Streams[s].offset = reader.ReadInt32();
m_Streams[s].stride = reader.ReadInt32();
m_Streams[s].align = reader.ReadUInt32();
}
}
}
#endregion
#region channels and streams for 4.0.0 and later
else
{
m_Channels = new ChannelInfo[reader.ReadInt32()];
for (int c = 0; c < m_Channels.Length; c++)
{
m_Channels[c] = new ChannelInfo();
m_Channels[c].stream = reader.ReadByte();
m_Channels[c].offset = reader.ReadByte();
m_Channels[c].format = reader.ReadByte();
m_Channels[c].dimension = reader.ReadByte();
if (m_Channels[c].stream >= streamCount) { streamCount = m_Channels[c].stream + 1; }
}
if (version[0] < 5)
{
m_Streams = new StreamInfo[reader.ReadInt32()];
for (int s = 0; s < m_Streams.Length; s++)
{
m_Streams[s] = new StreamInfo();
m_Streams[s].channelMask = new BitArray(new[] { reader.ReadInt32() });
m_Streams[s].offset = reader.ReadInt32();
m_Streams[s].stride = reader.ReadByte();
m_Streams[s].dividerOp = reader.ReadByte();
m_Streams[s].frequency = reader.ReadUInt16();
}
}
}
#endregion
if (version[0] >= 5) //ComputeCompressedStreams
{
m_Streams = new StreamInfo[streamCount];
int offset = 0;
for (int s = 0; s < streamCount; s++)
{
int chnMask = 0;
int stride = 0;
for (int chn = 0; chn < m_Channels.Length; chn++)
{
var m_Channel = m_Channels[chn];
if (m_Channel.stream == s)
{
if (m_Channel.dimension > 0)
{
chnMask |= 1 << chn;
stride += m_Channel.dimension * GetChannelFormatSize(m_Channel.format);
}
}
}
m_Streams[s] = new StreamInfo
{
channelMask = new BitArray(new[] { chnMask }),
offset = offset,
stride = stride,
dividerOp = 0,
frequency = 0
};
offset += m_VertexCount * stride;
//static size_t AlignStreamSize (size_t size) { return (size + (kVertexStreamAlign-1)) & ~(kVertexStreamAlign-1); }
offset = (offset + (16 - 1)) & ~(16 - 1);
}
}
//actual Vertex Buffer
var m_DataSize = reader.ReadBytes(reader.ReadInt32());
reader.AlignStream(4);
#endregion
#region compute FvF
#region 2018 and up
if (version[0] >= 2018)
{
foreach (var m_Channel in m_Channels)
{
if (m_Channel.dimension > 0)
{
var m_Stream = m_Streams[m_Channel.stream];
for (int b = 0; b < 14; b++)
{
if (m_Stream.channelMask.Get(b))
{
var componentByteSize = GetChannelFormatSize(m_Channel.format);
var componentBytes = new byte[m_VertexCount * m_Channel.dimension * componentByteSize];
for (int v = 0; v < m_VertexCount; v++)
{
int vertexOffset = m_Stream.offset + m_Channel.offset + m_Stream.stride * v;
for (int d = 0; d < m_Channel.dimension; d++)
{
int componentOffset = vertexOffset + componentByteSize * d;
Buffer.BlockCopy(m_DataSize, componentOffset, componentBytes, componentByteSize * (v * m_Channel.dimension + d), componentByteSize);
}
}
if (platform == BuildTarget.XBOX360 && componentByteSize > 1) //swap bytes for Xbox
{
for (var i = 0; i < componentBytes.Length / componentByteSize; i++)
{
var buff = new byte[componentByteSize];
Buffer.BlockCopy(componentBytes, i * componentByteSize, buff, 0, componentByteSize);
buff = buff.Reverse().ToArray();
Buffer.BlockCopy(buff, 0, componentBytes, i * componentByteSize, componentByteSize);
}
}
int[] componentsIntArray = null;
float[] componentsFloatArray = null;
if (m_Channel.format == 11)
componentsIntArray = BytesToIntArray(componentBytes);
else
componentsFloatArray = BytesToFloatArray(componentBytes, componentByteSize);
switch (b)
{
case 0: //kShaderChannelVertex
m_Vertices = componentsFloatArray;
break;
case 1: //kShaderChannelNormal
m_Normals = componentsFloatArray;
break;
case 2: //kShaderChannelTangent
m_Tangents = componentsFloatArray;
break;
case 3: //kShaderChannelColor
m_Colors = componentsFloatArray;
break;
case 4: //kShaderChannelTexCoord0
m_UV1 = componentsFloatArray;
break;
case 5: //kShaderChannelTexCoord1
m_UV2 = componentsFloatArray;
break;
case 6: //kShaderChannelTexCoord2
m_UV3 = componentsFloatArray;
break;
case 7: //kShaderChannelTexCoord3
m_UV4 = componentsFloatArray;
break;
//kShaderChannelTexCoord4 8
//kShaderChannelTexCoord5 9
//kShaderChannelTexCoord6 10
//kShaderChannelTexCoord7 11
//2018.2 and up
case 12: //kShaderChannelBlendWeight
if (m_Skin == null)
{
InitMSkin();
}
for (int i = 0; i < m_VertexCount; i++)
{
for (int j = 0; j < 4; j++)
{
m_Skin[i][j].weight = componentsFloatArray[i * 4 + j];
}
}
break;
case 13: //kShaderChannelBlendIndices
if (m_Skin == null)
{
InitMSkin();
}
for (int i = 0; i < m_VertexCount; i++)
{
for (int j = 0; j < 4; j++)
{
m_Skin[i][j].boneIndex = componentsIntArray[i * 4 + j];
}
}
break;
}
m_Stream.channelMask.Set(b, false);
break; //go to next channel
}
}
}
}
}
#endregion
else
{
#region 4.0 - 2017.x
if (m_Channels != null)
{
//it is better to loop channels instead of streams
//because channels are likely to be sorted by vertex property
foreach (var m_Channel in m_Channels)
{
if (m_Channel.dimension > 0)
{
var m_Stream = m_Streams[m_Channel.stream];
for (int b = 0; b < 8; b++)
{
if (m_Stream.channelMask.Get(b))
{
//in version 4.x the colors is ColorRGBA32, size 4 with 4 byte components
if (b == 2 && m_Channel.format == 2)
{
m_Channel.dimension = 4; //set this so that don't need to convert int to 4 bytes
}
var componentByteSize = GetChannelFormatSize(m_Channel.format);
var componentBytes = new byte[m_VertexCount * m_Channel.dimension * componentByteSize];
for (int v = 0; v < m_VertexCount; v++)
{
int vertexOffset = m_Stream.offset + m_Channel.offset + m_Stream.stride * v;
for (int d = 0; d < m_Channel.dimension; d++)
{
int componentOffset = vertexOffset + componentByteSize * d;
Buffer.BlockCopy(m_DataSize, componentOffset, componentBytes, componentByteSize * (v * m_Channel.dimension + d), componentByteSize);
}
}
if (platform == BuildTarget.XBOX360 && componentByteSize > 1) //swap bytes for Xbox
{
for (var i = 0; i < componentBytes.Length / componentByteSize; i++)
{
var buff = new byte[componentByteSize];
Buffer.BlockCopy(componentBytes, i * componentByteSize, buff, 0, componentByteSize);
buff = buff.Reverse().ToArray();
Buffer.BlockCopy(buff, 0, componentBytes, i * componentByteSize, componentByteSize);
}
}
var componentsFloatArray = BytesToFloatArray(componentBytes, componentByteSize);
switch (b)
{
case 0: //kShaderChannelVertex
m_Vertices = componentsFloatArray;
break;
case 1: //kShaderChannelNormal
m_Normals = componentsFloatArray;
break;
case 2: //kShaderChannelColor
m_Colors = componentsFloatArray;
break;
case 3: //kShaderChannelTexCoord0
m_UV1 = componentsFloatArray;
break;
case 4: //kShaderChannelTexCoord1
m_UV2 = componentsFloatArray;
break;
case 5: //kShaderChannelTangent & kShaderChannelTexCoord2
if (version[0] >= 5)
{
m_UV3 = componentsFloatArray;
}
else
{
m_Tangents = componentsFloatArray;
}
break;
case 6: //kShaderChannelTexCoord3
m_UV4 = componentsFloatArray;
break;
case 7: //kShaderChannelTangent
m_Tangents = componentsFloatArray;
break;
}
m_Stream.channelMask.Set(b, false);
break; //go to next channel
}
}
}
}
}
#endregion
#region 3.5.0 - 3.5.7
else if (m_Streams != null)
{
foreach (var m_Stream in m_Streams)
{
//a stream may have multiple vertex components but without channels there are no offsets, so I assume all vertex properties are in order
//version 3.5.x only uses floats, and that's probably why channels were introduced in version 4
ChannelInfo m_Channel = new ChannelInfo();//create my own channel so I can use the same methods
m_Channel.offset = 0;
int componentByteSize = 0;
for (int b = 0; b < 6; b++)
{
if (m_Stream.channelMask.Get(b))
{
switch (b)
{
case 0:
case 1:
componentByteSize = 4;
m_Channel.dimension = 3;
break;
case 2:
componentByteSize = 1;
m_Channel.dimension = 4;
break;
case 3:
case 4:
componentByteSize = 4;
m_Channel.dimension = 2;
break;
case 5:
componentByteSize = 4;
m_Channel.dimension = 4;
break;
}
var componentBytes = new byte[componentByteSize];
var componentsArray = new float[m_VertexCount * m_Channel.dimension];
for (int v = 0; v < m_VertexCount; v++)
{
int vertexOffset = m_Stream.offset + m_Channel.offset + m_Stream.stride * v;
for (int d = 0; d < m_Channel.dimension; d++)
{
int m_DataSizeOffset = vertexOffset + componentByteSize * d;
Buffer.BlockCopy(m_DataSize, m_DataSizeOffset, componentBytes, 0, componentByteSize);
componentsArray[v * m_Channel.dimension + d] = BytesToFloat(componentBytes, reader.endian);
}
}
switch (b)
{
case 0: m_Vertices = componentsArray; break;
case 1: m_Normals = componentsArray; break;
case 2: m_Colors = componentsArray; break;
case 3: m_UV1 = componentsArray; break;
case 4: m_UV2 = componentsArray; break;
case 5: m_Tangents = componentsArray; break;
}
m_Channel.offset += (byte)(m_Channel.dimension * componentByteSize); //safe to cast as byte because strides larger than 255 are unlikely
m_Stream.channelMask.Set(b, false);
}
}
}
}
#endregion
}
#endregion
}
#endregion
#region Compressed Mesh data for 2.6.0 and later - 160 bytes
if (version[0] >= 3 || (version[0] == 2 && version[1] >= 6))
{
#region m_Vertices;
var m_Vertices_Packed = new PackedFloatVector(reader);
if (m_Vertices_Packed.m_NumItems > 0)
{
m_VertexCount = (int)m_Vertices_Packed.m_NumItems / 3;
m_Vertices = m_Vertices_Packed.UnpackFloats(3, 4);
}
#endregion
#region m_UV
var m_UV_Packed = new PackedFloatVector(reader);
if (m_UV_Packed.m_NumItems > 0)
{
m_UV1 = m_UV_Packed.UnpackFloats(2, 4, 0, m_VertexCount);
if (m_UV_Packed.m_NumItems >= m_VertexCount * 4)
{
m_UV2 = m_UV_Packed.UnpackFloats(2, 4, m_VertexCount * 2, m_VertexCount);
}
if (m_UV_Packed.m_NumItems >= m_VertexCount * 6)
{
m_UV3 = m_UV_Packed.UnpackFloats(2, 4, m_VertexCount * 4, m_VertexCount);
}
if (m_UV_Packed.m_NumItems >= m_VertexCount * 8)
{
m_UV4 = m_UV_Packed.UnpackFloats(2, 4, m_VertexCount * 6, m_VertexCount);
}
}
#endregion
#region m_BindPoses
if (version[0] < 5)
{
var m_BindPoses_Packed = new PackedFloatVector(reader);
if (m_BindPoses_Packed.m_NumItems > 0)
{
m_BindPose = new float[m_BindPoses_Packed.m_NumItems / 16][,];
var m_BindPoses_Unpacked = m_BindPoses_Packed.UnpackFloats(16, 4 * 16);
throw new NotImplementedException();
}
}
#endregion
var m_Normals_Packed = new PackedFloatVector(reader);
var m_Tangents_Packed = new PackedFloatVector(reader);
var m_Weights = new PackedIntVector(reader);
#region m_Normals
var m_NormalSigns = new PackedIntVector(reader);
if (m_Normals_Packed.m_NumItems > 0)
{
var normalData = m_Normals_Packed.UnpackFloats(2, 4 * 2);
var signs = m_NormalSigns.UnpackInts();
m_Normals = new float[m_Normals_Packed.m_NumItems / 2 * 3];
for (int i = 0; i < m_Normals_Packed.m_NumItems / 2; ++i)
{
var x = normalData[i * 2 + 0];
var y = normalData[i * 2 + 1];
var zsqr = 1 - x * x - y * y;
float z;
if (zsqr >= 0f)
z = (float)Math.Sqrt(zsqr);
else
{
z = 0;
var normal = new Vector3(x, y, z);
normal.Normalize();
x = normal.X;
y = normal.Y;
z = normal.Z;
}
if (signs[i] == 0)
z = -z;
m_Normals[i * 3] = x;
m_Normals[i * 3 + 1] = y;
m_Normals[i * 3 + 2] = z;
}
}
#endregion
#region m_Tangents
var m_TangentSigns = new PackedIntVector(reader);
if (m_Tangents_Packed.m_NumItems > 0)
{
var tangentData = m_Tangents_Packed.UnpackFloats(2, 4 * 2);
var signs = m_TangentSigns.UnpackInts();
m_Tangents = new float[m_Tangents_Packed.m_NumItems / 2 * 4];
for (int i = 0; i < m_Tangents_Packed.m_NumItems / 2; ++i)
{
var x = tangentData[i * 2 + 0];
var y = tangentData[i * 2 + 1];
var zsqr = 1 - x * x - y * y;
float z;
if (zsqr >= 0f)
z = (float)Math.Sqrt(zsqr);
else
{
z = 0;
var vector3f = new Vector3(x, y, z);
vector3f.Normalize();
x = vector3f.X;
y = vector3f.Y;
z = vector3f.Z;
}
if (signs[i * 2 + 0] == 0)
z = -z;
var w = signs[i * 2 + 1] > 0 ? 1.0f : -1.0f;
m_Tangents[i * 4] = x;
m_Tangents[i * 4 + 1] = y;
m_Tangents[i * 4 + 2] = z;
m_Tangents[i * 4 + 3] = w;
}
}
#endregion
#region m_FloatColors
if (version[0] >= 5)
{
var m_FloatColors = new PackedFloatVector(reader);
if (m_FloatColors.m_NumItems > 0)
{
m_Colors = m_FloatColors.UnpackFloats(1, 4);
}
}
#endregion
#region m_Skin
var m_BoneIndices = new PackedIntVector(reader);
if (m_Weights.m_NumItems > 0)
{
var weights = m_Weights.UnpackInts();
var boneIndices = m_BoneIndices.UnpackInts();
InitMSkin();
int bonePos = 0;
int boneIndexPos = 0;
int j = 0;
int sum = 0;
for (int i = 0; i < m_Weights.m_NumItems; i++)
{
//read bone index and weight.
m_Skin[bonePos][j].weight = weights[i] / 31.0f;
m_Skin[bonePos][j].boneIndex = boneIndices[boneIndexPos++];
j++;
sum += weights[i];
//the weights add up to one. fill the rest for this vertex with zero, and continue with next one.
if (sum >= 31)
{
for (; j < 4; j++)
{
m_Skin[bonePos][j].weight = 0;
m_Skin[bonePos][j].boneIndex = 0;
}
bonePos++;
j = 0;
sum = 0;
}
//we read three weights, but they don't add up to one. calculate the fourth one, and read
//missing bone index. continue with next vertex.
else if (j == 3)
{
m_Skin[bonePos][j].weight = (31 - sum) / 31.0f;
m_Skin[bonePos][j].boneIndex = boneIndices[boneIndexPos++];
bonePos++;
j = 0;
sum = 0;
}
}
}
#endregion
#region m_IndexBuffer
var m_Triangles = new PackedIntVector(reader);
if (m_Triangles.m_NumItems > 0)
{
m_IndexBuffer = Array.ConvertAll(m_Triangles.UnpackInts(), x => (uint)x);
}
#endregion
}
#endregion
#region Colors & Collision triangles for 3.4.2 and earlier
if (version[0] <= 2 || (version[0] == 3 && version[1] <= 4))
{
reader.Position += 24; //Axis-Aligned Bounding Box
int m_Colors_size = reader.ReadInt32();
m_Colors = new float[m_Colors_size * 4];
for (int v = 0; v < m_Colors_size * 4; v++) { m_Colors[v] = (float)(reader.ReadByte()) / 0xFF; }
int m_CollisionTriangles_size = reader.ReadInt32();
reader.Position += m_CollisionTriangles_size * 4; //UInt32 indices
int m_CollisionVertexCount = reader.ReadInt32();
}
#endregion
#region Compressed colors
else
{
if (version[0] < 5)
{
var m_Colors_Packed = new PackedIntVector(reader);
if (m_Colors_Packed.m_NumItems > 0)
{
m_Colors_Packed.m_NumItems *= 4;
m_Colors_Packed.m_BitSize /= 4;
var tempColors = m_Colors_Packed.UnpackInts();
m_Colors = new float[m_Colors_Packed.m_NumItems];
for (int v = 0; v < m_Colors_Packed.m_NumItems; v++)
{
m_Colors[v] = tempColors[v] / 255f;
}
}
}
else
{
var m_UVInfo = reader.ReadUInt32();
}
reader.Position += 24; //AABB m_LocalAABB
}
#endregion
int m_MeshUsageFlags = reader.ReadInt32();
if (version[0] >= 5)
{
//int m_BakedConvexCollisionMesh = a_Stream.ReadInt32();
//a_Stream.Position += m_BakedConvexCollisionMesh;
//int m_BakedTriangleCollisionMesh = a_Stream.ReadInt32();
//a_Stream.Position += m_BakedConvexCollisionMesh;
}
#region Build face indices
for (int s = 0; s < m_SubMeshes_size; s++)
{
uint firstIndex = m_SubMeshes[s].firstByte / 2;
if (!m_Use16BitIndices) { firstIndex /= 2; }
if (m_SubMeshes[s].topology == 0)
{
for (int i = 0; i < m_SubMeshes[s].indexCount / 3; i++)
{
m_Indices.Add(m_IndexBuffer[firstIndex + i * 3]);
m_Indices.Add(m_IndexBuffer[firstIndex + i * 3 + 1]);
m_Indices.Add(m_IndexBuffer[firstIndex + i * 3 + 2]);
m_materialIDs.Add(s);
}
}
else
{
uint j = 0;
for (int i = 0; i < m_SubMeshes[s].indexCount - 2; i++)
{
uint fa = m_IndexBuffer[firstIndex + i];
uint fb = m_IndexBuffer[firstIndex + i + 1];
uint fc = m_IndexBuffer[firstIndex + i + 2];
if ((fa != fb) && (fa != fc) && (fc != fb))
{
m_Indices.Add(fa);
if ((i % 2) == 0)
{
m_Indices.Add(fb);
m_Indices.Add(fc);
}
else
{
m_Indices.Add(fc);
m_Indices.Add(fb);
}
m_materialIDs.Add(s);
j++;
}
}
//TODO just fix it
m_SubMeshes[s].indexCount = j * 3;
}
}
#endregion
}
}
}