public static void exportWMO(string file, BackgroundWorker exportworker = null) { if(exportworker == null) { exportworker = new BackgroundWorker(); } Console.WriteLine("Loading WMO file.."); exportworker.ReportProgress(5, "Reading WMO.."); var outdir = ConfigurationManager.AppSettings["outdir"]; WMOReader reader = new WMOReader(); reader.LoadWMO(file); // TODO: Support doodads! for (int i = 0; i < reader.wmofile.doodadNames.Count(); i++) { //Console.WriteLine(reader.wmofile.doodadNames[i].filename); //reader.wmofile.doodadDefinitions[i]. //reader.wmofile.doodadDefinitions[i]. } exportworker.ReportProgress(30, "Reading WMO.."); uint totalVertices = 0; var groups = new Structs.WMOGroup[reader.wmofile.group.Count()]; for (int g = 0; g < reader.wmofile.group.Count(); g++) { if (reader.wmofile.group[g].mogp.vertices == null) { continue; } for (int i = 0; i < reader.wmofile.groupNames.Count(); i++) { if (reader.wmofile.group[g].mogp.nameOffset == reader.wmofile.groupNames[i].offset) { groups[g].name = reader.wmofile.groupNames[i].name.Replace(" ", "_"); } } if (groups[g].name == "antiportal") { continue; } groups[g].verticeOffset = totalVertices; groups[g].vertices = new Structs.Vertex[reader.wmofile.group[g].mogp.vertices.Count()]; for (int i = 0; i < reader.wmofile.group[g].mogp.vertices.Count(); i++) { groups[g].vertices[i].Position = new Vector3(reader.wmofile.group[g].mogp.vertices[i].vector.X * -1, reader.wmofile.group[g].mogp.vertices[i].vector.Z, reader.wmofile.group[g].mogp.vertices[i].vector.Y); groups[g].vertices[i].Normal = new Vector3(reader.wmofile.group[g].mogp.normals[i].normal.X, reader.wmofile.group[g].mogp.normals[i].normal.Z, reader.wmofile.group[g].mogp.normals[i].normal.Y); groups[g].vertices[i].TexCoord = new Vector2(reader.wmofile.group[g].mogp.textureCoords[0][i].X, reader.wmofile.group[g].mogp.textureCoords[0][i].Y); totalVertices++; } var indicelist = new List<uint>(); for (int i = 0; i < reader.wmofile.group[g].mogp.indices.Count(); i++) { indicelist.Add(reader.wmofile.group[g].mogp.indices[i].indice); } groups[g].indices = indicelist.ToArray(); } exportworker.ReportProgress(55, "Exporting textures.."); // Create output directory if (!Directory.Exists(Path.Combine(outdir, Path.GetDirectoryName(file)))) { Directory.CreateDirectory(Path.Combine(outdir, Path.GetDirectoryName(file))); } var mtlsb = new StringBuilder(); var textureID = 0; var materials = new Structs.Material[reader.wmofile.materials.Count()]; for (int i = 0; i < reader.wmofile.materials.Count(); i++) { for (int ti = 0; ti < reader.wmofile.textures.Count(); ti++) { if (reader.wmofile.textures[ti].startOffset == reader.wmofile.materials[i].texture1) { //materials[i].textureID = BLPLoader.LoadTexture(reader.wmofile.textures[ti].filename, cache); materials[i].textureID = textureID + i; materials[i].filename = Path.GetFileNameWithoutExtension(reader.wmofile.textures[ti].filename); if (reader.wmofile.materials[i].blendMode == 0) { materials[i].transparent = false; } else { materials[i].transparent = true; } var blpreader = new BLPReader(); blpreader.LoadBLP(reader.wmofile.textures[ti].filename); try { blpreader.bmp.Save(Path.Combine(outdir, Path.GetDirectoryName(file), materials[i].filename + ".png")); } catch (Exception e) { Console.WriteLine(e.Message); } textureID++; } } } //No idea how MTL files really work yet. Needs more investigation. foreach (var material in materials) { mtlsb.Append("newmtl " + material.filename + "\n"); mtlsb.Append("Ns 96.078431\n"); mtlsb.Append("Ka 1.000000 1.000000 1.000000\n"); mtlsb.Append("Kd 0.640000 0.640000 0.640000\n"); mtlsb.Append("Ks 0.000000 0.000000 0.000000\n"); mtlsb.Append("Ke 0.000000 0.000000 0.000000\n"); mtlsb.Append("Ni 1.000000\n"); mtlsb.Append("d 1.000000\n"); mtlsb.Append("illum 2\n"); mtlsb.Append("map_Kd " + material.filename + ".png\n"); if (material.transparent) { mtlsb.Append("map_d " + material.filename + ".png\n"); } } File.WriteAllText(Path.Combine(outdir, file.Replace(".wmo", ".mtl")), mtlsb.ToString()); exportworker.ReportProgress(75, "Exporting model.."); int numRenderbatches = 0; //Get total amount of render batches for (int i = 0; i < reader.wmofile.group.Count(); i++) { if (reader.wmofile.group[i].mogp.renderBatches == null) { continue; } numRenderbatches = numRenderbatches + reader.wmofile.group[i].mogp.renderBatches.Count(); } int rb = 0; for (int g = 0; g < reader.wmofile.group.Count(); g++) { groups[g].renderBatches = new Structs.RenderBatch[numRenderbatches]; var group = reader.wmofile.group[g]; if (group.mogp.renderBatches == null) { continue; } for (int i = 0; i < group.mogp.renderBatches.Count(); i++) { var batch = group.mogp.renderBatches[i]; groups[g].renderBatches[rb].firstFace = batch.firstFace; groups[g].renderBatches[rb].numFaces = batch.numFaces; if (batch.flags == 2) { groups[g].renderBatches[rb].materialID = (uint)batch.possibleBox2_3; } else { groups[g].renderBatches[rb].materialID = batch.materialID; } groups[g].renderBatches[rb].blendType = reader.wmofile.materials[batch.materialID].blendMode; groups[g].renderBatches[rb].groupID = (uint)g; rb++; } } exportworker.ReportProgress(95, "Writing files.."); var objsw = new StreamWriter(Path.Combine(outdir, file.Replace(".wmo", ".obj"))); objsw.WriteLine("# Written by Marlamin's WoW OBJExporter. Original file: " + file); objsw.WriteLine("mtllib " + Path.GetFileNameWithoutExtension(file) + ".mtl"); foreach (var group in groups) { if (group.vertices == null) { continue; } objsw.WriteLine("g " + group.name); foreach (var vertex in group.vertices) { objsw.WriteLine("v " + vertex.Position.X + " " + vertex.Position.Y + " " + vertex.Position.Z); objsw.WriteLine("vt " + vertex.TexCoord.X + " " + -vertex.TexCoord.Y); objsw.WriteLine("vn " + vertex.Normal.X + " " + vertex.Normal.Y + " " + vertex.Normal.Z); } var indices = group.indices; foreach (var renderbatch in group.renderBatches) { var i = renderbatch.firstFace; if (renderbatch.numFaces > 0) { objsw.WriteLine("usemtl " + materials[renderbatch.materialID].filename); objsw.WriteLine("s 1"); while (i < (renderbatch.firstFace + renderbatch.numFaces)) { objsw.WriteLine("f " + (indices[i] + group.verticeOffset + 1) + "/" + (indices[i] + group.verticeOffset + 1) + "/" + (indices[i] + group.verticeOffset + 1) + " " + (indices[i + 1] + group.verticeOffset + 1) + "/" + (indices[i + 1] + group.verticeOffset + 1) + "/" + (indices[i + 1] + group.verticeOffset + 1) + " " + (indices[i + 2] + group.verticeOffset + 1) + "/" + (indices[i + 2] + group.verticeOffset + 1) + "/" + (indices[i + 2] + group.verticeOffset + 1)); i = i + 3; } } } } objsw.Close(); Console.WriteLine("Done loading WMO file!"); }
private void LoadWMO(string modelpath) { Console.WriteLine("Loading WMO file.."); WMOReader reader = new WMOReader(); string filename = modelpath; //Load WMO reader.LoadWMO(filename); //Enable Vertex Arrays GL.EnableClientState(ArrayCap.VertexArray); //Enable Normal Arrays GL.EnableClientState(ArrayCap.NormalArray); //Enable TexCoord arrays GL.EnableClientState(ArrayCap.TextureCoordArray); //Set up buffer IDs VBOid = new uint[(reader.wmofile.group.Count() * 2) + 2]; GL.GenBuffers((reader.wmofile.group.Count() * 2) + 2, VBOid); for (int i = 0; i < reader.wmofile.doodadNames.Count(); i++) { //Console.WriteLine(reader.wmofile.doodadNames[i].filename); } for (int g = 0; g < reader.wmofile.group.Count(); g++) { if (reader.wmofile.group[g].mogp.vertices == null) { continue; } //Switch to Vertex buffer GL.BindBuffer(BufferTarget.ArrayBuffer, VBOid[g * 2]); Vertex[] vertices = new Vertex[reader.wmofile.group[g].mogp.vertices.Count()]; for (int i = 0; i < reader.wmofile.group[g].mogp.vertices.Count(); i++) { vertices[i].Position = new Vector3(reader.wmofile.group[g].mogp.vertices[i].vector.X, reader.wmofile.group[g].mogp.vertices[i].vector.Z, reader.wmofile.group[g].mogp.vertices[i].vector.Y); vertices[i].Normal = new Vector3(reader.wmofile.group[g].mogp.normals[i].normal.X, reader.wmofile.group[g].mogp.normals[i].normal.Z, reader.wmofile.group[g].mogp.normals[i].normal.Y); vertices[i].TexCoord = new Vector2(reader.wmofile.group[g].mogp.textureCoords[0][i].X, reader.wmofile.group[g].mogp.textureCoords[0][i].Y); } //Push to buffer GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vertices.Length * 8 * sizeof(float)), vertices, BufferUsageHint.StaticDraw); //Switch to Index buffer GL.BindBuffer(BufferTarget.ElementArrayBuffer, VBOid[(g * 2) + 1]); List<uint> indicelist = new List<uint>(); for (int i = 0; i < reader.wmofile.group[g].mogp.indices.Count(); i++) { indicelist.Add(reader.wmofile.group[g].mogp.indices[i].indice); } uint[] indices = indicelist.ToArray(); //Push to buffer GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(indices.Length * sizeof(uint)), indices, BufferUsageHint.StaticDraw); } GL.Enable(EnableCap.Texture2D); materials = new Material[reader.wmofile.materials.Count()]; for (int i = 0; i < reader.wmofile.materials.Count(); i++) { for (int ti = 0; ti < reader.wmofile.textures.Count(); ti++) { if (reader.wmofile.textures[ti].startOffset == reader.wmofile.materials[i].texture1) { materials[i].textureID = BLPLoader.LoadTexture(reader.wmofile.textures[ti].filename, cache); materials[i].filename = reader.wmofile.textures[ti].filename; } } } int numRenderbatches = 0; //Get total amount of render batches for (int i = 0; i < reader.wmofile.group.Count(); i++) { if (reader.wmofile.group[i].mogp.renderBatches == null) { continue; } numRenderbatches = numRenderbatches + reader.wmofile.group[i].mogp.renderBatches.Count(); } renderbatches = new RenderBatch[numRenderbatches]; int rb = 0; for (int g = 0; g < reader.wmofile.group.Count(); g++) { var group = reader.wmofile.group[g]; if (group.mogp.renderBatches == null) { continue; } for (int i = 0; i < group.mogp.renderBatches.Count(); i++) { renderbatches[rb].firstFace = group.mogp.renderBatches[i].firstFace; renderbatches[rb].numFaces = group.mogp.renderBatches[i].numFaces; renderbatches[rb].materialID = group.mogp.renderBatches[i].materialID; renderbatches[rb].blendType = reader.wmofile.materials[group.mogp.renderBatches[i].materialID].blendMode; renderbatches[rb].groupID = (uint)g; rb++; } } Console.WriteLine(" " + reader.wmofile.group.Count() + " skins"); Console.WriteLine(" " + materials.Count() + " materials"); Console.WriteLine(" " + renderbatches.Count() + " renderbatches"); Console.WriteLine(" " + reader.wmofile.group[0].mogp.vertices.Count() + " vertices"); Console.WriteLine("Done loading WMO file!"); gLoaded = true; isWMO = true; }
public void Compile(string wmoname) { WMOReader wmoreader = new WMOReader(); wmoreader.LoadWMO(wmoname); WoWFormatLib.Structs.WMO.WMO wmo = wmoreader.wmofile; Console.WriteLine(wmoname); if (wmo.group.Count() == 0) { Console.WriteLine("WMO " + wmoname + " has no groups! Skipping.."); return; } int wmo_minx = 999999999; int wmo_maxx = 0; int wmo_miny = 999999999; int wmo_maxy = 0; int numtiles = 0; //Determine min max offset for (int i = 0; i < wmo.group.Count(); i++) { string groupid = i.ToString().PadLeft(3, '0'); double drawx1 = wmo.groupInfo[i].boundingBox1.X * 2; double drawy1 = wmo.groupInfo[i].boundingBox1.Y * 2; double drawx2 = wmo.groupInfo[i].boundingBox2.X * 2; double drawy2 = wmo.groupInfo[i].boundingBox2.Y * 2; if (drawx1 < wmo_minx) { wmo_minx = (int)drawx1; } if (drawx1 > wmo_maxx) { wmo_maxx = (int)drawx1; } if (drawy1 < wmo_miny) { wmo_miny = (int)drawy1; } if (drawy1 > wmo_maxy) { wmo_maxy = (int)drawy1; } if (drawx2 < wmo_minx) { wmo_minx = (int)drawx2; } if (drawx2 > wmo_maxx) { wmo_maxx = (int)drawx2; } if (drawy2 < wmo_miny) { wmo_minx = (int)drawy2; } if (drawy2 > wmo_maxy) { wmo_maxx = (int)drawy2; } } int wmoresx = 0; int wmoresy = 0; //Determine image height for (int i = 0; i < wmo.group.Count(); i++) { string groupid = i.ToString().PadLeft(3, '0'); double drawx1 = wmo.groupInfo[i].boundingBox1.X * 2; double drawy1 = wmo.groupInfo[i].boundingBox1.Y * 2; double drawx2 = wmo.groupInfo[i].boundingBox2.X * 2; double drawy2 = wmo.groupInfo[i].boundingBox2.Y * 2; int greenx1 = (int)drawx1 + Math.Abs(wmo_minx); int greeny1 = (int)drawy1 + Math.Abs(wmo_miny); int greenx2 = (int)drawx2 + Math.Abs(wmo_minx); int greeny2 = (int)drawy2 + Math.Abs(wmo_miny); if (greenx2 > wmoresx) { wmoresx = greenx2; } if (greeny2 > wmoresy) { wmoresy = greeny2; } } if (wmoresx == 0 || wmoresy == 0) { Console.WriteLine("WMO " + wmoname + " has invalid calculated resolution (" + wmoresx + "x" + wmoresy + ")"); return; } Bitmap wmobmp = new Bitmap(wmoresx, wmoresy); Graphics wmog = Graphics.FromImage(wmobmp); /* string wmodirname = Path.GetDirectoryName(wmoname.Replace("World" + Path.DirectorySeparatorChar, String.Empty)); if (!Directory.Exists(Path.Combine(basedir, "World", "Minimaps", wmodirname + Path.DirectorySeparatorChar))) { Console.WriteLine("WMO has no minimaps directory (" + Path.Combine(basedir, "World", "Minimaps", wmodirname + Path.DirectorySeparatorChar) + "). Skipping.."); return; } */ for (int i = 0; i < wmo.group.Count(); i++) { string groupid = i.ToString().PadLeft(3, '0'); double drawx1 = wmo.groupInfo[i].boundingBox1.X * 2; double drawy1 = wmo.groupInfo[i].boundingBox1.Y * 2; double drawx2 = wmo.groupInfo[i].boundingBox2.X * 2; double drawy2 = wmo.groupInfo[i].boundingBox2.Y * 2; int greenx1 = (int)drawx1 + Math.Abs(wmo_minx); int greeny1 = (int)drawy1 + Math.Abs(wmo_miny); int greenx2 = (int)drawx2 + Math.Abs(wmo_minx); int greeny2 = (int)drawy2 + Math.Abs(wmo_miny); /* //Check if minimaps wmo dir for this even exists string wmogroupfilename = Path.GetFileNameWithoutExtension(wmoname) + "_" + groupid; string[] filePaths = Directory.GetFiles(Path.Combine(basedir, "World", "Minimaps", wmodirname + Path.DirectorySeparatorChar), wmogroupfilename + "*"); //check if there are any minimap blps for this group if (filePaths.Count() == 0) { Console.WriteLine("WMO has no blps in minimap directory. Skipping.."); continue; } */ Bitmap minimapbmp = CompileGroup(wmoname, groupid); if (minimapbmp.Width > 1) { wmog.DrawImage(minimapbmp, greenx1, (wmoresy - (greeny1 + (greeny2 - greeny1)) + (greeny2 - greeny1)), new Rectangle(0, minimapbmp.Height, (greenx2 - greenx1), -(greeny2 - greeny1)), GraphicsUnit.Pixel); } numtiles++; minimapbmp.Dispose(); } wmog.Dispose(); if (numtiles > 0) //check if it even compiled anything { Directory.CreateDirectory("done" + Path.DirectorySeparatorChar + "WMO" + Path.DirectorySeparatorChar + Path.GetDirectoryName(wmoname)); wmobmp.Save("done" + Path.DirectorySeparatorChar + "WMO" + Path.DirectorySeparatorChar + Path.Combine(wmoname) + ".png"); } else { Console.WriteLine("WMO has no minimaps. Skipping.."); } }
public void LoadWMO() { WMOReader reader = new WMOReader(basedir); reader.LoadWMO(modelPath); WMOMaterial[] materials = new WMOMaterial[reader.wmofile.materials.Count()]; for (int i = 0; i < reader.wmofile.materials.Count(); i++) { for (int ti = 0; ti < reader.wmofile.textures.Count(); ti++) { if (reader.wmofile.textures[ti].startOffset == reader.wmofile.materials[i].texture1) { Texture2D texture; var blp = new BLPReader(basedir); blp.LoadBLP(reader.wmofile.textures[ti].filename); if (blp.bmp == null) { texture = Texture2D.FromFile<Texture2D>(device, "missingtexture.jpg"); } else { MemoryStream s = new MemoryStream(); blp.bmp.Save(s, System.Drawing.Imaging.ImageFormat.Png); s.Seek(0, SeekOrigin.Begin); texture = Texture2D.FromMemory<Texture2D>(device, s.ToArray()); s.Dispose(); } materials[i].materialID = (uint)i; materials[i].filename = reader.wmofile.textures[ti].filename; materials[i].texture = texture; } } } WoWWMOGroup[] groups = new WoWWMOGroup[reader.wmofile.header.nGroups]; for (int i = 0; i < reader.wmofile.header.nGroups; i++) { groups[i] = LoadGroupWMO(reader.wmofile.group[i]); } wmo.materials = materials; wmo.groups = groups; }
public static TerrainWindow.WorldModel LoadWMO(string filename, CacheStorage cache) { if (cache.worldModelBatches.ContainsKey(filename)) { return cache.worldModelBatches[filename]; } WoWFormatLib.Structs.WMO.WMO wmo = new WoWFormatLib.Structs.WMO.WMO(); if (cache.worldModels.ContainsKey(filename)) { wmo = cache.worldModels[filename]; } else { //Load WMO from file if (WoWFormatLib.Utils.CASC.FileExists(filename)) { var wmoreader = new WMOReader(); wmoreader.LoadWMO(filename); cache.worldModels.Add(filename, wmoreader.wmofile); wmo = wmoreader.wmofile; } else { throw new Exception("WMO " + filename + " does not exist!"); } } var wmobatch = new TerrainWindow.WorldModel(); wmobatch.groupBatches = new TerrainWindow.WorldModelGroupBatches[wmo.group.Count()]; string[] groupNames = new string[wmo.group.Count()]; for (int g = 0; g < wmo.group.Count(); g++) { if (wmo.group[g].mogp.vertices == null) { continue; } wmobatch.groupBatches[g].vertexBuffer = GL.GenBuffer(); wmobatch.groupBatches[g].indiceBuffer = GL.GenBuffer(); GL.BindBuffer(BufferTarget.ArrayBuffer, wmobatch.groupBatches[g].vertexBuffer); TerrainWindow.M2Vertex[] wmovertices = new TerrainWindow.M2Vertex[wmo.group[g].mogp.vertices.Count()]; for (int i = 0; i < wmo.groupNames.Count(); i++) { if (wmo.group[g].mogp.nameOffset == wmo.groupNames[i].offset) { groupNames[g] = wmo.groupNames[i].name.Replace(" ", "_"); } } if (groupNames[g] == "antiportal") { continue; } for (int i = 0; i < wmo.group[g].mogp.vertices.Count(); i++) { wmovertices[i].Position = new Vector3(wmo.group[g].mogp.vertices[i].vector.X, wmo.group[g].mogp.vertices[i].vector.Y, wmo.group[g].mogp.vertices[i].vector.Z); wmovertices[i].Normal = new Vector3(wmo.group[g].mogp.normals[i].normal.X, wmo.group[g].mogp.normals[i].normal.Y, wmo.group[g].mogp.normals[i].normal.Z); if (wmo.group[g].mogp.textureCoords[0] == null) { wmovertices[i].TexCoord = new Vector2(0.0f, 0.0f); } else { wmovertices[i].TexCoord = new Vector2(wmo.group[g].mogp.textureCoords[0][i].X, wmo.group[g].mogp.textureCoords[0][i].Y); } } //Push to buffer GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(wmovertices.Length * 8 * sizeof(float)), wmovertices, BufferUsageHint.StaticDraw); //Switch to Index buffer GL.BindBuffer(BufferTarget.ElementArrayBuffer, wmobatch.groupBatches[g].indiceBuffer); List<uint> wmoindicelist = new List<uint>(); for (int i = 0; i < wmo.group[g].mogp.indices.Count(); i++) { wmoindicelist.Add(wmo.group[g].mogp.indices[i].indice); } wmobatch.groupBatches[g].indices = wmoindicelist.ToArray(); GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(wmobatch.groupBatches[g].indices.Length * sizeof(uint)), wmobatch.groupBatches[g].indices, BufferUsageHint.StaticDraw); } GL.Enable(EnableCap.Texture2D); wmobatch.mats = new TerrainWindow.Material[wmo.materials.Count()]; for (int i = 0; i < wmo.materials.Count(); i++) { for (int ti = 0; ti < wmo.textures.Count(); ti++) { if (wmo.textures[ti].startOffset == wmo.materials[i].texture1) { wmobatch.mats[i].texture1 = wmo.materials[i].texture1; wmobatch.mats[i].textureID = BLPLoader.LoadTexture(wmo.textures[ti].filename, cache); wmobatch.mats[i].filename = wmo.textures[ti].filename; } } } wmobatch.doodads = new TerrainWindow.WMODoodad[wmo.doodadDefinitions.Count()]; for(int i = 0; i < wmo.doodadDefinitions.Count(); i++) { for(int j = 0; j < wmo.doodadNames.Count(); j++) { if (wmo.doodadDefinitions[i].offset == wmo.doodadNames[j].startOffset) { wmobatch.doodads[i].filename = wmo.doodadNames[j].filename; M2Loader.LoadM2(wmobatch.doodads[i].filename, cache); } } wmobatch.doodads[i].flags = wmo.doodadDefinitions[i].flags; wmobatch.doodads[i].position = new Vector3(wmo.doodadDefinitions[i].position.X, wmo.doodadDefinitions[i].position.Y, wmo.doodadDefinitions[i].position.Z); wmobatch.doodads[i].rotation = new Quaternion(wmo.doodadDefinitions[i].rotation.X, wmo.doodadDefinitions[i].rotation.Y, wmo.doodadDefinitions[i].rotation.Z, wmo.doodadDefinitions[i].rotation.W); wmobatch.doodads[i].scale = wmo.doodadDefinitions[i].scale; wmobatch.doodads[i].color = new Vector4(wmo.doodadDefinitions[i].color[0], wmo.doodadDefinitions[i].color[1], wmo.doodadDefinitions[i].color[2], wmo.doodadDefinitions[i].color[3]); } int numRenderbatches = 0; //Get total amount of render batches for (int i = 0; i < wmo.group.Count(); i++) { if (wmo.group[i].mogp.renderBatches == null) { continue; } numRenderbatches = numRenderbatches + wmo.group[i].mogp.renderBatches.Count(); } wmobatch.wmoRenderBatch = new TerrainWindow.RenderBatch[numRenderbatches]; int rb = 0; for (int g = 0; g < wmo.group.Count(); g++) { var group = wmo.group[g]; if (group.mogp.renderBatches == null) { continue; } for (int i = 0; i < group.mogp.renderBatches.Count(); i++) { wmobatch.wmoRenderBatch[rb].firstFace = group.mogp.renderBatches[i].firstFace; wmobatch.wmoRenderBatch[rb].numFaces = group.mogp.renderBatches[i].numFaces; uint matID = 0; if (group.mogp.renderBatches[i].flags == 2) { matID = (uint) group.mogp.renderBatches[i].possibleBox2_3; } else { matID = group.mogp.renderBatches[i].materialID; } for (int ti = 0; ti < wmobatch.mats.Count(); ti++) { if (wmo.materials[matID].texture1 == wmobatch.mats[ti].texture1) { wmobatch.wmoRenderBatch[rb].materialID = new uint[] { (uint)wmobatch.mats[ti].textureID }; } } wmobatch.wmoRenderBatch[rb].blendType = wmo.materials[group.mogp.renderBatches[i].materialID].blendMode; wmobatch.wmoRenderBatch[rb].groupID = (uint)g; rb++; } } cache.worldModelBatches.Add(filename, wmobatch); return wmobatch; }