private void OptimizeData( Vector3[] vertices, Vector3[] normals, Vector2[] texCoord, Color[] colors, short[] matIds, ref byte[][] boneIndices, ref float[][] boneWeights) { // Hack for meshes without skin if (boneIndices.Length == 0) boneIndices = null; if (boneWeights.Length == 0) boneWeights = null; // These lists will be used to store the new vertex data List<Vector3> newPositions = new List<Vector3>(); List<Vector3> newNormals = new List<Vector3>(); List<Vector2> newTexCoords = new List<Vector2>(); List<Color> newColors = new List<Color>(); List<byte[]> newBoneIndices = new List<byte[]>(); List<float[]> newBoneWeights = new List<float[]>(); // This list will be used to compare between vertices to check their equality List<ProceduralVertex> uniqueVertList = new List<ProceduralVertex>(); // This list will store the new optimized triangles generated from the new vertex data List<RWTriangle> newTriangles = new List<RWTriangle>(); int triIdx = -1; Console.WriteLine("Optimizing mesh with {0} vertices", vertices.Length); for (int vtxIndex = 0; vtxIndex < vertices.Length; vtxIndex += 3) { // Increment the triangle index each iteration for the material id list triIdx++; // Create a new empty triangle RWTriangle tri = new RWTriangle { MatID = matIds[triIdx] }; // Loop 3 times, covering A, B and C of the triangle for (int triVtxOffset = 0; triVtxOffset < 3; triVtxOffset++) { ProceduralVertex curVertex = new ProceduralVertex(); curVertex.position = vertices[vtxIndex + triVtxOffset]; curVertex.normal = normals[vtxIndex + triVtxOffset]; if (texCoord != null) curVertex.texCoord = texCoord[vtxIndex + triVtxOffset]; if (colors != null) curVertex.color = colors[vtxIndex + triVtxOffset]; if (boneIndices != null) { curVertex.boneIndices = boneIndices[vtxIndex + triVtxOffset].ToArray(); curVertex.boneWeights = boneWeights[vtxIndex + triVtxOffset].ToArray(); } bool isMatch = false; int uniqueVtxIndex = -1; foreach (ProceduralVertex uniqueVertex in uniqueVertList) { uniqueVtxIndex++; if (uniqueVertex.position == curVertex.position) { isMatch = true; } else { isMatch = false; // in case everything breaks, remove this line continue; } if (uniqueVertex.normal == curVertex.normal) { isMatch = true; } else { isMatch = false; continue; } if (texCoord != null) { if (uniqueVertex.texCoord == curVertex.texCoord) { isMatch = true; } else { isMatch = false; continue; } } if (colors != null) { if (uniqueVertex.color == curVertex.color) { isMatch = true; } else { isMatch = false; continue; } } if (boneIndices != null) { for (int i = 0; i < 4; i++) { if (uniqueVertex.boneIndices[i] != curVertex.boneIndices[i] || uniqueVertex.boneWeights[i] != curVertex.boneWeights[i]) { isMatch = false; break; } } } if (isMatch) break; } // We have a match // The new triangle vertex index is the index of the matched vertex if (isMatch) { tri[triVtxOffset] = (ushort)uniqueVtxIndex; } else { // We did not find a match in the unique vertex list // So we add the current vertex to the list uniqueVertList.Add(curVertex); // Add the other attributes to their lists newPositions.Add(curVertex.position); newNormals.Add(curVertex.normal); if (texCoord != null) newTexCoords.Add(curVertex.texCoord); if (colors != null) newColors.Add(curVertex.color); if (boneIndices != null) { newBoneIndices.Add(curVertex.boneIndices); newBoneWeights.Add(curVertex.boneWeights); } // And set the triangle index to the last checked vertex + 1 tri[triVtxOffset] = (ushort)(uniqueVtxIndex + 1); } } newTriangles.Add(tri); if (vtxIndex % 256 == 0) Console.WriteLine("{0} out of {1} vertices optimized", vtxIndex, vertices.Length); } Console.WriteLine("{0} out of {1} vertices optimized", vertices.Length, vertices.Length); Console.WriteLine("Done! Optimized vertex count = {0}, {1}% less vertices", uniqueVertList.Count, ((float)(vertices.Length - uniqueVertList.Count) / vertices.Length) * 100); if (texCoord != null) { _texCoordSets = new Vector2[1][]; _texCoordSets[0] = newTexCoords.ToArray(); } if (colors != null) _clrArray = newColors.ToArray(); if (boneIndices != null) { boneIndices = newBoneIndices.ToArray(); boneWeights = newBoneWeights.ToArray(); } _triArray = newTriangles.ToArray(); _posArray = newPositions.ToArray(); _nrmArray = newNormals.ToArray(); }
internal RWMeshStruct(RWNodeFactory.RWNodeInfo header, BinaryReader reader) : base(header) { _geoFlags = (RWGeometryFlags)reader.ReadUInt16(); byte numTexCoord = reader.ReadByte(); _nativeFlag = (RWGeometryNativeFlag)reader.ReadByte(); int numTris = reader.ReadInt32(); int numVerts = reader.ReadInt32(); int numMorphTargets = reader.ReadInt32(); if (numMorphTargets != SUPPORTED_MORPH_COUNT) { throw new NotImplementedException("More than 1 morph target are not implemented"); } if (_geoFlags.HasFlagUnchecked(RWGeometryFlags.HasColors)) { _clrArray = reader.ReadColorArray(numVerts); } if (_geoFlags.HasFlagUnchecked(RWGeometryFlags.HasTexCoord1) || (_geoFlags.HasFlagUnchecked(RWGeometryFlags.HasTexCoord2))) { _texCoordSets = new Vector2[numTexCoord][]; for (int i = 0; i < numTexCoord; i++) { _texCoordSets[i] = reader.ReadVector2Array(numVerts); } } _triArray = new RWTriangle[numTris]; for (int i = 0; i < numTris; i++) { _triArray[i] = new RWTriangle(reader); } _bSphere = new RWBoundingSphere(reader); if (_geoFlags.HasFlagUnchecked(RWGeometryFlags.HasVertices)) { _posArray = reader.ReadVector3Array(numVerts); } if (_geoFlags.HasFlagUnchecked(RWGeometryFlags.HasNormals)) { _nrmArray = reader.ReadVector3Array(numVerts); } }