Editor-side model classes for previewing 3D assets. PreviewState stores per-graph viewport settings (mesh/model, envmap, colors, flags, channel) and provides cloning/copying, while PreviewCamera stores orbit parameters with computed clamped properties and cloning.
namespace Editor.Prism.Model;
/// <summary>
/// Per-graph preview settings: what the viewport shows and how the camera is posed. Serialized with
/// the document so reopening a graph restores exactly the framing the author left it in.
/// </summary>
public sealed class PreviewState
{
/// <summary>Built-in mesh name from <see cref="Meshes"/>. Ignored when <see cref="Model"/> is set.</summary>
public string Mesh { get; set; } = "Sphere";
/// <summary>Custom model asset path. Takes precedence over <see cref="Mesh"/> when non-empty.</summary>
public string Model { get; set; }
/// <summary>Environment map asset path used for image-based lighting and the skybox.</summary>
public string Envmap { get; set; }
/// <summary>Whether the ground plane is drawn.</summary>
public bool ShowGround { get; set; }
/// <summary>Whether the skybox is drawn behind the subject.</summary>
public bool ShowSkybox { get; set; } = true;
/// <summary>Tint multiplied into the preview material.</summary>
public Color Tint { get; set; } = Color.White;
/// <summary>Viewport clear colour, used when the skybox is hidden.</summary>
public Color Background { get; set; } = new( 0.05f, 0.06f, 0.07f, 1f );
/// <summary>Camera orbit state.</summary>
public PreviewCamera Camera { get; set; } = new();
/// <summary>
/// The debug channel currently displayed. <c>null</c> or empty means the shaded result; anything
/// else names one of the preview channels (albedo, normal, roughness, uv, overdraw…).
/// </summary>
public string Channel { get; set; }
/// <summary>True when a custom model asset is chosen rather than a built-in mesh.</summary>
public bool UsesCustomModel => !string.IsNullOrWhiteSpace( Model );
/// <summary>The mesh name to build, falling back to a sphere when the stored name is unknown.</summary>
public string EffectiveMesh =>
!string.IsNullOrWhiteSpace( Mesh ) && Meshes.Contains( Mesh, StringComparer.OrdinalIgnoreCase )
? Mesh
: "Sphere";
/// <summary>Deep copy.</summary>
public PreviewState Clone() => new()
{
Mesh = Mesh,
Model = Model,
Envmap = Envmap,
ShowGround = ShowGround,
ShowSkybox = ShowSkybox,
Tint = Tint,
Background = Background,
Camera = Camera?.Clone() ?? new PreviewCamera(),
Channel = Channel
};
/// <summary>Copy every field from another instance without replacing the object identity.</summary>
public void CopyFrom( PreviewState other )
{
if ( other is null ) return;
Mesh = other.Mesh;
Model = other.Model;
Envmap = other.Envmap;
ShowGround = other.ShowGround;
ShowSkybox = other.ShowSkybox;
Tint = other.Tint;
Background = other.Background;
Camera = other.Camera?.Clone() ?? new PreviewCamera();
Channel = other.Channel;
}
/// <inheritdoc/>
public override string ToString() => UsesCustomModel ? $"Preview '{Model}'" : $"Preview {EffectiveMesh}";
/// <summary>The built-in preview meshes the preview package generates procedurally.</summary>
public static readonly IReadOnlyList<string> Meshes = new[]
{
"Sphere", "Cube", "Plane", "Quad", "Cylinder", "Cone", "Torus"
};
}
/// <summary>The preview camera's orbit around the subject.</summary>
public sealed class PreviewCamera
{
/// <summary>Orbit yaw in degrees.</summary>
public float Yaw { get; set; } = 135f;
/// <summary>Orbit pitch in degrees, clamped to +/-89 when applied.</summary>
public float Pitch { get; set; } = 30f;
/// <summary>Orbit distance in world units (inches).</summary>
public float Distance { get; set; } = 150f;
/// <summary>The pitch clamped into the range a look-at camera can actually use.</summary>
public float SafePitch => Math.Clamp( Pitch, -89f, 89f );
/// <summary>The distance clamped away from zero so the camera never lands inside the subject.</summary>
public float SafeDistance => Math.Clamp( Distance, 1f, 100000f );
/// <summary>Deep copy.</summary>
public PreviewCamera Clone() => new() { Yaw = Yaw, Pitch = Pitch, Distance = Distance };
/// <inheritdoc/>
public override string ToString() => $"yaw {Yaw:0.#} pitch {Pitch:0.#} dist {Distance:0.#}";
}