Editor/Conversion/ConversionModels.cs

Data model classes for the GMod to s&box asset conversion editor. Defines ConversionOptions and ConversionResult plus lightweight vector, quaternion, mesh, bone, material and model build DTOs used during import and conversion processing.

using System;
using System.Collections.Generic;

namespace GmodAssetImporter;

/// <summary>
/// Shared options for a GMod → s&box conversion run.
/// </summary>
public sealed class ConversionOptions
{
	/// <summary>GMod content root (folder that contains models/ and materials/), e.g. .../garrysmod.</summary>
	public string GmodRoot { get; set; } = "";

	/// <summary>Output Assets root (folder that will contain models/ and materials/).</summary>
	public string OutputRoot { get; set; } = "";

	/// <summary>Preserve Source-relative models/... and materials/... paths under the output root.</summary>
	public bool PreservePaths { get; set; } = true;

	/// <summary>
	/// Log Source sequence names during convert. Real AnimFile clips need FBX (SMD AnimFile is rejected by ModelDoc).
	/// Mesh always gets AnimBindPose.
	/// </summary>
	public bool ExportAnimations { get; set; } = false;

	/// <summary>When true, folder input also includes every subfolder (recursive).</summary>
	public bool Recursive { get; set; } = true;

	/// <summary>
	/// When false, models still reference their expected .vmat paths but no VMAT/PNG files are written,
	/// and folder batches skip loose .vtf textures.
	/// </summary>
	public bool ImportMaterials { get; set; } = true;

	/// <summary>When false, .mdl files are skipped (materials-only run).</summary>
	public bool ImportModels { get; set; } = true;

	/// <summary>Optional global scale applied via ModelModifier_ScaleAndMirror.</summary>
	public float ModelScale { get; set; } = 1.0f;

	public Action<string> OnLog { get; set; } = _ => { };
}

public sealed class ConversionResult
{
	public bool Success { get; set; }
	public string Message { get; set; } = "";
	public List<string> WrittenFiles { get; } = new();
	public List<string> Warnings { get; } = new();
}

public sealed class Vec3
{
	public float X, Y, Z;
	public Vec3() { }
	public Vec3( float x, float y, float z ) { X = x; Y = y; Z = z; }
	public static Vec3 Zero => new( 0, 0, 0 );
}

public sealed class Vec2
{
	public float X, Y;
	public Vec2() { }
	public Vec2( float x, float y ) { X = x; Y = y; }
}

public sealed class Quat
{
	public float X, Y, Z, W = 1;
	public Quat() { }
	public Quat( float x, float y, float z, float w ) { X = x; Y = y; Z = z; W = w; }
	public static Quat Identity => new( 0, 0, 0, 1 );
}

public sealed class BoneWeight
{
	public int Bone;
	public float Weight;
}

public sealed class MeshVertex
{
	public Vec3 Position = Vec3.Zero;
	public Vec3 Normal = new( 0, 0, 1 );
	public Vec2 Uv = new();
	public List<BoneWeight> Weights { get; } = new();
}

public sealed class MeshTriangle
{
	public int A, B, C;
	public string MaterialName = "";
}

public sealed class BoneData
{
	public string Name = "";
	public int Parent = -1;
	public Vec3 Position = Vec3.Zero;
	public Vec3 Rotation = Vec3.Zero; // degrees (SMD-friendly)
	public Quat Quaternion = Quat.Identity;
}

public sealed class AttachmentData
{
	public string Name = "";
	public string ParentBone = "";
	public Vec3 Origin = Vec3.Zero;
	public Vec3 Angles = Vec3.Zero;
}

public sealed class ImporterBodyGroupChoice
{
	public string Name = "";
	public List<string> MeshNames { get; } = new();
}

public sealed class ImporterBodyGroup
{
	public string Name = "";
	public List<ImporterBodyGroupChoice> Choices { get; } = new();
}

public sealed class MaterialRemap
{
	public string From = "";
	public string To = "";
}

public sealed class MaterialGroupData
{
	public string Name = "";
	public List<MaterialRemap> Remaps { get; } = new();
}

public sealed class AnimationClip
{
	public string Name = "";
	public string FileName = "";
	public float FrameRate = 30;
	public bool Looping;
}

public sealed class MeshExport
{
	public string Name = "";
	public string FileName = "";
	public List<MeshVertex> Vertices { get; } = new();
	public List<MeshTriangle> Triangles { get; } = new();
}

public sealed class ModelBuildContext
{
	public string ModelName = "";
	public string ModelAssetDirectory = ""; // e.g. models/foo/bar
	public List<BoneData> Bones { get; } = new();
	public List<MeshExport> Meshes { get; } = new();
	public List<AttachmentData> Attachments { get; } = new();
	public List<ImporterBodyGroup> BodyGroups { get; } = new();
	public List<MaterialRemap> MaterialRemaps { get; } = new();
	public List<MaterialGroupData> MaterialGroups { get; } = new();
	public List<AnimationClip> Animations { get; } = new();
	public float Scale = 1.0f;
	public bool IncludeBindPose = true;
}

public sealed class MaterialBuildResult
{
	public string SourceMaterialToken = "";
	public string VmatAssetPath = ""; // materials/.../name.vmat
	public bool UsedFallback;
}