Math utility for photo-based contour warping. It computes detector bias offsets for 2D projections and applies 3D kernel-based deformations to face contour points using precomputed basis data and per-face correction weights.
using System;
namespace CharacterEditor;
/// <summary>Exact ordered native Transform kernels for fine contour fitting.
/// Templates are packed by the installed engine; this pure-math evaluation is
/// safe on the fitting worker and does not create scene objects.</summary>
public static class PhotoContourWarp
{
public static void DetectorBias(Vector2[] projection, PhotoFace face, float yaw)
{
var ids = PhotoContourDetectorBasis.Ids;
var offsets = PhotoContourDetectorBasis.Offsets;
var yaws = PhotoContourDetectorBasis.Yaws;
int lo = yaw < 0 ? 0 : 1, hi = lo + 1;
float t = Math.Clamp((yaw - yaws[lo]) / (yaws[hi] - yaws[lo]), 0, 1);
for (int c = 0; c < ids.Length; c++)
{
float amount = face.Corrections.GetValueOrDefault(ids[c]);
if (MathF.Abs(amount) < .000001f)
continue;
var control = PhotoFaceCorrections.Controls.First(x => x.Id == ids[c]);
var weights = face.WeightsFor(control.Region);
float female = weights[1] + weights[3];
for (int family = 0; family < 2; family++)
{
float strength = (family == 0 ? 1 - female : female) * MathF.Abs(amount);
if (strength < .000001f)
continue;
var row = offsets[family * ids.Length * 2 + c * 2 + (amount >= 0 ? 1 : 0)];
for (int p = 0; p < projection.Length; p++)
projection[p] += (row[lo][p] * (1 - t) + row[hi][p] * t) * strength;
}
}
}
static float Dot(Vector4 a, Vector3 b) => a.x * b.x + a.y * b.y + a.z * b.z + a.w;
public static void Apply(Vector3[] points, PhotoFace face)
{
var ids = PhotoContourKernelBasis.Ids;
var kernels = PhotoContourKernelBasis.Rows;
for (int i = 0; i < ids.Length; i++)
{
float amount = face.Corrections.GetValueOrDefault(ids[i]);
if (MathF.Abs(amount) < .000001f)
continue;
foreach (var rows in kernels[i])
for (int p = 0; p < points.Length; p++)
points[p] = Apply(points[p], rows, amount);
}
}
static Vector3 Apply(Vector3 point, Vector4[] r, float amount)
{
var p = new Vector3(Dot(r[0], point), Dot(r[1], point), Dot(r[2], point)) - new Vector3(r[9].x, r[9].y, r[9].z);
float radius = r[6].w, distance = p.Length - radius;
if (distance >= 0)
return point;
float width = radius * r[7].w;
float t = width > 0 ? Math.Clamp(-distance / MathF.Max(width, .000001f), 0, 1) : 1;
float influence = r[8].x * t * t * (3 - 2 * t);
var transformed = new Vector3(Dot(r[10], p), Dot(r[11], p), Dot(r[12], p));
var candidate = p + (transformed - p) * (influence * amount);
if (candidate.Length > radius)
candidate = candidate.Normal * radius;
var delta = candidate - p;
return point + new Vector3(r[3].x * delta.x + r[3].y * delta.y + r[3].z * delta.z, r[4].x * delta.x + r[4].y * delta.y + r[4].z * delta.z, r[5].x * delta.x + r[5].y * delta.y + r[5].z * delta.z);
}
}