diff --git a/AssetStudio/Classes/AnimationClip.cs b/AssetStudio/Classes/AnimationClip.cs index 510e468..2739d0d 100644 --- a/AssetStudio/Classes/AnimationClip.cs +++ b/AssetStudio/Classes/AnimationClip.cs @@ -632,7 +632,10 @@ namespace AssetStudio { m_ConstantClip = new ConstantClip(reader); } - m_Binding = new ValueArrayConstant(reader); + if (version[0] < 2018 || (version[0] == 2018 && version[1] < 3)) //2018.3 down + { + m_Binding = new ValueArrayConstant(reader); + } } } @@ -947,6 +950,8 @@ namespace AssetStudio { m_ClipBindingConstant = new AnimationClipBindingConstant(reader); } + //m_HasGenericRootTransform 2018.3 + //m_HasMotionFloatCurves 2018.3 /*int numEvents = reader.ReadInt32(); m_Events = new List(numEvents); for (int i = 0; i < numEvents; i++) diff --git a/AssetStudio/Classes/Mesh.cs b/AssetStudio/Classes/Mesh.cs index 0a47b1d..0a690c2 100644 --- a/AssetStudio/Classes/Mesh.cs +++ b/AssetStudio/Classes/Mesh.cs @@ -1,22 +1,403 @@ using System; -using System.Collections.Generic; using System.Collections; +using System.Collections.Generic; using System.Linq; using SharpDX; namespace AssetStudio { + public class CompressedMesh + { + public PackedFloatVector m_Vertices; + public PackedFloatVector m_UV; + public PackedFloatVector m_BindPoses; + public PackedFloatVector m_Normals; + public PackedFloatVector m_Tangents; + public PackedIntVector m_Weights; + public PackedIntVector m_NormalSigns; + public PackedIntVector m_TangentSigns; + public PackedFloatVector m_FloatColors; + public PackedIntVector m_BoneIndices; + public PackedIntVector m_Triangles; + public PackedIntVector m_Colors; + public uint m_UVInfo; + + public CompressedMesh(ObjectReader reader) + { + var version = reader.version; + + m_Vertices = new PackedFloatVector(reader); + m_UV = new PackedFloatVector(reader); + if (version[0] < 5) + { + m_BindPoses = new PackedFloatVector(reader); + } + m_Normals = new PackedFloatVector(reader); + m_Tangents = new PackedFloatVector(reader); + m_Weights = new PackedIntVector(reader); + m_NormalSigns = new PackedIntVector(reader); + m_TangentSigns = new PackedIntVector(reader); + if (version[0] >= 5) + { + m_FloatColors = new PackedFloatVector(reader); + } + m_BoneIndices = new PackedIntVector(reader); + m_Triangles = new PackedIntVector(reader); + if (version[0] > 3 || (version[0] == 3 && version[1] >= 5)) //3.5 and up + { + if (version[0] < 5) + { + m_Colors = new PackedIntVector(reader); + } + else + { + m_UVInfo = reader.ReadUInt32(); + } + } + } + } + + public class StreamInfo + { + public uint channelMask; + public uint offset; + public uint stride; + public uint align; + public byte dividerOp; + public ushort frequency; + + public StreamInfo() { } + + public StreamInfo(ObjectReader reader) + { + var version = reader.version; + + channelMask = reader.ReadUInt32(); + offset = reader.ReadUInt32(); + + if (version[0] < 4) //4.0 down + { + stride = reader.ReadUInt32(); + align = reader.ReadUInt32(); + } + else + { + stride = reader.ReadByte(); + dividerOp = reader.ReadByte(); + frequency = reader.ReadUInt16(); + } + } + } + + public class ChannelInfo + { + public byte stream; + public byte offset; + public byte format; + public byte dimension; + + public ChannelInfo() { } + + public ChannelInfo(ObjectReader reader) + { + stream = reader.ReadByte(); + offset = reader.ReadByte(); + format = reader.ReadByte(); + dimension = reader.ReadByte(); + } + } + + public class VertexData + { + public uint m_CurrentChannels; + public uint m_VertexCount; + public ChannelInfo[] m_Channels; + public StreamInfo[] m_Streams; + public byte[] m_DataSize; + + public VertexData(ObjectReader reader) + { + var version = reader.version; + + if (version[0] < 2018)//2018 down + { + m_CurrentChannels = reader.ReadUInt32(); + } + + m_VertexCount = reader.ReadUInt32(); + + if (version[0] >= 4) //4.0 and up + { + var m_ChannelsSize = reader.ReadInt32(); + m_Channels = new ChannelInfo[m_ChannelsSize]; + for (int i = 0; i < m_ChannelsSize; i++) + { + m_Channels[i] = new ChannelInfo(reader); + } + } + + if (version[0] < 5) //5.0 down + { + if (version[0] < 4) + { + m_Streams = new StreamInfo[4]; + } + else + { + m_Streams = new StreamInfo[reader.ReadInt32()]; + } + + for (int i = 0; i < m_Streams.Length; i++) + { + m_Streams[i] = new StreamInfo(reader); + } + + if (version[0] < 4) //4.0 down + { + GetChannels(); + } + } + else //5.0 and up + { + GetStreams(); + } + + m_DataSize = reader.ReadBytes(reader.ReadInt32()); + reader.AlignStream(); + } + + public void GetStreams() + { + var streamCount = m_Channels.Max(x => x.stream) + 1; + m_Streams = new StreamInfo[streamCount]; + uint offset = 0; + for (int s = 0; s < streamCount; s++) + { + uint chnMask = 0; + uint 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 |= 1u << chn; + stride += m_Channel.dimension * MeshHelper.GetChannelFormatSize(m_Channel.format); + } + } + } + m_Streams[s] = new StreamInfo + { + channelMask = 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 + (16u - 1u)) & ~(16u - 1u); + } + } + + private void GetChannels() + { + m_Channels = new ChannelInfo[6]; + for (int i = 0; i < 6; i++) + { + m_Channels[i] = new ChannelInfo(); + } + for (var s = 0; s < m_Streams.Length; s++) + { + var m_Stream = m_Streams[s]; + var channelMask = new BitArray(new[] { (int)m_Stream.channelMask }); + byte offset = 0; + for (int i = 0; i < 6; i++) + { + if (channelMask.Get(i)) + { + var m_Channel = m_Channels[i]; + m_Channel.stream = (byte)s; + m_Channel.offset = offset; + switch (i) + { + case 0: //kShaderChannelVertex + case 1: //kShaderChannelNormal + m_Channel.format = 0; //kChannelFormatFloat + m_Channel.dimension = 3; + break; + case 2: //kShaderChannelColor + m_Channel.format = 2; //kChannelFormatColor + m_Channel.dimension = 4; + break; + case 3: //kShaderChannelTexCoord0 + case 4: //kShaderChannelTexCoord1 + m_Channel.format = 0; //kChannelFormatFloat + m_Channel.dimension = 2; + break; + case 5: //kShaderChannelTangent + m_Channel.format = 0; //kChannelFormatFloat + m_Channel.dimension = 4; + break; + } + offset += (byte)(m_Channel.dimension * MeshHelper.GetChannelFormatSize(m_Channel.format)); + } + } + } + } + } + + public class BoneWeights4 + { + public float[] weight; + public int[] boneIndex; + + public BoneWeights4() + { + weight = new float[4]; + boneIndex = new int[4]; + } + + public BoneWeights4(ObjectReader reader) + { + weight = reader.ReadSingleArray(4); + boneIndex = reader.ReadInt32Array(4); + } + } + + public class BlendShapeVertex + { + public Vector3 vertex; + public Vector3 normal; + public Vector3 tangent; + public uint index; + + public BlendShapeVertex(ObjectReader reader) + { + vertex = reader.ReadVector3(); + normal = reader.ReadVector3(); + tangent = reader.ReadVector3(); + index = reader.ReadUInt32(); + } + } + + public class MeshBlendShape + { + public uint firstVertex; + public uint vertexCount; + public bool hasNormals; + public bool hasTangents; + + public MeshBlendShape(ObjectReader reader) + { + firstVertex = reader.ReadUInt32(); + vertexCount = reader.ReadUInt32(); + hasNormals = reader.ReadBoolean(); + hasTangents = reader.ReadBoolean(); + reader.AlignStream(); + } + } + + public class MeshBlendShapeChannel + { + public string name; + public uint nameHash; + public int frameIndex; + public int frameCount; + + public MeshBlendShapeChannel(ObjectReader reader) + { + name = reader.ReadAlignedString(); + nameHash = reader.ReadUInt32(); + frameIndex = reader.ReadInt32(); + frameCount = reader.ReadInt32(); + } + } + + public class BlendShapeData + { + public List vertices; + public List shapes; + public List channels; + public float[] fullWeights; + + public BlendShapeData(ObjectReader reader) + { + int numVerts = reader.ReadInt32(); + vertices = new List(numVerts); + for (int i = 0; i < numVerts; i++) + { + vertices.Add(new BlendShapeVertex(reader)); + } + + int numShapes = reader.ReadInt32(); + shapes = new List(numShapes); + for (int i = 0; i < numShapes; i++) + { + shapes.Add(new MeshBlendShape(reader)); + } + + int numChannels = reader.ReadInt32(); + channels = new List(numChannels); + for (int i = 0; i < numChannels; i++) + { + channels.Add(new MeshBlendShapeChannel(reader)); + } + + fullWeights = reader.ReadSingleArray(); + } + } + + public class SubMesh + { + public uint firstByte; + public uint indexCount; + public int topology; + public uint triangleCount; + public uint baseVertex; + public uint firstVertex; + public uint vertexCount; + public AABB localAABB; + + public SubMesh(ObjectReader reader) + { + var version = reader.version; + + firstByte = reader.ReadUInt32(); + indexCount = reader.ReadUInt32(); + topology = reader.ReadInt32(); + + if (version[0] < 4) //4.0 down + { + triangleCount = reader.ReadUInt32(); + } + + if (version[0] > 2017 || (version[0] == 2017 && version[1] >= 3)) //2017.3 and up + { + baseVertex = reader.ReadUInt32(); + } + + if (version[0] >= 3) //3.0 and up + { + firstVertex = reader.ReadUInt32(); + vertexCount = reader.ReadUInt32(); + localAABB = new AABB(reader); + } + } + } + public sealed class Mesh : NamedObject { - public List m_SubMeshes = new List(); - public List m_Indices = new List(); //use a list because I don't always know the facecount for triangle strips + private bool m_Use16BitIndices = true; //3.5.0 and newer always uses 16bit indices; + public SubMesh[] m_SubMeshes; private uint[] m_IndexBuffer; - private ChannelInfo[] m_Channels; - private StreamInfo[] m_Streams; - public List[] m_Skin; + public BlendShapeData m_Shapes; public float[][,] m_BindPose; + public uint[] m_BoneNameHashes; public int m_VertexCount; public float[] m_Vertices; + public BoneWeights4[] m_Skin; public float[] m_Normals; public float[] m_Colors; public float[] m_UV0; @@ -24,277 +405,50 @@ namespace AssetStudio public float[] m_UV2; public float[] m_UV3; public float[] m_Tangents; - public uint[] m_BoneNameHashes; - public BlendShapeData m_Shapes; + private VertexData m_VertexData; + private CompressedMesh m_CompressedMesh; + private StreamingInfo m_StreamData; - 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; - public Vector3 normal; - public Vector3 tangent; - public uint index; - - public BlendShapeVertex(ObjectReader reader) - { - vertex = reader.ReadVector3(); - normal = reader.ReadVector3(); - tangent = reader.ReadVector3(); - index = reader.ReadUInt32(); - } - } - - public class MeshBlendShape - { - public uint firstVertex; - public uint vertexCount; - public bool hasNormals; - public bool hasTangents; - - public MeshBlendShape(ObjectReader reader) - { - firstVertex = reader.ReadUInt32(); - vertexCount = reader.ReadUInt32(); - hasNormals = reader.ReadBoolean(); - hasTangents = reader.ReadBoolean(); - reader.AlignStream(); - } - } - - public class MeshBlendShapeChannel - { - public string name; - public uint nameHash; - public int frameIndex; - public int frameCount; - - public MeshBlendShapeChannel(ObjectReader reader) - { - name = reader.ReadAlignedString(); - nameHash = reader.ReadUInt32(); - frameIndex = reader.ReadInt32(); - frameCount = reader.ReadInt32(); - } - } - - public List vertices; - public List shapes; - public List channels; - public List fullWeights; - - public BlendShapeData(ObjectReader reader) - { - int numVerts = reader.ReadInt32(); - vertices = new List(numVerts); - for (int i = 0; i < numVerts; i++) - { - vertices.Add(new BlendShapeVertex(reader)); - } - - int numShapes = reader.ReadInt32(); - shapes = new List(numShapes); - for (int i = 0; i < numShapes; i++) - { - shapes.Add(new MeshBlendShape(reader)); - } - - int numChannels = reader.ReadInt32(); - channels = new List(numChannels); - for (int i = 0; i < numChannels; i++) - { - channels.Add(new MeshBlendShapeChannel(reader)); - } - - int numWeights = reader.ReadInt32(); - fullWeights = new List(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[m_VertexCount]; - for (int i = 0; i < m_VertexCount; i++) - { - m_Skin[i] = new List(4); - for (int j = 0; j < 4; j++) - { - m_Skin[i].Add(new BoneInfluence()); - } - } - } + public List m_Indices = new List(); //use a list because I don't always know the facecount for triangle strips 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)) + if (version[0] < 3 || (version[0] == 3 && version[1] < 5)) //3.5 down { - m_Use16BitIndices = reader.ReadBoolean(); - reader.Position += 3; + m_Use16BitIndices = reader.ReadInt32() > 0; } - #region Index Buffer for 2.5.1 and earlier - if (version[0] == 2 && version[1] <= 5) + if (version[0] == 2 && version[1] <= 5) //2.5 and down { 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(); } + for (int i = 0; i < m_IndexBuffer_size / 2; i++) + { + m_IndexBuffer[i] = reader.ReadUInt16(); + } reader.AlignStream(); } 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 + for (int i = 0; i < m_IndexBuffer_size / 4; i++) { - var baseVertex = reader.ReadUInt32(); + m_IndexBuffer[i] = 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.IsAlpha) || (version[1] > 1 && version[1] <= 2))) + int m_SubMeshesSize = reader.ReadInt32(); + m_SubMeshes = new SubMesh[m_SubMeshesSize]; + for (int i = 0; i < m_SubMeshesSize; i++) + { + m_SubMeshes[i] = new SubMesh(reader); + } + + if (version[0] == 4 && ((version[1] == 1 && !buildType.IsAlpha) || (version[1] > 1 && version[1] <= 2))) //4.1.0 to 4.2.x, excluding 4.1.0 alpha { int m_Shapes_size = reader.ReadInt32(); if (m_Shapes_size > 0) @@ -303,16 +457,14 @@ namespace AssetStudio } for (int s = 0; s < m_Shapes_size; s++) //untested { - string shape_name = reader.ReadAlignedString(); + var 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)) + else if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3.0 and later { m_Shapes = new BlendShapeData(reader); @@ -326,73 +478,68 @@ namespace AssetStudio { 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(); - } + m_BoneNameHashes = reader.ReadUInt32Array(); - uint m_RootBoneNameHash = reader.ReadUInt32(); + var m_RootBoneNameHash = reader.ReadUInt32(); } - #endregion - #region Index Buffer for 2.6.0 and later - if (version[0] >= 3 || (version[0] == 2 && version[1] >= 6)) + if (version[0] > 2 || (version[0] == 2 && version[1] >= 6)) //2.6.0 and later { - m_MeshCompression = reader.ReadByte(); + var 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 (HasStructMember("m_UsedForStaticMeshColliderOnly")) - { - var m_UsedForStaticMeshColliderOnly = reader.ReadBoolean(); + var m_StreamCompression = reader.ReadByte(); } + var m_IsReadable = reader.ReadBoolean(); + var m_KeepVertices = reader.ReadBoolean(); + var m_KeepIndices = reader.ReadBoolean(); } reader.AlignStream(); - //This is a bug fixed in 2017.3.1p1 and later versions + + //Unity fixed it 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.IsPatch) || //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(); + 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(); } + for (int i = 0; i < m_IndexBuffer_size / 2; i++) + { + m_IndexBuffer[i] = reader.ReadUInt16(); + } reader.AlignStream(); } 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();//untested + for (int i = 0; i < m_IndexBuffer_size / 4; i++) + { + m_IndexBuffer[i] = reader.ReadUInt32(); + } + reader.AlignStream(); } } - #endregion - #region Vertex Buffer for 3.4.2 and earlier - if (version[0] < 3 || (version[0] == 3 && version[1] < 5)) + if (version[0] < 3 || (version[0] == 3 && version[1] < 5)) //3.4.2 and earlier { 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(); } + for (int v = 0; v < m_VertexCount * 3; v++) + { + m_Vertices[v] = reader.ReadSingle(); + } - m_Skin = new List[reader.ReadInt32()]; + m_Skin = new BoneWeights4[reader.ReadInt32()]; for (int s = 0; s < m_Skin.Length; s++) { - m_Skin[s] = new List(); - 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_Skin[s] = new BoneWeights4(reader); } m_BindPose = new float[reader.ReadInt32()][,]; @@ -405,15 +552,11 @@ namespace AssetStudio { reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle() } }; } - int m_UV1_size = reader.ReadInt32(); - m_UV0 = new float[m_UV1_size * 2]; - for (int v = 0; v < m_UV1_size * 2; v++) { m_UV0[v] = reader.ReadSingle(); } + m_UV0 = reader.ReadSingleArray(reader.ReadInt32() * 2); //Vector2 - int m_UV2_size = reader.ReadInt32(); - m_UV1 = new float[m_UV2_size * 2]; - for (int v = 0; v < m_UV2_size * 2; v++) { m_UV1[v] = reader.ReadSingle(); } + m_UV1 = reader.ReadSingleArray(reader.ReadInt32() * 2); //Vector2 - if (version[0] == 2 && version[1] <= 5) + if (version[0] == 2 && version[1] <= 5) //2.5 and down { int m_TangentSpace_size = reader.ReadInt32(); m_Normals = new float[m_TangentSpace_size * 3]; @@ -427,39 +570,23 @@ namespace AssetStudio } else //2.6.0 and later { - int m_Tangents_size = reader.ReadInt32(); - reader.Position += m_Tangents_size * 16; //Vector4f + m_Tangents = reader.ReadSingleArray(reader.ReadInt32() * 4); //Vector4 - 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(); } + m_Normals = reader.ReadSingleArray(reader.ReadInt32() * 3); //Vector3 } } - #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[reader.ReadInt32()]; + m_Skin = new BoneWeights4[reader.ReadInt32()]; for (int s = 0; s < m_Skin.Length; s++) { - m_Skin[s] = new List(); - 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_Skin[s] = new BoneWeights4(reader); } } - if (version[0] == 3 || (version[0] == 4 && version[1] <= 2)) + if (version[0] == 3 || (version[0] == 4 && version[1] <= 2)) //4.2 and down { m_BindPose = new float[reader.ReadInt32()][,]; for (int i = 0; i < m_BindPose.Length; i++) @@ -472,628 +599,417 @@ namespace AssetStudio } } - 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(); - #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_UV0 = componentsFloatArray; - break; - case 5: //kShaderChannelTexCoord1 - m_UV1 = componentsFloatArray; - break; - case 6: //kShaderChannelTexCoord2 - m_UV2 = componentsFloatArray; - break; - case 7: //kShaderChannelTexCoord3 - m_UV3 = 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_UV0 = componentsFloatArray; - break; - case 4: //kShaderChannelTexCoord1 - m_UV1 = componentsFloatArray; - break; - case 5: //kShaderChannelTangent & kShaderChannelTexCoord2 - if (version[0] >= 5) - { - m_UV2 = componentsFloatArray; - } - else - { - m_Tangents = componentsFloatArray; - } - break; - case 6: //kShaderChannelTexCoord3 - m_UV3 = 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_UV0 = componentsArray; break; - case 4: m_UV1 = 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 + m_VertexData = new VertexData(reader); } - #endregion - #region Compressed Mesh data for 2.6.0 and later - 160 bytes - if (version[0] >= 3 || (version[0] == 2 && version[1] >= 6)) + if (version[0] > 2 || (version[0] == 2 && version[1] >= 6)) //2.6.0 and later { - #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_UV0 = m_UV_Packed.UnpackFloats(2, 4, 0, m_VertexCount); - if (m_UV_Packed.m_NumItems >= m_VertexCount * 4) - { - m_UV1 = m_UV_Packed.UnpackFloats(2, 4, m_VertexCount * 2, m_VertexCount); - } - if (m_UV_Packed.m_NumItems >= m_VertexCount * 6) - { - m_UV2 = m_UV_Packed.UnpackFloats(2, 4, m_VertexCount * 4, m_VertexCount); - } - if (m_UV_Packed.m_NumItems >= m_VertexCount * 8) - { - m_UV3 = 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 + m_CompressedMesh = new CompressedMesh(reader); } - #endregion - #region Colors & Collision triangles for 3.4.2 and earlier - if (version[0] <= 2 || (version[0] == 3 && version[1] <= 4)) + reader.Position += 24; //AABB m_LocalAABB + + if (version[0] < 3 || (version[0] == 3 && version[1] <= 4)) //3.4.2 and earlier { - 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; } + 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) + if (version[0] >= 5) //5.0 and up { - //int m_BakedConvexCollisionMesh = a_Stream.ReadInt32(); - //a_Stream.Position += m_BakedConvexCollisionMesh; - //int m_BakedTriangleCollisionMesh = a_Stream.ReadInt32(); - //a_Stream.Position += m_BakedConvexCollisionMesh; + var m_BakedConvexCollisionMesh = reader.ReadBytes(reader.ReadInt32()); + reader.AlignStream(); + var m_BakedTriangleCollisionMesh = reader.ReadBytes(reader.ReadInt32()); + reader.AlignStream(); } - #region Build face indices - for (int s = 0; s < m_SubMeshes_size; s++) + if (version[0] > 2018 || (version[0] == 2018 && version[1] >= 2)) //2018.2 and up { - uint firstIndex = m_SubMeshes[s].firstByte / 2; - if (!m_Use16BitIndices) { firstIndex /= 2; } + var m_MeshMetrics = new float[2]; + m_MeshMetrics[0] = reader.ReadSingle(); + m_MeshMetrics[1] = reader.ReadSingle(); + } - if (m_SubMeshes[s].topology == 0) + if (version[0] > 2018 || (version[0] == 2018 && version[1] >= 3)) //2018.3 and up + { + reader.AlignStream(); + m_StreamData = new StreamingInfo(reader); + } + + ProcessData(); + } + + private void ProcessData() + { + if (!string.IsNullOrEmpty(m_StreamData?.path)) + { + if (m_VertexData.m_VertexCount > 0) { - for (int i = 0; i < m_SubMeshes[s].indexCount / 3; i++) + //Fix Normal Channel + m_VertexData.m_Channels[1].dimension = 4; + m_VertexData.GetStreams(); + + var resourceReader = new ResourceReader(m_StreamData.path, assetsFile, m_StreamData.offset, (int)m_StreamData.size); + m_VertexData.m_DataSize = resourceReader.GetData(); + } + } + + if (version[0] > 3 || (version[0] == 3 && version[1] >= 5)) //3.5 and up + { + ReadVertexData(); + } + + if (version[0] > 2 || (version[0] == 2 && version[1] >= 6)) //2.6.0 and later + { + DecompressCompressedMesh(); + } + + BuildFaces(); + } + + private void ReadVertexData() + { + m_VertexCount = (int)m_VertexData.m_VertexCount; + + if (version[0] > 2018 || (version[0] == 2018 && version[1] >= 2)) //2018.2 and up + { + InitMSkin(); + } + + for (var chn = 0; chn < m_VertexData.m_Channels.Length; chn++) + { + var m_Channel = m_VertexData.m_Channels[chn]; + if (m_Channel.dimension > 0) + { + var m_Stream = m_VertexData.m_Streams[m_Channel.stream]; + var channelMask = new BitArray(new[] { (int)m_Stream.channelMask }); + if (channelMask.Get(chn)) + { + if (version[0] < 2018 && chn == 2 && m_Channel.format == 2) + { + m_Channel.dimension = 4; + } + + var componentByteSize = (int)MeshHelper.GetChannelFormatSize(m_Channel.format); + var componentBytes = new byte[m_VertexCount * m_Channel.dimension * componentByteSize]; + for (int v = 0; v < m_VertexCount; v++) + { + var vertexOffset = (int)m_Stream.offset + m_Channel.offset + (int)m_Stream.stride * v; + for (int d = 0; d < m_Channel.dimension; d++) + { + var componentOffset = vertexOffset + componentByteSize * d; + Buffer.BlockCopy(m_VertexData.m_DataSize, componentOffset, componentBytes, componentByteSize * (v * m_Channel.dimension + d), componentByteSize); + } + } + + if (reader.endian == EndianType.BigEndian) //swap bytes + { + 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 = MeshHelper.BytesToIntArray(componentBytes); + else + componentsFloatArray = MeshHelper.BytesToFloatArray(componentBytes, componentByteSize); + + if (version[0] >= 2018) + { + switch (chn) + { + 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_UV0 = componentsFloatArray; + break; + case 5: //kShaderChannelTexCoord1 + m_UV1 = componentsFloatArray; + break; + case 6: //kShaderChannelTexCoord2 + m_UV2 = componentsFloatArray; + break; + case 7: //kShaderChannelTexCoord3 + m_UV3 = componentsFloatArray; + break; + //kShaderChannelTexCoord4 8 + //kShaderChannelTexCoord5 9 + //kShaderChannelTexCoord6 10 + //kShaderChannelTexCoord7 11 + //2018.2 and up + case 12: //kShaderChannelBlendWeight + for (int i = 0; i < m_VertexCount; i++) + { + for (int j = 0; j < 4; j++) + { + m_Skin[i].weight[j] = componentsFloatArray[i * 4 + j]; + } + } + break; + case 13: //kShaderChannelBlendIndices + for (int i = 0; i < m_VertexCount; i++) + { + for (int j = 0; j < 4; j++) + { + m_Skin[i].boneIndex[j] = componentsIntArray[i * 4 + j]; + } + } + break; + } + } + else + { + switch (chn) + { + 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_UV0 = componentsFloatArray; + break; + case 4: //kShaderChannelTexCoord1 + m_UV1 = componentsFloatArray; + break; + case 5: + if (version[0] >= 5) //kShaderChannelTexCoord2 + { + m_UV2 = componentsFloatArray; + } + else //kShaderChannelTangent + { + m_Tangents = componentsFloatArray; + } + break; + case 6: //kShaderChannelTexCoord3 + m_UV3 = componentsFloatArray; + break; + case 7: //kShaderChannelTangent + m_Tangents = componentsFloatArray; + break; + } + } + } + } + } + } + + private void DecompressCompressedMesh() + { + //Vertex + if (m_CompressedMesh.m_Vertices.m_NumItems > 0) + { + m_VertexCount = (int)m_CompressedMesh.m_Vertices.m_NumItems / 3; + m_Vertices = m_CompressedMesh.m_Vertices.UnpackFloats(3, 4); + } + //UV + if (m_CompressedMesh.m_UV.m_NumItems > 0) + { + m_UV0 = m_CompressedMesh.m_UV.UnpackFloats(2, 4, 0, m_VertexCount); + if (m_CompressedMesh.m_UV.m_NumItems >= m_VertexCount * 4) + { + m_UV1 = m_CompressedMesh.m_UV.UnpackFloats(2, 4, m_VertexCount * 2, m_VertexCount); + } + if (m_CompressedMesh.m_UV.m_NumItems >= m_VertexCount * 6) + { + m_UV2 = m_CompressedMesh.m_UV.UnpackFloats(2, 4, m_VertexCount * 4, m_VertexCount); + } + if (m_CompressedMesh.m_UV.m_NumItems >= m_VertexCount * 8) + { + m_UV3 = m_CompressedMesh.m_UV.UnpackFloats(2, 4, m_VertexCount * 6, m_VertexCount); + } + } + //BindPose + if (version[0] < 5) + { + if (m_CompressedMesh.m_BindPoses.m_NumItems > 0) + { + m_BindPose = new float[m_CompressedMesh.m_BindPoses.m_NumItems / 16][,]; + var m_BindPoses_Unpacked = m_CompressedMesh.m_BindPoses.UnpackFloats(16, 4 * 16); + for (int i = 0; i < m_BindPose.Length; i++) + { + Array.Copy(m_BindPoses_Unpacked, i * 16, m_BindPose[i], 0, 16); + } + } + } + //Normal + if (m_CompressedMesh.m_Normals.m_NumItems > 0) + { + var normalData = m_CompressedMesh.m_Normals.UnpackFloats(2, 4 * 2); + var signs = m_CompressedMesh.m_NormalSigns.UnpackInts(); + m_Normals = new float[m_CompressedMesh.m_Normals.m_NumItems / 2 * 3]; + for (int i = 0; i < m_CompressedMesh.m_Normals.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; + } + } + //Tangent + if (m_CompressedMesh.m_Tangents.m_NumItems > 0) + { + var tangentData = m_CompressedMesh.m_Tangents.UnpackFloats(2, 4 * 2); + var signs = m_CompressedMesh.m_TangentSigns.UnpackInts(); + m_Tangents = new float[m_CompressedMesh.m_Tangents.m_NumItems / 2 * 4]; + for (int i = 0; i < m_CompressedMesh.m_Tangents.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; + } + } + //FloatColor + if (version[0] >= 5) + { + if (m_CompressedMesh.m_FloatColors.m_NumItems > 0) + { + m_Colors = m_CompressedMesh.m_FloatColors.UnpackFloats(1, 4); + } + } + //Skin + if (m_CompressedMesh.m_Weights.m_NumItems > 0) + { + var weights = m_CompressedMesh.m_Weights.UnpackInts(); + var boneIndices = m_CompressedMesh.m_BoneIndices.UnpackInts(); + + InitMSkin(); + + int bonePos = 0; + int boneIndexPos = 0; + int j = 0; + int sum = 0; + + for (int i = 0; i < m_CompressedMesh.m_Weights.m_NumItems; i++) + { + //read bone index and weight. + m_Skin[bonePos].weight[j] = weights[i] / 31.0f; + m_Skin[bonePos].boneIndex[j] = 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].weight[j] = 0; + m_Skin[bonePos].boneIndex[j] = 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].weight[j] = (31 - sum) / 31.0f; + m_Skin[bonePos].boneIndex[j] = boneIndices[boneIndexPos++]; + bonePos++; + j = 0; + sum = 0; + } + } + } + //IndexBuffer + if (m_CompressedMesh.m_Triangles.m_NumItems > 0) + { + m_IndexBuffer = Array.ConvertAll(m_CompressedMesh.m_Triangles.UnpackInts(), x => (uint)x); + } + //Color + if (m_CompressedMesh.m_Colors?.m_NumItems > 0) + { + m_CompressedMesh.m_Colors.m_NumItems *= 4; + m_CompressedMesh.m_Colors.m_BitSize /= 4; + var tempColors = m_CompressedMesh.m_Colors.UnpackInts(); + m_Colors = new float[m_CompressedMesh.m_Colors.m_NumItems]; + for (int v = 0; v < m_CompressedMesh.m_Colors.m_NumItems; v++) + { + m_Colors[v] = tempColors[v] / 255f; + } + } + } + + private void BuildFaces() + { + foreach (var m_SubMesh in m_SubMeshes) + { + var firstIndex = m_SubMesh.firstByte / 2; + if (!m_Use16BitIndices) + { + firstIndex /= 2; + } + + if (m_SubMesh.topology == 0) + { + for (int i = 0; i < m_SubMesh.indexCount / 3; i++) { m_Indices.Add(m_IndexBuffer[firstIndex + i * 3]); m_Indices.Add(m_IndexBuffer[firstIndex + i * 3 + 1]); @@ -1103,7 +1019,7 @@ namespace AssetStudio else { uint j = 0; - for (int i = 0; i < m_SubMeshes[s].indexCount - 2; i++) + for (int i = 0; i < m_SubMesh.indexCount - 2; i++) { uint fa = m_IndexBuffer[firstIndex + i]; uint fb = m_IndexBuffer[firstIndex + i + 1]; @@ -1125,11 +1041,74 @@ namespace AssetStudio j++; } } - //TODO just fix it - m_SubMeshes[s].indexCount = j * 3; + //fix indexCount + m_SubMesh.indexCount = j * 3; } } - #endregion + } + + private void InitMSkin() + { + m_Skin = new BoneWeights4[m_VertexCount]; + for (int i = 0; i < m_VertexCount; i++) + { + m_Skin[i] = new BoneWeights4(); + } + } + } + + public static class MeshHelper + { + public static uint GetChannelFormatSize(int format) + { + switch (format) + { + case 0: //kChannelFormatFloat + return 4u; + case 1: //kChannelFormatFloat16 + return 2u; + case 2: //kChannelFormatColor + return 1u; + case 3: //kChannelFormatByte + return 1u; + case 11: //kChannelFormatInt32 + return 4u; + default: + return 0; + } + } + + public static float[] BytesToFloatArray(byte[] inputBytes, int size) + { + var result = new float[inputBytes.Length / size]; + for (int i = 0; i < inputBytes.Length / size; i++) + { + var 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; + } + + public 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; } } } diff --git a/AssetStudio/Classes/Renderer.cs b/AssetStudio/Classes/Renderer.cs index a7a070f..2feda34 100644 --- a/AssetStudio/Classes/Renderer.cs +++ b/AssetStudio/Classes/Renderer.cs @@ -62,6 +62,11 @@ namespace AssetStudio var m_RenderingLayerMask = reader.ReadUInt32(); } + if (version[0] > 2018 || (version[0] == 2018 && version[1] >= 3)) //2018.3 and up + { + var m_RendererPriority = reader.ReadInt32(); + } + var m_LightmapIndex = reader.ReadUInt16(); var m_LightmapIndexDynamic = reader.ReadUInt16(); } diff --git a/AssetStudio/Classes/Sprite.cs b/AssetStudio/Classes/Sprite.cs index 37767de..ea9e273 100644 --- a/AssetStudio/Classes/Sprite.cs +++ b/AssetStudio/Classes/Sprite.cs @@ -58,154 +58,6 @@ namespace AssetStudio } } - public class BoneWeights4 - { - public float[] weight; - public int[] boneIndex; - - public BoneWeights4(ObjectReader reader) - { - weight = reader.ReadSingleArray(4); - boneIndex = reader.ReadInt32Array(4); - } - } - - public class StreamInfo - { - public uint channelMask; - public uint offset; - public uint stride; - public uint align; - public byte dividerOp; - public ushort frequency; - - public StreamInfo() { } - - public StreamInfo(ObjectReader reader) - { - var version = reader.version; - - channelMask = reader.ReadUInt32(); - offset = reader.ReadUInt32(); - - if (version[0] < 4) - { - stride = reader.ReadUInt32(); - align = reader.ReadUInt32(); - } - else - { - stride = reader.ReadByte(); - dividerOp = reader.ReadByte(); - frequency = reader.ReadUInt16(); - } - } - } - - public class ChannelInfo - { - public byte stream; - public byte offset; - public byte format; - public byte dimension; - - public ChannelInfo(ObjectReader reader) - { - stream = reader.ReadByte(); - offset = reader.ReadByte(); - format = reader.ReadByte(); - dimension = reader.ReadByte(); - } - } - - public class VertexData - { - public uint m_CurrentChannels; - public uint m_VertexCount; - public ChannelInfo[] m_Channels; - public StreamInfo[] m_Streams; - public byte[] m_DataSize; - - public VertexData(ObjectReader reader) - { - var version = reader.version; - - if (version[0] < 2018)//2018 down - { - m_CurrentChannels = reader.ReadUInt32(); - } - m_VertexCount = reader.ReadUInt32(); - - if (version[0] >= 4) - { - var m_ChannelsSize = reader.ReadInt32(); - m_Channels = new ChannelInfo[m_ChannelsSize]; - for (int i = 0; i < m_ChannelsSize; i++) - { - m_Channels[i] = new ChannelInfo(reader); - } - } - - if (version[0] < 5) //5.0 down - { - if (version[0] < 4) - { - m_Streams = new StreamInfo[4]; - } - else - { - m_Streams = new StreamInfo[reader.ReadInt32()]; - } - - for (int i = 0; i < m_Streams.Length; i++) - { - m_Streams[i] = new StreamInfo(reader); - } - } - - m_DataSize = reader.ReadBytes(reader.ReadInt32()); - reader.AlignStream(); - } - } - - public class SubMesh - { - public uint firstByte; - public uint indexCount; - public int topology; - public uint triangleCount; - public uint baseVertex; - public uint firstVertex; - public uint vertexCount; - public AABB localAABB; - - public SubMesh(ObjectReader reader) - { - var version = reader.version; - - firstByte = reader.ReadUInt32(); - indexCount = reader.ReadUInt32(); - topology = reader.ReadInt32(); - - if (version[0] < 4) //4.0 down - { - triangleCount = reader.ReadUInt32(); - } - - if (version[0] > 2017 || (version[0] == 2017 && version[1] >= 3)) //2017.3 and up - { - baseVertex = reader.ReadUInt32(); - } - - if (version[0] >= 3) //3.0 and up - { - firstVertex = reader.ReadUInt32(); - vertexCount = reader.ReadUInt32(); - localAABB = new AABB(reader); - } - } - } - public class SpriteRenderData { public PPtr texture; diff --git a/AssetStudio/Classes/Texture2D.cs b/AssetStudio/Classes/Texture2D.cs index f9f40bf..cb6c755 100644 --- a/AssetStudio/Classes/Texture2D.cs +++ b/AssetStudio/Classes/Texture2D.cs @@ -7,74 +7,37 @@ using System.Runtime.InteropServices; namespace AssetStudio { - public sealed class Texture2D : Texture + public class StreamingInfo { - public int m_Width; - public int m_Height; - public int m_CompleteImageSize; - public TextureFormat m_TextureFormat; - public bool m_MipMap; - public int m_MipCount; - public bool m_IsReadable; - public bool m_ReadAllowed; - public int m_ImageCount; - public int m_TextureDimension; - //m_TextureSettings - public int m_FilterMode; - public int m_Aniso; - public float m_MipBias; - public int m_WrapMode; - public int m_LightmapFormat; - public int m_ColorSpace; - //image dataa - public int image_data_size; - public Lazy image_data; - //m_StreamData public uint offset; public uint size; public string path; - public Texture2D(ObjectReader reader) : base(reader) + public StreamingInfo(ObjectReader reader) { - m_Width = reader.ReadInt32(); - m_Height = reader.ReadInt32(); - m_CompleteImageSize = reader.ReadInt32(); - m_TextureFormat = (TextureFormat)reader.ReadInt32(); + offset = reader.ReadUInt32(); + size = reader.ReadUInt32(); + path = reader.ReadAlignedString(); + } + } - if (version[0] < 5 || (version[0] == 5 && version[1] < 2)) - { - m_MipMap = reader.ReadBoolean(); - } - else - { - m_MipCount = reader.ReadInt32(); - } + public class GLTextureSettings + { + public int m_FilterMode; + public int m_Aniso; + public float m_MipBias; + public int m_WrapMode; + + public GLTextureSettings(ObjectReader reader) + { + var version = reader.version; - m_IsReadable = reader.ReadBoolean(); //2.6.0 and up - m_ReadAllowed = reader.ReadBoolean(); //3.0.0 - 5.4 - //m_StreamingMipmaps 2018.2 and up - reader.AlignStream(); - if (version[0] > 2018 || (version[0] == 2018 && version[1] >= 2)) //2018.2 and up - { - var m_StreamingMipmapsPriority = reader.ReadInt32(); - } - else if (HasStructMember("m_StreamingMipmapsPriority")) //will fix in some patch version bundle - { - var m_StreamingMipmapsPriority = reader.ReadInt32(); - } - if (HasStructMember("m_StreamingGroupID")) //What the hell is this? - { - var m_StreamingGroupID = reader.ReadUInt32(); - } - m_ImageCount = reader.ReadInt32(); - m_TextureDimension = reader.ReadInt32(); - //m_TextureSettings m_FilterMode = reader.ReadInt32(); m_Aniso = reader.ReadInt32(); m_MipBias = reader.ReadSingle(); if (version[0] >= 2017)//2017.x and up { - int m_WrapU = reader.ReadInt32(); + m_WrapMode = reader.ReadInt32(); //m_WrapU int m_WrapV = reader.ReadInt32(); int m_WrapW = reader.ReadInt32(); } @@ -82,29 +45,63 @@ namespace AssetStudio { m_WrapMode = reader.ReadInt32(); } - if (version[0] >= 3) + } + } + + public sealed class Texture2D : Texture + { + public int m_Width; + public int m_Height; + public TextureFormat m_TextureFormat; + public bool m_MipMap; + public int m_MipCount; + public GLTextureSettings m_TextureSettings; + public Lazy image_data; + public StreamingInfo m_StreamData; + + public Texture2D(ObjectReader reader) : base(reader) + { + m_Width = reader.ReadInt32(); + m_Height = reader.ReadInt32(); + var m_CompleteImageSize = reader.ReadInt32(); + m_TextureFormat = (TextureFormat)reader.ReadInt32(); + if (version[0] < 5 || (version[0] == 5 && version[1] < 2)) //5.2 down { - m_LightmapFormat = reader.ReadInt32(); - if (version[0] >= 4 || version[1] >= 5)//3.5.0 and up - { - m_ColorSpace = reader.ReadInt32(); - } + m_MipMap = reader.ReadBoolean(); } - - image_data_size = reader.ReadInt32(); - + else + { + m_MipCount = reader.ReadInt32(); + } + var m_IsReadable = reader.ReadBoolean(); //2.6.0 and up + var m_ReadAllowed = reader.ReadBoolean(); //3.0.0 - 5.4 + //bool m_StreamingMipmaps 2018.2 and up + reader.AlignStream(); + if (version[0] > 2018 || (version[0] == 2018 && version[1] >= 2)) //2018.2 and up + { + var m_StreamingMipmapsPriority = reader.ReadInt32(); + } + var m_ImageCount = reader.ReadInt32(); + var m_TextureDimension = reader.ReadInt32(); + m_TextureSettings = new GLTextureSettings(reader); + if (version[0] >= 3) //3.0 and up + { + var m_LightmapFormat = reader.ReadInt32(); + } + if (version[0] > 3 || (version[0] == 3 && version[1] >= 5)) //3.5.0 and up + { + var m_ColorSpace = reader.ReadInt32(); + } + var image_data_size = reader.ReadInt32(); if (image_data_size == 0 && ((version[0] == 5 && version[1] >= 3) || version[0] > 5))//5.3.0 and up { - offset = reader.ReadUInt32(); - size = reader.ReadUInt32(); - image_data_size = (int)size; - path = reader.ReadAlignedString(); + m_StreamData = new StreamingInfo(reader); } ResourceReader resourceReader; - if (!string.IsNullOrEmpty(path)) + if (!string.IsNullOrEmpty(m_StreamData?.path)) { - resourceReader = new ResourceReader(path, assetsFile, offset, image_data_size); + resourceReader = new ResourceReader(m_StreamData.path, assetsFile, m_StreamData.offset, (int)m_StreamData.size); } else { diff --git a/AssetStudio/IImported.cs b/AssetStudio/IImported.cs index f0d765e..7cad52c 100644 --- a/AssetStudio/IImported.cs +++ b/AssetStudio/IImported.cs @@ -76,7 +76,7 @@ namespace AssetStudio { public Vector3 Position { get; set; } public float[] Weights { get; set; } - public byte[] BoneIndices { get; set; } + public int[] BoneIndices { get; set; } public Vector3 Normal { get; set; } public float[] UV { get; set; } public Vector4 Tangent { get; set; } diff --git a/AssetStudioFBX/AssetStudioFBXExporter.cpp b/AssetStudioFBX/AssetStudioFBXExporter.cpp index cbef509..cba3a12 100644 --- a/AssetStudioFBX/AssetStudioFBXExporter.cpp +++ b/AssetStudioFBX/AssetStudioFBXExporter.cpp @@ -554,8 +554,8 @@ namespace AssetStudio if (hasBones && vertex->BoneIndices != nullptr) { - array^ boneIndices = vertex->BoneIndices; - array^ weights4 = vertex->Weights; + auto boneIndices = vertex->BoneIndices; + auto weights4 = vertex->Weights; for (int k = 0; k < weights4->Length; k++) { if (boneIndices[k] < boneList->Count && weights4[k] > 0) diff --git a/AssetStudioGUI/AssetStudioGUIForm.cs b/AssetStudioGUI/AssetStudioGUIForm.cs index c004d4b..b394255 100644 --- a/AssetStudioGUI/AssetStudioGUIForm.cs +++ b/AssetStudioGUI/AssetStudioGUIForm.cs @@ -713,14 +713,14 @@ namespace AssetStudioGUI if (bitmap != null) { assetItem.InfoText = $"Width: {m_Texture2D.m_Width}\nHeight: {m_Texture2D.m_Height}\nFormat: {m_Texture2D.m_TextureFormat}"; - switch (m_Texture2D.m_FilterMode) + switch (m_Texture2D.m_TextureSettings.m_FilterMode) { case 0: assetItem.InfoText += "\nFilter Mode: Point "; break; case 1: assetItem.InfoText += "\nFilter Mode: Bilinear "; break; case 2: assetItem.InfoText += "\nFilter Mode: Trilinear "; break; } - assetItem.InfoText += $"\nAnisotropic level: {m_Texture2D.m_Aniso}\nMip map bias: {m_Texture2D.m_MipBias}"; - switch (m_Texture2D.m_WrapMode) + assetItem.InfoText += $"\nAnisotropic level: {m_Texture2D.m_TextureSettings.m_Aniso}\nMip map bias: {m_Texture2D.m_TextureSettings.m_MipBias}"; + switch (m_Texture2D.m_TextureSettings.m_WrapMode) { case 0: assetItem.InfoText += "\nWrap mode: Repeat"; break; case 1: assetItem.InfoText += "\nWrap mode: Clamp"; break; @@ -1061,10 +1061,14 @@ namespace AssetStudioGUI #endregion glControl1.Visible = true; createVAO(); + StatusStripUpdate("Using OpenGL Version: " + GL.GetString(StringName.Version) + "\n" + + "'Mouse Left'=Rotate | 'Mouse Right'=Move | 'Mouse Wheel'=Zoom \n" + + "'Ctrl W'=Wireframe | 'Ctrl S'=Shade | 'Ctrl N'=ReNormal "); + } + else + { + StatusStripUpdate("Unable to preview this mesh"); } - StatusStripUpdate("Using OpenGL Version: " + GL.GetString(StringName.Version) + "\n" - + "'Mouse Left'=Rotate | 'Mouse Right'=Move | 'Mouse Wheel'=Zoom \n" - + "'Ctrl W'=Wireframe | 'Ctrl S'=Shade | 'Ctrl N'=ReNormal "); } private void PreviewSprite(AssetItem assetItem, Sprite m_Sprite) diff --git a/AssetStudioGUI/Exporter.cs b/AssetStudioGUI/Exporter.cs index f861409..5ac5b16 100644 --- a/AssetStudioGUI/Exporter.cs +++ b/AssetStudioGUI/Exporter.cs @@ -190,7 +190,7 @@ namespace AssetStudioGUI #region Face int sum = 0; - for (var i = 0; i < m_Mesh.m_SubMeshes.Count; i++) + for (var i = 0; i < m_Mesh.m_SubMeshes.Length; i++) { sb.AppendLine($"g {m_Mesh.m_Name}_{i}"); int indexCount = (int)m_Mesh.m_SubMeshes[i].indexCount; diff --git a/AssetStudioGUI/Studio.cs b/AssetStudioGUI/Studio.cs index 2dbee00..49dfa7c 100644 --- a/AssetStudioGUI/Studio.cs +++ b/AssetStudioGUI/Studio.cs @@ -115,8 +115,8 @@ namespace AssetStudioGUI assetItem.Text = m_GameObject.m_Name; break; case Texture2D m_Texture2D: - if (!string.IsNullOrEmpty(m_Texture2D.path)) - assetItem.FullSize = asset.byteSize + m_Texture2D.size; + if (!string.IsNullOrEmpty(m_Texture2D.m_StreamData?.path)) + assetItem.FullSize = asset.byteSize + m_Texture2D.m_StreamData.size; assetItem.Text = m_Texture2D.m_Name; exportable = true; break; diff --git a/AssetStudioUtility/ModelConverter.cs b/AssetStudioUtility/ModelConverter.cs index 7ec6e9e..26aafd2 100644 --- a/AssetStudioUtility/ModelConverter.cs +++ b/AssetStudioUtility/ModelConverter.cs @@ -263,7 +263,7 @@ namespace AssetStudio combine = true; } int firstFace = 0; - for (int i = 0; i < mesh.m_SubMeshes.Count; i++) + for (int i = 0; i < mesh.m_SubMeshes.Length; i++) { int numFaces = (int)mesh.m_SubMeshes[i].indexCount / 3; if (subHashSet.Count > 0 && !subHashSet.Contains(i)) @@ -338,12 +338,12 @@ namespace AssetStudio if (mesh.m_Skin?.Length > 0) { var inf = mesh.m_Skin[j]; - iVertex.BoneIndices = new byte[inf.Count]; - iVertex.Weights = new float[inf.Count]; - for (var k = 0; k < inf.Count; k++) + iVertex.BoneIndices = new int[4]; + iVertex.Weights = new float[4]; + for (var k = 0; k < 4; k++) { - iVertex.BoneIndices[k] = (byte)inf[k].boneIndex; - iVertex.Weights[k] = inf[k].weight; + iVertex.BoneIndices[k] = inf.boneIndex[k]; + iVertex.Weights[k] = inf.weight[k]; } } iSubmesh.VertexList.Add(iVertex); @@ -490,7 +490,7 @@ namespace AssetStudio int shapeIdx = mesh.m_Shapes.channels[i].frameIndex + frameIdx; keyframe.VertexList = new List((int)mesh.m_Shapes.shapes[shapeIdx].vertexCount); keyframe.MorphedVertexIndices = new List((int)mesh.m_Shapes.shapes[shapeIdx].vertexCount); - keyframe.Weight = shapeIdx < mesh.m_Shapes.fullWeights.Count ? mesh.m_Shapes.fullWeights[shapeIdx] : 100f; + keyframe.Weight = shapeIdx < mesh.m_Shapes.fullWeights.Length ? mesh.m_Shapes.fullWeights[shapeIdx] : 100f; int lastVertIndex = (int)(mesh.m_Shapes.shapes[shapeIdx].firstVertex + mesh.m_Shapes.shapes[shapeIdx].vertexCount); for (int j = (int)mesh.m_Shapes.shapes[shapeIdx].firstVertex; j < lastVertIndex; j++) { diff --git a/AssetStudioUtility/SpriteHelper.cs b/AssetStudioUtility/SpriteHelper.cs index 846c6e9..c60b5cf 100644 --- a/AssetStudioUtility/SpriteHelper.cs +++ b/AssetStudioUtility/SpriteHelper.cs @@ -138,7 +138,6 @@ namespace AssetStudio private static Vector2[][] GetTriangles(VertexData m_VertexData, SubMesh[] m_SubMeshes, byte[] m_IndexBuffer) { var triangles = new List(); - GetStreams(m_VertexData); var m_Channel = m_VertexData.m_Channels[0]; //kShaderChannelVertex var m_Stream = m_VertexData.m_Streams[m_Channel.stream]; using (BinaryReader vertexReader = new BinaryReader(new MemoryStream(m_VertexData.m_DataSize)), @@ -170,62 +169,5 @@ namespace AssetStudio } return triangles.ToArray(); } - - private static void GetStreams(VertexData vertexData) - { - var m_Channels = vertexData.m_Channels; - var streamCount = m_Channels.Max(x => x.stream) + 1; - var m_Streams = new StreamInfo[streamCount]; - uint offset = 0; - for (int s = 0; s < streamCount; s++) - { - uint chnMask = 0; - uint 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 |= 1u << chn; - stride += m_Channel.dimension * GetChannelFormatSize(m_Channel.format); - } - } - } - m_Streams[s] = new StreamInfo - { - channelMask = chnMask, - offset = offset, - stride = stride, - dividerOp = 0, - frequency = 0 - }; - offset += vertexData.m_VertexCount * stride; - //static size_t AlignStreamSize (size_t size) { return (size + (kVertexStreamAlign-1)) & ~(kVertexStreamAlign-1); } - offset = (offset + (16u - 1u)) & ~(16u - 1u); - } - - vertexData.m_Streams = m_Streams; - } - - private static uint GetChannelFormatSize(int format) - { - switch (format) - { - case 0: //kChannelFormatFloat - return 4u; - case 1: //kChannelFormatFloat16 - return 2u; - case 2: //kChannelFormatColor, in 4.x is size 4 - return 1u; - case 3: //kChannelFormatByte - return 1u; - case 11: //kChannelFormatInt32 - return 4u; - default: - return 0; - } - } } } diff --git a/AssetStudioUtility/Texture2DConverter.cs b/AssetStudioUtility/Texture2DConverter.cs index 4b6ae74..eb0a2ad 100644 --- a/AssetStudioUtility/Texture2DConverter.cs +++ b/AssetStudioUtility/Texture2DConverter.cs @@ -86,8 +86,10 @@ namespace AssetStudio public Texture2DConverter(Texture2D m_Texture2D) { - image_data_size = m_Texture2D.image_data_size; - image_data = m_Texture2D.image_data.Value; + var image_data_value = m_Texture2D.image_data.Value; + image_data_size = image_data_value.Length; + image_data = new byte[image_data_size]; + Buffer.BlockCopy(image_data_value, 0, image_data, 0, image_data_size); m_Width = m_Texture2D.m_Width; m_Height = m_Texture2D.m_Height; m_TextureFormat = m_Texture2D.m_TextureFormat;