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.
@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);
}