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;
}