Well that time is finally here. We are going to get the last remaining bit of data (at least right now) into the application. There may be other things we need later, and off the top of my head I can say for sure bounding boxes are needed which we don’t have in there all the way just yet, but this will give you all the geometry that you need in your scene.
Well after a bit of toying I got M2’s working in the new codebase. The only thing I didn’t do yet with them is normal vectors. I don’t feel it’s needed at this time because you can pretty well see M2’s without them. Like with any new object we are going to make a new folder called M2 and put it under the ADT folder. We are then going to create an M2.cs file that’ll be the object to represent our actual M2’s that we are pulling. You’re M2.cs file should look something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
namespace MPQNav.ADT
{
class M2
{
}
} |
Pretty basic stuff there just like most of our others. Make sure the using directives are setup properly and you may have to change the namespace. That’s about it really.
First two variables for this class are going to be for bounding boxes. We don’t fill them in this code segment, but we will another time. We put them there for when we need them.
1
2
| public Vector3 boundingBox1;
public Vector3 boundingBox2; |
Next we are going to have two variables that will hold the vectors for this M2. You may ask why we have two of these and the answer is one is for the pre-transformed vectors and the second is for the transformed vectors. Holding the vectors before transformation in the object is probably a waste of time and space but I haven’t decided 100% if they will be needed for anything else yet. Hence they are there.
1
2
| public List<Vector3> verticiesList = new List<Vector3>();
public List<VertexPositionNormalColored> finalVertexList = new List<VertexPositionNormalColored>(); |
The final variable is a list for our indices (we are again building a triangle list):
1
| public List<int> finalIndiciesList = new List<int>(); |
Tada! Your M2.cs file is complete!
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
namespace MPQNav.ADT
{
class M2
{
public Vector3 boundingBox1;
public Vector3 boundingBox2;
//public List<int> indiciesList = new List<int>();
public List<Vector3> verticiesList = new List<Vector3>();
public List<VertexPositionNormalColored> finalVertexList = new List<VertexPositionNormalColored>();
public List<int> finalIndiciesList = new List<int>();
}
} |
That was easy enough right? Right!
Now it’s time to go back to ADT.cs. M2’s are a lot like WMO’s, we are going to have a list of strings that end with a 0 specifying what M2 is used, as well as a bunch of chunks that tell us the placement for those M2s. Those chunks are called MDDF chunks. So let’s make a class to hold that information for us:
1
2
3
4
5
6
7
8
9
10
11
12
| public class MDDF
{
// Placement information for M2s
public String name;
public String filePath;
public UInt32 uniqid;
public Vector3 position;
public float orientation_a;
public float orientation_b;
public float orientation_c;
public float scale = 1;
} |
Almost identical to the previous placement information for WMO’s with the exception of the scale value. M2’s can be scaled whereas WMO’s are never scaled. Next we add a few variables for the ADT to hold the M2 information for us:
1
2
3
| public List<String> m2FileNameList = new List<string>();
public List<MDDF> MDDFList = new List<MDDF>();
public List<M2> M2List = new List<M2>(); |
These are again pretty basic and just like our WMO’s. We have a list of strings for the filenames, a list of the MODF (placement information) chunks, and a list of actual M2 objects. Nothing too special here and that’s it for our ADT.cs file.
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
namespace MPQNav.ADT
{
class ADT
{
public String FileName; // File name such as Azeroth_32_32.adt
public String mapName; // Map name such as Azeroth
public int xOffset; // First number in the filename.
public int yOffset; // Second number in the filename.
public List<String> wmoFileNameList = new List<string>();
public List<String> m2FileNameList = new List<string>(); // NEW
public List<MODF> MODFList = new List<MODF>();
public List<MDDF> MDDFList = new List<MDDF>(); // NEW
public List<WMO> WMOList = new List<WMO>();
public List<M2> M2List = new List<M2>();// NEW
public MCNK[,] MCNKArray = new MCNK[16, 16];
#region Constructors
public ADT(String fName)
{
String[] fName_split = fName.Split(Convert.ToChar("_"));
this.mapName = fName_split[0];
this.FileName = fName;
this.xOffset = Int32.Parse(fName_split[1]);
String y = fName_split[2].Substring(0, (fName_split[2].Length - 4)).ToString();
this.yOffset = Int32.Parse(y);
}
public ADT(String fName, int xOffset, int yOffset)
{
// We know that the file format is in the style of mapname_xx_yy.adt
String[] fName_split = fName.Split(Convert.ToChar("_"));
this.mapName = fName_split[0];
this.FileName = fName;
this.xOffset = xOffset;
this.yOffset = yOffset;
}
#endregion
public void addMCNK(int xOffset, int yOffset, MCNK MCNK)
{
this.MCNKArray[xOffset, yOffset] = MCNK;
}
public class MODF
{
// Placement information for WMOs
public String name;
public String filePath;
public UInt32 uniqid;
public Vector3 position;
public float orientation_a;
public float orientation_b;
public float orientation_c;
}
// NEW
public class MDDF
{
// Placement information for M2s
public String name;
public String filePath;
public UInt32 uniqid;
public Vector3 position;
public float orientation_a;
public float orientation_b;
public float orientation_c;
public float scale = 1;
}
public class MCNK
{
public float x;
public float y;
public float z;
public MCVT _MCVT = new MCVT();
public MCNR _MCNR = new MCNR();
public MCLQ _MCLQ = new MCLQ();
public MCNK()
{
}
public MCNK(float x, float y, float z)
{
this.x = x;
this.y = y;
this.z = z;
}
public class MCVT
{
public float[] heights = new float[145];
}
public class MCNR
{
// x, y, x format. One byte per integer. Each int is a signed int and between -127 and 127
// with -127 being -1 and 127 being 1. This is for our normal vectors.
public int[] normals = new int[435];
}
public class MCLQ
{
public UInt32 size = 8;
public float altitude = new float();
public float base_magma_height = new float();
public float[] heights = new float[81];
public Boolean[] render = new Boolean[64];
}
}
}
} |
Now with that all taken care of we’re ready to get the data into our classes. For that we go back to our ADTManager.cs file. There’s two lines of code we already have that will be useful to us:
1
2
| UInt32 offsModels = this.br.ReadUInt32(); /// 20 Bytes behind MDDX (filenames for M2s)
UInt32 offsDoodsDef = this.br.ReadUInt32(); // 20 Bytes Behind MDDF (Placement for M2s) |
That’s right! We were smart enough to already get the offsets that we need for the bits of M2 information! If you scroll down a bit in the processADT() method you’ll see these lines:
1
2
| this.buildWMONamesList((int)offsMapObjects, br, currentADT);
this.processWMOs((int)offsObjectsDef, br, currentADT); |
We’re going to do the same thing for m2’s so add:
1
2
| this.buildm2NamesList((int)offsModels, br, currentADT);
this.processM2s((int)offsDoodsDef, br, currentADT); |
Now it’s time to actually write those methods! Let’s start with the buildm2NamesList method:
1
2
3
4
| public void buildm2NamesList(int Offset, BinaryReader br, ADT currentADT)
{
} |
Just like our WMO names method. In fact it’s almost a direct copy/paste of said method so I am not going to walk through the code step by step again. Please review the WMO post if you want details:
public void buildm2NamesList(int Offset, BinaryReader br, ADT currentADT)
{
br.BaseStream.Position = Offset + 24; // 20 To get to where the list starts, 8 to get off the header.
UInt32 chunkSize = br.ReadUInt32();
long EndPosition = br.BaseStream.Position + chunkSize;
String m2Name = "";
byte[] nextByte = new byte[1];
while (br.BaseStream.Position < EndPosition)
{
nextByte = br.ReadBytes(1);
if (nextByte[0] != (byte)0)
{
m2Name += System.Text.ASCIIEncoding.ASCII.GetString(nextByte);
}
else
{
currentADT.m2FileNameList.Add(m2Name.ToString());
m2Name = "";
}
}
} |
Now that we have that code it’s time to write the processM2s method which is very similar to the one we already wrote for WMOs:
1
2
3
| public void processM2s(int offset, BinaryReader br, ADT currentADT)
{
} |
Now just like before we are going to move the position of our BinaryReader a bit, and get the size of the chunk. We’re then going to setup an integer that tracks how many byte’s we’ve read so we know when we’re at the end of the chunk. Remember that this is the MDDF chunks which happen to be the placement information for all the M2’s in the current ADT:
1
2
3
| br.BaseStream.Position = offset + 24; // 20 to compensate and 4 to get off the header
UInt32 chunkSize = br.ReadUInt32();
int bytesRead = 0; |
Next we start a little loop to get the data:
1
2
3
4
| while (bytesRead < chunkSize)
{
} |
Within this loop we’re going to once again setup a temporary object for the chunk, get all the information we want, and then loop back through. MDDF chunks are 36 bytes in size (nothing we haven’t done before):
1
2
3
4
5
6
7
8
9
10
| ADT.MDDF currentMDDF = new ADT.MDDF();
currentMDDF.filePath = currentADT.m2FileNameList[(int)br.ReadUInt32()]; // 4 bytes
currentMDDF.uniqid = br.ReadUInt32(); // 4 bytes
currentMDDF.position = new Vector3((float)br.ReadSingle(), (float)br.ReadSingle(), (float)br.ReadSingle()); // 12 Bytes
currentMDDF.orientation_a = (float)br.ReadSingle(); // 4 Bytes
currentMDDF.orientation_b = (float)br.ReadSingle(); // 4 Bytes
currentMDDF.orientation_c = (float)br.ReadSingle(); // 4 Bytes
currentMDDF.scale = (float)(br.ReadUInt32() / 1024f); // 4 bytes
bytesRead += 36; // 36 total bytes
currentADT.MDDFList.Add(currentMDDF); |
The only thing to note here is that we divide the value for the scale by 1024. For some reason blizzard went with an integer for the scale instead of a floating point number, but dividing it by 1024 gives us the value that works.
That’s it for that loop! We now should have all the MDDF data. Now let’s go ahead and loop through them:
1
2
3
| for (int i = 0; i < currentADT.MDDFList.Count; i++)
{
} |
Next we are going to check to make sure the file exists. We should really throw an error here if it doesn’t, but my laziness is really shining lately and I didn’t bother. There is one interesting thing here though. The files we extract from the MPQ’s have the extension of m2 whereas they are referred to with an extension of mdx so we must fix that when we check for the file:
1
2
3
| if (File.Exists(this.basePath + currentADT.MDDFList[i].filePath.Replace(".mdx", ".m2")))
{
} |
Once we know the file exists, we should probably open it up so we can read it eh?
1
2
3
| String currentFilePath = currentADT.MDDFList[i].filePath;
FileStream fs = File.OpenRead(this.basePath + currentADT.MDDFList[i].filePath.Replace(".mdx",".m2"));
BinaryReader m2Reader = new BinaryReader(fs); |
And why not create a new instance of our M2 class to put all the data we’re going to get in?
1
| M2 currentM2 = new M2(); |
Got that? Good! Now like everything else M2 files have a header. Without boring you about the details of the header, I can tell you 68 bytes in is how far you must go for the data we want. This is all given on http://wowdev.org/wiki/index.php/M2 if you are curious. As such we skip those bytes, and then read in some variables.
1
2
3
4
5
| m2Reader.ReadBytes(68);
UInt32 numberOfVerts = m2Reader.ReadUInt32();
UInt32 vertsOffset = m2Reader.ReadUInt32();
UInt32 numberOfViews = m2Reader.ReadUInt32();
UInt32 viewsOffset = m2Reader.ReadUInt32(); |
- numberOfVerts - Number of vertices in this M2
- vertsOffset - Offset to the vertices information
- numberOfViews - Not really used, thought it might be so I took it anyway.
- viewsOffset - Going to help us get to the indices information.
First thing we are going to get is the vertices. Let’s move the reader to their offset and setup a loop to get them all.
1
2
3
4
5
6
7
8
9
| m2Reader.BaseStream.Position = vertsOffset;
for (int v = 0; v < (int)numberOfVerts; v++)
{
float vert_x = (float)m2Reader.ReadSingle() * -1;
float vert_z = (float)m2Reader.ReadSingle();
float vert_y = (float)m2Reader.ReadSingle();
currentM2.verticiesList.Add(new Vector3(vert_x,vert_y,vert_z));
m2Reader.ReadBytes(36); // Extra Junk for now
} |
Pretty basic stuff. We loop through once for each vertex, and we get the X,Y, and Z for each one (again in the weird format with Z being up so we have to adjust it). We then add it to the list for our M2 (the one that has non-transformed vertices) and then we read the extra 36 bytes of data we don’t need (normals are in there when/if we decide to use them).
After that is all done it’s time to get the indices information. So we move the reader again to that offset, and we get some data:
1
2
3
4
5
| m2Reader.BaseStream.Position = viewsOffset;
UInt32 numElementsIndex = m2Reader.ReadUInt32();
UInt32 offsElementsIndex = m2Reader.ReadUInt32();
UInt32 numElementsTriangle = m2Reader.ReadUInt32();
UInt32 offsTriangleList = m2Reader.ReadUInt32(); |
Now the way this is done is a bit odd. There’s one list of indices that point to the list of vertices. Then the actual indices list points to the first one. The first one is almost like a middle-man.
As you can see it’s not made to make much sense, but it’s there so we simply work around it. Back to the variables:
- numElementsIndex - Number of elements in our Jump indices list
- offsElementsIndex - Offset to our Jump indices list
- numElementsTriangle - Number of elements in our final indices list
- offsTriangleList - Offset to that final list
With that in hand we first get the jump list. We create a local variable to hold this information (we don’t need to store it) and then loop through getting the data:
1
2
3
4
5
| List<int> jumpList = new List<int>();
for(int v = 0; v < (int)numElementsIndex; v++)
{
jumpList.Add((int)m2Reader.ReadUInt16());
} |
After that we go to the real list, and get the data. Remember the diagram when reading this code:
1
2
3
4
5
| m2Reader.BaseStream.Position = offsTriangleList;
for (int v = 0; v < (int)numElementsTriangle; v++)
{
currentM2.finalIndiciesList.Add(jumpList[(int)m2Reader.ReadUInt16()]);
} |
That lets us go through our "jump" just like we planned! Now the final step is to transform the vectors (scale them, put them in real world coordinates, and rotate them). To do this I did what I should have done earlier and created a separate method called transformVector. It looks something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| public Vector3 transformVector(Vector3 baseVector, float x, float y, float z, float orientation_a, float orientation_b, float orientation_c, float scale)
{
// Real work positions for a transform
float pos_x = (x - 17066.666666666656f) * -1;
float pos_y = y;
float pos_z = (z - 17066.666666666656f) * -1;
Vector3 origin = new Vector3(pos_x, pos_y, pos_z);
float my_x = (float)baseVector.X + pos_x;
float my_y = (float)baseVector.Y + pos_y;
float my_z = (float)baseVector.Z + pos_z;
Vector3 baseVertex = new Vector3(my_x, my_y, my_z);
//1 degrees = 0.0174532925 radians
float rad = 0.0174532925f;
// Creation the rotations
float a = orientation_a * -1 * rad;
float b = (orientation_b - 90) * rad;
float c = orientation_c * rad;
// Fancy things to rotate our model
Matrix rotateY = Matrix.CreateRotationY(b);
Matrix rotateZ = Matrix.CreateRotationZ(a);
Matrix rotateX = Matrix.CreateRotationX(c);
Matrix scaleMatrix = Matrix.CreateScale(scale);
Vector3 rotatedVector = Vector3.Transform(baseVertex - origin, rotateY);
Vector3 scaledVector = Vector3.Transform(rotatedVector, scaleMatrix);
Vector3 finalVector = scaledVector + origin;
return finalVector;
} |
I already discussed this in the WMO section (identical code here with the exception of the scale which should be simple enough to read and understand, so I’m not going to do so again. Back to our previous method (processM2s) we were about to use that new method of ours:
1
2
3
4
5
6
7
| for (int v = 0; v < currentM2.verticiesList.Count; v++)
{
Vector3 modifiedVector = transformVector(currentM2.verticiesList[v], currentADT.MDDFList[i].position.X,currentADT.MDDFList[i].position.Y,currentADT.MDDFList[i].position.Z, currentADT.MDDFList[i].orientation_a, currentADT.MDDFList[i].orientation_b, currentADT.MDDFList[i].orientation_c, currentADT.MDDFList[i].scale);
currentM2.finalVertexList.Add(new VertexPositionNormalColored(modifiedVector, Color.Red, Vector3.Up));
}
currentADT.M2List.Add(currentM2); |
We loop through each vector, use our new method to transform it, and then we put it into our final list. Notice I went with red for the color here and just set the normal vector to a basic vector that is pointing straight up. That does it for our processM2s method!
public void processM2s(int offset, BinaryReader br, ADT currentADT)
{
br.BaseStream.Position = offset + 24; // 20 to compensate and 4 to get off the header
UInt32 chunkSize = br.ReadUInt32();
int bytesRead = 0;
while (bytesRead < chunkSize)
{
ADT.MDDF currentMDDF = new ADT.MDDF();
currentMDDF.filePath = currentADT.m2FileNameList[(int)br.ReadUInt32()]; // 4 bytes
currentMDDF.uniqid = br.ReadUInt32(); // 4 bytes
currentMDDF.position = new Vector3((float)br.ReadSingle(), (float)br.ReadSingle(), (float)br.ReadSingle()); // 12 Bytes
currentMDDF.orientation_a = (float)br.ReadSingle(); // 4 Bytes
currentMDDF.orientation_b = (float)br.ReadSingle(); // 4 Bytes
currentMDDF.orientation_c = (float)br.ReadSingle(); // 4 Bytes
currentMDDF.scale = (float)(br.ReadUInt32() / 1024f); // 4 bytes
bytesRead += 36; // 36 total bytes
currentADT.MDDFList.Add(currentMDDF);
}
for (int i = 0; i < currentADT.MDDFList.Count; i++)
{
if (File.Exists(this.basePath + currentADT.MDDFList[i].filePath.Replace(".mdx", ".m2")))
{
String currentFilePath = currentADT.MDDFList[i].filePath;
FileStream fs = File.OpenRead(this.basePath + currentADT.MDDFList[i].filePath.Replace(".mdx",".m2"));
BinaryReader m2Reader = new BinaryReader(fs);
M2 currentM2 = new M2();
m2Reader.ReadBytes(68);
UInt32 numberOfVerts = m2Reader.ReadUInt32();
UInt32 vertsOffset = m2Reader.ReadUInt32();
UInt32 numberOfViews = m2Reader.ReadUInt32();
UInt32 viewsOffset = m2Reader.ReadUInt32();
// Time to get the verticies
m2Reader.BaseStream.Position = vertsOffset;
for (int v = 0; v < (int)numberOfVerts; v++)
{
float vert_x = (float)m2Reader.ReadSingle() * -1;
float vert_z = (float)m2Reader.ReadSingle();
float vert_y = (float)m2Reader.ReadSingle();
currentM2.verticiesList.Add(new Vector3(vert_x,vert_y,vert_z));
m2Reader.ReadBytes(36); // Extra Junk for now
}
// Now it's time to get the indicies
m2Reader.BaseStream.Position = viewsOffset;
UInt32 numElementsIndex = m2Reader.ReadUInt32();
UInt32 offsElementsIndex = m2Reader.ReadUInt32();
UInt32 numElementsTriangle = m2Reader.ReadUInt32();
UInt32 offsTriangleList = m2Reader.ReadUInt32();
m2Reader.BaseStream.Position = offsElementsIndex;
List<int> jumpList = new List<int>();
for(int v = 0; v < (int)numElementsIndex; v++)
{
jumpList.Add((int)m2Reader.ReadUInt16());
}
m2Reader.BaseStream.Position = offsTriangleList;
for (int v = 0; v < (int)numElementsTriangle; v++)
{
currentM2.finalIndiciesList.Add(jumpList[(int)m2Reader.ReadUInt16()]);
}
//currentM2.finalIndiciesList = currentM2.indiciesList;
for (int v = 0; v < currentM2.verticiesList.Count; v++)
{
Vector3 modifiedVector = transformVector(currentM2.verticiesList[v], currentADT.MDDFList[i].position.X,currentADT.MDDFList[i].position.Y,currentADT.MDDFList[i].position.Z, currentADT.MDDFList[i].orientation_a, currentADT.MDDFList[i].orientation_b, currentADT.MDDFList[i].orientation_c, currentADT.MDDFList[i].scale);
currentM2.finalVertexList.Add(new VertexPositionNormalColored(modifiedVector, Color.Red, Vector3.Up));
}
currentADT.M2List.Add(currentM2);
}
}
} |
Now there’s only two other methods we need in our manager. Identical to WMO’s we need a method to get the indices for all the M2’s used by an ADT as well as the vertices. Once again review the WMO post for more information on how these two methods work:
public List<int[]> getM2Indicies(int ADTIndex)
{
ADT currentADT = this.ADTList[ADTIndex];
List<int[]> returnList = new List<int[]>();
for (int i = 0; i < currentADT.M2List.Count; i++)
{
returnList.Add(currentADT.M2List[i].finalIndiciesList.ToArray());
}
return returnList;
} |
public List<VertexPositionNormalColored[]> getM2Verticies(int ADTIndex)
{
ADT currentADT = this.ADTList[ADTIndex];
List<VertexPositionNormalColored[]> returnList = new List<VertexPositionNormalColored[]>();
for (int i = 0; i < currentADT.M2List.Count; i++)
{
returnList.Add(currentADT.M2List[i].finalVertexList.ToArray());
}
return returnList;
} |
Because I’m so nice here’s the entire ADTManger.cs file:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
namespace MPQNav.ADT
{
class ADTManager
{
public String basePath = "C:\\temp\\mpq\\"; // *NEW*
public BinaryReader br;
public List<ADT> ADTList = new List<ADT>();
private Boolean loaded = false;
private String filePath;
public List<short[]> indicieList = new List<short[]>();
public List<VertexPositionNormalColored[]> verticieList = new List<VertexPositionNormalColored[]>();
public List<MPQNav.ADT.WMO> WMOList = new List<MPQNav.ADT.WMO>();
public ADTManager(String filePath)
{
if (File.Exists(filePath))
{
FileStream fs = File.OpenRead(filePath);
this.br = new BinaryReader(fs);
this.filePath = filePath;
this.loaded = true;
}
}
public Boolean loadADT(String filePath)
{
if (File.Exists(filePath))
{
FileStream fs = File.OpenRead(filePath);
this.br = new BinaryReader(fs);
this.filePath = filePath;
this.loaded = true;
return true;
}
return false;
}
public int findChunk(BinaryReader br, String chunkName)
{
// We assume we're giving the chunk name reversed.
// That is to say chunkName for what we call MOVI would be given as IVOM
String frstTwoChars = chunkName.Substring(0, 2);
String lastTwoChars = chunkName.Substring(2, 2);
br.BaseStream.Position = 0;
byte[] nextLine; // Next two bytes
byte[] nextLine2; // Bytes after that
Boolean keepSearch = true;
nextLine = br.ReadBytes(2);
try
{
while (keepSearch == true)
{
nextLine2 = br.ReadBytes(2);
if (System.Text.ASCIIEncoding.ASCII.GetString(nextLine) == frstTwoChars && System.Text.ASCIIEncoding.ASCII.GetString(nextLine2, 0, 2) == lastTwoChars)
{
keepSearch = false;
}
if (keepSearch == true)
{
nextLine = nextLine2;
}
}
br.BaseStream.Position = br.BaseStream.Position - 4;
return (int)br.BaseStream.Position;
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
return -1;
}
}
public void buildTriangles(int ADTIndex)
{
if (ADTIndex >= this.ADTList.Count || ADTIndex < 0)
{
// The index is out of bounds, just return
return;
}
ADT currentADT = this.ADTList[ADTIndex];
// Each ADT contains 16 x 16 MCNK chunks
VertexPositionNormalColored[,][] totalVertList = new VertexPositionNormalColored[16, 16][];
for (int x = 0; x < 16; x++)
{
for (int y = 0; y < 16; y++)
{
// First we get the MCNK Chunk
ADT.MCNK currentMCNK = currentADT.MCNKArray[x, y];
// We next get the X, Y, and Z for this chunk.
float vert_x = currentMCNK.x;
float vert_y = currentMCNK.y;
float vert_z = currentMCNK.z;
//List<Vector3> positionList = new List<Vector3>();
List<VertexPositionColor> positionList = new List<VertexPositionColor>();
// Each MCNK has 145 floats for height
float row_spacing = (533.33333f / 16) / 16;
float column_spacing_9 = (533.33333f / 16) / 8;
//float column_spacing_8 = (533.33333f / 16) / 7;
int current_pos = 0;
for (int row = 1; row < 18; row++)
{
if (row % 2 != 0)
{
// Odd row that has 9 heights
for (int i = 0; i < 9; i++)
{
positionList.Add(new VertexPositionColor(new Vector3(vert_x - (column_spacing_9 * i), vert_y + currentMCNK._MCVT.heights[current_pos], vert_z - ((row - 1) * row_spacing)), Color.Green));
current_pos++;
}
}
else
{
// Even row that has 8 heights
for (int i = 0; i < 8; i++)
{
//positionList.Add(new Vector3(vert_x - (column_spacing_8 * i), vert_y - currentMCNK._MCVT.heights[current_pos], vert_z - ((row - 1) * row_spacing)));
current_pos++;
}
}
}
for (int water_row = 0; water_row < 8; water_row++)
{
for (int water_col = 0; water_col < 8; water_col++)
{
if (currentMCNK._MCLQ.render[(water_row * 8) + water_col])
{
// We should render it
if (positionList[(water_row * 9) + water_col].Position.Y < currentMCNK._MCLQ.altitude)
{
VertexPositionColor tempVPC = positionList[(water_row * 9) + water_col];
tempVPC.Color = Color.Blue;
tempVPC.Position.Y = currentMCNK._MCLQ.altitude;
positionList[(water_row * 9) + water_col] = tempVPC;
}
if (positionList[(water_row * 9) + water_col + 1].Position.Y < currentMCNK._MCLQ.altitude)
{
VertexPositionColor tempVPC = positionList[(water_row * 9) + water_col + 1];
tempVPC.Color = Color.Blue;
tempVPC.Position.Y = currentMCNK._MCLQ.altitude;
positionList[(water_row * 9) + water_col + 1] = tempVPC;
}
if (positionList[((water_row + 1) * 9) + water_col].Position.Y < currentMCNK._MCLQ.altitude)
{
VertexPositionColor tempVPC = positionList[((water_row + 1) * 9) + water_col];
tempVPC.Color = Color.Blue;
tempVPC.Position.Y = currentMCNK._MCLQ.altitude;
positionList[((water_row + 1) * 9) + water_col] = tempVPC;
}
if (positionList[((water_row + 1) * 9) + water_col + 1].Position.Y < currentMCNK._MCLQ.altitude)
{
VertexPositionColor tempVPC = positionList[((water_row + 1) * 9) + water_col + 1];
tempVPC.Color = Color.Blue;
tempVPC.Position.Y = currentMCNK._MCLQ.altitude;
positionList[((water_row + 1) * 9) + water_col + 1] = tempVPC;
}
}
}
}
List<Vector3> normalList = new List<Vector3>();
for (int n = 0; n < 435 / 3; n++)
{
float norm_x = ((float)(currentMCNK._MCNR.normals[(n * 3) + 0]) / 127f);
float norm_y = ((float)(currentMCNK._MCNR.normals[(n * 3) + 1]) / 127f);
float norm_z = ((float)(currentMCNK._MCNR.normals[(n * 3) + 2]) / 127f);
normalList.Add(new Vector3(norm_x, norm_y, norm_z));
}
VertexPositionNormalColored[] tempVertexList = new VertexPositionNormalColored[81];
// We have a 9 x 9 and an inner 8 x 8 set of data. We only want the 9 x 9
// That means we only want every other row
for (int i = 0; i < 81; i++)
{
tempVertexList[i] = new VertexPositionNormalColored(positionList[i].Position, positionList[i].Color, normalList[i]);
}
totalVertList[x, y] = tempVertexList;
}
}
/**
* Now it's time to get rid of the verticies that we don't need (overlapping duplicates) as well
* as build the actual indicie list for the traingles. Keep in mind our array only has the outter
* 9 x 9 data so we won't need to worry about the 8 x 8 grid anymore.
*
* Knowing that we have 16 blocks by 16 blocks worth of data, each being 9 points in size we
* can calculate just how big our final group should be. Keeping in mind that we are dropping the left
* and/or top sides.
*
* So the top left block is going to be 9 x 9 where the remaining blocks will be either 8 x 9, 9 x 8, or 8 x 8.
* Let's simplify it a bit by a 4 x 4 block.
*
* 1 2 3 4
* 5 6 7 8
* 9 10 11 12
* 13 14 15 16
*
* Block 1: 9 x 9
* Blocks 2-4: 8 x 9
* Blocks 5,9,13: 9 x 8
* Remaining blocks: 8 x 8
*
* We have 1 block of full size, the width of the setup - 1 blocks of 8x9, the height - 1 of 9x8, the reamining 8x8.
*
* (9x9) + ((width - 1) * (8x9)) + ((height - 1) * (9x8)) + (full_count - width - (height - 1)) * (8x8)
*
* with our 4 x 4 that means
* (9x9) + (3 * (8x9)) + (3 * (9x8)) + (9 * 8x8)
*
* with a 16 x 16 (and putting the counts in for the actual grids):
* 81 + (15 * 72) + (15 * 72) + (225 * 64)
* 81 + 1080 + 1080 + 14400 = 16641 (129 x 129)
*
**/
VertexPositionNormalColored[] finalVertexArray = new VertexPositionNormalColored[16641];
int count = 0;
for (int f_r = 0; f_r < 129; f_r++)
{
for (int f_c = 0; f_c < 129; f_c++)
{
int offset_x = Math.Abs((int)(f_c - 1) / 8);
int offset_y = Math.Abs((int)(f_r - 1) / 8);
int pos_x; // X Position inside of the 9x9
int pos_y; // Y Position inside of the 9x9
if (offset_x == 0)
{
pos_x = f_c;
}
else
{
pos_x = f_c - (((offset_x - 1) * 8) + 9);
pos_x += 1;
}
if (offset_y == 0)
{
pos_y = f_r;
}
else
{
pos_y = f_r - (((offset_y - 1) * 8) + 9);
pos_y += 1;
}
finalVertexArray[count] = totalVertList[offset_x, offset_y][(pos_y * 9) + pos_x];
count++;
}
}
// now it's time to actually build the triangle indicie list
// 128 = the actual width of 129 - 1
// 2 because we have 2 triangles for each square
// 3 because there's 3 verticies for each triangle
short[] triangleListIndices = new short[128 * 128 * 2 * 3];
int count2 = 0;
for (int y = 0; y < 128; y++)
{
for (int x = 0; x < 128; x++)
{
triangleListIndices[count2] = (short)(x + (y * 129));
count2++;
triangleListIndices[count2] = (short)(x + ((y + 1) * 129));
count2++;
triangleListIndices[count2] = (short)((x + 1) + (y * 129));
count2++;
triangleListIndices[count2] = (short)((x + 1) + ((y + 1) * 129));
count2++;
triangleListIndices[count2] = (short)(x + ((y + 1) * 129));
count2++;
triangleListIndices[count2] = (short)((x + 1) + (y * 129));
count2++;
}
}
while (this.verticieList.Count < ADTIndex + 1)
{
this.verticieList.Add(new VertexPositionNormalColored[0]);
}
while (this.indicieList.Count < ADTIndex + 1)
{
this.indicieList.Add(new short[0]);
}
this.verticieList[ADTIndex] = finalVertexArray;
this.indicieList[ADTIndex] = triangleListIndices;
}
public short[] getIndicies(int offset)
{
return this.indicieList[offset];
}
public VertexPositionNormalColored[] getVerticies(int offset)
{
return this.verticieList[offset];
}
public List<int[]> getWMOIndicies(int ADTIndex)
{
ADT currentADT = this.ADTList[ADTIndex];
List<int[]> returnList = new List<int[]>();
for (int i = 0; i < currentADT.WMOList.Count; i++)
{
returnList.Add(currentADT.WMOList[i].IndicieList.ToArray());
}
return returnList;
}
public List<VertexPositionNormalColored[]> getWMOVerticies(int ADTIndex)
{
ADT currentADT = this.ADTList[ADTIndex];
List<VertexPositionNormalColored[]> returnList = new List<VertexPositionNormalColored[]>();
for (int i = 0; i < currentADT.WMOList.Count; i++)
{
returnList.Add(currentADT.WMOList[i].VertexList.ToArray());
}
return returnList;
}
// NEW
public List<int[]> getM2Indicies(int ADTIndex)
{
ADT currentADT = this.ADTList[ADTIndex];
List<int[]> returnList = new List<int[]>();
for (int i = 0; i < currentADT.M2List.Count; i++)
{
returnList.Add(currentADT.M2List[i].finalIndiciesList.ToArray());
}
return returnList;
}
// NEW
public List<VertexPositionNormalColored[]> getM2Verticies(int ADTIndex)
{
ADT currentADT = this.ADTList[ADTIndex];
List<VertexPositionNormalColored[]> returnList = new List<VertexPositionNormalColored[]>();
for (int i = 0; i < currentADT.M2List.Count; i++)
{
returnList.Add(currentADT.M2List[i].finalVertexList.ToArray());
}
return returnList;
}
public Boolean procesADT()
{
// Check to ensure that the BinaryReader has been loaded
if (this.loaded)
{
String[] filePath_split = this.filePath.Split(Convert.ToChar("\\"));
ADT currentADT = new ADT(filePath_split[filePath_split.Length - 1]);
this.br.BaseStream.Position = 0;
this.br.ReadBytes(20);
UInt32 pad = this.br.ReadUInt32();
UInt32 offsInfo = this.br.ReadUInt32();
UInt32 offsTex = this.br.ReadUInt32();
UInt32 offsModels = this.br.ReadUInt32(); /// 20 Bytes behind MDDX (filenames for M2s)
UInt32 offsModelsIds = this.br.ReadUInt32(); //
UInt32 offsMapObjects = this.br.ReadUInt32(); // 20 Bytes Behind Offset to the list of filenames for WMO's (0 terminated strings)MWMO Chunk
UInt32 offsMapObjectsIds = this.br.ReadUInt32();
UInt32 offsDoodsDef = this.br.ReadUInt32(); // 20 Bytes Behind MDDF (Placement for M2s)
UInt32 offsObjectsDef = this.br.ReadUInt32(); // 20 Bytes behind MODF (Placement for WMOs)
UInt32 pad1 = this.br.ReadUInt32();
UInt32 pad2 = this.br.ReadUInt32();
UInt32 pad3 = this.br.ReadUInt32();
UInt32 pad4 = this.br.ReadUInt32();
UInt32 pad5 = this.br.ReadUInt32();
UInt32 pad6 = this.br.ReadUInt32();
UInt32 pad7 = this.br.ReadUInt32();
this.br.ReadBytes(8); // Get off the MCIN Header
UInt32[] MCNKOffsets = new UInt32[256];
for (int i = 0; i < 256; i++)
{
MCNKOffsets[i] = this.br.ReadUInt32();
br.ReadBytes(12); // Get past the remaining data
}
int counter = 0;
for (int y = 0; y < 16; y++)
{
for (int x = 0; x < 16; x++)
{
br.BaseStream.Position = MCNKOffsets[counter] + 8;
br.ReadUInt32();
int index_x = (int)br.ReadUInt32();
int index_y = (int)br.ReadUInt32();
//br.BaseStream.Position += 92; // Get past the data we don't want
br.BaseStream.Position += 84; // Get past the data we don't want
UInt32 offsLiquid = br.ReadUInt32();
UInt32 sizeLiquid = br.ReadUInt32(); // If there's no liquid information it will be 8
float temp_z = (float)br.ReadSingle();
float temp_x = (float)br.ReadSingle();
float temp_y = (float)br.ReadSingle();
ADT.MCNK currentMCNK = new ADT.MCNK(temp_x, temp_y, temp_z);
br.ReadBytes(20); // Read the reaming 12 bytes of data and 8 for the next header which is a MCVT chunk
for (int i = 0; i < 145; i++)
{
currentMCNK._MCVT.heights[i] = (float)br.ReadSingle();
}
br.ReadBytes(8); // We're going to read past the next header which is for the normals
for (int i = 0; i < 435; i++)
{
currentMCNK._MCNR.normals[i] = (int)br.ReadByte() - 127;
}
if (sizeLiquid > 8)
{
br.BaseStream.Position = offsLiquid + MCNKOffsets[counter] + 8;
currentMCNK._MCLQ.size = sizeLiquid;
currentMCNK._MCLQ.altitude = (float)br.ReadSingle();
currentMCNK._MCLQ.base_magma_height = (float)br.ReadSingle();
for (int i = 0; i < 81; i++)
{
br.ReadInt16();
br.ReadInt16();
currentMCNK._MCLQ.heights[i] = (float)br.ReadSingle();
}
for (int i = 0; i < 64; i++)
{
if (br.ReadByte() == (byte)8)
{
currentMCNK._MCLQ.render[i] = false;
}
else
{
currentMCNK._MCLQ.render[i] = true;
}
}
}
currentADT.MCNKArray[index_x, index_y] = currentMCNK;
counter++;
}
}
this.buildWMONamesList((int)offsMapObjects, br, currentADT);
this.processWMOs((int)offsObjectsDef, br, currentADT);
this.buildm2NamesList((int)offsModels, br, currentADT);
this.processM2s((int)offsDoodsDef, br, currentADT);
this.ADTList.Add(currentADT);
return true;
}
else
{
return false;
}
}
public void buildWMONamesList(int Offset, BinaryReader br, ADT currentADT)
{
br.BaseStream.Position = Offset + 24; // 20 To get to where the list starts, 8 to get off the header.
UInt32 chunkSize = br.ReadUInt32();
long EndPosition = br.BaseStream.Position + chunkSize;
String wmoName = "";
byte[] nextByte = new byte[1];
while (br.BaseStream.Position < EndPosition)
{
nextByte = br.ReadBytes(1);
if (nextByte[0] != (byte)0)
{
wmoName += System.Text.ASCIIEncoding.ASCII.GetString(nextByte);
}
else
{
currentADT.wmoFileNameList.Add(wmoName.ToString());
wmoName = "";
}
}
}
public void buildm2NamesList(int Offset, BinaryReader br, ADT currentADT)
{
br.BaseStream.Position = Offset + 24; // 20 To get to where the list starts, 8 to get off the header.
UInt32 chunkSize = br.ReadUInt32();
long EndPosition = br.BaseStream.Position + chunkSize;
String m2Name = "";
byte[] nextByte = new byte[1];
while (br.BaseStream.Position < EndPosition)
{
nextByte = br.ReadBytes(1);
if (nextByte[0] != (byte)0)
{
m2Name += System.Text.ASCIIEncoding.ASCII.GetString(nextByte);
}
else
{
currentADT.m2FileNameList.Add(m2Name.ToString());
m2Name = "";
}
}
}
public void processM2s(int offset, BinaryReader br, ADT currentADT)
{
br.BaseStream.Position = offset + 24; // 20 to compensate and 4 to get off the header
UInt32 chunkSize = br.ReadUInt32();
int bytesRead = 0;
while (bytesRead < chunkSize)
{
ADT.MDDF currentMDDF = new ADT.MDDF();
currentMDDF.filePath = currentADT.m2FileNameList[(int)br.ReadUInt32()]; // 4 bytes
currentMDDF.uniqid = br.ReadUInt32(); // 4 bytes
currentMDDF.position = new Vector3((float)br.ReadSingle(), (float)br.ReadSingle(), (float)br.ReadSingle()); // 12 Bytes
currentMDDF.orientation_a = (float)br.ReadSingle(); // 4 Bytes
currentMDDF.orientation_b = (float)br.ReadSingle(); // 4 Bytes
currentMDDF.orientation_c = (float)br.ReadSingle(); // 4 Bytes
currentMDDF.scale = (float)(br.ReadUInt32() / 1024f); // 4 bytes
bytesRead += 36; // 36 total bytes
currentADT.MDDFList.Add(currentMDDF);
}
for (int i = 0; i < currentADT.MDDFList.Count; i++)
{
if (File.Exists(this.basePath + currentADT.MDDFList[i].filePath.Replace(".mdx", ".m2")))
{
String currentFilePath = currentADT.MDDFList[i].filePath;
FileStream fs = File.OpenRead(this.basePath + currentADT.MDDFList[i].filePath.Replace(".mdx",".m2"));
BinaryReader m2Reader = new BinaryReader(fs);
M2 currentM2 = new M2();
m2Reader.ReadBytes(68);
UInt32 numberOfVerts = m2Reader.ReadUInt32();
UInt32 vertsOffset = m2Reader.ReadUInt32();
UInt32 numberOfViews = m2Reader.ReadUInt32();
UInt32 viewsOffset = m2Reader.ReadUInt32();
// Time to get the verticies
m2Reader.BaseStream.Position = vertsOffset;
for (int v = 0; v < (int)numberOfVerts; v++)
{
float vert_x = (float)m2Reader.ReadSingle() * -1;
float vert_z = (float)m2Reader.ReadSingle();
float vert_y = (float)m2Reader.ReadSingle();
currentM2.verticiesList.Add(new Vector3(vert_x,vert_y,vert_z));
m2Reader.ReadBytes(36); // Extra Junk for now
}
// Now it's time to get the indicies
m2Reader.BaseStream.Position = viewsOffset;
UInt32 numElementsIndex = m2Reader.ReadUInt32();
UInt32 offsElementsIndex = m2Reader.ReadUInt32();
UInt32 numElementsTriangle = m2Reader.ReadUInt32();
UInt32 offsTriangleList = m2Reader.ReadUInt32();
m2Reader.BaseStream.Position = offsElementsIndex;
List<int> jumpList = new List<int>();
for(int v = 0; v < (int)numElementsIndex; v++)
{
jumpList.Add((int)m2Reader.ReadUInt16());
}
m2Reader.BaseStream.Position = offsTriangleList;
for (int v = 0; v < (int)numElementsTriangle; v++)
{
currentM2.finalIndiciesList.Add(jumpList[(int)m2Reader.ReadUInt16()]);
}
//currentM2.finalIndiciesList = currentM2.indiciesList;
for (int v = 0; v < currentM2.verticiesList.Count; v++)
{
Vector3 modifiedVector = transformVector(currentM2.verticiesList[v], currentADT.MDDFList[i].position.X,currentADT.MDDFList[i].position.Y,currentADT.MDDFList[i].position.Z, currentADT.MDDFList[i].orientation_a, currentADT.MDDFList[i].orientation_b, currentADT.MDDFList[i].orientation_c, currentADT.MDDFList[i].scale);
currentM2.finalVertexList.Add(new VertexPositionNormalColored(modifiedVector, Color.Red, Vector3.Up));
}
currentADT.M2List.Add(currentM2);
}
}
}
public Vector3 transformVector(Vector3 baseVector, float x, float y, float z, float orientation_a, float orientation_b, float orientation_c, float scale)
{
// Real work positions for a transform
float pos_x = (x - 17066.666666666656f) * -1;
float pos_y = y;
float pos_z = (z - 17066.666666666656f) * -1;
Vector3 origin = new Vector3(pos_x, pos_y, pos_z);
float my_x = (float)baseVector.X + pos_x;
float my_y = (float)baseVector.Y + pos_y;
float my_z = (float)baseVector.Z + pos_z;
Vector3 baseVertex = new Vector3(my_x, my_y, my_z);
//1 degrees = 0.0174532925 radians
float rad = 0.0174532925f;
// Creation the rotations
float a = orientation_a * -1 * rad;
float b = (orientation_b - 90) * rad;
float c = orientation_c * rad;
// Fancy things to rotate our model
Matrix rotateY = Matrix.CreateRotationY(b);
Matrix rotateZ = Matrix.CreateRotationZ(a);
Matrix rotateX = Matrix.CreateRotationX(c);
Matrix scaleMatrix = Matrix.CreateScale(scale);
Vector3 rotatedVector = Vector3.Transform(baseVertex - origin, rotateY);
Vector3 scaledVector = Vector3.Transform(rotatedVector, scaleMatrix);
Vector3 finalVector = scaledVector + origin;
return finalVector;
}
public void processWMOs(int offset, BinaryReader br, ADT currentADT)
{
br.BaseStream.Position = offset + 24; // 20 to compensate and 4 to get off the header
UInt32 chunkSize = br.ReadUInt32();
int bytesRead = 0;
while (bytesRead < chunkSize)
{
ADT.MODF currentMODF = new ADT.MODF();
currentMODF.filePath = currentADT.wmoFileNameList[(int)br.ReadUInt32()]; // 4 bytes
currentMODF.uniqid = br.ReadUInt32(); // 4 bytes
currentMODF.position = new Vector3((float)br.ReadSingle(), (float)br.ReadSingle(), (float)br.ReadSingle()); // 12 Bytes
currentMODF.orientation_a = (float)br.ReadSingle(); // 4 Bytes
currentMODF.orientation_b = (float)br.ReadSingle(); // 4 Bytes
currentMODF.orientation_c = (float)br.ReadSingle(); // 4 Bytes
br.ReadBytes(32); // 32 bytes
bytesRead += 64; // 64 total bytes
currentADT.MODFList.Add(currentMODF);
}
// Now we have all the placement information for our WMO's in our ADT class
for (int i = 0; i < currentADT.MODFList.Count; i++)
{
if (File.Exists(this.basePath + currentADT.MODFList[i].filePath))
{
String currentFilePath = currentADT.MODFList[i].filePath;
FileStream fs = File.OpenRead(this.basePath + currentADT.MODFList[i].filePath);
BinaryReader wmoReader = new BinaryReader(fs);
WMO currentWMO = new WMO();
String[] FilePathSplit = currentADT.MODFList[i].filePath.Split("\\".ToCharArray());
currentWMO.name = FilePathSplit[FilePathSplit.Length - 1];
// First we read the header, skip 20 bytes for the WMO File Header
wmoReader.ReadBytes(20);
UInt32 nTextures = wmoReader.ReadUInt32();
UInt32 nGroups = wmoReader.ReadUInt32(); // This is the number of "sub-wmos" or group files that we need to read
UInt32 nPortals = wmoReader.ReadUInt32();
UInt32 nLights = wmoReader.ReadUInt32();
UInt32 nModels = wmoReader.ReadUInt32();
UInt32 nDoodads = wmoReader.ReadUInt32();
UInt32 nSets = wmoReader.ReadUInt32();
UInt32 ambientColor = wmoReader.ReadUInt32();
UInt32 WMOID = wmoReader.ReadUInt32(); // Column 2i n the WMOAreaTable.dbc
currentWMO.boundingBox1 = new Vector3((float)br.ReadUInt32(), (float)br.ReadUInt32(), (float)br.ReadUInt32());
currentWMO.boundingBox2 = new Vector3((float)br.ReadUInt32(), (float)br.ReadUInt32(), (float)br.ReadUInt32());
currentWMO.total_groups = (int)nGroups;
for (int wmoGroup = 0; wmoGroup < nGroups; wmoGroup++)
{
String currentFileName = currentWMO.name.Substring(0, currentWMO.name.Length - 4) + "_" + wmoGroup.ToString().PadLeft(3, "0".ToCharArray()[0]) + ".wmo";
currentFileName = this.basePath + currentFilePath.Substring(0, currentFilePath.Length - currentWMO.name.Length) + currentFileName; |
The last bit is to update our Game1.cs file to render these new puppies!
Two new variables as before to hold all of our inforamtion:
1
2
| ArrayList M2VerticiesArrayList = new ArrayList();
ArrayList M2IndiciesArrayList = new ArrayList(); |
And after
1
2
| this.WMOIndiciesArrayList.Add(manager.getWMOIndicies(0));
this.WMOVerticiesArrayList.Add(manager.getWMOVerticies(0)); |
Put:
1
2
| this.M2IndiciesArrayList.Add(manager.getM2Indicies(0));
this.M2VerticiesArrayList.Add(manager.getM2Verticies(0)); |
Now make a new method called DrawM2 which is identical to our DrawWMO method:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| void DrawM2()
{
for (int i = 0; i < this.M2VerticiesArrayList.Count; i++)
{
List<int[]> currentIndicieList = (List<int[]>)this.M2IndiciesArrayList[i];
List<VertexPositionNormalColored[]> currentVertexList = (List<VertexPositionNormalColored[]>)this.M2VerticiesArrayList[i];
for (int x = 0; x < currentIndicieList.Count; x++)
{
VertexPositionNormalColored[] currentWMOVerticies = currentVertexList[x];
int[] currentWMOIndicies = currentIndicieList[x];
graphics.GraphicsDevice.RenderState.FillMode = FillMode.WireFrame;
graphics.GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionNormalColored>(
PrimitiveType.TriangleList,
currentWMOVerticies,
0, // vertex buffer offset to add to each element of the index buffer
currentWMOVerticies.Length, // number of vertices to draw
currentWMOIndicies,
0, // first index element to read
currentWMOIndicies.Length / 3 // number of primitives to draw
);
}
}
} |
And finally in our Draw() method after:
Put:
That’s it! M2’s are good to go!
Download for the entire thing: mpqnav005
If you enjoyed this post, please consider to leave a comment or subscribe to the feed and get future articles delivered to your feed reader.
Have you explained what are M2’s or WMO’s? I’m totally new to these terms. If you already have, please let me know the links to those posts.
Thanks :)