public M2Vertex ToWoW() { var vert = new M2Vertex { Position = MayaToM2.AxisInvert(Position), Normal = MayaToM2.AxisInvert(Normal) }; for (var i = 0; i < vert.TexCoords.Length && i < UvCoordinates.Count; i++) { vert.TexCoords[i] = new C2Vector(UvCoordinates[i].Item1, 1 - UvCoordinates[i].Item2); } if (Weights.Count > MaxWeightsNumber) { MGlobal.displayWarning("This vertex is connected to more than " + MaxWeightsNumber + " joints. Only the " + MaxWeightsNumber + " biggest will be exported."); } var exportedWeights = Weights.OrderByDescending(p => p.Item2).Take(MaxWeightsNumber).ToList(); Debug.Assert(exportedWeights.Count <= MaxWeightsNumber); //Normalize weight var weightSum = exportedWeights.Sum(p => p.Item2); if (Math.Abs(weightSum) <= MayaToM2.Epsilon) { vert.BoneWeights[0] = byte.MaxValue; } else { var availableWeight = byte.MaxValue; for (var j = 0; j < exportedWeights.Count; j++) { vert.BoneIndices[j] = (byte)exportedWeights[j].Item1.GlobalIndex; //Stored weight is the minimum between actual weight and remaining weight, to keep the sum == 255 var byteWeight = (byte)(exportedWeights[j].Item2 / weightSum * byte.MaxValue); vert.BoneWeights[j] = byteWeight > availableWeight ? availableWeight : byteWeight; availableWeight -= vert.BoneWeights[j]; } if (availableWeight > 0) // Remains { vert.BoneWeights[Weights.Count - 1] += availableWeight; } } var totalWeight = vert.BoneWeights.Sum(w => w); Debug.Assert(totalWeight == byte.MaxValue, "Total sum of weights is not 255 but " + totalWeight); return(vert); }
public static void M2Vertices() { SpecialSeek(openedFile.verticesPos); for (int i = 0; i < openedFile.verticesNum; i++) { M2Vertex vert = new M2Vertex(); vert.position = new float[] { reader.ReadUInt32(), reader.ReadUInt32(), reader.ReadUInt32() }; vert.boneWeights = new byte[] { reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte() }; vert.boneIndices = new byte[] { reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte() }; vert.normal = new float[] { reader.ReadUInt32(), reader.ReadUInt32(), reader.ReadUInt32() }; vert.texCoords1 = new float[] { reader.ReadUInt32(), reader.ReadUInt32() }; vert.texCoords2 = new float[] { reader.ReadUInt32(), reader.ReadUInt32() }; openedFile.vertices.Add(vert); } }
public M2Array <M2Vertex> GetVertices() { M2Array <M2Vertex> vertices = new M2Array <M2Vertex>(); Func <int, byte[]> GetWeights = (c) => { byte[] w = new byte[4]; for (int i = 0; i < c; i++) { w[i] = (byte)Math.Ceiling(255f / c); } if ((255 % c) != 0) { w[c - 1]--; //Must add up to 255 } return(w); }; foreach (var geo in _model.Get <GEOS>()) { var indicies = geo.GetIndicies(); for (int i = 0; i < geo.Vertices.Count; i++) { //Calculate weights int count = indicies[i].Skip(1).TakeWhile(x => x != 0).Count() + 1; //Count non-zero indicies excluding first byte[] weights = GetWeights(count); //Build weights M2Vertex vertex = new M2Vertex() { BoneIndices = indicies[i], BoneWeights = weights, Normal = geo.Normals[i].ToC3Vector, Position = geo.Vertices[i].ToC3Vector, TexCoords = new[] { geo.TexCoords[i].ToC2Vector, new C2Vector() } }; vertices.Add(vertex); } } return(vertices); }
public static void LoadM2(string filename, CacheStorage cache, int modelShader) { filename = filename.ToLower().Replace(".mdx", ".m2"); filename = filename.ToLower().Replace(".mdl", ".m2"); if (cache.doodadBatches.ContainsKey(filename)) { return; } WoWFormatLib.Structs.M2.M2Model model = new WoWFormatLib.Structs.M2.M2Model(); if (cache.models.ContainsKey(filename)) { model = cache.models[filename]; } else { //Load model from file if (WoWFormatLib.Utils.CASC.FileExists(filename)) { var modelreader = new M2Reader(); modelreader.LoadM2(filename); cache.models.Add(filename, modelreader.model); model = modelreader.model; } else { throw new Exception("Model " + filename + " does not exist!"); } } var ddBatch = new DoodadBatch(); // Textures ddBatch.mats = new Material[model.textures.Count()]; for (int i = 0; i < model.textures.Count(); i++) { string texturefilename = model.textures[i].filename; ddBatch.mats[i].flags = model.textures[i].flags; switch (model.textures[i].type) { case 0: // Console.WriteLine(" Texture given in file!"); texturefilename = model.textures[i].filename; break; case 1: string[] csfilenames = WoWFormatLib.DBC.DBCHelper.getTexturesByModelFilename(filename, (int)model.textures[i].type, i); if (csfilenames.Count() > 0) { texturefilename = csfilenames[0]; } else { //Console.WriteLine(" No type 1 texture found, falling back to placeholder texture"); } break; case 2: if (WoWFormatLib.Utils.CASC.FileExists(System.IO.Path.ChangeExtension(filename, ".blp"))) { // Console.WriteLine(" BLP exists!"); texturefilename = System.IO.Path.ChangeExtension(filename, ".blp"); } else { //Console.WriteLine(" Type 2 does not exist!"); //needs lookup? } break; case 11: string[] cdifilenames = WoWFormatLib.DBC.DBCHelper.getTexturesByModelFilename(filename, (int)model.textures[i].type); for (int ti = 0; ti < cdifilenames.Count(); ti++) { if (WoWFormatLib.Utils.CASC.FileExists(filename.Replace(model.name + ".M2", cdifilenames[ti] + ".blp"))) { texturefilename = filename.Replace(model.name + ".M2", cdifilenames[ti] + ".blp"); } } break; default: //Console.WriteLine(" Falling back to placeholder texture"); texturefilename = "Dungeons\\Textures\\testing\\COLOR_13.blp"; break; } ddBatch.mats[i].textureID = BLPLoader.LoadTexture(texturefilename, cache); ddBatch.mats[i].filename = texturefilename; } // Submeshes ddBatch.submeshes = new Submesh[model.skins[0].submeshes.Count()]; for (int i = 0; i < model.skins[0].submeshes.Count(); i++) { if (filename.StartsWith("character")) { if (model.skins[0].submeshes[i].submeshID != 0) { if (!model.skins[0].submeshes[i].submeshID.ToString().EndsWith("01")) { continue; } } } ddBatch.submeshes[i].firstFace = model.skins[0].submeshes[i].startTriangle; ddBatch.submeshes[i].numFaces = model.skins[0].submeshes[i].nTriangles; for (int tu = 0; tu < model.skins[0].textureunit.Count(); tu++) { if (model.skins[0].textureunit[tu].submeshIndex == i) { ddBatch.submeshes[i].blendType = model.renderflags[model.skins[0].textureunit[tu].renderFlags].blendingMode; if (!cache.materials.ContainsKey(model.textures[model.texlookup[model.skins[0].textureunit[tu].texture].textureID].filename.ToLower())) { throw new Exception("MaterialCache does not have texture " + model.textures[model.texlookup[model.skins[0].textureunit[tu].texture].textureID].filename.ToLower()); } ddBatch.submeshes[i].material = (uint)cache.materials[model.textures[model.texlookup[model.skins[0].textureunit[tu].texture].textureID].filename.ToLower()]; } } } ddBatch.vao = GL.GenVertexArray(); GL.BindVertexArray(ddBatch.vao); // Vertices & indices ddBatch.vertexBuffer = GL.GenBuffer(); ddBatch.indiceBuffer = GL.GenBuffer(); GL.BindBuffer(BufferTarget.ArrayBuffer, ddBatch.vertexBuffer); GL.BindBuffer(BufferTarget.ElementArrayBuffer, ddBatch.indiceBuffer); List <uint> modelindicelist = new List <uint>(); for (int i = 0; i < model.skins[0].triangles.Count(); i++) { modelindicelist.Add(model.skins[0].triangles[i].pt1); modelindicelist.Add(model.skins[0].triangles[i].pt2); modelindicelist.Add(model.skins[0].triangles[i].pt3); } uint[] modelindices = modelindicelist.ToArray(); //Console.WriteLine(modelindicelist.Count() + " indices!"); ddBatch.indices = modelindices; GL.BindBuffer(BufferTarget.ElementArrayBuffer, ddBatch.indiceBuffer); GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(ddBatch.indices.Length * sizeof(uint)), ddBatch.indices, BufferUsageHint.StaticDraw); M2Vertex[] modelvertices = new M2Vertex[model.vertices.Count()]; for (int i = 0; i < model.vertices.Count(); i++) { modelvertices[i].Position = new Vector3(model.vertices[i].position.X, model.vertices[i].position.Y, model.vertices[i].position.Z); modelvertices[i].Normal = new Vector3(model.vertices[i].normal.X, model.vertices[i].normal.Y, model.vertices[i].normal.Z); modelvertices[i].TexCoord = new Vector2(model.vertices[i].textureCoordX, model.vertices[i].textureCoordY); } GL.BindBuffer(BufferTarget.ArrayBuffer, ddBatch.vertexBuffer); GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(modelvertices.Length * 8 * sizeof(float)), modelvertices, BufferUsageHint.StaticDraw); var texCoordLoc = GL.GetAttribLocation(modelShader, "vTexCoord"); GL.EnableVertexAttribArray(texCoordLoc); GL.VertexAttribPointer(texCoordLoc, 2, VertexAttribPointerType.Float, false, 8 * sizeof(float), 3 * sizeof(float)); var posLoc = GL.GetAttribLocation(modelShader, "vPosition"); GL.EnableVertexAttribArray(posLoc); GL.VertexAttribPointer(posLoc, 3, VertexAttribPointerType.Float, false, 8 * sizeof(float), 5 * sizeof(float)); GL.BindVertexArray(0); cache.doodadBatches.Add(filename, ddBatch); }
/// <inheritdoc/> public void LoadBinaryData(byte[] inData) { using (var ms = new MemoryStream(inData)) using (var br = new BinaryReader(ms)) { br.ReadUInt32(); // Signature Version = br.ReadUInt32(); Name = br.ReadMD20String(br.ReadUInt32(), br.ReadUInt32()); Flags = br.ReadUInt32(); // TODO: Implement Flags // Global Sequences UInt32 count = br.ReadUInt32(); UInt32 offset = br.ReadUInt32(); long headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { UInt32 value = br.ReadUInt32(); GlobalSequences.Add(value); } br.BaseStream.Position = headerpos; // Sequences count = br.ReadUInt32(); offset = br.ReadUInt32(); headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { M2Sequence seq = new M2Sequence(); seq.AnimationID = br.ReadUInt16(); seq.SubAnimationID = br.ReadUInt16(); seq.Length = br.ReadUInt32(); seq.MovingSpeed = br.ReadSingle(); seq.Flags = br.ReadUInt32(); seq.Probability = br.ReadInt16(); seq.Padding = br.ReadUInt16(); seq.MinimumRepetitions = br.ReadUInt32(); seq.MaximumRepetitions = br.ReadUInt32(); seq.BlendTime = br.ReadUInt32(); seq.BoundsMinimumExtend = new C3Vector(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); seq.BoundsMaximumExtend = new C3Vector(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); seq.BoundRadius = br.ReadSingle(); seq.NextAnimation = br.ReadInt16(); seq.aliasNext = br.ReadUInt16(); Sequences.Add(seq); } br.BaseStream.Position = headerpos; //SequencesLookups count = br.ReadUInt32(); offset = br.ReadUInt32(); headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { SequencesLookups.Add(br.ReadInt16()); } br.BaseStream.Position = headerpos; // Bones count = br.ReadUInt32(); offset = br.ReadUInt32(); headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { M2Bone bone = new M2Bone(); bone.KeyBoneID = br.ReadInt32(); bone.Flags = br.ReadUInt32(); bone.ParentBone = br.ReadInt16(); bone.SubmeshID = br.ReadUInt16(); bone.CompressData[0] = br.ReadUInt16(); bone.CompressData[1] = br.ReadUInt16(); //translation M2Track translation = new M2Track(); translation.readM2Track(br); bone.translation = translation; // rotation M2Track rotation = new M2Track(); rotation.readM2Track(br); bone.rotation = rotation; // Scale M2Track scale = new M2Track(); scale.readM2Track(br); bone.scale = scale; bone.pivot = new C3Vector(br.ReadUInt32(), br.ReadUInt32(), br.ReadUInt32()); Bones.Add(bone); } br.BaseStream.Position = headerpos; // key_bone_lookup count = br.ReadUInt32(); offset = br.ReadUInt32(); headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { KeyBoneLookup.Add(br.ReadInt16()); } br.BaseStream.Position = headerpos; // Vetices count = br.ReadUInt32(); offset = br.ReadUInt32(); headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { M2Vertex temp = new M2Vertex(); temp.Pos = new C3Vector(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); for (int a = 0; a < 4; a++) { temp.BoneWeights.Add(br.ReadByte()); } for (int a = 0; a < 4; a++) { temp.BoneIndices.Add(br.ReadByte()); } temp.Normal = new C3Vector(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); temp.TexCords.Add(new C2Vector(br.ReadSingle(), br.ReadSingle())); temp.TexCords.Add(new C2Vector(br.ReadSingle(), br.ReadSingle())); } br.BaseStream.Position = headerpos; // Number of Skin profiles NumberSkinProfiles = br.ReadUInt32(); // Colors count = br.ReadUInt32(); offset = br.ReadUInt32(); headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { M2Color temp = new M2Color(); // Color M2Track color = new M2Track(); color.readM2Track(br); temp.Color = color; // Alpha M2Track alpha = new M2Track(); alpha.readM2Track(br); temp.Alpha = alpha; Color.Add(temp); } br.BaseStream.Position = headerpos; // Textures count = br.ReadUInt32(); offset = br.ReadUInt32(); headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { M2Texture temp = new M2Texture(); temp.Type = br.ReadUInt32(); temp.Flags = br.ReadUInt32(); UInt32 tCount = br.ReadUInt32(); UInt32 tOffset = br.ReadUInt32(); long tHeaderpos = br.BaseStream.Position; br.BaseStream.Position = tOffset; temp.Filename = ""; for (int a = 0; a < tCount; a++) { temp.Filename += br.ReadChar(); } br.BaseStream.Position = tHeaderpos; Texture.Add(temp); } br.BaseStream.Position = headerpos; // Texture Weights count = br.ReadUInt32(); offset = br.ReadUInt32(); headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { M2Track temp = new M2Track(); temp.readM2Track(br); TextureWeights.Add(temp); } br.BaseStream.Position = headerpos; // UV Animations count = br.ReadUInt32(); offset = br.ReadUInt32(); headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { M2TextureTransform temp = new M2TextureTransform(); M2Track translation = new M2Track(); translation.readM2Track(br); temp.Translation = translation; M2Track rotation = new M2Track(); rotation.readM2Track(br); temp.Rotation = rotation; M2Track scaling = new M2Track(); scaling.readM2Track(br); temp.Scaling = scaling; UvAnimations.Add(temp); } br.BaseStream.Position = headerpos; // Texture Replacements count = br.ReadUInt32(); offset = br.ReadUInt32(); headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { TextureReplacements.Add(br.ReadInt16()); } br.BaseStream.Position = headerpos; // Materials count = br.ReadUInt32(); offset = br.ReadUInt32(); headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { M2Material temp = new M2Material(); temp.Flags = br.ReadUInt16(); temp.BlendMode = br.ReadUInt16(); Materials.Add(temp); } br.BaseStream.Position = headerpos; // Bone Lookups count = br.ReadUInt32(); offset = br.ReadUInt32(); headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { BoneLookups.Add(br.ReadUInt16()); } br.BaseStream.Position = headerpos; // Texture Units count = br.ReadUInt32(); offset = br.ReadUInt32(); headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { TextureUnits.Add(br.ReadUInt16()); } br.BaseStream.Position = headerpos; // Texture Weights Lookups count = br.ReadUInt32(); offset = br.ReadUInt32(); headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { TextureWeightsLookups.Add(br.ReadUInt16()); } br.BaseStream.Position = headerpos; // Animation Lookups count = br.ReadUInt32(); offset = br.ReadUInt32(); headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { UvAnimationLookups.Add(br.ReadInt16()); } br.BaseStream.Position = headerpos; } }
public static WorldModel LoadWMO(string filename, CacheStorage cache, int modelShader) { 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, false); cache.worldModels.Add(filename, wmoreader.wmofile); wmo = wmoreader.wmofile; } else { throw new Exception("WMO " + filename + " does not exist!"); } } var wmobatch = new WorldModel(); wmobatch.groupBatches = new 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; } 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; } // GL.UseProgram(modelShader); wmobatch.groupBatches[g].vao = GL.GenVertexArray(); Console.WriteLine("Generated WMO VAO " + wmobatch.groupBatches[g].vao); GL.BindVertexArray(wmobatch.groupBatches[g].vao); wmobatch.groupBatches[g].vertexBuffer = GL.GenBuffer(); wmobatch.groupBatches[g].indiceBuffer = GL.GenBuffer(); GL.BindBuffer(BufferTarget.ArrayBuffer, wmobatch.groupBatches[g].vertexBuffer); M2Vertex[] wmovertices = new M2Vertex[wmo.group[g].mogp.vertices.Count()]; 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); var texCoordLoc = GL.GetAttribLocation(modelShader, "vTexCoord"); GL.EnableVertexAttribArray(texCoordLoc); GL.VertexAttribPointer(texCoordLoc, 2, VertexAttribPointerType.Float, false, 8 * sizeof(float), 3 * sizeof(float)); var posLoc = GL.GetAttribLocation(modelShader, "vPosition"); GL.EnableVertexAttribArray(posLoc); GL.VertexAttribPointer(posLoc, 3, VertexAttribPointerType.Float, false, 8 * sizeof(float), 5 * sizeof(float)); GL.BindVertexArray(0); } wmobatch.mats = new 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 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 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); }