Experiments/FaceTransferLab.razor

A Razor UI component for a Character Editor experiments page. It renders controls and three FaceTransferPreview views, manages UI state (Pair, Mode, Amount, Yaw), handles Escape menu toggling, and navigates back to the main scene.

File Access
@using Sandbox
@using Sandbox.UI
@using System
@namespace CharacterEditor
@inherits PanelComponent

<root>
 <div class="labheader"><div><div class="eyebrow">CHARACTER EDITOR / EXPERIMENTS</div><h1>One face, toward another.</h1><p>Compare shape alone. Same surface topology, material, camera and lighting.</p></div><button onclick=@Back>Back to collection</button></div>
 <div class="toolbar">
  <button class=@(Pair=="human03-to-human07"?"chosen":"") onclick=@(()=>Pair="human03-to-human07")>Human 03 → 07</button>
  <button class=@(Pair=="human02-to-human04"?"chosen":"") onclick=@(()=>Pair="human02-to-human04")>Human 02 → 04</button>
  <div class="spacer"></div>
  <button onclick=@(()=>Yaw=0)>Front</button><button onclick=@(()=>Yaw=35)>Three-quarter</button><button onclick=@(()=>Yaw=90)>Profile</button>
 </div>
 <div class="views">
  <div class="view"><div class="caption"><h2>Source</h2><span>Unmodified starting face</span></div><FaceTransferPreview Pair=@Pair Mode="Source" Yaw=@Yaw /></div>
  <div class="view"><div class="caption"><h2>@ModeLabel</h2><span>@($"{Amount:P0} applied")</span></div><FaceTransferPreview Pair=@Pair Mode=@Mode Amount=@Amount Yaw=@Yaw /></div>
  <div class="view"><div class="caption"><h2>Target</h2><span>Reference head surface</span></div><FaceTransferPreview Pair=@Pair Mode="Target" Amount=@(1f) Yaw=@Yaw /></div>
 </div>
 <div class="controls"><div class="toolbar">
  <button class=@(Mode=="Existing"?"chosen":"") onclick=@(()=>{Mode="Existing";Amount=1;})>Fitted editor sliders</button>
  <button class=@(Mode=="Experimental"?"chosen":"") onclick=@(()=>{Mode="Experimental";Amount=1;})>Experimental volumes</button>
  <button class=@(Mode=="Target"?"chosen":"") onclick=@(()=>{Mode="Target";Amount=1;})>Direct vertex morph</button>
 </div><div class="blend"><span>Source</span><SliderControl Value=@Amount OnValueChanged=@((float v)=>{Amount=v;}) Min=@(0f) Max=@(1f) Step=@(.01f) ShowTextEntry=@false /><span>Full result</span></div>
 <p>@Explanation</p><div class="footnote">Rest-pose head surface only • Eye interiors, teeth, skin detail and animation excluded • Results measure geometry, not likeness</div></div>
 <StudioEscapeMenu @ref=EscapeMenu MainMenu=@Back EnableExperiments=@false />
</root>

@code {
	public StudioEscapeMenu EscapeMenu { get; set; }

	protected override void OnUpdate()
	{
		base.OnUpdate();
		if (EscapeMenu is not null && Input.EscapePressed)
		{
			Input.EscapePressed = false;
			EscapeMenu.Toggle();
		}
	}

	public string Pair { get; set; } = "human03-to-human07";
	public string Mode { get; set; } = "Existing";
	public float Amount { get; set; } = 1;
	public float Yaw { get; set; }

	string ModeLabel => Mode == "Existing" ? "Fitted editor sliders" : Mode == "Experimental" ? "Experimental volumes" : "Direct vertex morph";

	string Explanation => Mode == "Target" ? "The direct morph adds the measured per-vertex difference. At 100%, it reaches the target vertex positions. This is a reference shape, not a new avatar preset." : Mode == "Existing" ? (Pair == "human03-to-human07" ? "24 facial controls: front-surface RMS error falls from 1.84 mm to 1.51 mm (18%)." : "24 facial controls: front-surface RMS error falls from 4.07 mm to 3.84 mm (6%).") : "A separate fit using local translation volumes. It improves some regions, but does not consistently beat the existing controls. Intermediate blends interpolate the fitted surface, not the control values.";

	void Back() => StudioSession.OpenScene("scenes/main_menu.scene");
	protected override int BuildHash() => HashCode.Combine(Pair, Mode, Amount, Yaw);
}