Editor/Effigy/SculptLayer.cs
using System;
namespace Effigy;
/// <summary>
/// One sculpt's displacements, stored as per-vertex coefficients in <see cref="SculptFrame"/>.
/// Capture reads world positions into that frame; Apply writes them back. The mesh is not the
/// source of truth — the deltas are — so a cage rebuild can re-derive frames and re-apply.
/// </summary>
public sealed class SculptLayer
{
public readonly Vec3[] Deltas;
public SculptLayer( Vec3[] deltas )
{
Deltas = deltas ?? throw new ArgumentNullException( nameof( deltas ) );
}
public int Count => Deltas.Length;
/// <summary>Frame-space deltas that take <paramref name="rest"/> onto <paramref name="displaced"/>.</summary>
public static SculptLayer Capture( PolyMesh rest, PolyMesh displaced, SculptFrames frames )
{
if ( rest is null )
throw new ArgumentNullException( nameof( rest ) );
if ( displaced is null )
throw new ArgumentNullException( nameof( displaced ) );
if ( frames is null )
throw new ArgumentNullException( nameof( frames ) );
if ( rest.VertexCount != displaced.VertexCount || rest.VertexCount != frames.Count )
throw new ArgumentException(
$"Capture needs matching vertex counts (rest {rest.VertexCount}, displaced {displaced.VertexCount}, frames {frames.Count})" );
var deltas = new Vec3[rest.VertexCount];
for ( var i = 0; i < rest.VertexCount; i++ )
deltas[i] = frames.At[i].FromWorld( displaced.Positions[i] - rest.Positions[i] );
return new SculptLayer( deltas );
}
/// <summary>Add this layer's deltas onto <paramref name="mesh"/>, which must be at rest.</summary>
public void Apply( PolyMesh mesh, SculptFrames frames )
{
if ( mesh is null )
throw new ArgumentNullException( nameof( mesh ) );
if ( frames is null )
throw new ArgumentNullException( nameof( frames ) );
if ( mesh.VertexCount != Deltas.Length || mesh.VertexCount != frames.Count )
throw new ArgumentException(
$"Apply needs matching vertex counts (mesh {mesh.VertexCount}, deltas {Deltas.Length}, frames {frames.Count})" );
for ( var i = 0; i < mesh.VertexCount; i++ )
mesh.Positions[i] += frames.At[i].ToWorld( Deltas[i] );
}
}