diff --git a/Source/Framework/Constants/MapConst.cs b/Source/Framework/Constants/MapConst.cs index 093bbd6ca..3889ca6d5 100644 --- a/Source/Framework/Constants/MapConst.cs +++ b/Source/Framework/Constants/MapConst.cs @@ -66,7 +66,7 @@ namespace Framework.Constants public const uint MapLiquidMagic = 0x51494C4D; //"MLIQ"; public const uint mmapMagic = 0x4D4D4150; // 'MMAP' - public const int mmapVersion = 10; + public const int mmapVersion = 11; public const string VMapMagic = "VMAP_4.A"; public const float VMAPInvalidHeightValue = -200000.0f; diff --git a/Source/Framework/GameMath/Box.cs b/Source/Framework/GameMath/Box.cs index bf0d41338..82209bc96 100644 --- a/Source/Framework/GameMath/Box.cs +++ b/Source/Framework/GameMath/Box.cs @@ -37,7 +37,7 @@ namespace Framework.GameMath for (int i = 0; i < 3; ++i) { - if (!float.IsInfinity(_edgeVector[i].Length())) + if (!float.IsFinite(_edgeVector[i].Length())) { finiteExtent = false; // If the extent is infinite along an axis, make the center zero to avoid NaNs @@ -95,7 +95,7 @@ namespace Framework.GameMath public bool isFinite() { - return float.IsInfinity(_volume); + return float.IsFinite(_volume); } } } diff --git a/Source/Framework/RecastDetour/Detour/DetourCommon.cs b/Source/Framework/RecastDetour/Detour/DetourCommon.cs index 64136917c..8bcdafdac 100644 --- a/Source/Framework/RecastDetour/Detour/DetourCommon.cs +++ b/Source/Framework/RecastDetour/Detour/DetourCommon.cs @@ -211,6 +211,8 @@ public static partial class Detour /// @param[out] h The resulting height. public static bool dtClosestHeightPointTriangle(float[] p, int pStart, float[] a, int aStart, float[] b, int bStart, float[] c, int cStart, ref float h) { + const float EPS = 1e-6f; + float[] v0 = new float[3]; float[] v1 = new float[3]; float[] v2 = new float[3]; @@ -218,25 +220,24 @@ public static partial class Detour dtVsub(v1, 0, b, bStart, a, aStart); dtVsub(v2, 0, p, pStart, a, aStart); - float dot00 = dtVdot2D(v0, v0); - float dot01 = dtVdot2D(v0, v1); - float dot02 = dtVdot2D(v0, v2); - float dot11 = dtVdot2D(v1, v1); - float dot12 = dtVdot2D(v1, v2); + // Compute scaled barycentric coordinates + float denom = v0[0] * v1[2] - v0[2] * v1[0]; + if (MathF.Abs(denom) < EPS) + return false; - // Compute barycentric coordinates - float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01); - float u = (dot11 * dot02 - dot01 * dot12) * invDenom; - float v = (dot00 * dot12 - dot01 * dot02) * invDenom; + float u = v1[2] * v2[0] - v1[0] * v2[2]; + float v = v0[0] * v2[2] - v0[2] * v2[0]; - // The (sloppy) epsilon is needed to allow to get height of points which - // are interpolated along the edges of the triangles. - const float EPS = 1e-4f; - - // If point lies inside the triangle, return interpolated ycoord. - if (u >= -EPS && v >= -EPS && (u + v) <= 1 + EPS) + if (denom < 0) { - h = a[aStart + 1] + v0[1] * u + v1[1] * v; + denom = -denom; + u = -u; + v = -v; + } + + if (u >= 0.0f && v >= 0.0f && (u + v) <= denom) + { + h = a[1] + (v0[1] * u + v1[1] * v) / denom; return true; } @@ -779,6 +780,30 @@ public static partial class Detour float d = dtVdistSqr(p0, p0Start, p1, p1Start); return d < thr; } + + /// Checks that the specified vector's components are all finite. + /// @param[in] v A point. [(x, y, z)] + /// @return True if all of the point's components are finite, i.e. not NaN + /// or any of the infinities. + public static bool dtVisfinite(float[] v) + { + + bool result = + float.IsFinite(v[0]) && + float.IsFinite(v[1]) && + float.IsFinite(v[2]); + + return result; + } + + /// Checks that the specified vector's 2D components are finite. + /// @param[in] v A point. [(x, y, z)] + public static bool dtVisfinite2D(float[] v) + { + bool result = float.IsFinite(v[0]) && float.IsFinite(v[2]); + return result; + } + /// Derives the dot product of two vectors on the xz-plane. (@p u . @p v) /// @param[in] u A vector [(x, y, z)] /// @param[in] v A vector [(x, y, z)] diff --git a/Source/Framework/RecastDetour/Detour/DetourNavMesh.cs b/Source/Framework/RecastDetour/Detour/DetourNavMesh.cs index 02c3143f1..75025567a 100644 --- a/Source/Framework/RecastDetour/Detour/DetourNavMesh.cs +++ b/Source/Framework/RecastDetour/Detour/DetourNavMesh.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Linq; /** @typedef dtPolyRef @par @@ -725,68 +726,84 @@ public static partial class Detour } } - public void closestPointOnPoly(dtPolyRef polyRef, float[] pos, int posStart, float[] closest, ref bool posOverPoly) + void closestPointOnDetailEdges(bool onlyBoundary, dtMeshTile tile, dtPoly poly, float[] pos, float[] closest) { - dtMeshTile tile = null; - dtPoly poly = null; - uint ip = 0; - getTileAndPolyByRefUnsafe(polyRef, ref tile, ref poly, ref ip); - - // Off-mesh connections don't have detail polygons. - if (poly.getType() == (byte)dtPolyTypes.DT_POLYTYPE_OFFMESH_CONNECTION) - { - //const float* v0 = &tile.verts[poly.verts[0]*3]; - //const float* v1 = &tile.verts[poly.verts[1]*3]; - int v0Start = poly.verts[0] * 3; - int v1Start = poly.verts[1] * 3; - float d0 = dtVdist(pos, posStart, tile.verts, v0Start); - float d1 = dtVdist(pos, posStart, tile.verts, v1Start); - float u = d0 / (d0 + d1); - dtVlerp(closest, 0, tile.verts, v0Start, tile.verts, v1Start, u); - - posOverPoly = false; - return; - } - - //uint ip = (uint)(poly - tile.polys); + uint ip = (uint)tile.polys.ToList().IndexOf(poly); + dtPolyDetail pd = tile.detailMeshes[ip]; + + float dmin = float.MaxValue; + float tmin = 0; + int pmin = 0; + int pmax = 0; + float[] v = new float[0]; + + for (int i = 0; i < pd.triCount; i++) + { + int trisIndex = (int)((pd.triBase + i) * 4); + const int ANY_BOUNDARY_EDGE = + ((int)dtDetailTriEdgeFlags.DT_DETAIL_EDGE_BOUNDARY << 0) | + ((int)dtDetailTriEdgeFlags.DT_DETAIL_EDGE_BOUNDARY << 2) | + ((int)dtDetailTriEdgeFlags.DT_DETAIL_EDGE_BOUNDARY << 4); + if (onlyBoundary && (tile.detailTris[trisIndex + 3] & ANY_BOUNDARY_EDGE) == 0) + continue; + + v = new float[3]; + for (int j = 0; j < 3; ++j) + { + if (tile.detailTris[trisIndex + j] < poly.vertCount) + v[j] = tile.verts[poly.verts[tile.detailTris[trisIndex + j]] * 3]; + else + v[j] = tile.detailVerts[(pd.vertBase + (tile.detailTris[trisIndex + j] - poly.vertCount)) * 3]; + } + + for (int k = 0, j = 2; k < 3; j = k++) + { + if ((dtGetDetailTriEdgeFlags(tile.detailTris[trisIndex + 3], j) & (int)dtDetailTriEdgeFlags.DT_DETAIL_EDGE_BOUNDARY) == 0 && + (onlyBoundary || tile.detailTris[trisIndex + j] < tile.detailTris[trisIndex + k])) + { + // Only looking at boundary edges and this is internal, or + // this is an inner edge that we will see again or have already seen. + continue; + } + + float t = 0; + float d = dtDistancePtSegSqr2D(pos, 0, v, j, v, k, ref t); + if (d < dmin) + { + dmin = d; + tmin = t; + pmin = j; + pmax = k; + } + } + } + + dtVlerp(closest, 0, v, pmin, v, pmax, tmin); + } + + public bool getPolyHeight(dtMeshTile tile, dtPoly poly, float[] pos, float height) + { + // Off-mesh connections do not have detail polys and getting height + // over them does not make sense. + if (poly.getType() == (byte)dtPolyTypes.DT_POLYTYPE_OFFMESH_CONNECTION) + return false; + + uint ip = (uint)tile.polys.ToList().IndexOf(poly); dtPolyDetail pd = tile.detailMeshes[ip]; - // Clamp point to be inside the polygon. float[] verts = new float[DT_VERTS_PER_POLYGON * 3]; - float[] edged = new float[DT_VERTS_PER_POLYGON]; - float[] edget = new float[DT_VERTS_PER_POLYGON]; + int nv = poly.vertCount; for (int i = 0; i < nv; ++i) { dtVcopy(verts, i * 3, tile.verts, poly.verts[i] * 3); } - dtVcopy(closest, 0, pos, posStart); - if (!dtDistancePtPolyEdgesSqr(pos, posStart, verts, nv, edged, edget)) - { - // Point is outside the polygon, dtClamp to nearest edge. - float dmin = float.MaxValue; - int imin = -1; - for (int i = 0; i < nv; ++i) - { - if (edged[i] < dmin) - { - dmin = edged[i]; - imin = i; - } - } - //float[] va = &verts[imin*3]; - //const float* vb = &verts[((imin+1)%nv)*3]; - int vaStart = imin * 3; - int vbStart = ((imin + 1) % nv) * 3; - dtVlerp(closest, 0, verts, vaStart, verts, vbStart, edget[imin]); + if (!dtPointInPolygon(pos, verts, nv)) + return false; - posOverPoly = false; - } - else - { - posOverPoly = true; - } + if (height == 0) + return true; // Find height at the location. for (int j = 0; j < pd.triCount; ++j) @@ -812,12 +829,54 @@ public static partial class Detour } } float h = .0f; - if (dtClosestHeightPointTriangle(closest, posStart, vArrays[0], vIndices[0], vArrays[1], vIndices[1], vArrays[2], vIndices[2], ref h)) + if (dtClosestHeightPointTriangle(pos, 0, vArrays[0], vIndices[0], vArrays[1], vIndices[1], vArrays[2], vIndices[2], ref h)) { - closest[1] = h; - break; + height = h; + return true; } } + + // If all triangle checks failed above (can happen with degenerate triangles + // or larger floating point values) the point is on an edge, so just select + // closest. This should almost never happen so the extra iteration here is + // ok. + float[] closest = new float[3]; + closestPointOnDetailEdges(false, tile, poly, pos, closest); + height = closest[1]; + return true; + } + + public void closestPointOnPoly(dtPolyRef polyRef, float[] pos, float[] closest, ref bool posOverPoly) + { + + dtMeshTile tile = new(); + dtPoly poly = new(); + getTileAndPolyByRefUnsafe(polyRef, ref tile, ref poly); + + dtVcopy(closest, pos); + if (getPolyHeight(tile, poly, pos, closest[1])) + { + if (posOverPoly) + posOverPoly = true; + return; + } + + if (posOverPoly) + posOverPoly = false; + + // Off-mesh connections don't have detail polygons. + if (poly.getType() == (byte)dtPolyTypes.DT_POLYTYPE_OFFMESH_CONNECTION) + { + int v0 = poly.verts[0] * 3; + int v1 = poly.verts[1] * 3; + float t = 0; + dtDistancePtSegSqr2D(pos, 0, tile.verts, v0, tile.verts, v1, ref t); + dtVlerp(closest, 0, tile.verts, v0, tile.verts, v1, t); + return; + } + + // Outside poly that is not an offmesh connection. + closestPointOnDetailEdges(true, tile, poly, pos, closest); } public dtPolyRef findNearestPolyInTile(dtMeshTile tile, float[] center, int centerStart, float[] halfExtents, float[] nearestPt) @@ -841,7 +900,7 @@ public static partial class Detour float[] diff = new float[3]; bool posOverPoly = false; float d = 0; - closestPointOnPoly(polyRef, center, centerStart, closestPtPoly, ref posOverPoly); + closestPointOnPoly(polyRef, center, closestPtPoly, ref posOverPoly); // If a point is directly over a polygon and closer than // climb height, favor that instead of straight line nearest point. @@ -996,7 +1055,7 @@ public static partial class Detour // Make sure the location is free. if (getTileAt(header.x, header.y, header.layer) != null) - return DT_FAILURE; + return DT_FAILURE | DT_ALREADY_OCCUPIED; // Allocate a tile. dtMeshTile tile = null; diff --git a/Source/Framework/RecastDetour/Detour/DetourNavMeshBuilder.cs b/Source/Framework/RecastDetour/Detour/DetourNavMeshBuilder.cs index 85036e242..5ba50d1da 100644 --- a/Source/Framework/RecastDetour/Detour/DetourNavMeshBuilder.cs +++ b/Source/Framework/RecastDetour/Detour/DetourNavMeshBuilder.cs @@ -80,6 +80,11 @@ public static partial class Detour DT_RAYCAST_USE_COSTS = 0x01, ///< Raycast should calculate movement cost along the ray and fill RaycastHit::cost }; + enum dtDetailTriEdgeFlags + { + DT_DETAIL_EDGE_BOUNDARY = 0x01, ///< Detail triangle edge is part of the poly boundary + }; + /// Flags representing the type of a navigation mesh polygon. public enum dtPolyTypes { @@ -616,7 +621,8 @@ public static partial class Detour /// The detail mesh's unique vertices. [(x, y, z) * dtMeshHeader::detailVertCount] public float[] detailVerts; - /// The detail mesh's triangles. [(vertA, vertB, vertC) * dtMeshHeader::detailTriCount] + /// The detail mesh's triangles. [(vertA, vertB, vertC, triFlags) * dtMeshHeader::detailTriCount]. + /// See dtDetailTriEdgeFlags and dtGetDetailTriEdgeFlags. public byte[] detailTris; /// The tile bounding volume nodes. [Size: dtMeshHeader::bvNodeCount] @@ -631,6 +637,15 @@ public static partial class Detour public dtMeshTile next; //< The next free tile, or the next tile in the spatial grid. }; + /// Get flags for edge in detail triangle. + /// @param triFlags[in] The flags for the triangle (last component of detail vertices above). + /// @param edgeIndex[in] The index of the first vertex of the edge. For instance, if 0, + /// returns flags for edge AB. + public static int dtGetDetailTriEdgeFlags(byte triFlags, int edgeIndex) + { + return (triFlags >> (edgeIndex * 2)) & 0x3; + } + /// Configuration parameters used to define multi-tile navigation meshes. /// The values are used to allocate space during the initialization of a navigation mesh. // @see dtNavMesh::init() diff --git a/Source/Framework/RecastDetour/Detour/DetourNavMeshQuery.cs b/Source/Framework/RecastDetour/Detour/DetourNavMeshQuery.cs index 0dbf56011..919a9cc82 100644 --- a/Source/Framework/RecastDetour/Detour/DetourNavMeshQuery.cs +++ b/Source/Framework/RecastDetour/Detour/DetourNavMeshQuery.cs @@ -36,6 +36,7 @@ using System; using System.Diagnostics; using dtPolyRef = System.UInt64; using dtStatus = System.UInt32; +using System.Collections.Generic; // Define DT_VIRTUAL_QUERYFILTER if you wish to derive a custom filter from dtQueryFilter. // On certain platforms indirect or virtual function call is expensive. The default @@ -310,6 +311,9 @@ public static partial class Detour { Debug.Assert(m_nav != null); + if (filter == null || frand == null || randomPt == null) + return DT_FAILURE | DT_INVALID_PARAM; + // Randomly pick one tile. Assume that all tiles cover roughly the same area. dtMeshTile tile = null; float tsum = 0.0f; @@ -409,14 +413,17 @@ public static partial class Detour /// @param[out] randomRef The reference id of the random location. /// @param[out] randomPt The random location. [(x, y, z)] // @returns The status flags for the query. - public dtStatus findRandomPointAroundCircle(dtPolyRef startRef, float[] centerPos, float radius, dtQueryFilter filter, randomFloatGenerator frand, ref dtPolyRef randomRef, ref float[] randomPt) + public dtStatus findRandomPointAroundCircle(dtPolyRef startRef, float[] centerPos, float maxRadius, dtQueryFilter filter, randomFloatGenerator frand, ref dtPolyRef randomRef, ref float[] randomPt) { Debug.Assert(m_nav != null); Debug.Assert(m_nodePool != null); Debug.Assert(m_openList != null); // Validate input - if (startRef == 0 || !m_nav.isValidPolyRef(startRef)) + if (!m_nav.isValidPolyRef(startRef) || + centerPos == null || !dtVisfinite(centerPos) || + maxRadius < 0 || !float.IsFinite(maxRadius) || + filter == null || frand == null || randomPt == null) return DT_FAILURE | DT_INVALID_PARAM; dtMeshTile startTile = null; @@ -439,7 +446,7 @@ public static partial class Detour dtStatus status = DT_SUCCESS; - float radiusSqr = dtSqr(radius); + float radiusSqr = dtSqr(maxRadius); float areaSum = 0.0f; dtMeshTile randomTile = null; @@ -617,104 +624,13 @@ public static partial class Detour public dtStatus closestPointOnPoly(dtPolyRef polyRef, float[] pos, float[] closest, ref bool posOverPoly) { Debug.Assert(m_nav != null); - dtMeshTile tile = null; - dtPoly poly = null; - uint ip = 0; - if (dtStatusFailed(m_nav.getTileAndPolyByRef(polyRef, ref tile, ref poly, ref ip))) - return DT_FAILURE | DT_INVALID_PARAM; - if (tile == null) + + if (!m_nav.isValidPolyRef(polyRef) || + pos == null || !dtVisfinite(pos) || + closest == null) return DT_FAILURE | DT_INVALID_PARAM; - // Off-mesh connections don't have detail polygons. - if (poly.getType() == (byte)dtPolyTypes.DT_POLYTYPE_OFFMESH_CONNECTION) - { - //const float* v0 = &tile.verts[poly.verts[0]*3]; - //const float* v1 = &tile.verts[poly.verts[1]*3]; - int v0Start = poly.verts[0] * 3; - int v1Start = poly.verts[1] * 3; - float d0 = dtVdist(pos, 0, tile.verts, v0Start); - float d1 = dtVdist(pos, 0, tile.verts, v1Start); - float u = d0 / (d0 + d1); - dtVlerp(closest, 0, tile.verts, v0Start, tile.verts, v1Start, u); - //if (posOverPoly) - posOverPoly = false; - return DT_SUCCESS; - } - - //uint ip = (uint)(poly - tile.polys); - dtPolyDetail pd = tile.detailMeshes[ip]; - - // Clamp point to be inside the polygon. - float[] verts = new float[DT_VERTS_PER_POLYGON * 3]; - float[] edged = new float[DT_VERTS_PER_POLYGON]; - float[] edget = new float[DT_VERTS_PER_POLYGON]; - int nv = poly.vertCount; - for (int i = 0; i < nv; ++i) - { - dtVcopy(verts, i * 3, tile.verts, poly.verts[i] * 3); - } - - dtVcopy(closest, pos); - if (!dtDistancePtPolyEdgesSqr(pos, 0, verts, nv, edged, edget)) - { - // Point is outside the polygon, dtClamp to nearest edge. - float dmin = float.MaxValue; - int imin = -1; - for (int i = 0; i < nv; ++i) - { - if (edged[i] < dmin) - { - dmin = edged[i]; - imin = i; - } - } - //const float* va = &verts[imin*3]; - //const float* vb = &verts[((imin+1)%nv)*3]; - int vaStart = imin * 3; - int vbStart = ((imin + 1) % nv) * 3; - dtVlerp(closest, 0, verts, vaStart, verts, vbStart, edget[imin]); - - //if (posOverPoly) - posOverPoly = false; - } - else - { - //if (posOverPoly) - posOverPoly = true; - } - - // Find height at the location. - for (int j = 0; j < pd.triCount; ++j) - { - //byte[] t = &tile.detailTris[(pd.triBase+j)*4]; - //const float* v[3]; - int tStart = (int)(pd.triBase + j) * 4; - int[] vStarts = new int[3]; - float[][] vSrc = new float[3][]; - for (int k = 0; k < 3; ++k) - { - byte tk = tile.detailTris[tStart + k]; - byte vCount = poly.vertCount; - if (tk < vCount) - { - //v[k] = &tile.verts[poly.verts[tile.detailTris[tStart + k]]*3]; - vStarts[k] = poly.verts[tk] * 3; - vSrc[k] = tile.verts; - } - else - { - //v[k] = &tile.detailVerts[(pd.vertBase+(t[k]-poly.vertCount))*3]; - vStarts[k] = (int)(pd.vertBase + (tk - vCount)) * 3; - vSrc[k] = tile.detailVerts; - } - } - float h = .0f; - if (dtClosestHeightPointTriangle(closest, 0, vSrc[0], vStarts[0], vSrc[1], vStarts[1], vSrc[2], vStarts[2], ref h)) - { - closest[1] = h; - break; - } - } + m_nav.closestPointOnPoly(polyRef, pos, closest, ref posOverPoly); return DT_SUCCESS; } @@ -745,6 +661,9 @@ public static partial class Detour if (dtStatusFailed(m_nav.getTileAndPolyByRef(polyRef, ref tile, ref poly))) return DT_FAILURE | DT_INVALID_PARAM; + if (pos == null || !dtVisfinite(pos) || closest == null) + return DT_FAILURE | DT_INVALID_PARAM; + // Collect vertices. float[] verts = new float[DT_VERTS_PER_POLYGON * 3]; float[] edged = new float[DT_VERTS_PER_POLYGON]; @@ -792,10 +711,10 @@ public static partial class Detour // @returns The status flags for the query. // @par /// - /// Will return #DT_FAILURE if the provided position is outside the xz-bounds + /// Will return #DT_FAILURE | DT_INVALID_PARAM if the provided position is outside the xz-bounds /// of the polygon. /// - public dtStatus getPolyHeight(dtPolyRef polyRef, float[] pos, ref float height) + public dtStatus getPolyHeight(dtPolyRef polyRef, float[] pos, ref float height) { Debug.Assert(m_nav != null); @@ -805,57 +724,26 @@ public static partial class Detour if (dtStatusFailed(m_nav.getTileAndPolyByRef(polyRef, ref tile, ref poly, ref ip))) return DT_FAILURE | DT_INVALID_PARAM; + if (pos == null || !dtVisfinite2D(pos)) + return DT_FAILURE | DT_INVALID_PARAM; + + // We used to return success for offmesh connections, but the + // getPolyHeight in DetourNavMesh does not do this, so special + // case it here. if (poly.getType() == (byte)dtPolyTypes.DT_POLYTYPE_OFFMESH_CONNECTION) { //const float* v0 = &tile.verts[poly.verts[0]*3]; //const float* v1 = &tile.verts[poly.verts[1]*3]; int v0Start = poly.verts[0] * 3; int v1Start = poly.verts[1] * 3; - float d0 = dtVdist2D(pos, 0, tile.verts, v0Start); - float d1 = dtVdist2D(pos, 0, tile.verts, v1Start); - float u = d0 / (d0 + d1); + float t = 0; + dtDistancePtSegSqr2D(pos, 0, tile.verts, v0Start, tile.verts, v1Start, ref t); //if (height) - height = tile.verts[v0Start + 1] + (tile.verts[v1Start + 1] - tile.verts[v0Start + 1]) * u; + height = tile.verts[v0Start + 1] + (tile.verts[v1Start + 1] - tile.verts[v0Start + 1]) * t; return DT_SUCCESS; } - else - { - //const uint ip = (uint)(poly - tile.polys); - dtPolyDetail pd = tile.detailMeshes[ip]; - for (int j = 0; j < pd.triCount; ++j) - { - //byte[] t = tile.detailTris[(pd.triBase+j)*4] ; - //float* v[3]; - int tStart = (int)(pd.triBase + j) * 4; - int[] vStarts = new int[3]; - float[][] vSrc = new float[3][]; - for (int k = 0; k < 3; ++k) - { - if (tile.detailTris[tStart + k] < poly.vertCount) - { - //v[k] = &tile.verts[poly.verts[tile.detailTris[tStart + k]]*3]; - vStarts[k] = poly.verts[tile.detailTris[tStart + k]] * 3; - vSrc[k] = tile.verts; - } - else - { - //v[k] = &tile.detailVerts[(pd.vertBase+(tile.detailTris[tStart + k]-poly.vertCount))*3]; - vStarts[k] = (int)(pd.vertBase + (tile.detailTris[tStart + k] - poly.vertCount)) * 3; - vSrc[k] = tile.detailVerts; - } - } - float h = .0f; - if (dtClosestHeightPointTriangle(pos, 0, vSrc[0], vStarts[0], vSrc[1], vStarts[1], vSrc[2], vStarts[2], ref h)) - { - //if (height) - height = h; - return DT_SUCCESS; - } - } - } - - return DT_FAILURE | DT_INVALID_PARAM; + return m_nav.getPolyHeight(tile, poly, pos, height) ? DT_SUCCESS : DT_FAILURE | DT_INVALID_PARAM; } // @} @@ -884,6 +772,8 @@ public static partial class Detour nearestRef = 0; + // queryPolygons below will check rest of params + dtFindNearestPolyQuery query = new(this, center); dtStatus status = queryPolygons(center, halfExtents, filter, query); @@ -1049,6 +939,13 @@ public static partial class Detour { Debug.Assert(m_nav != null); + if (center == null || !dtVisfinite(center) || + halfExtents == null || !dtVisfinite(halfExtents) || + filter == null || query == null) + { + return DT_FAILURE | DT_INVALID_PARAM; + } + float[] bmin = new float[3]; float[] bmax = new float[3]; dtVsub(bmin, center, halfExtents); @@ -1106,12 +1003,11 @@ public static partial class Detour pathCount = 0; - if (maxPath <= 0) - return DT_FAILURE | DT_INVALID_PARAM; - // Validate input if (!m_nav.isValidPolyRef(startRef) || !m_nav.isValidPolyRef(endRef) || - startPos == null || endPos == null || filter == null || maxPath <= 0 || path == null) + startPos == null || !dtVisfinite(startPos) || + endPos == null || !dtVisfinite(endPos) || + filter == null || path == null || maxPath <= 0) return DT_FAILURE | DT_INVALID_PARAM; if (startRef == endRef) @@ -1372,11 +1268,10 @@ public static partial class Detour m_query.options = options; m_query.raycastLimitSqr = float.MaxValue; - if (startRef == 0 || endRef == 0) - return DT_FAILURE | DT_INVALID_PARAM; - // Validate input - if (!m_nav.isValidPolyRef(startRef) || !m_nav.isValidPolyRef(endRef)) + if (!m_nav.isValidPolyRef(startRef) || !m_nav.isValidPolyRef(endRef) || + startPos == null || !dtVisfinite(startPos) || + endPos == null || !dtVisfinite(endPos) || filter == null) return DT_FAILURE | DT_INVALID_PARAM; // trade quality with performance? @@ -1646,6 +1541,9 @@ public static partial class Detour { pathCount = 0; + if (path == null || maxPath <= 0) + return DT_FAILURE | DT_INVALID_PARAM; + if (dtStatusFailed(m_query.status)) { // Reset query. @@ -1747,10 +1645,8 @@ public static partial class Detour { pathCount = 0; - if (existingSize == 0) - { - return DT_FAILURE; - } + if (existing == null || existingSize <= 0 || path == null || maxPath <= 0) + return DT_FAILURE | DT_INVALID_PARAM; if (dtStatusFailed(m_query.status)) { @@ -1971,10 +1867,10 @@ public static partial class Detour straightPathCount = 0; - if (maxStraightPath == 0) - return DT_FAILURE | DT_INVALID_PARAM; - - if (path[0] == 0) + if (startPos == null || !dtVisfinite(startPos) || + endPos == null || !dtVisfinite(endPos) || + path == null || pathSize <= 0 || path[0] == 0 || + maxStraightPath <= 0) return DT_FAILURE | DT_INVALID_PARAM; dtStatus stat = 0; @@ -2225,9 +2121,11 @@ public static partial class Detour visitedCount = 0; // Validate input - if (startRef == 0) - return DT_FAILURE | DT_INVALID_PARAM; - if (!m_nav.isValidPolyRef(startRef)) + if (!m_nav.isValidPolyRef(startRef) || + startPos == null || !dtVisfinite(startPos) || + endPos == null || !dtVisfinite(endPos) || + filter == null || resultPos == null || visited == null || + maxVisitedSize <= 0) return DT_FAILURE | DT_INVALID_PARAM; dtStatus status = DT_SUCCESS; @@ -2578,7 +2476,6 @@ public static partial class Detour /// public dtStatus raycast(dtPolyRef startRef, float[] startPos, float[] endPos, dtQueryFilter filter, ref float t, float[] hitNormal, dtPolyRef[] path, ref uint pathCount, int maxPath) { - dtRaycastHit hit = new(); hit.path = path; hit.maxPath = maxPath; @@ -2647,15 +2544,19 @@ public static partial class Detour { Debug.Assert(m_nav != null); + if (hit == null) + return DT_FAILURE | DT_INVALID_PARAM; + hit.t = 0; hit.pathCount = 0; hit.pathCost = 0; // Validate input - if (startRef == 0 || !m_nav.isValidPolyRef(startRef)) - return DT_FAILURE | DT_INVALID_PARAM; - - if (prevRef != 0 && !m_nav.isValidPolyRef(prevRef)) + if (!m_nav.isValidPolyRef(startRef) || + startPos == null || !dtVisfinite(startPos) || + endPos == null || !dtVisfinite(endPos) || + filter == null || + (prevRef != 0 && !m_nav.isValidPolyRef(prevRef))) return DT_FAILURE | DT_INVALID_PARAM; float[] dir = new float[3]; @@ -2925,7 +2826,10 @@ public static partial class Detour resultCount = 0; // Validate input - if (startRef == 0 || !m_nav.isValidPolyRef(startRef)) + if (!m_nav.isValidPolyRef(startRef) || + centerPos == null || !dtVisfinite(centerPos) || + radius < 0 || !float.IsFinite(radius) || + filter == null || maxResult < 0) return DT_FAILURE | DT_INVALID_PARAM; m_nodePool.clear(); @@ -3113,6 +3017,11 @@ public static partial class Detour resultCount = 0; + if (!m_nav.isValidPolyRef(startRef) || + verts == null || nverts < 3 || + filter == null || maxResult < 0) + return DT_FAILURE | DT_INVALID_PARAM; + // Validate input if (startRef == 0 || !m_nav.isValidPolyRef(startRef)) return DT_FAILURE | DT_INVALID_PARAM; @@ -3305,8 +3214,10 @@ public static partial class Detour resultCount = 0; - // Validate input - if (startRef == 0 || !m_nav.isValidPolyRef(startRef)) + if (!m_nav.isValidPolyRef(startRef) || + centerPos == null || !dtVisfinite(centerPos) || + radius < 0 || !float.IsFinite(radius) || + filter == null || maxResult < 0) return DT_FAILURE | DT_INVALID_PARAM; const int MAX_STACK = 48; @@ -3543,6 +3454,9 @@ public static partial class Detour if (dtStatusFailed(m_nav.getTileAndPolyByRef(polyRef, ref tile, ref poly))) return DT_FAILURE | DT_INVALID_PARAM; + if (filter == null || segmentVerts == null || maxSegments < 0) + return DT_FAILURE | DT_INVALID_PARAM; + int n = 0; const int MAX_INTERVAL = 16; dtSegInterval[] ints = new dtSegInterval[MAX_INTERVAL]; @@ -3705,7 +3619,10 @@ public static partial class Detour Debug.Assert(m_openList != null); // Validate input - if (startRef == 0 || !m_nav.isValidPolyRef(startRef)) + if (!m_nav.isValidPolyRef(startRef) || + centerPos == null || !dtVisfinite(centerPos) || + maxRadius < 0 || !float.IsFinite(maxRadius) || + filter == null || hitPos == null || hitNormal == null) return DT_FAILURE | DT_INVALID_PARAM; m_nodePool.clear(); diff --git a/Source/Framework/RecastDetour/Detour/DetourStatus.cs b/Source/Framework/RecastDetour/Detour/DetourStatus.cs index 4999082be..3f66563b0 100644 --- a/Source/Framework/RecastDetour/Detour/DetourStatus.cs +++ b/Source/Framework/RecastDetour/Detour/DetourStatus.cs @@ -16,7 +16,7 @@ public static partial class Detour public const uint DT_BUFFER_TOO_SMALL = 1 << 4; // Result buffer for the query was too small to store all results. public const uint DT_OUT_OF_NODES = 1 << 5; // Query ran out of nodes during search. public const uint DT_PARTIAL_RESULT = 1 << 6; // Query did not reach the end location, returning best guess. - + public const uint DT_ALREADY_OCCUPIED = 1 << 7; // A tile has already been assigned to the given x,y coordinate // Returns true of status is success. public static bool dtStatusSucceed(dtStatus status) diff --git a/Source/Game/DataStorage/AreaTriggerDataStorage.cs b/Source/Game/DataStorage/AreaTriggerDataStorage.cs index 14c4bc875..5c5337db9 100644 --- a/Source/Game/DataStorage/AreaTriggerDataStorage.cs +++ b/Source/Game/DataStorage/AreaTriggerDataStorage.cs @@ -239,28 +239,28 @@ namespace Game.DataStorage orbitInfo.StartDelay = circularMovementInfos.Read(1); orbitInfo.Radius = circularMovementInfos.Read(2); - if (!float.IsInfinity(orbitInfo.Radius)) + if (!float.IsFinite(orbitInfo.Radius)) { Log.outError(LogFilter.Sql, $"Table `areatrigger_create_properties_orbit` has listed areatrigger (AreaTriggerCreatePropertiesId: {areaTriggerCreatePropertiesId}) with invalid Radius ({orbitInfo.Radius}), set to 0!"); orbitInfo.Radius = 0.0f; } orbitInfo.BlendFromRadius = circularMovementInfos.Read(3); - if (!float.IsInfinity(orbitInfo.BlendFromRadius)) + if (!float.IsFinite(orbitInfo.BlendFromRadius)) { Log.outError(LogFilter.Sql, $"Table `areatrigger_create_properties_orbit` has listed areatrigger (AreaTriggerCreatePropertiesId: {areaTriggerCreatePropertiesId}) with invalid BlendFromRadius ({orbitInfo.BlendFromRadius}), set to 0!"); orbitInfo.BlendFromRadius = 0.0f; } orbitInfo.InitialAngle = circularMovementInfos.Read(4); - if (!float.IsInfinity(orbitInfo.InitialAngle)) + if (!float.IsFinite(orbitInfo.InitialAngle)) { Log.outError(LogFilter.Sql, $"Table `areatrigger_create_properties_orbit` has listed areatrigger (AreaTriggerCreatePropertiesId: {areaTriggerCreatePropertiesId}) with invalid InitialAngle ({orbitInfo.InitialAngle}), set to 0!"); orbitInfo.InitialAngle = 0.0f; } orbitInfo.ZOffset = circularMovementInfos.Read(5); - if (!float.IsInfinity(orbitInfo.ZOffset)) + if (!float.IsFinite(orbitInfo.ZOffset)) { Log.outError(LogFilter.Sql, $"Table `spell_areatrigger_circular` has listed areatrigger (MiscId: {areaTriggerCreatePropertiesId}) with invalid ZOffset ({orbitInfo.ZOffset}), set to 0!"); orbitInfo.ZOffset = 0.0f; diff --git a/Source/Game/Entities/Player/Player.DB.cs b/Source/Game/Entities/Player/Player.DB.cs index e947ba8f0..c50a520e3 100644 --- a/Source/Game/Entities/Player/Player.DB.cs +++ b/Source/Game/Entities/Player/Player.DB.cs @@ -3377,7 +3377,7 @@ namespace Game.Entities stmt.AddValue(0, GetGUID().GetCounter()); characterTransaction.Append(stmt); - static float finiteAlways(float f) { return !float.IsInfinity(f) ? f : 0.0f; }; + static float finiteAlways(float f) { return !float.IsFinite(f) ? f : 0.0f; }; if (create) { diff --git a/Source/Game/Maps/GridDefines.cs b/Source/Game/Maps/GridDefines.cs index 1d688b075..84d6c36e8 100644 --- a/Source/Game/Maps/GridDefines.cs +++ b/Source/Game/Maps/GridDefines.cs @@ -25,7 +25,7 @@ namespace Game.Maps { public static bool IsValidMapCoord(float c) { - return !float.IsInfinity(c) && (Math.Abs(c) <= (MapConst.MapHalfSize - 0.5f)); + return !float.IsFinite(c) && (Math.Abs(c) <= (MapConst.MapHalfSize - 0.5f)); } public static bool IsValidMapCoord(float x, float y) @@ -40,7 +40,7 @@ namespace Game.Maps public static bool IsValidMapCoord(float x, float y, float z, float o) { - return IsValidMapCoord(x, y, z) && !float.IsInfinity(o); + return IsValidMapCoord(x, y, z) && float.IsFinite(o); } public static bool IsValidMapCoord(uint mapid, float x, float y) diff --git a/Source/Game/Movement/Generators/PathGenerator.cs b/Source/Game/Movement/Generators/PathGenerator.cs index 3b00eaff9..0d54ba829 100644 --- a/Source/Game/Movement/Generators/PathGenerator.cs +++ b/Source/Game/Movement/Generators/PathGenerator.cs @@ -703,10 +703,14 @@ namespace Game.Movement ulong[] visited = new ulong[MAX_VISIT_POLY]; int nvisited = 0; - _navMeshQuery.moveAlongSurface(polys[0], iterPos, moveTgt, _filter, result, visited, ref nvisited, MAX_VISIT_POLY); + if (Detour.dtStatusFailed(_navMeshQuery.moveAlongSurface(polys[0], iterPos, moveTgt, _filter, result, visited, ref nvisited, MAX_VISIT_POLY))) + return Detour.DT_FAILURE; + npolys = FixupCorridor(polys, npolys, 74, visited, nvisited); - _navMeshQuery.getPolyHeight(polys[0], result, ref result[1]); + if (Detour.dtStatusFailed(_navMeshQuery.getPolyHeight(polys[0], result, ref result[1]))) + return Detour.DT_FAILURE; + result[1] += 0.5f; Detour.dtVcopy(iterPos, result); @@ -752,7 +756,9 @@ namespace Game.Movement } // Move position at the other side of the off-mesh link. Detour.dtVcopy(iterPos, connectionEndPos); - _navMeshQuery.getPolyHeight(polys[0], iterPos, ref iterPos[1]); + if (Detour.dtStatusFailed(_navMeshQuery.getPolyHeight(polys[0], iterPos, ref iterPos[1]))) + return Detour.DT_FAILURE; + iterPos[1] += 0.5f; } }