protected unsafe Mesh ReadSubMesh(GensReader reader, float scale = 1) { uint offset; // Generic uint reused for different data // Offsets uint materialNameOffset, faceCount, faceOffset, vertexCount, vertexSize, vertexOffset, vertexFormatOffset, boneCount, boneOffset, textureUnitCount, textureUnitOffsetsOffset; if (Header.RootNodeType == PS3RootType) { uint unknownOffset1 = reader.ReadUInt32(); // VertexFormat? materialNameOffset = reader.ReadUInt32(); boneCount = reader.ReadUInt32(); boneOffset = reader.ReadUInt32(); // ? textureUnitCount = reader.ReadUInt32(); textureUnitOffsetsOffset = reader.ReadUInt32(); // Possibly vertex format? reader.JumpTo(unknownOffset1, false); int unknown1 = reader.ReadInt32(); int unknown2 = reader.ReadInt32(); int unknown3 = reader.ReadInt32(); int unknown4 = reader.ReadInt32(); uint unknownOffset2 = reader.ReadUInt32(); uint unknownCount1 = reader.ReadUInt32(); vertexOffset = reader.ReadUInt32(); // TODO throw new NotImplementedException( "ERROR: Cannot yet read PS3 terrain-models"); } else { materialNameOffset = reader.ReadUInt32(); faceCount = reader.ReadUInt32(); faceOffset = reader.ReadUInt32(); vertexCount = reader.ReadUInt32(); vertexSize = reader.ReadUInt32(); vertexOffset = reader.ReadUInt32(); vertexFormatOffset = reader.ReadUInt32(); boneCount = reader.ReadUInt32(); boneOffset = reader.ReadUInt32(); textureUnitCount = reader.ReadUInt32(); textureUnitOffsetsOffset = reader.ReadUInt32(); } // Faces var faces = new ushort[faceCount]; reader.JumpTo(faceOffset, false); // Convert faces from triangle strips // Basically taken from LibGens because I honestly // don't think there's another way to do it lol int newStrip = 3, newIndex = 0; ushort face1 = 0, face2 = 0, face3 = 0, t; uint count = 0, faceIndex = 0; // HACK: We go through the whole loop twice, the first time just // to get the new faceCount, second time to actually get the faces. // This way we don't have to do any copying (yes, lists do it too) // which is a huge performance improvement. for (uint i = 0; i < faceCount; ++i) { t = reader.ReadUInt16(); faces[i] = t; if (t == 0xFFFF) { newStrip = 3; newIndex = 0; } else { newStrip -= 1; face3 = face2; face2 = face1; face1 = t; if (newStrip == 0) { if ((face1 != face2) && (face2 != face3) && (face1 != face3)) { count += 3; } newStrip = 1; ++newIndex; } } } // Alright, we've got the new count! // Time to actually get the faces. var newFaces = new uint[count]; newStrip = 3; newIndex = 0; face1 = face2 = face3 = 0; for (uint i = 0; i < faceCount; ++i) { t = faces[i]; if (t == 0xFFFF) { newStrip = 3; newIndex = 0; } else { newStrip -= 1; face3 = face2; face2 = face1; face1 = t; if (newStrip == 0) { if ((face1 != face2) && (face2 != face3) && (face1 != face3)) { if ((newIndex % 2) == 0) { newFaces[faceIndex] = face1; newFaces[++faceIndex] = face2; newFaces[++faceIndex] = face3; } else { newFaces[faceIndex] = face3; newFaces[++faceIndex] = face2; newFaces[++faceIndex] = face1; } ++faceIndex; } newStrip = 1; ++newIndex; } } } // Vertex Format var vertexFormat = new List <VertexFormatElement>(); var fs = reader.BaseStream; reader.JumpTo(vertexFormatOffset, false); for (byte i = 0; i < 0xFF; ++i) { offset = reader.ReadUInt32(); if (offset > 1000) { break; } var element = new VertexFormatElement() { Offset = offset, Type = reader.ReadUInt32(), ID = reader.ReadUInt16(), Index = reader.ReadByte() }; vertexFormat.Add(element); fs.ReadByte(); } // Vertices var data = new float[vertexCount * Mesh.StructureLength]; var idc = new float[vertexCount * 4]; // TODO: Care // HACK: Read this stuff using pointers to avoid boundry checks. fixed(float *dataStart = data) fixed(float *idcp = idc) { float *dp; // Pointer to data uint structStart; for (uint i = 0; i < vertexCount; ++i) { // Set default vertex coloring structStart = (i * Mesh.StructureLength); dp = dataStart + structStart + Mesh.ColorPos; // RGBA = 1, 1, 1, 1 *dp = 1; *++dp = 1; *++dp = 1; *++dp = 1; // Just to be accurate offset = (vertexOffset + (i * vertexSize)); foreach (var element in vertexFormat) { reader.JumpTo(offset + element.Offset, false); switch (element.ID) { // Vertex Positions case 0: element.Read(reader, dataStart + structStart + Mesh.VertPos, scale); break; // Bone Weights case 1: // TODO element.Read(reader, idcp); break; // Bone Indices case 2: // TODO element.Read(reader, idcp); break; // Normals case 3: // TODO: Do I need to scale normals? element.Read(reader, dataStart + structStart + Mesh.NormPos); break; // UV Coordinates case 5: if (element.Index < 1) { element.Read(reader, dataStart + structStart + Mesh.UVPos); } else { // TODO: Read multi-UV Channels element.Read(reader, idcp); } break; // Tangents case 6: // TODO element.Read(reader, idcp); break; // Binormals case 7: // TODO element.Read(reader, idcp); break; // Vertex Colors case 10: element.Read(reader, dataStart + structStart + Mesh.ColorPos); break; } } ++dp; } } // Bones reader.JumpTo(boneOffset, false); var bones = reader.ReadBytes((int)boneCount); // Texture Unit Offsets var textureUnitOffsets = new uint[textureUnitCount]; reader.JumpTo(textureUnitOffsetsOffset, false); for (uint i = 0; i < textureUnitCount; ++i) { textureUnitOffsets[i] = reader.ReadUInt32(); } // Texture Units for (uint i = 0; i < textureUnitCount; ++i) { // TODO: Actually use this data reader.JumpTo(textureUnitOffsets[i], false); uint textureUnitNameOffset = reader.ReadUInt32(); uint textureUnitID = reader.ReadUInt32(); // Texture Unit Name reader.JumpTo(textureUnitNameOffset, false); string textureUnitName = reader.ReadNullTerminatedString(); } // Material Name reader.JumpTo(materialNameOffset, false); string materialName = reader.ReadNullTerminatedString(); // Generate a HedgeLib mesh and add it to the array var mesh = new Mesh() { VertexData = data, Triangles = newFaces, MaterialName = materialName }; Meshes.Add(mesh); return(mesh); }