egametang/ET · critical · Exception

rcBuildPolyMesh: The resulting mesh has too many polygons {m

Error message

rcBuildPolyMesh: The resulting mesh has too many polygons {mesh.npolys} (max {MAX_MESH_VERTS_POLY}). Data can be corrupted.

What it means

The post-condition check at RecastMesh.cs:1206 verifies mesh.npolys does not exceed MAX_MESH_VERTS_POLY (65535) at the end of the primary rcBuildPolyMesh path. Exceeding it means the 16-bit polygon index fields overflowed, so the returned mesh may be corrupted.

Source

Thrown at Packages/cn.etetet.recast/Scripts/Core/Share/Recast/RecastMesh.cs:1206

                            mesh.polys[p + nvp + j] = 0x8000 | 2;
                        else if (mesh.verts[va + 2] == 0 && mesh.verts[vb + 2] == 0)
                            mesh.polys[p + nvp + j] = 0x8000 | 3;
                    }
                }
            }

            // Just allocate the mesh flags array. The user is resposible to fill it.
            mesh.flags = new int[mesh.npolys];

            if (mesh.nverts > MAX_MESH_VERTS_POLY)
            {
                throw new Exception("rcBuildPolyMesh: The resulting mesh has too many vertices " + mesh.nverts
                                                                                                 + " (max " + MAX_MESH_VERTS_POLY + "). Data can be corrupted.");
            }

            if (mesh.npolys > MAX_MESH_VERTS_POLY)
            {
                throw new Exception("rcBuildPolyMesh: The resulting mesh has too many polygons " + mesh.npolys
                                                                                                 + " (max " + MAX_MESH_VERTS_POLY + "). Data can be corrupted.");
            }

            return mesh;
        }

        /// @see rcAllocPolyMesh, rcPolyMesh
        public static RcPolyMesh MergePolyMeshes(RcTelemetry ctx, RcPolyMesh[] meshes, int nmeshes)
        {
            if (nmeshes == 0 || meshes == null)
                return null;

            using var timer = ctx.ScopedTimer(RcTimerLabel.RC_TIMER_MERGE_POLYMESH);

            RcPolyMesh mesh = new RcPolyMesh();
            mesh.nvp = meshes[0].nvp;
            mesh.cs = meshes[0].cs;
            mesh.ch = meshes[0].ch;

View on GitHub (pinned to 5cab01f7a8)

Solutions

  1. Tile the navmesh (rcConfig.tileSize) so each tile's polygon count stays far below 65535.
  2. Raise maxVertsPerPoly to reduce polygon count per contour.
  3. Raise cellSize / contourSimplificationError to coarsen geometry.
  4. If building a merged mesh (MergePolyMeshes), confirm inputs aren't already near the limit.
Defensive patterns

Strategy: validation

Try / catch

try { rcBuildPolyMesh(ctx, cset, nvp, out mesh); }
catch (Exception e) when (e.Message.Contains("too many polygons") && e.Message.Contains("Data can be corrupted")) {
    Log.Error($"Corrupt polymesh: {e.Message}");
    // discard mesh, re-bake with coarser config or smaller tiles
}

Prevention

When it happens

Trigger: Fires immediately after the nverts check at :1200 in the same finalization block, when mesh.npolys > 65535 after all polygon storage is complete.

Common situations: Massively polygon-heavy navmesh from a huge or untiled level; low maxVertsPerPoly combined with dense contours; often co-occurs with error 302. Indicates the conservative maxTris guard (301) was not the binding constraint but the final 16-bit cap was.

Related errors


AI-assisted analysis of egametang/ET@5cab01f7a8 (2026-08-13). Data as JSON: /api/errors/b6ec65cfe79105b4. Report an issue: GitHub.