/// Returns the segments for the specified polygon, optionally including portals. /// @param[in] ref The reference id of the polygon. /// @param[in] filter The polygon filter to apply to the query. /// @param[out] segmentVerts The segments. [(ax, ay, az, bx, by, bz) * segmentCount] /// @param[out] segmentRefs The reference ids of each segment's neighbor polygon. /// Or zero if the segment is a wall. [opt] [(parentRef) * @p segmentCount] /// @param[out] segmentCount The number of segments returned. /// @param[in] maxSegments The maximum number of segments the result arrays can hold. /// @returns The status flags for the query. /// @par /// /// If the @p segmentRefs parameter is provided, then all polygon segments will be returned. /// Otherwise only the wall segments are returned. /// /// A segment that is normally a portal will be included in the result set as a /// wall if the @p filter results in the neighbor polygon becoomming impassable. /// /// The @p segmentVerts and @p segmentRefs buffers should normally be sized for the /// maximum segments per polygon of the source navigation mesh. /// dtStatus getPolyWallSegments(dtPolyRef polyRef, dtQueryFilter filter, float[] segmentVerts, dtPolyRef[] segmentRefs, ref int segmentCount, int maxSegments) { Debug.Assert(m_nav != null); segmentCount = 0; dtMeshTile tile = null; dtPoly poly = null; if (dtStatusFailed(m_nav.getTileAndPolyByRef(polyRef, ref tile, ref poly))) return DT_FAILURE | DT_INVALID_PARAM; int n = 0; const int MAX_INTERVAL = 16; dtSegInterval[] ints = new dtSegInterval[MAX_INTERVAL]; dtcsArrayItemsCreate(ints); int nints; bool storePortals = segmentRefs != null; dtStatus status = DT_SUCCESS; for (int i = 0, j = (int)poly.vertCount-1; i < (int)poly.vertCount; j = i++) { // Skip non-solid edges. nints = 0; if ((poly.neis[j] & DT_EXT_LINK) != 0) { // Tile border. for (uint k = poly.firstLink; k != DT_NULL_LINK; k = tile.links[k].next) { dtLink link = tile.links[k]; if (link.edge == j) { if (link.polyRef != 0) { dtMeshTile neiTile = null; dtPoly neiPoly = null; m_nav.getTileAndPolyByRefUnsafe(link.polyRef, ref neiTile, ref neiPoly); if (filter.passFilter(link.polyRef, neiTile, neiPoly)) { insertInterval(ints, ref nints, MAX_INTERVAL, link.bmin, link.bmax, link.polyRef); } } } } } else { // Internal edge dtPolyRef neiRef = 0; if (poly.neis[j] != 0) { uint idx = (uint)(poly.neis[j] - 1); neiRef = m_nav.getPolyRefBase(tile) | idx; if (!filter.passFilter(neiRef, tile, tile.polys[idx])) neiRef = 0; } // If the edge leads to another polygon and portals are not stored, skip. if (neiRef != 0 && !storePortals) continue; if (n < maxSegments) { //const float* vj = &tile.verts[poly.verts[j]*3]; //const float* vi = &tile.verts[poly.verts[i]*3]; //float* seg = &segmentVerts[n*6]; int vjStart = poly.verts[j] * 3; int viStart = poly.verts[i] * 3; int segStart = n * 6; dtVcopy(segmentVerts, segStart, tile.verts, vjStart); dtVcopy(segmentVerts, segStart + 3, tile.verts, viStart); if (segmentRefs != null) segmentRefs[n] = neiRef; n++; } else { status |= DT_BUFFER_TOO_SMALL; } continue; } // Add sentinels insertInterval(ints, ref nints, MAX_INTERVAL, -1, 0, 0); insertInterval(ints, ref nints, MAX_INTERVAL, 255, 256, 0); // Store segments. //const float* vj = &tile.verts[poly.verts[j]*3]; //const float* vi = &tile.verts[poly.verts[i]*3]; int vjStart2 = poly.verts[j] * 3; int viStart2 = poly.verts[i] * 3; for (int k = 1; k < nints; ++k) { // Portal segment. if (storePortals && ints[k].polyRef != 0) { float tmin = ints[k].tmin / 255.0f; float tmax = ints[k].tmax / 255.0f; if (n < maxSegments) { //float* seg = &segmentVerts[n*6]; int segStart = n * 6; dtVlerp(segmentVerts, segStart, tile.verts, vjStart2, tile.verts, viStart2, tmin); dtVlerp(segmentVerts, segStart + 3, tile.verts, vjStart2, tile.verts, viStart2, tmax); if (segmentRefs != null) segmentRefs[n] = ints[k].polyRef; n++; } else { status |= DT_BUFFER_TOO_SMALL; } } // Wall segment. int imin = ints[k - 1].tmax; int imax = ints[k].tmin; if (imin != imax) { float tmin = imin / 255.0f; float tmax = imax / 255.0f; if (n < maxSegments) { //float* seg = &segmentVerts[n*6]; int segStart = n * 6; dtVlerp(segmentVerts, segStart, tile.verts, vjStart2, tile.verts, viStart2, tmin); dtVlerp(segmentVerts, segStart + 3, tile.verts, vjStart2, tile.verts, viStart2, tmax); if (segmentRefs != null) segmentRefs[n] = 0; n++; } else { status |= DT_BUFFER_TOO_SMALL; } } } } segmentCount = n; return status; }
static void insertInterval(dtSegInterval[] ints, ref int nints, int maxInts, short tmin, short tmax, dtPolyRef polyRef) { if (nints+1 > maxInts) return; // Find insertion point. int idx = 0; while (idx < nints) { if (tmax <= ints[idx].tmin) break; idx++; } // Move current results. if (nints-idx != 0){ //memmove(ints+idx+1, ints+idx, sizeof(dtSegInterval)*(nints-idx)); for (int i=0;i<(nints-idx);++i){ ints[idx+1+i] = ints[idx+i]; } } // Store ints[idx].polyRef = polyRef; ints[idx].tmin = tmin; ints[idx].tmax = tmax; nints++; }