Divide out player scale from ConcealmentMeter baked mesh

SkinnedMeshRenderer.BakeMesh(mesh) bakes the renderer's world scale
into the vertices. SkinnedColliderBaker already accounts for this and
divides it back out, but ConcealmentMeter did not: it lifted the
already-scaled verts to world via localToWorldMatrix (scale again),
yielding scale-squared voxel positions. This threw off the embedded
broadphase box, the EmbeddedPercent computation (which now drives the
X-ray reveal) and the debug gizmos whenever PlayerScaler != 1.

Rescale the baked mesh to unit scale right after baking (no-op at
scale 1), matching the SkinnedColliderBaker convention documented in
PlayerScaler, so world = localToWorldMatrix holds on unit geometry.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 4.8 committed 2026-07-01 13:08:22 +02:00
1 parent 2f38cf0e32
commit 8603b0d884
1 file changed
+34 -2
+34 -2
View File
@@ -10,8 +10,10 @@ using UnityEngine;
/// <see cref="VisiblePercent"/> its complement.
///
/// Pipeline:
/// 1. <see cref="SkinnedMeshRenderer.BakeMesh(Mesh)"/> -> mesh in the renderer's LOCAL space
/// (same convention MeshPainter uses for its collider; world = transform.localToWorldMatrix).
/// 1. <see cref="SkinnedMeshRenderer.BakeMesh(Mesh)"/> -> mesh in the renderer's LOCAL space, then the
/// player's world scale is divided back out (BakeMesh bakes that scale IN; see PlayerScaler) so the
/// convention world = transform.localToWorldMatrix holds at ANY size — the same fixup SkinnedColliderBaker
/// applies, and the unit-scale mesh MeshPainter borrows from it.
/// 2. Column ray-stabbing in local space: each (x,z) grid column collects the Y where the mesh
/// surface crosses it; parity (odd # of crossings below a voxel center) marks interior voxels.
/// This is the same XZ triangle rasterization MeshPainter.GetIslandMask uses for UV islands.
@@ -74,6 +76,7 @@ public class ConcealmentMeter : MonoBehaviour
private Vector3[] _verts;
private int[] _tris;
private float _timer;
private readonly List<Vector3> _vbuf = new List<Vector3>(); // reused scale-fixup scratch; nothing allocated per eval
// Per-column Y crossings (index = iz * nx + ix). Reused across evals; sizes grow as needed.
private List<float>[] _columns;
@@ -122,6 +125,17 @@ public class ConcealmentMeter : MonoBehaviour
if (!targetRenderer) return;
targetRenderer.BakeMesh(_baked);
// BakeMesh freezes the bone deformation — which already carries the player's world scale (PlayerScaler)
// — into the mesh. Everything below treats the bake as unit-scale LOCAL geometry and raises it to world
// via targetRenderer.localToWorldMatrix, which re-applies that same scale. Divide it back out first,
// else a scaled player is measured at scale² (voxel world positions, broadphase box AND gizmos), which
// also corrupts EmbeddedPercent. No-op at scale 1; the uniform divide is rotation-independent, which the
// player's uniform scale guarantees. Mirrors SkinnedColliderBaker's identical fixup.
Vector3 ls = targetRenderer.transform.lossyScale;
if (ls.x != 1f || ls.y != 1f || ls.z != 1f)
RescaleBakedToUnitScale(ls);
_verts = _baked.vertices;
_tris = _baked.triangles;
if (_verts.Length == 0 || _tris.Length < 3) return;
@@ -199,6 +213,24 @@ public class ConcealmentMeter : MonoBehaviour
HasResult = true;
}
/// <summary>Divides the player's world scale back out of the freshly baked mesh so it is true unit-scale
/// local geometry (the space this class raises to world with). Reuses <see cref="_vbuf"/> — no per-eval alloc.</summary>
private void RescaleBakedToUnitScale(Vector3 lossyScale)
{
float ix = lossyScale.x != 0f ? 1f / lossyScale.x : 0f;
float iy = lossyScale.y != 0f ? 1f / lossyScale.y : 0f;
float iz = lossyScale.z != 0f ? 1f / lossyScale.z : 0f;
_baked.GetVertices(_vbuf);
for (int i = 0; i < _vbuf.Count; i++)
{
Vector3 v = _vbuf[i];
v.x *= ix; v.y *= iy; v.z *= iz;
_vbuf[i] = v;
}
_baked.SetVertices(_vbuf);
_baked.RecalculateBounds();
}
/// <summary>
/// Rasterizes every triangle into the XZ column grid; each column accumulates the Y values
/// where the surface crosses it (one per triangle whose XZ projection covers the column center).