/// /// <param name="v1"> </param> /// <param name="v2"> /// @return </param> internal static void vAdd(float[] dest, VectorPtr v1, VectorPtr v2) { if (dest == null) { dest = new float[3]; } dest[0] = v1.get(0) + v2.get(0); dest[1] = v1.get(1) + v2.get(1); dest[2] = v1.get(2) + v2.get(2); }
/// /// <param name="out"> </param> /// <param name="v1"> </param> /// <param name="v2"> /// @return </param> public static void vSub(float[] dest, VectorPtr v1, VectorPtr v2) { if (dest == null) { dest = new float[3]; } dest[0] = v1.get(0) - v2.get(0); dest[1] = v1.get(1) - v2.get(1); dest[2] = v1.get(2) - v2.get(2); }
private static int classifyOffMeshPoint(VectorPtr pt, float[] bmin, float[] bmax) { int outcode = 0; outcode |= (pt.get(0) >= bmax[0]) ? XP : 0; outcode |= (pt.get(2) >= bmax[2]) ? ZP : 0; outcode |= (pt.get(0) < bmin[0]) ? XM : 0; outcode |= (pt.get(2) < bmin[2]) ? ZM : 0; switch (outcode) { case 1 << 0: return(0); case 1 << 0 | 1 << 1: return(1); case 1 << 1: return(2); case 1 << 2 | 1 << 1: return(3); case 1 << 2: return(4); case 1 << 2 | 1 << 3: return(5); case 1 << 3: return(6); case 1 << 0 | 1 << 3: return(7); } return(0xff); }
/// <summary> /// Builds navigation mesh tile data from the provided tile creation data. /// </summary> /// <param name="params"> /// Tile creation data. /// </param> /// <returns> created tile data </returns> public static MeshData createNavMeshData(NavMeshCreateParams @params) { if (@params.vertCount >= 0xffff) { return(null); } if (@params.vertCount == 0 || @params.verts == null) { return(null); } if (@params.polyCount == 0 || @params.polys == null) { return(null); } int nvp = @params.nvp; // Classify off-mesh connection points. We store only the connections // whose start point is inside the tile. int[] offMeshConClass = null; int storedOffMeshConCount = 0; int offMeshConLinkCount = 0; if (@params.offMeshConCount > 0) { offMeshConClass = new int[@params.offMeshConCount * 2]; // Find tight heigh bounds, used for culling out off-mesh start // locations. float hmin = float.MaxValue; float hmax = -float.MaxValue; if (@params.detailVerts != null && @params.detailVertsCount != 0) { for (int i = 0; i < @params.detailVertsCount; ++i) { float h = @params.detailVerts[i * 3 + 1]; hmin = Math.Min(hmin, h); hmax = Math.Max(hmax, h); } } else { for (int i = 0; i < @params.vertCount; ++i) { int iv = i * 3; float h = @params.bmin[1] + @params.verts[iv + 1] * @params.ch; hmin = Math.Min(hmin, h); hmax = Math.Max(hmax, h); } } hmin -= @params.walkableClimb; hmax += @params.walkableClimb; float[] bmin = new float[3]; float[] bmax = new float[3]; DetourCommon.vCopy(bmin, @params.bmin); DetourCommon.vCopy(bmax, @params.bmax); bmin[1] = hmin; bmax[1] = hmax; for (int i = 0; i < @params.offMeshConCount; ++i) { VectorPtr p0 = new VectorPtr(@params.offMeshConVerts, (i * 2 + 0) * 3); VectorPtr p1 = new VectorPtr(@params.offMeshConVerts, (i * 2 + 1) * 3); offMeshConClass[i * 2 + 0] = classifyOffMeshPoint(p0, bmin, bmax); offMeshConClass[i * 2 + 1] = classifyOffMeshPoint(p1, bmin, bmax); // Zero out off-mesh start positions which are not even // potentially touching the mesh. if (offMeshConClass[i * 2 + 0] == 0xff) { if (p0.get(1) < bmin[1] || p0.get(1) > bmax[1]) { offMeshConClass[i * 2 + 0] = 0; } } // Count how many links should be allocated for off-mesh // connections. if (offMeshConClass[i * 2 + 0] == 0xff) { offMeshConLinkCount++; } if (offMeshConClass[i * 2 + 1] == 0xff) { offMeshConLinkCount++; } if (offMeshConClass[i * 2 + 0] == 0xff) { storedOffMeshConCount++; } } } // Off-mesh connectionss are stored as polygons, adjust values. int totPolyCount = @params.polyCount + storedOffMeshConCount; int totVertCount = @params.vertCount + storedOffMeshConCount * 2; // Find portal edges which are at tile borders. int edgeCount = 0; int portalCount = 0; for (int i = 0; i < @params.polyCount; ++i) { int p = i * 2 * nvp; for (int j = 0; j < nvp; ++j) { if (@params.polys[p + j] == MESH_NULL_IDX) { break; } edgeCount++; if ((@params.polys[p + nvp + j] & 0x8000) != 0) { int dir = @params.polys[p + nvp + j] & 0xf; if (dir != 0xf) { portalCount++; } } } } int maxLinkCount = edgeCount + portalCount * 2 + offMeshConLinkCount * 2; // Find unique detail vertices. int uniqueDetailVertCount = 0; int detailTriCount = 0; if (@params.detailMeshes != null) { // Has detail mesh, count unique detail vertex count and use input // detail tri count. detailTriCount = @params.detailTriCount; for (int i = 0; i < @params.polyCount; ++i) { int p = i * nvp * 2; int ndv = @params.detailMeshes[i * 4 + 1]; int nv = 0; for (int j = 0; j < nvp; ++j) { if (@params.polys[p + j] == MESH_NULL_IDX) { break; } nv++; } ndv -= nv; uniqueDetailVertCount += ndv; } } else { // No input detail mesh, build detail mesh from nav polys. uniqueDetailVertCount = 0; // No extra detail verts. detailTriCount = 0; for (int i = 0; i < @params.polyCount; ++i) { int p = i * nvp * 2; int nv = 0; for (int j = 0; j < nvp; ++j) { if (@params.polys[p + j] == MESH_NULL_IDX) { break; } nv++; } detailTriCount += nv - 2; } } int bvTreeSize = @params.buildBvTree ? @params.polyCount * 2 : 0; MeshHeader header = new MeshHeader(); float[] navVerts = new float[3 * totVertCount]; Poly[] navPolys = new Poly[totPolyCount]; PolyDetail[] navDMeshes = new PolyDetail[@params.polyCount]; float[] navDVerts = new float[3 * uniqueDetailVertCount]; int[] navDTris = new int[4 * detailTriCount]; BVNode[] navBvtree = new BVNode[bvTreeSize]; OffMeshConnection[] offMeshCons = new OffMeshConnection[storedOffMeshConCount]; // Store header header.magic = MeshHeader.DT_NAVMESH_MAGIC; header.version = MeshHeader.DT_NAVMESH_VERSION; header.x = @params.tileX; header.y = @params.tileY; header.layer = @params.tileLayer; header.userId = @params.userId; header.polyCount = totPolyCount; header.vertCount = totVertCount; header.maxLinkCount = maxLinkCount; DetourCommon.vCopy(header.bmin, @params.bmin); DetourCommon.vCopy(header.bmax, @params.bmax); header.detailMeshCount = @params.polyCount; header.detailVertCount = uniqueDetailVertCount; header.detailTriCount = detailTriCount; header.bvQuantFactor = 1.0f / @params.cs; header.offMeshBase = @params.polyCount; header.walkableHeight = @params.walkableHeight; header.walkableRadius = @params.walkableRadius; header.walkableClimb = @params.walkableClimb; header.offMeshConCount = storedOffMeshConCount; header.bvNodeCount = bvTreeSize; int offMeshVertsBase = @params.vertCount; int offMeshPolyBase = @params.polyCount; // Store vertices // Mesh vertices for (int i = 0; i < @params.vertCount; ++i) { int iv = i * 3; int v = i * 3; navVerts[v] = @params.bmin[0] + @params.verts[iv] * @params.cs; navVerts[v + 1] = @params.bmin[1] + @params.verts[iv + 1] * @params.ch; navVerts[v + 2] = @params.bmin[2] + @params.verts[iv + 2] * @params.cs; } // Off-mesh link vertices. int n = 0; for (int i = 0; i < @params.offMeshConCount; ++i) { // Only store connections which start from this tile. if (offMeshConClass[i * 2 + 0] == 0xff) { int linkv = i * 2 * 3; int v = (offMeshVertsBase + n * 2) * 3; Array.Copy(@params.offMeshConVerts, linkv, navVerts, v, 6); n++; } } // Store polygons // Mesh polys int src = 0; for (int i = 0; i < @params.polyCount; ++i) { Poly p = new Poly(i, nvp); navPolys[i] = p; p.vertCount = 0; p.flags = @params.polyFlags[i]; p.Area = @params.polyAreas[i]; p.Type = Poly.DT_POLYTYPE_GROUND; for (int j = 0; j < nvp; ++j) { if (@params.polys[src + j] == MESH_NULL_IDX) { break; } p.verts[j] = @params.polys[src + j]; if ((@params.polys[src + nvp + j] & 0x8000) != 0) { // Border or portal edge. int dir = @params.polys[src + nvp + j] & 0xf; if (dir == 0xf) // Border { p.neis[j] = 0; } else if (dir == 0) // Portal x- { p.neis[j] = NavMesh.DT_EXT_LINK | 4; } else if (dir == 1) // Portal z+ { p.neis[j] = NavMesh.DT_EXT_LINK | 2; } else if (dir == 2) // Portal x+ { p.neis[j] = NavMesh.DT_EXT_LINK | 0; } else if (dir == 3) // Portal z- { p.neis[j] = NavMesh.DT_EXT_LINK | 6; } } else { // Normal connection p.neis[j] = @params.polys[src + nvp + j] + 1; } p.vertCount++; } src += nvp * 2; } // Off-mesh connection vertices. n = 0; for (int i = 0; i < @params.offMeshConCount; ++i) { // Only store connections which start from this tile. if (offMeshConClass[i * 2 + 0] == 0xff) { Poly p = new Poly(offMeshPolyBase + n, nvp); navPolys[offMeshPolyBase + n] = p; p.vertCount = 2; p.verts[0] = offMeshVertsBase + n * 2; p.verts[1] = offMeshVertsBase + n * 2 + 1; p.flags = @params.offMeshConFlags[i]; p.Area = @params.offMeshConAreas[i]; p.Type = Poly.DT_POLYTYPE_OFFMESH_CONNECTION; n++; } } // Store detail meshes and vertices. // The nav polygon vertices are stored as the first vertices on each // mesh. // We compress the mesh data by skipping them and using the navmesh // coordinates. if (@params.detailMeshes != null) { int vbase = 0; for (int i = 0; i < @params.polyCount; ++i) { PolyDetail dtl = new PolyDetail(); navDMeshes[i] = dtl; int vb = @params.detailMeshes[i * 4 + 0]; int ndv = @params.detailMeshes[i * 4 + 1]; int nv = navPolys[i].vertCount; dtl.vertBase = vbase; dtl.vertCount = (ndv - nv); dtl.triBase = @params.detailMeshes[i * 4 + 2]; dtl.triCount = @params.detailMeshes[i * 4 + 3]; // Copy vertices except the first 'nv' verts which are equal to // nav poly verts. if (ndv - nv != 0) { Array.Copy(@params.detailVerts, (vb + nv) * 3, navDVerts, vbase * 3, 3 * (ndv - nv)); vbase += ndv - nv; } } // Store triangles. Array.Copy(@params.detailTris, 0, navDTris, 0, 4 * @params.detailTriCount); } else { // Create dummy detail mesh by triangulating polys. int tbase = 0; for (int i = 0; i < @params.polyCount; ++i) { PolyDetail dtl = new PolyDetail(); navDMeshes[i] = dtl; int nv = navPolys[i].vertCount; dtl.vertBase = 0; dtl.vertCount = 0; dtl.triBase = tbase; dtl.triCount = (nv - 2); // Triangulate polygon (local indices). for (int j = 2; j < nv; ++j) { int t = tbase * 4; navDTris[t + 0] = 0; navDTris[t + 1] = (j - 1); navDTris[t + 2] = j; // Bit for each edge that belongs to poly boundary. navDTris[t + 3] = (1 << 2); if (j == 2) { navDTris[t + 3] |= (1 << 0); } if (j == nv - 1) { navDTris[t + 3] |= (1 << 4); } tbase++; } } } // Store and create BVtree. // TODO: take detail mesh into account! use byte per bbox extent? if (@params.buildBvTree) { // Do not set header.bvNodeCount set to make it work look exactly the same as in original Detour header.bvNodeCount = createBVTree(@params.verts, @params.vertCount, @params.polys, @params.polyCount, nvp, @params.cs, @params.ch, navBvtree); } // Store Off-Mesh connections. n = 0; for (int i = 0; i < @params.offMeshConCount; ++i) { // Only store connections which start from this tile. if (offMeshConClass[i * 2 + 0] == 0xff) { OffMeshConnection con = new OffMeshConnection(); offMeshCons[n] = con; con.poly = (offMeshPolyBase + n); // Copy connection end-points. int endPts = i * 2 * 3; Array.Copy(@params.offMeshConVerts, endPts, con.pos, 0, 6); con.rad = @params.offMeshConRad[i]; con.flags = @params.offMeshConDir[i] != 0 ? NavMesh.DT_OFFMESH_CON_BIDIR : 0; con.side = offMeshConClass[i * 2 + 1]; if (@params.offMeshConUserID != null) { con.userId = @params.offMeshConUserID[i]; } n++; } } MeshData nmd = new MeshData(); nmd.header = header; nmd.verts = navVerts; nmd.polys = navPolys; nmd.detailMeshes = navDMeshes; nmd.detailVerts = navDVerts; nmd.detailTris = navDTris; nmd.bvTree = navBvtree; nmd.offMeshCons = offMeshCons; /* try * { * FileWriter fw = new FileWriter("test_java.log", true); * for (int iter = 0; iter < header.bvNodeCount; iter++) * { * String result = String.format("node.i = %d\n", navBvtree[iter].i); * fw.write(result); * } * fw.close(); * } * catch (Exception e) * { * e.printStackTrace(); * } */return(nmd); }