public CompiledNavMesh FindSubgraphUnderPoint(float x, float y, float z, float maxFall = 20f) { CompiledNavMesh bestGraph = null; float bestDistance = maxFall; foreach (var sg in Subgraphs) { if (!sg.BoundsContainsXY(x, y)) { continue; } var node = sg.FindFloorUnderPoint(x, y, z, maxFall); if (node.blockIndex != -1) { float hitZ = NavMeshUtil.DecodeHeight(sg.Z1, sg.Z2, node.encZ); if (hitZ <= z) { float d = z - hitZ; if (d < bestDistance) { bestDistance = d; bestGraph = sg; } } } } return(bestGraph); }
public static CompiledNavMeshSet Load(Stream s) { var br = new BinaryReader(s); if (br.ReadInt32() != 0x30344C41) { throw new InvalidOperationException("invalid file format - expected CompiledNavMesh header"); } int count = br.ReadInt32(); var list = new List <CompiledNavMesh>(count); for (int i = 0; i < count; i++) { list.Add(CompiledNavMesh.Load(s)); } return(new CompiledNavMeshSet(list)); }
/* * height grid: * each entry: * 0xFF000000 - number of heights (if <= 1, inline. else index to list). * 0x00FFFFFF - index to heights, or 3 byte single height. max distinct = 16 million (4096^2). * * height format (3 bytes): * 0xFF - 8 directions, from top cw, top TR R BR bot BL left TL. * 0xFFFF - encoded height = (height_value - z1) * 0xFFFF / (z2-z1). * * if 8dir = 0xFF, space is impassable. impassable should always be inline (no index) with floor count 0. * * each node edge is implied by neighbor having a close height that is not blocked in that direction. */ public CompiledNavMesh Build(HashSet <EdgeVertex> subgraph, int startBX, int startBY, int newBlockWidth, int newBlockHeight, float x1, float y1, // world XY of the lower bound of the mesh bounding box. int z1, int z2) // min and max z values, range is used for encoding height { if (startBX < 0 || startBY < 0 || newBlockWidth <= 0 || newBlockHeight <= 0 || startBX + newBlockWidth > m_blockWidth || startBY + newBlockHeight > m_blockHeight) { throw new ArgumentOutOfRangeException(); } if (z1 < 0 || z2 <= z1) { throw new ArgumentOutOfRangeException(); } int endBX = startBX + newBlockWidth; int endBY = startBY + newBlockHeight; uint[] grid = new uint[newBlockWidth * newBlockHeight]; var multiheights = new List <byte>(newBlockWidth * newBlockHeight); // arbitrary capacity var bytes = new List <byte>(); // temp storage per cell int iSub = 0; // index to new grid for subgraph for (int y = startBY; y < endBY; y++) { int iAll = y * m_blockWidth + startBX; // index to floor cell for (int x = startBX; x < endBX; x++, iAll++, iSub++) { bytes.Clear(); if (m_floorData[iAll] != null) { foreach (var floor in m_floorData[iAll]) { ushort encHeight = NavMeshUtil.EncodeHeight(z1, z2, floor.Z100i / 100.0f); var flags = floor.DirectionFlags; if (flags == 0xFF) { continue; } // exclude vertices that are not part of the subgraph. if (!subgraph.Contains(new EdgeVertex { BX = (ushort)x, BY = (ushort)y, Z100i = floor.Z100i })) { continue; } bytes.Add(flags); bytes.Add((byte)(encHeight)); bytes.Add((byte)(encHeight >> 8)); } } if (bytes.Count == 3) { grid[iSub] = (1 << 24) | // count 1 ((uint)bytes[0] << 16) | // flags ((uint)bytes[2] << 8) | // encoded height bytes[1]; } else if (bytes.Count > 3) { if (bytes.Count > 255 * 3) { throw new InvalidOperationException("too many heights"); } int index = StoreBytesInMultiFloorSet(multiheights, bytes); grid[iSub] = (uint)((bytes.Count / 3) << 24) | (uint)index; } else { grid[iSub] = 0x00FF0000; } } } if (multiheights.Count > 0xFFFFFF) { throw new InvalidOperationException("height limit exceeded"); } Debug.WriteLine($"Compiled NavMesh {newBlockWidth}x{newBlockHeight}x{z2-z1} bytes: grid:{grid.Length*4}, Zs:{multiheights.Count}"); var compiledNavMesh = new CompiledNavMesh(newBlockWidth, newBlockHeight, m_step, m_maxZStep, x1, y1, z1, z2, grid, multiheights.ToArray()); compiledNavMesh.Validate(); return(compiledNavMesh); }