public IEnumerable <WMOScene> GenerateWMOs() { if (MCRW == null || ADT.MODF == null) { yield break; } var drawn = new HashSet <uint>(); for (var i = 0; i < MCRW.MODFEntryIndex.Length; i++) { var wmo = ADT.MODF.Entries[MCRW.MODFEntryIndex[i]]; if (drawn.Contains(wmo.UniqueId)) { continue; } drawn.Add(wmo.UniqueId); if (wmo.MWIDEntryIndex >= ADT.ModelPaths.Count) { continue; } var path = ADT.ModelPaths[(int)wmo.MWIDEntryIndex]; var model = new WMORoot(path); yield return(GenerateWMOScene(wmo, model)); } }
public void AddDungeon(WMORoot model, MODF.MODFEntry def) { var verts = new List<Vector3>(); var inds = new List<Triangle<uint>>(); MapChunk.InsertWMOGeometry(def, model, verts, inds); AddGeometry(verts, inds); }
public static WMOScene GenerateWMOScene(MODF.MODFEntry wmoDefinition, WMORoot model) { return(new WMOScene { Terrain = model.Groups.Select(g => g.GenerateTerrain(wmoDefinition)).OfType <Mesh>() ?? Enumerable.Empty <Mesh>(), Doodads = model.GenerateDoodads(wmoDefinition.DoodadSet, wmoDefinition).OfType <Mesh>() ?? Enumerable.Empty <Mesh>(), Liquids = model.Groups.Select(g => g.GenerateLiquid(wmoDefinition)).OfType <Mesh>() ?? Enumerable.Empty <Mesh>(), }); }
private void GenerateWMOs() { if (ADT.Type != ADTType.Objects || ADT.MODF == null) { return; } var drawn = new HashSet <uint>(); for (int i = 0; i < MCRW.MODFEntryIndex.Length; i++) { var wmo = ADT.MODF.Entries[MCRW.MODFEntryIndex[i]]; if (drawn.Contains(wmo.UniqueId)) { continue; } drawn.Add(wmo.UniqueId); if (wmo.MWIDEntryIndex >= ADT.ModelPaths.Count) { continue; } var path = ADT.ModelPaths[(int)wmo.MWIDEntryIndex]; var model = new WMORoot(path); if (WMOVertices == null) { WMOVertices = new List <Vector3>(1000); } if (WMOIndices == null) { WMOIndices = new List <Triangle <uint> >(1000); } InsertWMOGeometry(wmo, model, WMOVertices, WMOIndices); } }
public void GenerateGlobalModel() { ModelFile = MWMO.Filenames.FirstOrDefault().Value; if (string.IsNullOrEmpty(ModelFile)) { return; } GlobalModel = new WMORoot(ModelFile); if (GlobalModel == null) { return; } var placementEntry = MODF.Entries.FirstOrDefault(); if (placementEntry == null) { return; } ModelScene = MapChunk.GenerateWMOScene(placementEntry, GlobalModel) .Transform(Matrix4.CreateRotationX((float)(-Math.PI / 2.0f))); }
public static void InsertWMOGeometry(MODF.MODFEntry wmo, WMORoot model, List <Vector3> vertices, List <Triangle <uint> > indices) { var transform = Transformation.GetTransform(wmo.Position, wmo.Rotation); foreach (var group in model.Groups) { var vo = (uint)vertices.Count; foreach (var v in group.MOVT.Vertices) { vertices.Add((Vector3)Vector3.Transform(v, transform)); } for (int i = 0; i < group.MOVI.Indices.Length; i++) { if (((byte)group.MOPY.Entries[i].Flags & 0x04) != 0 && group.MOPY.Entries[i].MaterialId != 0xFF) { continue; } var idx = group.MOVI.Indices[i]; indices.Add(new Triangle <uint>(TriangleType.Wmo, vo + idx.V0, vo + idx.V1, vo + idx.V2)); } } if (wmo.DoodadSet >= 0 && wmo.DoodadSet < model.MODS.Entries.Length) { var set = model.MODS.Entries[wmo.DoodadSet]; var instances = new List <MODD.MODDEntry>((int)set.nDoodads); for (uint i = set.FirstInstanceIndex; i < (set.nDoodads + set.FirstInstanceIndex); i++) { if (i >= model.MODD.Entries.Length) { break; } instances.Add(model.MODD.Entries[(int)i]); } foreach (var instance in instances) { string path; if (!model.MODN.Filenames.TryGetValue(instance.ofsMODN, out path)) { continue; } var doodad = new M2(path); if (!doodad.IsCollidable) { continue; } var doodadTransform = Transformation.GetWmoDoodadTransform(instance, wmo); var vo = (uint)vertices.Count; foreach (var v in doodad.Vertices) { vertices.Add((Vector3)Vector3.Transform(v, doodadTransform)); } foreach (var t in doodad.Indices) { indices.Add(new Triangle <uint>(TriangleType.Doodad, t.V0 + vo, t.V1 + vo, t.V2 + vo)); } } } foreach (var group in model.Groups) { if ((group.LiquidVertices == null || group.LiquidVertices.Count == 0) || (group.LiquidIndices == null || group.LiquidIndices.Count == 0)) { continue; } var vo = (uint)vertices.Count; foreach (var v in group.LiquidVertices) { vertices.Add((Vector3)Vector3.Transform(v, transform)); } foreach (var t in group.LiquidIndices) { indices.Add(new Triangle <uint>(t.Type, t.V1 + vo, t.V0 + vo, t.V2 + vo)); } } }
public static void InsertWMOGeometry(MODF.MODFEntry wmo, WMORoot model, List<Vector3> vertices, List<Triangle<uint>> indices) { var transform = Transformation.GetTransform(wmo.Position, wmo.Rotation); foreach (var group in model.Groups) { var vo = (uint)vertices.Count; foreach (var v in group.MOVT.Vertices) vertices.Add((Vector3)Vector3.Transform(v, transform)); for (int i = 0; i < group.MOVI.Indices.Length; i++) { if (((byte)group.MOPY.Entries[i].Flags & 0x04) != 0 && group.MOPY.Entries[i].MaterialId != 0xFF) continue; var idx = group.MOVI.Indices[i]; indices.Add(new Triangle<uint>(TriangleType.Wmo, vo + idx.V0, vo + idx.V1, vo + idx.V2)); } } if (wmo.DoodadSet >= 0 && wmo.DoodadSet < model.MODS.Entries.Length) { var set = model.MODS.Entries[wmo.DoodadSet]; var instances = new List<MODD.MODDEntry>((int)set.nDoodads); for (uint i = set.FirstInstanceIndex; i < (set.nDoodads + set.FirstInstanceIndex); i++) { if (i >= model.MODD.Entries.Length) break; instances.Add(model.MODD.Entries[(int)i]); } foreach (var instance in instances) { string path; if (!model.MODN.Filenames.TryGetValue(instance.ofsMODN, out path)) continue; var doodad = new M2(path); if (!doodad.IsCollidable) continue; var doodadTransform = Transformation.GetWmoDoodadTransform(instance, wmo); var vo = (uint)vertices.Count; foreach (var v in doodad.Vertices) vertices.Add((Vector3)Vector3.Transform(v, doodadTransform)); foreach (var t in doodad.Indices) indices.Add(new Triangle<uint>(TriangleType.Doodad, t.V0 + vo, t.V1 + vo, t.V2 + vo)); } } foreach (var group in model.Groups) { if ((group.LiquidVertices == null || group.LiquidVertices.Count == 0) || (group.LiquidIndices == null || group.LiquidIndices.Count == 0)) continue; var vo = (uint)vertices.Count; foreach (var v in group.LiquidVertices) vertices.Add((Vector3)Vector3.Transform(v, transform)); foreach (var t in group.LiquidIndices) indices.Add(new Triangle<uint>(t.Type, t.V1 + vo, t.V0 + vo, t.V2 + vo)); } }
private void GenerateWMOs() { if (ADT.Type != ADTType.Objects || ADT.MODF == null) return; var drawn = new HashSet<uint>(); for (int i = 0; i < MCRW.MODFEntryIndex.Length; i++) { var wmo = ADT.MODF.Entries[MCRW.MODFEntryIndex[i]]; if (drawn.Contains(wmo.UniqueId)) continue; drawn.Add(wmo.UniqueId); if (wmo.MWIDEntryIndex >= ADT.ModelPaths.Count) continue; var path = ADT.ModelPaths[(int)wmo.MWIDEntryIndex]; var model = new WMORoot(path); if (WMOVertices == null) WMOVertices = new List<Vector3>(1000); if (WMOIndices == null) WMOIndices = new List<Triangle<uint>>(1000); InsertWMOGeometry(wmo, model, WMOVertices, WMOIndices); } }
static void ReadWMO() { const string path = @"World\wmo\Northrend\HowlingFjord\DaggercapCave.wmo"; var wmo = new WMORoot(path); using (var sw = new StreamWriter(Path.GetFileNameWithoutExtension(path) + ".obj", false)) { //sw.WriteLine("o " + filename); var nf = System.Globalization.CultureInfo.InvariantCulture.NumberFormat; int vo = 0; foreach (var g in wmo.Groups) { foreach (var v in g.MOVT.Vertices) sw.WriteLine("v " + v.X.ToString(nf) + " " + v.Z.ToString(nf) + " " + v.Y.ToString(nf)); foreach (var t in g.MOVI.Indices) sw.WriteLine("f " + (t.V0 + vo + 1) + " " + (t.V1 + vo + 1) + " " + (t.V2 + vo + 1)); vo += g.MOVT.Vertices.Count(); if (g.LiquidVertices == null || g.LiquidIndices == null) continue; foreach (var v in g.LiquidVertices) sw.WriteLine("v " + v.X.ToString(nf) + " " + v.Z.ToString(nf) + " " + v.Y.ToString(nf)); foreach (var t in g.LiquidIndices) sw.WriteLine("f " + (t.V0 + vo + 1) + " " + (t.V1 + vo + 1) + " " + (t.V2 + vo + 1)); vo += g.LiquidVertices.Count; } } }
public byte[] Build() { var wdt = new WDT("World\\maps\\" + Dungeon + "\\" + Dungeon + ".wdt"); if (!wdt.IsGlobalModel || !wdt.IsValid) return null; Geometry = new Geometry.Geometry {}; var model = new WMORoot(wdt.ModelFile); Geometry.AddDungeon(model, wdt.ModelDefinition); if (Geometry.Vertices.Count == 0 && Geometry.Indices.Count == 0) throw new InvalidOperationException("Can't build mesh with empty geometry"); Geometry.SaveWavefrontObject($"{Dungeon}.obj"); Context = new RecastContext(); // get raw geometry - lots of slowness here float[] vertices; int[] triangles; byte[] areas; Geometry.GetRawData(out vertices, out triangles, out areas); Geometry.Indices.Clear(); float[] bmin, bmax; Geometry.CalculateBoundingBox(out bmin, out bmax); Geometry.Vertices.Clear(); // Allocate voxel heightfield where we rasterize our input data to. Heightfield hf; int width, height; Recast.CalcGridSize(bmin, bmax, Config.CellSize, out width, out height); if (!Context.CreateHeightfield(out hf, width, height, bmin, bmax, Config.CellSize, Config.CellHeight)) throw new OutOfMemoryException("CreateHeightfield ran out of memory"); // Find triangles which are walkable based on their slope and rasterize them. Context.ClearUnwalkableTriangles(Config.WalkableSlopeAngle, ref vertices, ref triangles, areas); Context.RasterizeTriangles(ref vertices, ref triangles, ref areas, hf, Config.WalkableClimb); // Once all geometry is rasterized, we do initial pass of filtering to // remove unwanted overhangs caused by the conservative rasterization // as well as filter spans where the character cannot possibly stand. Context.FilterLowHangingWalkableObstacles(Config.WalkableClimb, hf); Context.FilterLedgeSpans(Config.WalkableHeight, Config.WalkableClimb, hf); Context.FilterWalkableLowHeightSpans(Config.WalkableHeight, hf); // Compact the heightfield so that it is faster to handle from now on. // This will result in more cache coherent data as well as the neighbours // between walkable cells will be calculated. CompactHeightfield chf; if (!Context.BuildCompactHeightfield(Config.WalkableHeight, Config.WalkableClimb, hf, out chf)) throw new OutOfMemoryException("BuildCompactHeightfield ran out of memory"); hf.Delete(); // Erode the walkable area by agent radius. if (!Context.ErodeWalkableArea(Config.WalkableRadius, chf)) throw new OutOfMemoryException("ErodeWalkableArea ran out of memory"); // Prepare for region partitioning, by calculating distance field along the walkable surface. if (!Context.BuildDistanceField(chf)) throw new OutOfMemoryException("BuildDistanceField ran out of memory"); // Partition the walkable surface into simple regions without holes. if (!Context.BuildRegions(chf, Config.BorderSize, Config.MinRegionArea, Config.MergeRegionArea)) throw new OutOfMemoryException("BuildRegions ran out of memory"); // Create contours. ContourSet cset; if (!Context.BuildContours(chf, Config.MaxSimplificationError, Config.MaxEdgeLength, out cset)) throw new OutOfMemoryException("BuildContours ran out of memory"); // Build polygon navmesh from the contours. PolyMesh pmesh; if (!Context.BuildPolyMesh(cset, Config.MaxVertsPerPoly, out pmesh)) throw new OutOfMemoryException("BuildPolyMesh ran out of memory"); // Build detail mesh. PolyMeshDetail dmesh; if (!Context.BuildPolyMeshDetail(pmesh, chf, Config.DetailSampleDistance, Config.DetailSampleMaxError, out dmesh)) throw new OutOfMemoryException("BuildPolyMeshDetail ran out of memory"); chf.Delete(); cset.Delete(); // Set flags according to area types (e.g. Swim for Water) pmesh.MarkAll(); byte[] meshData; if (!Detour.CreateNavMeshData(out meshData, pmesh, dmesh, 0, 0, bmin, bmax, Config.WorldWalkableHeight, Config.WorldWalkableRadius, Config.WorldWalkableClimb, Config.CellSize, Config.CellHeight, Config.TileWidth, null)) { pmesh.Delete(); dmesh.Delete(); return null; } pmesh.Delete(); dmesh.Delete(); if (Directory.Exists(Dungeon)) Directory.Delete(Dungeon, true); Directory.CreateDirectory(Dungeon); if (meshData != null) File.WriteAllBytes(Dungeon + "\\" + Dungeon + ".dmesh", meshData); return meshData; }