public static void Open(string FileName, SBScene Scene) { ISSBH_File File; if (SSBH.TryParseSSBHFile(FileName, out File)) { if (File is MESH mesh) { if (mesh.VersionMajor != 1 && mesh.VersionMinor != 10) { SBConsole.WriteLine($"Mesh Version {mesh.VersionMajor}.{mesh.VersionMinor} not supported"); return; } if (mesh.UnknownOffset != 0 || mesh.UnknownSize != 0) { SBConsole.WriteLine($"Warning: Unknown Mesh format detected"); } SBUltimateModel model = new SBUltimateModel(); model.Name = mesh.ModelName; model.BoundingSphere = new Vector4(mesh.BoundingSphereX, mesh.BoundingSphereY, mesh.BoundingSphereZ, mesh.BoundingSphereRadius); ((SBSceneSSBH)Scene).Model = model; SSBHVertexAccessor accessor = new SSBHVertexAccessor(mesh); { foreach (var meshObject in mesh.Objects) { SBUltimateMesh sbMesh = new SBUltimateMesh(); foreach (var attr in meshObject.Attributes) { foreach (var atstring in attr.AttributeStrings) { UltimateVertexAttribute at; if (Enum.TryParse(atstring.Name, out at)) { sbMesh.EnableAttribute(at); } } } sbMesh.Name = meshObject.Name; sbMesh.ParentBone = meshObject.ParentBoneName; sbMesh.BoundingSphere = new BoundingSphere(meshObject.BoundingSphereX, meshObject.BoundingSphereY, meshObject.BoundingSphereZ, meshObject.BoundingSphereRadius); sbMesh.AABoundingBox = new AABoundingBox(new Vector3(meshObject.MinBoundingBoxX, meshObject.MinBoundingBoxY, meshObject.MinBoundingBoxZ), new Vector3(meshObject.MaxBoundingBoxX, meshObject.MaxBoundingBoxY, meshObject.MaxBoundingBoxZ)); sbMesh.OrientedBoundingBox = new OrientedBoundingBox(new Vector3(meshObject.OBBCenterX, meshObject.OBBCenterY, meshObject.OBBCenterZ), new Vector3(meshObject.OBBSizeX, meshObject.OBBSizeY, meshObject.OBBSizeZ), new Matrix3(meshObject.M11, meshObject.M12, meshObject.M13, meshObject.M21, meshObject.M22, meshObject.M23, meshObject.M31, meshObject.M32, meshObject.M33)); sbMesh.Indices = new List <uint>(accessor.ReadIndices(0, meshObject.IndexCount, meshObject)); sbMesh.Vertices = CreateVertices(mesh, Scene.Skeleton, meshObject, accessor, sbMesh.Indices.ToArray()); model.Meshes.Add(sbMesh); } } } } }
public RModel GetRenderModel(RSkeleton Skeleton = null) { RModel model = new RModel(); // The bounding sphere containing all meshes. var modelSphere = mesh.GetBoundingSphere(); model.BoundingSphere = new Vector4(modelSphere.Item1, modelSphere.Item2, modelSphere.Item3, modelSphere.Item4); foreach (MeshObject meshObject in mesh.Objects) { PrintAttributeInformation(meshObject); RMesh rMesh = new RMesh { Name = meshObject.Name, SingleBindName = meshObject.ParentBoneName, }; // Get bounding sphere. var sphere = meshObject.GetBoundingSphere(); rMesh.BoundingSphere = new Vector4(sphere.Item1, sphere.Item2, sphere.Item3, sphere.Item4); // Get vertex data. var vertexAccessor = new SSBHVertexAccessor(mesh); var vertexIndices = vertexAccessor.ReadIndices(0, meshObject.IndexCount, meshObject); List <CustomVertex> vertices = CreateVertices(Skeleton, meshObject, vertexAccessor, vertexIndices); rMesh.RenderMesh = new RenderMesh(vertices, new List <uint>(vertexIndices)); model.subMeshes.Add(rMesh); } return(model); }
public static void Open(string FileName, SBScene Scene) { ISSBH_File File; if (SSBH.TryParseSSBHFile(FileName, out File)) { if (File is MESH mesh) { if (mesh.VersionMajor != 1 && mesh.VersionMinor != 10) { SBConsole.WriteLine($"Mesh Version {mesh.VersionMajor}.{mesh.VersionMinor} not supported"); return; } if (mesh.UnknownOffset != 0 || mesh.UnknownSize != 0) { SBConsole.WriteLine($"Warning: Unknown Mesh format detected"); } SBUltimateModel model = new SBUltimateModel(); model.Name = mesh.ModelName; model.BoundingSphere = new Vector4(mesh.BoundingSphereX, mesh.BoundingSphereY, mesh.BoundingSphereZ, mesh.BoundingSphereRadius / 2); Vector3 min = new Vector3(mesh.MinBoundingBoxX, mesh.MinBoundingBoxY, mesh.MinBoundingBoxZ); Vector3 max = new Vector3(mesh.MaxBoundingBoxX, mesh.MaxBoundingBoxY, mesh.MaxBoundingBoxZ); model.VolumeCenter = (max + min) / 2; model.VolumeSize = (max - min) / 2; ((SBSceneSSBH)Scene).Model = model; using (SSBHVertexAccessor accessor = new SSBHVertexAccessor(mesh)) { foreach (var meshObject in mesh.Objects) { SBUltimateMesh <UltimateVertex> sbMesh = new SBUltimateMesh <UltimateVertex>(); foreach (var attr in meshObject.Attributes) { foreach (var atstring in attr.AttributeStrings) { MESHAttribute at; if (Enum.TryParse <MESHAttribute>(atstring.Name, out at)) { //SBConsole.WriteLine("\tLoaded:" + at.ToString()); sbMesh.ExportAttributes.Add(at); } } } sbMesh.Name = meshObject.Name; sbMesh.BoundingSphere = new Vector4(meshObject.BoundingSphereX, meshObject.BoundingSphereY, meshObject.BoundingSphereZ, meshObject.BoundingSphereRadius); sbMesh.ParentBone = meshObject.ParentBoneName; sbMesh.Indices = new List <uint>(accessor.ReadIndices(0, meshObject.IndexCount, meshObject)); sbMesh.Vertices = CreateVertices(mesh, Scene.Skeleton, meshObject, accessor, sbMesh.Indices.ToArray()); model.Meshes.Add(sbMesh); } } } } }
private RenderMesh GetRenderMesh(RSkeleton Skeleton, MeshObject meshObject, RMesh rMesh) { var vertexAccessor = new SSBHVertexAccessor(mesh); { var vertexIndices = vertexAccessor.ReadIndices(0, meshObject.IndexCount, meshObject); System.Diagnostics.Debug.WriteLine($"Vertex Count: {vertexIndices.Length}"); List <CustomVertex> vertices = CreateVertices(Skeleton, meshObject, vertexAccessor, vertexIndices); /*if(obs.IndexOf(meshObject) != 0x2B && ExtendedMesh != null && ExtendedMesh.MeshToIndexBuffer.ContainsKey(obs.IndexOf(meshObject))) * { * rMesh.RenderMesh = new RenderMesh(vertices, new List<uint>(ExtendedMesh.MeshToIndexBuffer[obs.IndexOf(meshObject)]), PrimitiveType.TriangleFan); * } * else*/ return(new RenderMesh(vertices, new List <uint>(vertexIndices))); } }
private List <CustomVertex> CreateVertices(RSkeleton Skeleton, MeshObject meshObject, SSBHVertexAccessor vertexAccessor, uint[] vertexIndices) { // Read attribute values. var positions = vertexAccessor.ReadAttribute("Position0", 0, meshObject.VertexCount, meshObject); var normals = vertexAccessor.ReadAttribute("Normal0", 0, meshObject.VertexCount, meshObject); var tangents = vertexAccessor.ReadAttribute("Tangent0", 0, meshObject.VertexCount, meshObject); var map1Values = vertexAccessor.ReadAttribute("map1", 0, meshObject.VertexCount, meshObject); var bake1Values = vertexAccessor.ReadAttribute("bake1", 0, meshObject.VertexCount, meshObject); var colorSet1Values = vertexAccessor.ReadAttribute("colorSet1", 0, meshObject.VertexCount, meshObject); var colorSet5Values = vertexAccessor.ReadAttribute("colorSet5", 0, meshObject.VertexCount, meshObject); Vector3[] generatedBitangents = GenerateBitangents(vertexIndices, positions, map1Values); var boneIndices = new IVec4[positions.Length]; var boneWeights = new Vector4[positions.Length]; var riggingAccessor = new SSBHRiggingAccessor(mesh); SSBHVertexInfluence[] influences = riggingAccessor.ReadRiggingBuffer(meshObject.Name, (int)meshObject.SubMeshIndex); Dictionary <string, int> indexByBoneName = new Dictionary <string, int>(); if (Skeleton != null) { for (int i = 0; i < Skeleton.Bones.Count; i++) { indexByBoneName.Add(Skeleton.Bones[i].Name, i); } } foreach (SSBHVertexInfluence influence in influences) { // Some influences refer to bones that don't exist in the skeleton. // _eff bones? if (!indexByBoneName.ContainsKey(influence.BoneName)) { continue; } if (boneWeights[influence.VertexIndex].X == 0) { boneIndices[influence.VertexIndex].X = indexByBoneName[influence.BoneName]; boneWeights[influence.VertexIndex].X = influence.Weight; } else if (boneWeights[influence.VertexIndex].Y == 0) { boneIndices[influence.VertexIndex].Y = indexByBoneName[influence.BoneName]; boneWeights[influence.VertexIndex].Y = influence.Weight; } else if (boneWeights[influence.VertexIndex].Z == 0) { boneIndices[influence.VertexIndex].Z = indexByBoneName[influence.BoneName]; boneWeights[influence.VertexIndex].Z = influence.Weight; } else if (boneWeights[influence.VertexIndex].W == 0) { boneIndices[influence.VertexIndex].W = indexByBoneName[influence.BoneName]; boneWeights[influence.VertexIndex].W = influence.Weight; } } var vertices = new List <CustomVertex>(); for (int i = 0; i < positions.Length; i++) { var position = GetVector4(positions[i]).Xyz; var normal = GetVector4(normals[i]).Xyz; var tangent = GetVector4(tangents[i]).Xyz; var bitangent = GetBitangent(generatedBitangents, i, normal); var map1 = GetVector4(map1Values[i]).Xy; var bones = boneIndices[i]; var weights = boneWeights[i]; // Accessors return length 0 when the attribute isn't present. var bake1 = new Vector2(0); if (bake1Values.Length != 0) { bake1 = GetVector4(bake1Values[i]).Xy; } // The values are read as float, so we can't use OpenGL to convert. // Convert the range [0, 128] to [0, 255]. var colorSet1 = new Vector4(1); if (colorSet1Values.Length != 0) { colorSet1 = GetVector4(colorSet1Values[i]) / 128.0f; } var colorSet5 = new Vector4(1); if (colorSet5Values.Length != 0) { colorSet5 = GetVector4(colorSet5Values[i]) / 128.0f; } vertices.Add(new CustomVertex(position, normal, tangent, bitangent, map1, bones, weights, bake1, colorSet1, colorSet5)); } return(vertices); }
private static List <UltimateVertex> CreateVertices(MESH mesh, ISBSkeleton Skeleton, MeshObject meshObject, SSBHVertexAccessor vertexAccessor, uint[] vertexIndices) { // Read attribute values. var positions = vertexAccessor.ReadAttribute("Position0", 0, meshObject.VertexCount, meshObject); var normals = vertexAccessor.ReadAttribute("Normal0", 0, meshObject.VertexCount, meshObject); var tangents = vertexAccessor.ReadAttribute("Tangent0", 0, meshObject.VertexCount, meshObject); var map1Values = vertexAccessor.ReadAttribute("map1", 0, meshObject.VertexCount, meshObject); var uvSetValues = vertexAccessor.ReadAttribute("uvSet", 0, meshObject.VertexCount, meshObject); var uvSet1Values = vertexAccessor.ReadAttribute("uvSet1", 0, meshObject.VertexCount, meshObject); var bake1Values = vertexAccessor.ReadAttribute("bake1", 0, meshObject.VertexCount, meshObject); var colorSet1Values = vertexAccessor.ReadAttribute("colorSet1", 0, meshObject.VertexCount, meshObject); var colorSet5Values = vertexAccessor.ReadAttribute("colorSet5", 0, meshObject.VertexCount, meshObject); var generatedBitangents = GenerateBitangents(vertexIndices, positions, map1Values); var riggingAccessor = new SSBHRiggingAccessor(mesh); var influences = riggingAccessor.ReadRiggingBuffer(meshObject.Name, (int)meshObject.SubMeshIndex); var indexByBoneName = new Dictionary <string, int>(); if (Skeleton != null) { var Bones = Skeleton.Bones; for (int i = 0; i < Bones.Length; i++) { indexByBoneName.Add(Bones[i].Name, i); } } GetRiggingData(positions, influences, indexByBoneName, out IVec4[] boneIndices, out Vector4[] boneWeights);
public IOModel GetIOModel() { IOModel outModel = new IOModel(); MESH meshFile = null; foreach (FileNode n in Parent.Nodes) { if (n.Text.Equals(_model.MeshString)) { meshFile = ((NUMSHB_Node)n).mesh; } if (n.Text.Equals(_model.SkeletonFileName)) { outModel.Skeleton = (RSkeleton)((SKEL_Node)n).GetRenderableNode(); } } Dictionary <string, int> indexByBoneName = new Dictionary <string, int>(); if (outModel.Skeleton != null) { for (int i = 0; i < outModel.Skeleton.Bones.Count; i++) { indexByBoneName.Add(outModel.Skeleton.Bones[i].Name, i); } } if (meshFile != null) { using (SSBHVertexAccessor vertexAccessor = new SSBHVertexAccessor(meshFile)) { SSBHRiggingAccessor riggingAccessor = new SSBHRiggingAccessor(meshFile); foreach (MeshObject obj in meshFile.Objects) { IOMesh outMesh = new IOMesh() { Name = obj.Name, }; outModel.Meshes.Add(outMesh); IOVertex[] vertices = new IOVertex[obj.VertexCount]; for (int i = 0; i < vertices.Length; i++) { vertices[i] = new IOVertex(); } foreach (MeshAttribute attr in obj.Attributes) { SSBHVertexAttribute[] values = vertexAccessor.ReadAttribute(attr.AttributeStrings[0].Name, 0, obj.VertexCount, obj); if (attr.AttributeStrings[0].Name.Equals("Position0")) { outMesh.HasPositions = true; for (int i = 0; i < values.Length; i++) { vertices[i].Position = new OpenTK.Vector3(values[i].X, values[i].Y, values[i].Z); } } if (attr.AttributeStrings[0].Name.Equals("Normal0")) { outMesh.HasNormals = true; for (int i = 0; i < values.Length; i++) { vertices[i].Normal = new OpenTK.Vector3(values[i].X, values[i].Y, values[i].Z); } } if (attr.AttributeStrings[0].Name.Equals("map1")) { outMesh.HasUV0 = true; for (int i = 0; i < values.Length; i++) { vertices[i].UV0 = new OpenTK.Vector2(values[i].X, values[i].Y); } } if (attr.AttributeStrings[0].Name.Equals("uvSet")) { outMesh.HasUV1 = true; for (int i = 0; i < values.Length; i++) { vertices[i].UV1 = new OpenTK.Vector2(values[i].X, values[i].Y); } } if (attr.AttributeStrings[0].Name.Equals("uvSet1")) { outMesh.HasUV2 = true; for (int i = 0; i < values.Length; i++) { vertices[i].UV2 = new OpenTK.Vector2(values[i].X, values[i].Y); } } if (attr.AttributeStrings[0].Name.Equals("uvSet2")) { outMesh.HasUV3 = true; for (int i = 0; i < values.Length; i++) { vertices[i].UV3 = new OpenTK.Vector2(values[i].X, values[i].Y); } } if (attr.AttributeStrings[0].Name.Equals("colorSet1")) { outMesh.HasColor = true; for (int i = 0; i < values.Length; i++) { vertices[i].Color = new OpenTK.Vector4(values[i].X, values[i].Y, values[i].Z, values[i].W); } } } // Fix SingleBinds if (outModel.Skeleton != null && !obj.ParentBoneName.Equals("")) { int parentIndex = outModel.Skeleton.GetBoneIndex(obj.ParentBoneName); if (parentIndex != -1) { for (int i = 0; i < vertices.Length; i++) { vertices[i].Position = OpenTK.Vector3.TransformPosition(vertices[i].Position, outModel.Skeleton.Bones[parentIndex].WorldTransform); vertices[i].Normal = OpenTK.Vector3.TransformNormal(vertices[i].Normal, outModel.Skeleton.Bones[parentIndex].WorldTransform); vertices[i].BoneIndices.X = indexByBoneName[obj.ParentBoneName]; vertices[i].BoneWeights.X = 1; } } } // Apply Rigging SSBHVertexInfluence[] influences = riggingAccessor.ReadRiggingBuffer(obj.Name, (int)obj.SubMeshIndex); foreach (SSBHVertexInfluence influence in influences) { // Some influences refer to bones that don't exist in the skeleton. // _eff bones? if (!indexByBoneName.ContainsKey(influence.BoneName)) { continue; } if (vertices[influence.VertexIndex].BoneWeights.X == 0) { vertices[influence.VertexIndex].BoneIndices.X = indexByBoneName[influence.BoneName]; vertices[influence.VertexIndex].BoneWeights.X = influence.Weight; } else if (vertices[influence.VertexIndex].BoneWeights.Y == 0) { vertices[influence.VertexIndex].BoneIndices.Y = indexByBoneName[influence.BoneName]; vertices[influence.VertexIndex].BoneWeights.Y = influence.Weight; } else if (vertices[influence.VertexIndex].BoneWeights.Z == 0) { vertices[influence.VertexIndex].BoneIndices.Z = indexByBoneName[influence.BoneName]; vertices[influence.VertexIndex].BoneWeights.Z = influence.Weight; } else if (vertices[influence.VertexIndex].BoneWeights.W == 0) { vertices[influence.VertexIndex].BoneIndices.W = indexByBoneName[influence.BoneName]; vertices[influence.VertexIndex].BoneWeights.W = influence.Weight; } } outMesh.Vertices.AddRange(vertices); outMesh.Indices.AddRange(vertexAccessor.ReadIndices(0, obj.IndexCount, obj)); } } } return(outModel); }
private List <CustomVertex> CreateVertices(RSkeleton Skeleton, MeshObject meshObject, SSBHVertexAccessor vertexAccessor, uint[] vertexIndices) { // Read attribute values. var positions = vertexAccessor.ReadAttribute("Position0", 0, meshObject.VertexCount, meshObject); var normals = vertexAccessor.ReadAttribute("Normal0", 0, meshObject.VertexCount, meshObject); var tangents = vertexAccessor.ReadAttribute("Tangent0", 0, meshObject.VertexCount, meshObject); var map1Values = vertexAccessor.ReadAttribute("map1", 0, meshObject.VertexCount, meshObject); var uvSetValues = vertexAccessor.ReadAttribute("uvSet", 0, meshObject.VertexCount, meshObject); var uvSet1Values = vertexAccessor.ReadAttribute("uvSet1", 0, meshObject.VertexCount, meshObject); var bake1Values = vertexAccessor.ReadAttribute("bake1", 0, meshObject.VertexCount, meshObject); var colorSet1Values = vertexAccessor.ReadAttribute("colorSet1", 0, meshObject.VertexCount, meshObject); var colorSet5Values = vertexAccessor.ReadAttribute("colorSet5", 0, meshObject.VertexCount, meshObject); // TODO: Convert vectors. var intIndices = new List <int>(); foreach (var value in vertexIndices) { intIndices.Add((int)value); } SFGraphics.Utils.TriangleListUtils.CalculateTangentsBitangents(GetVectors3d(positions), GetVectors3d(normals), GetVectors2d(map1Values), intIndices, out Vector3[] newTangents, out Vector3[] bitangents);
private static List <UltimateVertex> CreateVertices(MESH mesh, ISBSkeleton Skeleton, MeshObject meshObject, SSBHVertexAccessor vertexAccessor, uint[] vertexIndices) { // Read attribute values. var positions = vertexAccessor.ReadAttribute("Position0", 0, meshObject.VertexCount, meshObject); var normals = vertexAccessor.ReadAttribute("Normal0", 0, meshObject.VertexCount, meshObject); var tangents = vertexAccessor.ReadAttribute("Tangent0", 0, meshObject.VertexCount, meshObject); var map1Values = vertexAccessor.ReadAttribute("map1", 0, meshObject.VertexCount, meshObject); var uvSetValues = vertexAccessor.ReadAttribute("uvSet", 0, meshObject.VertexCount, meshObject); var uvSet1Values = vertexAccessor.ReadAttribute("uvSet1", 0, meshObject.VertexCount, meshObject); var uvSet2Values = vertexAccessor.ReadAttribute("uvSet2", 0, meshObject.VertexCount, meshObject); var bake1Values = vertexAccessor.ReadAttribute("bake1", 0, meshObject.VertexCount, meshObject); var colorSet1Values = vertexAccessor.ReadAttribute("colorSet1", 0, meshObject.VertexCount, meshObject); var colorSet2Values = vertexAccessor.ReadAttribute("colorSet2", 0, meshObject.VertexCount, meshObject); var colorSet21Values = vertexAccessor.ReadAttribute("colorSet21", 0, meshObject.VertexCount, meshObject); var colorSet22Values = vertexAccessor.ReadAttribute("colorSet22", 0, meshObject.VertexCount, meshObject); var colorSet23Values = vertexAccessor.ReadAttribute("colorSet23", 0, meshObject.VertexCount, meshObject); var colorSet3Values = vertexAccessor.ReadAttribute("colorSet3", 0, meshObject.VertexCount, meshObject); var colorSet4Values = vertexAccessor.ReadAttribute("colorSet4", 0, meshObject.VertexCount, meshObject); var colorSet5Values = vertexAccessor.ReadAttribute("colorSet5", 0, meshObject.VertexCount, meshObject); var colorSet6Values = vertexAccessor.ReadAttribute("colorSet6", 0, meshObject.VertexCount, meshObject); var colorSet7Values = vertexAccessor.ReadAttribute("colorSet7", 0, meshObject.VertexCount, meshObject); // Generate bitangents. // TODO: Use vec4 tangents instead. var positionVectors = GetVectors3d(positions); var normalVectors = GetVectors3d(normals); var map1Vectors = GetVectors2d(map1Values); SFGraphics.Utils.TriangleListUtils.CalculateTangentsBitangents(positionVectors, normalVectors, map1Vectors, (int[])(object)vertexIndices, out Vector3[] tangentVectors, out Vector3[] bitangentVectors);
public IOModel GetIOModel() { IOModel outModel = new IOModel(); MESH meshFile = null; MATL materialFile = null; foreach (FileNode n in Parent.Nodes) { if (n.Text.Equals(_model.MeshString)) { meshFile = ((NUMSHB_Node)n).mesh; } if (n.Text.Equals(_model.SkeletonFileName)) { outModel.Skeleton = (RSkeleton)((SKEL_Node)n).GetRenderableNode(); } if (n.Text.Equals(_model.MaterialFileNames[0].MaterialFileName)) { materialFile = ((MATL_Node)n).Material; } } Dictionary <string, int> indexByBoneName = new Dictionary <string, int>(); if (outModel.Skeleton != null) { for (int i = 0; i < outModel.Skeleton.Bones.Count; i++) { indexByBoneName.Add(outModel.Skeleton.Bones[i].Name, i); } } Dictionary <string, int> materialNameToIndex = new Dictionary <string, int>(); if (materialFile != null) { int materialIndex = 0; foreach (var entry in materialFile.Entries) { materialNameToIndex.Add(entry.MaterialLabel, materialIndex++); IOMaterial material = new IOMaterial(); material.Name = entry.MaterialLabel; outModel.Materials.Add(material); foreach (var attr in entry.Attributes) { if (attr.ParamID == MatlEnums.ParamId.Texture0) { IOTexture dif = new IOTexture(); dif.Name = attr.DataObject.ToString(); material.DiffuseTexture = dif; } } } } if (meshFile != null) { SSBHVertexAccessor vertexAccessor = new SSBHVertexAccessor(meshFile); { SSBHRiggingAccessor riggingAccessor = new SSBHRiggingAccessor(meshFile); foreach (MeshObject obj in meshFile.Objects) { IOMesh outMesh = new IOMesh() { Name = obj.Name, }; outModel.Meshes.Add(outMesh); // get material if (materialFile != null) { foreach (var entry in _model.ModelEntries) { if (entry.MeshName.Equals(obj.Name) && entry.SubIndex == obj.SubMeshIndex) { outMesh.MaterialIndex = materialNameToIndex[entry.MaterialName]; break; } } } IOVertex[] vertices = new IOVertex[obj.VertexCount]; for (int i = 0; i < vertices.Length; i++) { vertices[i] = new IOVertex(); } foreach (MeshAttribute attr in obj.Attributes) { SSBHVertexAttribute[] values = vertexAccessor.ReadAttribute(attr.AttributeStrings[0].Name, 0, obj.VertexCount, obj); if (attr.AttributeStrings[0].Name.Equals("Position0")) { outMesh.HasPositions = true; for (int i = 0; i < values.Length; i++) { vertices[i].Position = new OpenTK.Vector3(values[i].X, values[i].Y, values[i].Z); } } if (attr.AttributeStrings[0].Name.Equals("Normal0")) { outMesh.HasNormals = true; for (int i = 0; i < values.Length; i++) { vertices[i].Normal = new OpenTK.Vector3(values[i].X, values[i].Y, values[i].Z); } } // Flip UVs vertically for export. if (attr.AttributeStrings[0].Name.Equals("map1")) { outMesh.HasUV0 = true; for (int i = 0; i < values.Length; i++) { vertices[i].UV0 = new OpenTK.Vector2(values[i].X, 1 - values[i].Y); } } if (attr.AttributeStrings[0].Name.Equals("uvSet")) { outMesh.HasUV1 = true; for (int i = 0; i < values.Length; i++) { vertices[i].UV1 = new OpenTK.Vector2(values[i].X, 1 - values[i].Y); } } if (attr.AttributeStrings[0].Name.Equals("uvSet1")) { outMesh.HasUV2 = true; for (int i = 0; i < values.Length; i++) { vertices[i].UV2 = new OpenTK.Vector2(values[i].X, 1 - values[i].Y); } } if (attr.AttributeStrings[0].Name.Equals("uvSet2")) { outMesh.HasUV3 = true; for (int i = 0; i < values.Length; i++) { vertices[i].UV3 = new OpenTK.Vector2(values[i].X, 1 - values[i].Y); } } if (attr.AttributeStrings[0].Name.Equals("colorSet1")) { outMesh.HasColor = true; for (int i = 0; i < values.Length; i++) { vertices[i].Color = new OpenTK.Vector4(values[i].X, values[i].Y, values[i].Z, values[i].W) / 127f; } } } // Fix SingleBinds if (outModel.Skeleton != null && !obj.ParentBoneName.Equals("")) { int parentIndex = outModel.Skeleton.GetBoneIndex(obj.ParentBoneName); if (parentIndex != -1) { for (int i = 0; i < vertices.Length; i++) { vertices[i].Position = OpenTK.Vector3.TransformPosition(vertices[i].Position, outModel.Skeleton.Bones[parentIndex].WorldTransform); vertices[i].Normal = OpenTK.Vector3.TransformNormal(vertices[i].Normal, outModel.Skeleton.Bones[parentIndex].WorldTransform); vertices[i].BoneIndices.X = indexByBoneName[obj.ParentBoneName]; vertices[i].BoneWeights.X = 1; outMesh.HasBoneWeights = true; } } } // Apply Rigging SSBHVertexInfluence[] influences = riggingAccessor.ReadRiggingBuffer(obj.Name, (int)obj.SubMeshIndex); foreach (SSBHVertexInfluence influence in influences) { outMesh.HasBoneWeights = true; // Some influences refer to bones that don't exist in the skeleton. // _eff bones? if (!indexByBoneName.ContainsKey(influence.BoneName)) { continue; } if (vertices[influence.VertexIndex].BoneWeights.X == 0) { vertices[influence.VertexIndex].BoneIndices.X = indexByBoneName[influence.BoneName]; vertices[influence.VertexIndex].BoneWeights.X = influence.Weight; } else if (vertices[influence.VertexIndex].BoneWeights.Y == 0) { vertices[influence.VertexIndex].BoneIndices.Y = indexByBoneName[influence.BoneName]; vertices[influence.VertexIndex].BoneWeights.Y = influence.Weight; } else if (vertices[influence.VertexIndex].BoneWeights.Z == 0) { vertices[influence.VertexIndex].BoneIndices.Z = indexByBoneName[influence.BoneName]; vertices[influence.VertexIndex].BoneWeights.Z = influence.Weight; } else if (vertices[influence.VertexIndex].BoneWeights.W == 0) { vertices[influence.VertexIndex].BoneIndices.W = indexByBoneName[influence.BoneName]; vertices[influence.VertexIndex].BoneWeights.W = influence.Weight; } } outMesh.Vertices.AddRange(vertices); outMesh.Indices.AddRange(vertexAccessor.ReadIndices(0, obj.IndexCount, obj)); } } } return(outModel); }