/// <summary> /// Inserts a new mesh and returns its index in MeshPointers. /// </summary> public static int InsertMesh(TR2Level level, TRMesh newMesh) { //get the final mesh we currently have TRMesh lastMesh = level.Meshes[level.Meshes.Length - 1]; //new mesh pointer will be the current final mesh's pointer plus its length newMesh.Pointer = lastMesh.Pointer + (uint)lastMesh.Serialize().Length; List <TRMesh> meshes = level.Meshes.ToList(); meshes.Add(newMesh); level.Meshes = meshes.ToArray(); List <uint> pointers = level.MeshPointers.ToList(); pointers.Add(newMesh.Pointer); level.MeshPointers = pointers.ToArray(); level.NumMeshPointers++; //NumMeshData needs the additional mesh size added level.NumMeshData += (uint)newMesh.Serialize().Length / 2; //the pointer index will be the final index in the array return(level.MeshPointers.Length - 1); }
public override void Import() { // Copy the MeshTreeNodes and Meshes into the level, making a note of the first // inserted index for each - this is used to update the Model to point to the // correct starting positions. for (int i = 0; i < Definition.MeshTrees.Length; i++) { TRMeshTreeNode tree = Definition.MeshTrees[i]; int insertedIndex = TR2LevelUtilities.InsertMeshTreeNode(Level, tree); if (i == 0) { Definition.Model.MeshTree = 4 * (uint)insertedIndex; } } for (int i = 0; i < Definition.Meshes.Length; i++) { TRMesh mesh = Definition.Meshes[i]; int insertedIndex = TR2LevelUtilities.InsertMesh(Level, mesh); if (i == 0) { Definition.Model.StartingMesh = (ushort)insertedIndex; } } }
private void AdjustOutfit(TR2CombinedLevel level, TR2Entities lara) { if (level.Is(LevelNames.HOME) && lara != TR2Entities.LaraHome) { // This ensures that Lara's hips match the new outfit for the starting animation and shower cutscene, // otherwise the dressing gown hips are rendered, but the mesh is completely different for this, plus // its textures will have been removed. TRMesh laraMiscMesh = TR2LevelUtilities.GetModelFirstMesh(level.Data, TR2Entities.LaraMiscAnim_H); TRMesh laraHipsMesh = TR2LevelUtilities.GetModelFirstMesh(level.Data, TR2Entities.Lara); TR2LevelUtilities.DuplicateMesh(level.Data, laraMiscMesh, laraHipsMesh); } }
/// <summary> /// Duplicates the data from one mesh to another and ensures that the contents /// of MeshPointers remains consistent with respect to the mesh lengths. /// </summary> public static void DuplicateMesh(TR2Level level, TRMesh originalMesh, TRMesh replacementMesh) { int oldLength = originalMesh.Serialize().Length; originalMesh.Centre = replacementMesh.Centre; originalMesh.CollRadius = replacementMesh.CollRadius; originalMesh.ColouredRectangles = replacementMesh.ColouredRectangles; originalMesh.ColouredTriangles = replacementMesh.ColouredTriangles; originalMesh.Lights = replacementMesh.Lights; originalMesh.Normals = replacementMesh.Normals; originalMesh.NumColouredRectangles = replacementMesh.NumColouredRectangles; originalMesh.NumColouredTriangles = replacementMesh.NumColouredTriangles; originalMesh.NumNormals = replacementMesh.NumNormals; originalMesh.NumTexturedRectangles = replacementMesh.NumTexturedRectangles; originalMesh.NumTexturedTriangles = replacementMesh.NumTexturedTriangles; originalMesh.NumVertices = replacementMesh.NumVertices; originalMesh.TexturedRectangles = replacementMesh.TexturedRectangles; originalMesh.TexturedTriangles = replacementMesh.TexturedTriangles; originalMesh.Vertices = replacementMesh.Vertices; // The length will have changed so all pointers above the original one will need adjusting int lengthDiff = originalMesh.Serialize().Length - oldLength; List <uint> pointers = level.MeshPointers.ToList(); int pointerIndex = pointers.IndexOf(originalMesh.Pointer); for (int i = pointerIndex + 1; i < pointers.Count; i++) { if (pointers[i] > 0) { int newPointer = (int)pointers[i] + lengthDiff; pointers[i] = (uint)newPointer; } } level.MeshPointers = pointers.ToArray(); int numMeshData = (int)level.NumMeshData + lengthDiff / 2; level.NumMeshData = (uint)numMeshData; }
private TRMesh[] ConstructMeshData(uint DataCount, uint NumPointers, ushort[] MeshData) { //Track where we are in mesh data int MeshDataOffset = 0; //Temp storage for forming an int from two ushorts ushort LowBytes; ushort HighBytes; //We know the amount of meshes via NumPointers and we know amount of mesh data via DataCount //The mesh data as uint16s/words are stored in MeshData //We need to pack that data into the objects TRMesh[] meshes = new TRMesh[NumPointers]; for (int i = 0; i < NumPointers; i++) { TRMesh mesh = new TRMesh(); //Centre TRVertex centre = new TRVertex(); centre.X = UnsafeConversions.UShortToShort(MeshData[MeshDataOffset]); MeshDataOffset++; centre.Y = UnsafeConversions.UShortToShort(MeshData[MeshDataOffset]); MeshDataOffset++; centre.Z = UnsafeConversions.UShortToShort(MeshData[MeshDataOffset]); MeshDataOffset++; mesh.Centre = centre; //Coll Radius LowBytes = MeshData[MeshDataOffset]; MeshDataOffset++; HighBytes = MeshData[MeshDataOffset]; MeshDataOffset++; mesh.CollRadius = LowBytes | HighBytes; //Vertices mesh.NumVetices = UnsafeConversions.UShortToShort(MeshData[MeshDataOffset]); MeshDataOffset++; mesh.Vertices = new TRVertex[mesh.NumVetices]; for (int j = 0; j < mesh.NumVetices; j++) { TRVertex v = new TRVertex(); v.X = UnsafeConversions.UShortToShort(MeshData[MeshDataOffset]); MeshDataOffset++; v.Y = UnsafeConversions.UShortToShort(MeshData[MeshDataOffset]); MeshDataOffset++; v.Z = UnsafeConversions.UShortToShort(MeshData[MeshDataOffset]); MeshDataOffset++; mesh.Vertices[j] = v; } //Lights or Normals mesh.NumNormals = UnsafeConversions.UShortToShort(MeshData[MeshDataOffset]); MeshDataOffset++; if (mesh.NumNormals > 0) { mesh.Normals = new TRVertex[mesh.NumNormals]; for (int j = 0; j < mesh.NumNormals; j++) { TRVertex v = new TRVertex(); v.X = UnsafeConversions.UShortToShort(MeshData[MeshDataOffset]); MeshDataOffset++; v.Y = UnsafeConversions.UShortToShort(MeshData[MeshDataOffset]); MeshDataOffset++; v.Z = UnsafeConversions.UShortToShort(MeshData[MeshDataOffset]); MeshDataOffset++; mesh.Normals[j] = v; } } else { mesh.Lights = new short[Math.Abs(mesh.NumNormals)]; for (int j = 0; j < mesh.Lights.Count(); j++) { mesh.Lights[j] = UnsafeConversions.UShortToShort(MeshData[MeshDataOffset]); MeshDataOffset++; } } //Textured Rectangles mesh.NumTexturedRectangles = UnsafeConversions.UShortToShort(MeshData[MeshDataOffset]); MeshDataOffset++; mesh.TexturedRectangles = new TRFace4[mesh.NumTexturedRectangles]; for (int j = 0; j < mesh.NumTexturedRectangles; j++) { TRFace4 face = new TRFace4(); face.Vertices = new ushort[4]; face.Vertices[0] = MeshData[MeshDataOffset]; MeshDataOffset++; face.Vertices[1] = MeshData[MeshDataOffset]; MeshDataOffset++; face.Vertices[2] = MeshData[MeshDataOffset]; MeshDataOffset++; face.Vertices[3] = MeshData[MeshDataOffset]; MeshDataOffset++; face.Texture = MeshData[MeshDataOffset]; MeshDataOffset++; mesh.TexturedRectangles[j] = face; } //Textured Triangles mesh.NumTexturedTriangles = UnsafeConversions.UShortToShort(MeshData[MeshDataOffset]); MeshDataOffset++; mesh.TexturedTriangles = new TRFace3[mesh.NumTexturedTriangles]; for (int j = 0; j < mesh.NumTexturedTriangles; j++) { TRFace3 face = new TRFace3(); face.Vertices = new ushort[3]; face.Vertices[0] = MeshData[MeshDataOffset]; MeshDataOffset++; face.Vertices[1] = MeshData[MeshDataOffset]; MeshDataOffset++; face.Vertices[2] = MeshData[MeshDataOffset]; MeshDataOffset++; face.Texture = MeshData[MeshDataOffset]; MeshDataOffset++; mesh.TexturedTriangles[j] = face; } //Coloured Rectangles mesh.NumColouredRectangles = UnsafeConversions.UShortToShort(MeshData[MeshDataOffset]); MeshDataOffset++; mesh.ColouredRectangles = new TRFace4[mesh.NumColouredRectangles]; for (int j = 0; j < mesh.NumColouredRectangles; j++) { TRFace4 face = new TRFace4(); face.Vertices = new ushort[4]; face.Vertices[0] = MeshData[MeshDataOffset]; MeshDataOffset++; face.Vertices[1] = MeshData[MeshDataOffset]; MeshDataOffset++; face.Vertices[2] = MeshData[MeshDataOffset]; MeshDataOffset++; face.Vertices[3] = MeshData[MeshDataOffset]; MeshDataOffset++; face.Texture = MeshData[MeshDataOffset]; MeshDataOffset++; mesh.ColouredRectangles[j] = face; } //Coloured Triangles mesh.NumColouredTriangles = UnsafeConversions.UShortToShort(MeshData[MeshDataOffset]); MeshDataOffset++; mesh.ColouredTriangles = new TRFace3[mesh.NumColouredTriangles]; for (int j = 0; j < mesh.NumColouredTriangles; j++) { TRFace3 face = new TRFace3(); face.Vertices = new ushort[3]; face.Vertices[0] = MeshData[MeshDataOffset]; MeshDataOffset++; face.Vertices[1] = MeshData[MeshDataOffset]; MeshDataOffset++; face.Vertices[2] = MeshData[MeshDataOffset]; MeshDataOffset++; face.Texture = MeshData[MeshDataOffset]; MeshDataOffset++; mesh.ColouredTriangles[j] = face; } meshes[i] = mesh; } //The offset should match the total amount of data. Debug.Assert(MeshDataOffset == DataCount); return(meshes); }
private TRMesh[] ConstructMeshData(uint[] meshPointers, ushort[] rawMeshData) { byte[] target = new byte[rawMeshData.Length * 2]; Buffer.BlockCopy(rawMeshData, 0, target, 0, target.Length); // The mesh pointer list can contain duplicates so we must make // sure to iterate over distinct values only meshPointers = meshPointers.Distinct().ToArray(); List <TRMesh> meshes = new List <TRMesh>(); using (MemoryStream ms = new MemoryStream(target)) using (BinaryReader br = new BinaryReader(ms)) { for (int i = 0; i < meshPointers.Length; i++) { TRMesh mesh = new TRMesh(); meshes.Add(mesh); uint meshPointer = meshPointers[i]; br.BaseStream.Position = meshPointer; //Pointer mesh.Pointer = meshPointer; //Centre mesh.Centre = TR2FileReadUtilities.ReadVertex(br); //CollRadius mesh.CollRadius = br.ReadInt32(); //Vertices mesh.NumVertices = br.ReadInt16(); mesh.Vertices = new TRVertex[mesh.NumVertices]; for (int j = 0; j < mesh.NumVertices; j++) { mesh.Vertices[j] = TR2FileReadUtilities.ReadVertex(br); } //Lights or Normals mesh.NumNormals = br.ReadInt16(); if (mesh.NumNormals > 0) { mesh.Normals = new TRVertex[mesh.NumNormals]; for (int j = 0; j < mesh.NumNormals; j++) { mesh.Normals[j] = TR2FileReadUtilities.ReadVertex(br); } } else { mesh.Lights = new short[Math.Abs(mesh.NumNormals)]; for (int j = 0; j < mesh.Lights.Length; j++) { mesh.Lights[j] = br.ReadInt16(); } } //Textured Rectangles mesh.NumTexturedRectangles = br.ReadInt16(); mesh.TexturedRectangles = new TRFace4[mesh.NumTexturedRectangles]; for (int j = 0; j < mesh.NumTexturedRectangles; j++) { mesh.TexturedRectangles[j] = TR2FileReadUtilities.ReadTRFace4(br); } //Textured Triangles mesh.NumTexturedTriangles = br.ReadInt16(); mesh.TexturedTriangles = new TRFace3[mesh.NumTexturedTriangles]; for (int j = 0; j < mesh.NumTexturedTriangles; j++) { mesh.TexturedTriangles[j] = TR2FileReadUtilities.ReadTRFace3(br); } //Coloured Rectangles mesh.NumColouredRectangles = br.ReadInt16(); mesh.ColouredRectangles = new TRFace4[mesh.NumColouredRectangles]; for (int j = 0; j < mesh.NumColouredRectangles; j++) { mesh.ColouredRectangles[j] = TR2FileReadUtilities.ReadTRFace4(br); } //Coloured Triangles mesh.NumColouredTriangles = br.ReadInt16(); mesh.ColouredTriangles = new TRFace3[mesh.NumColouredTriangles]; for (int j = 0; j < mesh.NumColouredTriangles; j++) { mesh.ColouredTriangles[j] = TR2FileReadUtilities.ReadTRFace3(br); } // There may be alignment padding at the end of the mesh, but rather than // storing it, when the mesh is serialized the alignment should be considered. // It seems to be 4-byte alignment for mesh data. The basestream position is // moved to the next pointer in the next iteration, so we don't need to process // the additional data here. // See https://www.tombraiderforums.com/archive/index.php/t-215247.html } } return(meshes.ToArray()); }