public void Dispose() { if (ObjWriter == null) { return; } DoorWriter.Flush(); DoorWriter.Dispose(); GeomSetWriter.Flush(); GeomSetWriter.Dispose(); ObjWriter.Save(); if (ObjWriter.Empty) { File.Delete(ObjWriter.File); } ObjWriter = null; }
public Zone2Obj(Zone2 zone) { Zone = zone; if (!Directory.Exists("zones")) { Directory.CreateDirectory("zones"); } string filename = Path.Combine("zones", string.Format("zone{0:D3}", zone.ID)); DoorWriter = new DoorWriter(filename + ".doors"); GeomSetWriter = new GeomSetWriter(filename + ".geomset"); ObjWriter = new WavefrontObjFile(filename + ".obj") { Scale = MESH_SCALE }; GeomSetWriter.WriteLoadMesh(filename + ".obj"); FirstPass = !NavmeshMgr.IsPathingEnabled(zone); }
private void ExtractDoor(NiFile model, Matrix4 worldMatrix, int fixtureid) { // take everything that is under a doorX node and use it for the hull... if (model == null) { return; } var doorVertices = new Dictionary <string, List <Vector3> >(); // find all trimeshs and tristrips foreach (var obj in model.ObjectsByRef.Values) { var avNode = obj as NiAVObject; if (avNode == null) { continue; } if (!IsMatched(avNode, new[] { "visible", "collidee", "collide" })) { continue; } var doorName = FindMatchRegex(avNode, new[] { DoorRegex }); if (doorName == string.Empty) { continue; } Vector3[] vertices = null; Triangle[] triangles = null; TryExtractTriShape(obj, worldMatrix, false, false, ref vertices, ref triangles); if (vertices == null) { TryExtractTriStrips(obj, worldMatrix, false, false, ref vertices, ref triangles); } if (vertices == null) { continue; } if (!doorVertices.ContainsKey(doorName)) { doorVertices.Add(doorName, new List <Vector3>()); } doorVertices[doorName].AddRange(vertices); } foreach (var key in doorVertices.Keys) { float hullMinZ = float.MaxValue; float hullMaxZ = float.MinValue; var verts = doorVertices[key].ToArray(); var pts = new List <PointF>(); foreach (Vector3 vert in verts) { hullMinZ = Math.Min(vert.Z, hullMinZ); hullMaxZ = Math.Max(vert.Z, hullMaxZ); pts.Add(new PointF(vert.X, vert.Y)); } MCvBox2D box = PointCollection.MinAreaRect(pts.ToArray()); float maxSize = box.size.Width; maxSize = Math.Max(maxSize, box.size.Height); maxSize = Math.Max(maxSize, hullMaxZ - hullMinZ); // There are some weird door ids in e.g. Jordheim (z120): "door01:0" e.g. -- how do they translate to IDs? var doorID = Regex.Match(key.Replace(":", ""), "([0-9]+)").Groups[1].Value; var heading = ((box.angle + (box.size.Width < box.size.Height ? 0.0 : 90.0 + 90.0)) * DEGREES_TO_HEADING) % 0x1000; if (heading < 0) { heading += 0x1000; } DoorWriter.WriteDoor(Zone.ID * 1000000 + fixtureid * 100 + int.Parse(doorID), model.FileName, (int)box.center.X, (int)box.center.Y, (int)hullMinZ, (int)heading, (int)maxSize); // Make sure we have a min of 20f for doors on width/height box.size.Width = Math.Max(20f, box.size.Width); box.size.Height = Math.Max(20f, box.size.Height); // Make sure the door touches the ground... hullMinZ -= 16.0f; var boxVertices = box.GetVertices(); GeomSetWriter.WriteConvexVolume(boxVertices.Length, hullMinZ, hullMaxZ, CEM.GeomSetWriter.eAreas.Door); foreach (var vert in boxVertices) { GeomSetWriter.WriteConvexVolumeVertex(new Vector3(vert.X, vert.Y, hullMinZ)); // debug } } }
private void ExtractLadder(NiFile model, Matrix4 worldMatrix) { // take everything that is under a climbX node and use it for the hull... if (model == null) { return; } var climbVertices = new Dictionary <string /* climbXXX */, List <Vector3> >(); // Find all trimeshs and tristrips that belong to climb nodes foreach (var obj in model.ObjectsByRef.Values) { var avNode = obj as NiAVObject; if (avNode == null) { continue; } var climbName = FindMatchRegex(avNode, new[] { LadderRegex }); if (climbName == string.Empty) { // No ladder in node continue; } Vector3[] vertices = null; Triangle[] triangles = null; TryExtractTriShape(obj, worldMatrix, false, false, ref vertices, ref triangles); if (vertices == null) { TryExtractTriStrips(obj, worldMatrix, false, false, ref vertices, ref triangles); } if (vertices == null) { continue; } if (!climbVertices.ContainsKey(climbName)) { climbVertices.Add(climbName, new List <Vector3>()); } climbVertices[climbName].AddRange(vertices); } // Compute each Ladder individually foreach (var climbNode in climbVertices.Keys) { var minZ = float.MaxValue; var maxZ = float.MinValue; var verts = climbVertices[climbNode]; // Find min/max foreach (var vert in verts) { minZ = Math.Min(vert.Z, minZ); maxZ = Math.Max(vert.Z, maxZ); } // Divide points into two sets var minVecs = new List <Vector3>(); var maxVecs = new List <Vector3>(); foreach (var vert in verts) { if (vert.Z < minZ + (maxZ - minZ) / 2) { minVecs.Add(vert); } else { maxVecs.Add(vert); } } if (minVecs.Count == 0 || maxVecs.Count == 0) { throw new InvalidDataException("ladder seems invalid"); } var minExt = new Vector3(128, 128, 128); var maxExt = new Vector3(128, 128, 128); var minPt = GetClosestNavmeshPoint(Average(minVecs), minExt.X, minExt.Y, minExt.Z); var maxPt = GetClosestNavmeshPoint(Average(maxVecs), maxExt.X, maxExt.Y, maxExt.Z); if (minPt == Vector3.Zero || maxPt == Vector3.Zero) { Log.Error("Could not fit ladder {0} at {2} {3} {4} to navmesh in {1}; ignoring", climbNode, Zone.Name, minVecs[0].X, minVecs[0].Y, minVecs[0].Z); return; } // Write the off-mesh connection from min to max that is the primary ladder. This connection has to be connected the navmesh. Log.Debug("Extracted Ladder: {0} with verts={1}", climbNode, verts.Count); // Have some visual markers that show us how well we can fit ladders to the navmesh if (FirstPass) { foreach (var endPoint in new[] { new[] { minPt, minExt }, new[] { maxPt, maxExt } }) { var pt = endPoint[0]; var ext = endPoint[1]; var area = (pt == minPt) ? GeomSetWriter.eAreas.Road : GeomSetWriter.eAreas.Grass; GeomSetWriter.WriteConvexVolume(4, pt.Z - ext.Z, pt.Z + ext.Z, area); GeomSetWriter.WriteConvexVolumeVertex(new Vector3(pt.X - ext.X, pt.Y - ext.Y, pt.Z - ext.Z)); GeomSetWriter.WriteConvexVolumeVertex(new Vector3(pt.X + ext.X, pt.Y - ext.Y, pt.Z - ext.Z)); GeomSetWriter.WriteConvexVolumeVertex(new Vector3(pt.X + ext.X, pt.Y + ext.Y, pt.Z - ext.Z)); GeomSetWriter.WriteConvexVolumeVertex(new Vector3(pt.X - ext.X, pt.Y + ext.Y, pt.Z - ext.Z)); } } else { GeomSetWriter.WriteOffMeshConnection(minPt, maxPt, true, GeomSetWriter.eAreas.Jump, GeomSetWriter.eFlags.Jump); } } }
private void ExportHeightmap() { int[,] heightmap = LoadHeightmapData(); #region rivers // Export rivers List <List <Vector3> > riverPoints = Zone.GetRiverPoints(); int numWater = riverPoints.Count; int[] waterHeights = Zone.GetWaterHeights(); for (int i = 0; i < numWater; i++) { int points = riverPoints[i].Count / 2; int index = 0; while (index < points) { int toWrite = Math.Min(points - index, 2); GeomSetWriter.WriteConvexVolume(toWrite * 2, 0, waterHeights[i], CEM.GeomSetWriter.eAreas.Water); for (int j = 0; j < toWrite; j++) { float x = riverPoints[i][(index + j) * 2].X; float y = riverPoints[i][(index + j) * 2].Y; float z = waterHeights[i]; GeomSetWriter.WriteConvexVolumeVertex(new Vector3(x, y, z)); } for (int j = toWrite - 1; j >= 0; j--) { float x = riverPoints[i][(index + j) * 2 + 1].X; float y = riverPoints[i][(index + j) * 2 + 1].Y; float z = waterHeights[i]; GeomSetWriter.WriteConvexVolumeVertex(new Vector3(x, y, z)); } if (points - index > toWrite) { index += toWrite - 1; } else { index += toWrite; } } } #endregion // Export Heightmap (but pay attention to water map) var water = Zone.LoadWaterMap(); for (int sx = 0; sx < 8; sx++) { for (int sy = 0; sy < 8; sy++) { Matrix4 myWorldMatrix = Matrix4.CreateTranslation(Zone.OffsetVector); myWorldMatrix *= Matrix4.CreateTranslation(8192 * (sx), 8192 * (sy), 0); const int xVectors = 33; const int yVectors = 33; var myVerticesFakeWater = new List <Vector3>(); // with water.Z var myTriangles = new List <Triangle>(); var myVerticesReal = new List <Vector3>(); // no water.Z for (int y = 0; y < yVectors; y++) { for (int x = 0; x < xVectors; x++) { int z = 0; var waterZ = -1; if (sx == 7 && x == (xVectors - 1)) { if (sy == 7 && y == (yVectors - 1)) { z = heightmap[sx * 32 + (x - 1), sy * 32 + (y - 1)]; waterZ = water[sx * 32 + (x - 1), sy * 32 + (y - 1)]; } else { z = heightmap[sx * 32 + (x - 1), sy * 32 + y]; waterZ = water[sx * 32 + (x - 1), sy * 32 + y]; } } else if (sy == 7 && y == (yVectors - 1)) { z = heightmap[sx * 32 + x, sy * 32 + (y - 1)]; waterZ = water[sx * 32 + x, sy * 32 + (y - 1)]; } else { z = heightmap[sx * 32 + x, sy * 32 + y]; waterZ = water[sx * 32 + x, sy * 32 + y]; } Vector3 vector = Vector3.Transform(new Vector3(x * 256, y * 256, z), myWorldMatrix); myVerticesReal.Add(vector); if (waterZ != 255 && waterZ < waterHeights.Length && waterHeights[waterZ] > vector.Z) { vector.Z = waterHeights[waterZ]; } myVerticesFakeWater.Add(new Vector3(vector.X, vector.Y, vector.Z)); if (y == yVectors - 1 || x == xVectors - 1) { continue; } myTriangles.Add(new Triangle((ushort)(x + ((y + 1) * xVectors)), (ushort)(x + 1 + (y * xVectors)), (ushort)(x + (y * xVectors)))); myTriangles.Add(new Triangle((ushort)(x + ((y + 1) * xVectors)), (ushort)(x + 1 + ((y + 1) * xVectors)), (ushort)(x + 1 + (y * xVectors)))); } } //ObjWriter.AddMesh(myVerticesReal.ToArray(), myTriangles.ToArray()); ObjWriter.AddMesh(myVerticesFakeWater.ToArray(), myTriangles.ToArray()); } } }