private SsbhVertexAttribute[] ReadAttributeVec4(MeshAttribute attr, int position, int count, BinaryReader buffer, int offset, int stride) { var format = (MeshAttribute.AttributeDataType)attr.DataType; var attributes = new SsbhVertexAttribute[count]; for (int i = 0; i < count; i++) { buffer.BaseStream.Position = offset + attr.BufferOffset + stride * (position + i); switch (format) { // TODO: Add option to not convert smaller types to larger types. case MeshAttribute.AttributeDataType.Byte: attributes[i] = new SsbhVertexAttribute(buffer.ReadByte(), buffer.ReadByte(), buffer.ReadByte(), buffer.ReadByte()); break; case MeshAttribute.AttributeDataType.Float: attributes[i] = new SsbhVertexAttribute(buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle()); break; case MeshAttribute.AttributeDataType.HalfFloat: attributes[i] = new SsbhVertexAttribute(ReadHalfFloat(buffer), ReadHalfFloat(buffer), ReadHalfFloat(buffer), ReadHalfFloat(buffer)); break; case MeshAttribute.AttributeDataType.HalfFloat2: attributes[i] = new SsbhVertexAttribute(ReadHalfFloat(buffer), ReadHalfFloat(buffer), ReadHalfFloat(buffer), ReadHalfFloat(buffer)); break; } } return(attributes); }
private SsbhVertexAttribute[] ReadAttributeVec4(MeshAttribute attr, int position, int count, string attributeName, BinaryReader buffer, int offset, int stride) { var format = (SsbVertexAttribFormat)attr.DataType; var attributes = new SsbhVertexAttribute[count]; for (int i = 0; i < count; i++) { buffer.BaseStream.Position = offset + attr.BufferOffset + stride * (position + i); switch (format) { case SsbVertexAttribFormat.Byte: attributes[i] = new SsbhVertexAttribute(buffer.ReadByte(), buffer.ReadByte(), buffer.ReadByte(), buffer.ReadByte()); break; case SsbVertexAttribFormat.Float: attributes[i] = new SsbhVertexAttribute(buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle(), buffer.ReadSingle()); break; case SsbVertexAttribFormat.HalfFloat: attributes[i] = new SsbhVertexAttribute(ReadHalfFloat(buffer), ReadHalfFloat(buffer), ReadHalfFloat(buffer), ReadHalfFloat(buffer)); break; case SsbVertexAttribFormat.HalfFloat2: attributes[i] = new SsbhVertexAttribute(ReadHalfFloat(buffer), ReadHalfFloat(buffer), ReadHalfFloat(buffer), ReadHalfFloat(buffer)); break; } } return(attributes); }
private SsbhVertexAttribute[] ReadAttribute(MeshAttribute attr, int count, MeshObject meshObject) { if (attr == null) { return(new SsbhVertexAttribute[0]); } string attributeName = attr.AttributeStrings[0].Text; // TODO: There are optimizations possible for reading if the data is tighly packed. // The stride may not allow this. int offset = meshObject.VertexOffset; int stride = meshObject.Stride; if (attr.BufferIndex == 1) { offset = meshObject.VertexOffset2; stride = meshObject.Stride2; } SsbhVertexAttribute[] attributes = null; int attributeLength = GetAttributeComponentCount(attributeName); using (BinaryReader attributeBuffer = new BinaryReader(new MemoryStream(meshFile.VertexBuffers[attr.BufferIndex].Buffer))) { attributes = ReadAttribute(attr, count, attributeBuffer, offset, stride, attributeLength); } return(attributes); }
public void LoadRawAsset() { if (this.RawRes != null || !File.Exists(this.AssetPath)) { return; } switch (this.ResourceType) { case EResoucresTypes.TextureType: RawRes = new TextureAttribute(this); break; case EResoucresTypes.MatrialType: RawRes = new MaterialAttribute(this); break; case EResoucresTypes.ShaderType: RawRes = new ShaderAttribute(this); break; case EResoucresTypes.MeshType: RawRes = new MeshAttribute(this); break; } }
/// <summary> /// /// </summary> /// <param name="attr"></param> /// <param name="position"></param> /// <param name="count"></param> /// <param name="meshObject"></param> /// <returns></returns> private SsbhVertexAttribute[] ReadAttribute(MeshAttribute attr, int position, int count, MeshObject meshObject) { if (attr == null) { return(new SsbhVertexAttribute[0]); } string attributeName = attr.AttributeStrings[0].Name; var buffers = new BinaryReader[meshFile.VertexBuffers.Length]; for (int i = 0; i < meshFile.VertexBuffers.Length; i++) { buffers[i] = new BinaryReader(new MemoryStream(meshFile.VertexBuffers[i].Buffer)); } BinaryReader selectedBuffer = buffers[attr.BufferIndex]; int offset = meshObject.VertexOffset; int stride = meshObject.Stride; if (attr.BufferIndex == 1) { offset = meshObject.VertexOffset2; stride = meshObject.Stride2; } SsbhVertexAttribute[] attributes = null; int attributeLength = GetAttributeLength(attributeName); switch (attributeLength) { case 1: attributes = ReadAttributeScalar(attr, position, count, attributeName, selectedBuffer, offset, stride); break; case 2: attributes = ReadAttributeVec2(attr, position, count, attributeName, selectedBuffer, offset, stride); break; case 3: attributes = ReadAttributeVec3(attr, position, count, attributeName, selectedBuffer, offset, stride); break; case 4: attributes = ReadAttributeVec4(attr, position, count, attributeName, selectedBuffer, offset, stride); break; } foreach (var buffer in buffers) { buffer.Close(); buffer.Dispose(); } return(attributes); }
public SSBHVertexAttribute[] ReadAttribute(string AttributeName, int Position, int Count, MeshObject MeshObject) { MeshAttribute attr = GetAttribute(AttributeName, MeshObject); if (attr == null) { return(new SSBHVertexAttribute[0]); } BinaryReader SelectedBuffer = buffers[attr.BufferIndex]; int Offset = MeshObject.VertexOffset; int Stride = MeshObject.Stride; if (attr.BufferIndex == 1) { Offset = MeshObject.VertexOffset2; Stride = MeshObject.Stride2; } int Size = 3; if (AttributeName.Contains("colorSet")) { Size = 4; } if (AttributeName.Equals("map1") || AttributeName.Equals("bake1") || AttributeName.Contains("uvSet")) { Size = 2; } SSBHVertexAttribute[] a = new SSBHVertexAttribute[Count]; for (int i = 0; i < Count; i++) { SelectedBuffer.BaseStream.Position = Offset + attr.BufferOffset + Stride * (Position + i); a[i] = new SSBHVertexAttribute(); if (Size > 0) { a[i].X = ReadAttribute(SelectedBuffer, (SSBVertexAttribFormat)attr.DataType); } if (Size > 1) { a[i].Y = ReadAttribute(SelectedBuffer, (SSBVertexAttribFormat)attr.DataType); } if (Size > 2) { a[i].Z = ReadAttribute(SelectedBuffer, (SSBVertexAttribFormat)attr.DataType); } if (Size > 3) { a[i].W = ReadAttribute(SelectedBuffer, (SSBVertexAttribFormat)attr.DataType); } } return(a); }
/// <summary> /// Sets a vertex attribute array for the given <paramref name="bindingID"/>. /// </summary> /// <param name="bindingID">The binding ID.</param> /// <param name="data">The attribute array data.</param> /// <param name="perInstance"> /// if set to <c>true</c> attribute array contains one entry for each instance /// if set to <c>false</c> all attribute array elements are for one instance /// </param> public void SetAttribute <ELEMENT_TYPE>(int bindingID, ELEMENT_TYPE[] data, bool perInstance = false) where ELEMENT_TYPE : struct { var type = typeof(ELEMENT_TYPE); var baseTypeCount = MeshAttribute.GetBaseTypeCount(type); if (baseTypeCount > 1) { type = typeof(float); } SetAttribute(bindingID, data, type, baseTypeCount, perInstance); }
public VAO GetDrawable(DefaultMesh mesh, DrawableType type) { MeshAttribute uvs = mesh.GetAttribute("uv"); int elems = uvs.ToArray().Length; if (elems == 0) { mesh.SetConstantUV(new Vector2(0, 0)); } IShaderProgram shader = GetShader(type); VAO res = VAOLoader.FromMesh(mesh, shader); return(res); }
private SsbhVertexAttribute[] ReadAttribute(MeshAttribute attr, int count, BinaryReader buffer, int offset, int stride, int attributeLength) { var format = attr.DataType; var attributes = new SsbhVertexAttribute[count]; for (int i = 0; i < count; i++) { buffer.BaseStream.Position = offset + attr.BufferOffset + stride * i; attributes[i] = new SsbhVertexAttribute(); switch (format) { // TODO: Add option to not convert smaller types to larger types. case MeshAttribute.AttributeDataType.Byte: for (int j = 0; j < attributeLength; j++) { attributes[i][j] = buffer.ReadByte(); } break; case MeshAttribute.AttributeDataType.Float: for (int j = 0; j < attributeLength; j++) { attributes[i][j] = buffer.ReadSingle(); } break; case MeshAttribute.AttributeDataType.HalfFloat: for (int j = 0; j < attributeLength; j++) { attributes[i][j] = ReadHalfFloat(buffer); } break; case MeshAttribute.AttributeDataType.HalfFloat2: for (int j = 0; j < attributeLength; j++) { attributes[i][j] = ReadHalfFloat(buffer); } break; } } return(attributes); }
private static VertexAttribPointerType GetAttributeType(MeshAttribute meshAttribute) { switch (meshAttribute.DataType) { case 0: return(VertexAttribPointerType.Float); case 2: return(VertexAttribPointerType.Byte); // color case 5: return(VertexAttribPointerType.HalfFloat); case 8: return(VertexAttribPointerType.HalfFloat); default: return(VertexAttribPointerType.Float); } }
private static int TryParseAttrib(string line, ref MeshAttribute attrib) { if (line.StartsWith("VERTICES")) { attrib = MeshAttribute.Position; } else if (line.StartsWith("VERTEXNORMALS")) { attrib = MeshAttribute.Normal; } else if (line.StartsWith("POLYGONS")) { attrib = MeshAttribute.Polygon; } else if (line.StartsWith("WEIGHT")) { attrib = MeshAttribute.Weight; } else if (line.StartsWith("MORPH")) { attrib = MeshAttribute.Morph; } else if (line.StartsWith("UV")) { attrib = MeshAttribute.Uv; } else { return(-1); } string[] split = line.Split(':'); int size = 0; int.TryParse(split[split.Length - 1], out size); return(size); }
/// <summary> /// Import from ODVertexData file. /// </summary> /// <param name="path"></param> /// <param name="mesh"></param> /// <param name="materials"></param> /// <returns></returns> public static bool Import(string path, out Mesh mesh, out string[] materials) { MeshAttribute attrib = MeshAttribute.Null; List <Vector3> positions = new List <Vector3>(); List <Vector3> normals = new List <Vector3>(); List <Polygon> polygons = new List <Polygon>(); List <UVCoord> uvs = new List <UVCoord>(); using (StreamReader reader = new StreamReader(path)) { for (string line = reader.ReadLine(); line != null; line = reader.ReadLine()) { int attributeCount = TryParseAttrib(line, ref attrib); if (attributeCount > -1) { continue; } if (attrib == MeshAttribute.Position) { TryParseVector3(line, positions); } else if (attrib == MeshAttribute.Normal) { TryParseVector3(line, normals); } else if (attrib == MeshAttribute.Uv) { TryParseUv(line, uvs); } else if (attrib == MeshAttribute.Polygon) { TryParsePolygon(line, polygons); } } } bool hasNormals = normals.Count == positions.Count; if (PasteConvertsHandedness) { for (int i = 0; i < positions.Count; i++) { // todo More options for swapping coordinate systems around (eg, max w/ z up) positions[i] = new Vector3(-positions[i].x, positions[i].y, positions[i].z); if (hasNormals) { normals[i] = new Vector3(-normals[i].x, normals[i].y, normals[i].z); } } } materials = new string[0]; mesh = null; List <Vertex> vertices; Dictionary <string, List <int> > indices; if (!MeshUtility.GeneratePerTriangleVertices(positions, normals, uvs, polygons, out vertices, out indices)) { return(false); } materials = indices.Select(x => x.Key).ToArray(); mesh = MeshUtility.CompileMesh(vertices, indices, !SplitVertices); mesh.name = "ODCopyPaste_Mesh"; mesh.RecalculateNormals(); mesh.RecalculateTangents(); mesh.RecalculateBounds(); return(true); }
/// <summary> /// Reads the attribute data from the mesh object with the given name /// </summary> /// <param name="attributeName"></param> /// <param name="position"></param> /// <param name="count"></param> /// <param name="meshObject"></param> /// <returns>null if attribute name not found</returns> public SsbhVertexAttribute[] ReadAttribute(string attributeName, int position, int count, MeshObject meshObject) { MeshAttribute attr = GetAttribute(attributeName, meshObject); return(ReadAttribute(attr, position, count, meshObject)); }
/// <summary> /// Reads the vertex attribute information for the given attribute name inside of the mesh object. /// </summary> /// <param name="attributeName"></param> /// <param name="meshObject"></param> /// <returns>null if attribute name not found</returns> public SsbhVertexAttribute[] ReadAttribute(MeshAttribute attributeName, MeshObject meshObject) { return(ReadAttribute(attributeName, 0, meshObject.VertexCount, meshObject)); }
/// <summary> /// Creates and returns a mesh file /// </summary> /// <returns></returns> public Mesh GetMeshFile() { Mesh mesh = new Mesh { //TODO: bounding box stuff // Rigging Objects = new MeshObject[meshes.Count] }; // create mesh objects and buffers BinaryWriter vertexBuffer1 = new BinaryWriter(new MemoryStream()); BinaryWriter vertexBuffer2 = new BinaryWriter(new MemoryStream()); // there are actually 4 buffers, but only 2 seem to be used BinaryWriter indexBuffer = new BinaryWriter(new MemoryStream()); int finalBufferOffset = 0; int meshIndex = 0; Dictionary <string, int> meshGroups = new Dictionary <string, int>(); List <MeshRiggingGroup> riggingGroups = new List <MeshRiggingGroup>(); foreach (var tempmesh in meshes) { MeshObject mo = new MeshObject { Name = tempmesh.Name }; if (meshGroups.ContainsKey(mo.Name)) { meshGroups[mo.Name] += 1; } else { meshGroups.Add(mo.Name, 0); } mo.SubIndex = meshGroups[mo.Name]; mo.BoundingSphereCenter = new Formats.Vector3 { X = tempmesh.BoundingSphere.X, Y = tempmesh.BoundingSphere.Y, Z = tempmesh.BoundingSphere.Z }; mo.BoundingSphereRadius = tempmesh.BoundingSphere.W; mo.BoundingBoxMax = new Formats.Vector3 { X = tempmesh.BbMax.X, Y = tempmesh.BbMax.Y, Z = tempmesh.BbMax.Z }; mo.BoundingBoxMin = new Formats.Vector3 { X = tempmesh.BbMin.X, Y = tempmesh.BbMin.Y, Z = tempmesh.BbMin.Z }; mo.OrientedBoundingBoxCenter = new Formats.Vector3 { X = tempmesh.ObbCenter.X, Y = tempmesh.ObbCenter.Y, Z = tempmesh.ObbCenter.Z }; mo.OrientedBoundingBoxSize = new Formats.Vector3 { X = tempmesh.ObbSize.X, Y = tempmesh.ObbSize.Y, Z = tempmesh.ObbSize.Z }; mo.OrientedBoundingBoxTransform.Row1.X = tempmesh.ObbMatrix3X3[0]; mo.OrientedBoundingBoxTransform.Row1.Y = tempmesh.ObbMatrix3X3[1]; mo.OrientedBoundingBoxTransform.Row1.Z = tempmesh.ObbMatrix3X3[2]; mo.OrientedBoundingBoxTransform.Row2.X = tempmesh.ObbMatrix3X3[3]; mo.OrientedBoundingBoxTransform.Row2.Y = tempmesh.ObbMatrix3X3[4]; mo.OrientedBoundingBoxTransform.Row2.Z = tempmesh.ObbMatrix3X3[5]; mo.OrientedBoundingBoxTransform.Row3.X = tempmesh.ObbMatrix3X3[6]; mo.OrientedBoundingBoxTransform.Row3.Y = tempmesh.ObbMatrix3X3[7]; mo.OrientedBoundingBoxTransform.Row3.Z = tempmesh.ObbMatrix3X3[8]; // Create Rigging riggingGroups.Add(SsbhRiggingCompiler.CreateRiggingGroup(mo.Name, (int)mo.SubIndex, tempmesh.Influences.ToArray())); // set object mesh.Objects[meshIndex++] = mo; mo.ParentBoneName = tempmesh.ParentBone; if (tempmesh.Influences.Count > 0 && (tempmesh.ParentBone == null || tempmesh.ParentBone.Equals(""))) { mo.RiggingType = RiggingType.Regular; } int stride1 = 0; int stride2 = 0; mo.VertexOffset = (int)vertexBuffer1.BaseStream.Length; mo.VertexOffset2 = (int)vertexBuffer2.BaseStream.Length; mo.ElementOffset = (uint)indexBuffer.BaseStream.Length; // gather strides mo.Attributes = new MeshAttribute[tempmesh.VertexData.Count]; int attributeIndex = 0; foreach (var keypair in tempmesh.VertexData) { // For some reason the attribute string doesn't match the attribute's name for Tangent0. var attributeName = keypair.Key.Name; if (keypair.Key.Name == "Tangent0") { attributeName = "map1"; } MeshAttribute attr = new MeshAttribute { Name = attributeName, Index = keypair.Key.Index, BufferIndex = keypair.Key.BufferIndex, DataType = keypair.Key.DataType, BufferOffset = keypair.Key.BufferIndex == 0 ? stride1 : stride2, AttributeStrings = new MeshAttributeString[] { new MeshAttributeString() { Name = keypair.Key.Name } } }; mo.Attributes[attributeIndex++] = attr; if (keypair.Key.BufferIndex == 0) { stride1 += keypair.Key.ComponentCount * keypair.Key.DataType.GetSizeInBytes(); } else { stride2 += keypair.Key.ComponentCount * keypair.Key.DataType.GetSizeInBytes(); } } // now that strides are known... long buffer1Start = vertexBuffer1.BaseStream.Length; long buffer2Start = vertexBuffer2.BaseStream.Length; vertexBuffer1.Write(new byte[stride1 * tempmesh.VertexCount]); vertexBuffer2.Write(new byte[stride2 * tempmesh.VertexCount]); attributeIndex = 0; foreach (var keypair in tempmesh.VertexData) { var attr = mo.Attributes[attributeIndex++]; float[] data = keypair.Value; var buffer = attr.BufferIndex == 0 ? vertexBuffer1 : vertexBuffer2; int bufferOffset = (int)(attr.BufferIndex == 0 ? buffer1Start : buffer2Start); int stride = (attr.BufferIndex == 0 ? stride1 : stride2); int componentCount = keypair.Key.ComponentCount; for (int vertexIndex = 0; vertexIndex < tempmesh.VertexCount; vertexIndex++) { buffer.Seek(bufferOffset + stride * vertexIndex + attr.BufferOffset, SeekOrigin.Begin); for (int j = 0; j < componentCount; j++) { WriteType(buffer, attr.DataType, data[vertexIndex * componentCount + j]); } } // seek to end just to make sure buffer.Seek((int)buffer.BaseStream.Length, SeekOrigin.Begin); } mo.FinalBufferOffset = finalBufferOffset; finalBufferOffset += (4 + stride1) * tempmesh.VertexCount; mo.VertexCount = tempmesh.VertexCount; mo.IndexCount = tempmesh.Indices.Count; mo.Stride = stride1; mo.Stride2 = stride2; // write index buffer if (tempmesh.VertexCount > ushort.MaxValue) { mo.DrawElementType = DrawElementType.UnsignedInt; foreach (var i in tempmesh.Indices) { indexBuffer.Write(i); } } else { foreach (var i in tempmesh.Indices) { indexBuffer.Write((ushort)i); } } } mesh.PolygonIndexSize = indexBuffer.BaseStream.Length; mesh.BufferSizes = new int[] { (int)vertexBuffer1.BaseStream.Length, (int)vertexBuffer2.BaseStream.Length, 0, 0 }; mesh.PolygonBuffer = ((MemoryStream)indexBuffer.BaseStream).ToArray(); Console.WriteLine(mesh.PolygonBuffer.Length + " " + indexBuffer.BaseStream.Length); mesh.VertexBuffers = new MeshBuffer[] { new MeshBuffer { Buffer = ((MemoryStream)vertexBuffer1.BaseStream).ToArray() }, new MeshBuffer { Buffer = ((MemoryStream)vertexBuffer2.BaseStream).ToArray() }, new MeshBuffer { Buffer = new byte[0] }, new MeshBuffer { Buffer = new byte[0] } }; mesh.RiggingBuffers = riggingGroups.ToArray().OrderBy(o => o.MeshName, StringComparer.Ordinal).ToArray(); vertexBuffer1.Close(); vertexBuffer2.Close(); indexBuffer.Close(); return(mesh); }
/// <summary> /// Creates and returns a mesh file /// </summary> /// <returns></returns> public MESH GetMeshFile() { MESH mesh = new MESH { //TODO: bounding box stuff // Rigging Objects = new MeshObject[meshes.Count] }; // create mesh objects and buffers BinaryWriter vertexBuffer1 = new BinaryWriter(new MemoryStream()); BinaryWriter vertexBuffer2 = new BinaryWriter(new MemoryStream()); // there are actually 4 buffers, but only 2 seem to be used BinaryWriter indexBuffer = new BinaryWriter(new MemoryStream()); int finalBufferOffset = 0; int meshIndex = 0; Dictionary <string, int> MeshGroups = new Dictionary <string, int>(); List <MeshRiggingGroup> RiggingGroups = new List <MeshRiggingGroup>(); foreach (var tempmesh in meshes) { MeshObject mo = new MeshObject { Name = tempmesh.Name }; if (MeshGroups.ContainsKey(mo.Name)) { MeshGroups[mo.Name] += 1; } else { MeshGroups.Add(mo.Name, 0); } mo.SubMeshIndex = MeshGroups[mo.Name]; mo.BoundingSphereX = tempmesh.BoundingSphere.X; mo.BoundingSphereY = tempmesh.BoundingSphere.Y; mo.BoundingSphereZ = tempmesh.BoundingSphere.Z; mo.BoundingSphereRadius = tempmesh.BoundingSphere.W; mo.MaxBoundingBoxX = tempmesh.BBMax.X; mo.MaxBoundingBoxY = tempmesh.BBMax.Y; mo.MaxBoundingBoxZ = tempmesh.BBMax.Z; mo.MinBoundingBoxX = tempmesh.BBMin.X; mo.MinBoundingBoxY = tempmesh.BBMin.Y; mo.MinBoundingBoxZ = tempmesh.BBMin.Z; mo.OBBCenterX = tempmesh.OBBCenter.X; mo.OBBCenterY = tempmesh.OBBCenter.Y; mo.OBBCenterZ = tempmesh.OBBCenter.Z; mo.OBBSizeX = tempmesh.OBBSize.X; mo.OBBSizeY = tempmesh.OBBSize.Y; mo.OBBSizeZ = tempmesh.OBBSize.Z; mo.M11 = tempmesh.OBBMatrix3x3[0]; mo.M12 = tempmesh.OBBMatrix3x3[1]; mo.M13 = tempmesh.OBBMatrix3x3[2]; mo.M21 = tempmesh.OBBMatrix3x3[3]; mo.M22 = tempmesh.OBBMatrix3x3[4]; mo.M23 = tempmesh.OBBMatrix3x3[5]; mo.M31 = tempmesh.OBBMatrix3x3[6]; mo.M32 = tempmesh.OBBMatrix3x3[7]; mo.M33 = tempmesh.OBBMatrix3x3[8]; // Create Rigging RiggingGroups.Add(SSBHRiggingCompiler.CreateRiggingGroup(mo.Name, (int)mo.SubMeshIndex, tempmesh.Influences.ToArray())); // set object mesh.Objects[meshIndex++] = mo; mo.ParentBoneName = tempmesh.ParentBone; if (tempmesh.Influences.Count > 0 && (tempmesh.ParentBone == null || tempmesh.ParentBone.Equals(""))) { mo.HasRigging = 1; } int Stride1 = 0; int Stride2 = 0; mo.VertexOffset = (int)vertexBuffer1.BaseStream.Length; mo.VertexOffset2 = (int)vertexBuffer2.BaseStream.Length; mo.ElementOffset = (uint)indexBuffer.BaseStream.Length; // gather strides mo.Attributes = new MeshAttribute[tempmesh.VertexData.Count]; int attributeIndex = 0; foreach (var keypair in tempmesh.VertexData) { MeshAttribute attr = new MeshAttribute { Name = GetAttributeName(keypair.Key), Index = GetAttributeIndex(keypair.Key), BufferIndex = GetBufferIndex(keypair.Key), DataType = GetAttributeDataType(keypair.Key), BufferOffset = (GetBufferIndex(keypair.Key) == 0) ? Stride1 : Stride2, AttributeStrings = new MeshAttributeString[] { new MeshAttributeString() { Name = keypair.Key.ToString() } } }; mo.Attributes[attributeIndex++] = attr; if (GetBufferIndex(keypair.Key) == 0) { Stride1 += GetAttributeSize(keypair.Key) * GetAttributeDataSize(keypair.Key); } else { Stride2 += GetAttributeSize(keypair.Key) * GetAttributeDataSize(keypair.Key); } } // now that strides are known... long Buffer1Start = vertexBuffer1.BaseStream.Length; long Buffer2Start = vertexBuffer2.BaseStream.Length; vertexBuffer1.Write(new byte[Stride1 * tempmesh.VertexCount]); vertexBuffer2.Write(new byte[Stride2 * tempmesh.VertexCount]); attributeIndex = 0; foreach (var keypair in tempmesh.VertexData) { var attr = mo.Attributes[attributeIndex++]; float[] Data = keypair.Value; var buffer = attr.BufferIndex == 0 ? vertexBuffer1 : vertexBuffer2; int bufferOffset = (int)(attr.BufferIndex == 0 ? Buffer1Start : Buffer2Start); int stride = (attr.BufferIndex == 0 ? Stride1 : Stride2); int size = GetAttributeSize(keypair.Key); for (int vertexIndex = 0; vertexIndex < tempmesh.VertexCount; vertexIndex++) { buffer.Seek(bufferOffset + stride * vertexIndex + attr.BufferOffset, SeekOrigin.Begin); for (int j = 0; j < size; j++) { WriteType(buffer, attr.DataType, Data[vertexIndex * size + j]); } } // seek to end just to make sure buffer.Seek((int)buffer.BaseStream.Length, SeekOrigin.Begin); } mo.FinalBufferOffset = finalBufferOffset; finalBufferOffset += (4 + Stride1) * tempmesh.VertexCount; mo.VertexCount = tempmesh.VertexCount; mo.IndexCount = tempmesh.Indices.Count; mo.Stride = Stride1; mo.Stride2 = Stride2; // write index buffer if (tempmesh.VertexCount > ushort.MaxValue) { mo.DrawElementType = 1; foreach (var i in tempmesh.Indices) { indexBuffer.Write(i); } } else { foreach (var i in tempmesh.Indices) { indexBuffer.Write((ushort)i); } } } mesh.PolygonIndexSize = indexBuffer.BaseStream.Length; mesh.BufferSizes = new int[] { (int)vertexBuffer1.BaseStream.Length, (int)vertexBuffer2.BaseStream.Length, 0, 0 }; mesh.PolygonBuffer = ((MemoryStream)indexBuffer.BaseStream).ToArray(); Console.WriteLine(mesh.PolygonBuffer.Length + " " + indexBuffer.BaseStream.Length); mesh.VertexBuffers = new MeshBuffer[] { new MeshBuffer() { Buffer = ((MemoryStream)vertexBuffer1.BaseStream).ToArray() }, new MeshBuffer() { Buffer = ((MemoryStream)vertexBuffer2.BaseStream).ToArray() }, new MeshBuffer() { Buffer = new byte[0] }, new MeshBuffer() { Buffer = new byte[0] } }; mesh.RiggingBuffers = RiggingGroups.ToArray().OrderBy(o => o.Name, StringComparer.Ordinal).ToArray(); vertexBuffer1.Close(); vertexBuffer2.Close(); indexBuffer.Close(); return(mesh); }