Skip to content

refactor(sourceengine): extract Source Engine into a plugin + schema-driven exporter contract#28

Merged
dhkatz merged 1 commit into
mainfrom
refactor/source-plugin-and-options-schema
May 25, 2026
Merged

refactor(sourceengine): extract Source Engine into a plugin + schema-driven exporter contract#28
dhkatz merged 1 commit into
mainfrom
refactor/source-plugin-and-options-schema

Conversation

@dhkatz
Copy link
Copy Markdown
Contributor

@dhkatz dhkatz commented May 25, 2026

Summary

Source Engine support now lives in a standalone GMConverter.SourceEngine plugin assembly. Core no longer contains MDL import/export code, the Source/ directory, the MdlCrowbar package reference, or the CoACD MSBuild target. The SDK exporter contract also moves to a schema-driven shape that a future generic UI panel can render directly.

Plugin extraction

  • New GMConverter.SourceEngine project (net10.0 library) referencing only GMConverter.SDK + MdlCrowbar + ImageSharp.
  • 12 files moved from Core: MDLImporter, MDLExporter, all of Source/* (GameInfo, MaterialOptimizationOptions, PhysicsOptions, SourceMaterialCompiler, SourceMaterialSurfaceProps, SourcePhongSettings, SourceToolPaths), plus the Source-only helpers ProcessRunner, CoacdNative, CoacdDecompositionOptions.
  • The plugin's CoACD MSBuild download target moved with it; lib_coacd is resolved at runtime via PluginLoadContext's sibling-folder fallback (introduced in fix(unrealengine): copy NuGet runtime DLLs into plugin bin so deploy ships them #26 for CUE4Parse-Natives).
  • SourceEnginePlugin : IPlugin registers MDLImporter and MDLExporter via DI; both receive ITextureFactory via constructor injection.
  • plugin.json id: gmconverter.sourceengine.
  • Source-specific texture transforms (ToSourcePhongExponent, WithMaskInAlpha) reimplemented in the plugin against the SDK's new Texture.GetRgbaPixels() surface + ITextureFactory.FromRgba(). Core's TextureExtensions keeps the general-purpose transforms (ToGltfMetallicRoughness, WithOpenGlNormalMap, ToSpecularFactorMask).

Schema-driven exporter contract

  • New SDK types: OptionType (enum), OptionDescriptor (data, with deferred-default factory hook), OptionGroup, ExporterOptionSchema, ExportOptions (typed-accessor bag).
  • IExporter<TOptions> and IExporterDescriptor collapsed into a single non-generic IExporter exposing OptionSchema + Export(Model, string, string, ExportOptions). Plugins read what they need from the bag and assemble their own internal typed options.
  • MDLExporter declares the full ~12-option schema across Tools / Materials / Physics groups. GLTFExporter declares a minimal {binary, bakeUvTransforms} schema; OBJExporter declares ExporterOptionSchema.Empty.

Host bundling

  • GMConverter.CLI.csproj and GMConverter.UI.csproj both add a <BundledPlugin> entry for SourceEngine (same shape as the existing UnrealEngine entry). CopyBundledPlugins stages the plugin's bin/ into bin/.../plugins/sourceengine/.
  • The host's MdlCrowbar package ref is removed from Core's csproj.

Host-side surface cleanup

  • CLI Program.cs and UI ConversionService.cs no longer reference plugin-private Source types (PhysicsOptions, PhysicsMode, CoacdOptions, SourceToolPaths, MDLExporter, MDLImporter). Both build ExportOptions bags from primitive inputs and look up the exporter via PluginHost.Registry.GetExporter(\"mdl\"). Failing that, they surface a clear error pointing at the missing plugin.
  • ConvertViewModel's auto-fill of StudioMDL / VTFCmd paths uses a small local copy of the path-discovery convention (./tools/<tool>/...) instead of calling into the plugin. The plugin keeps its own copy for the resolve path used at export time.
  • UI's CoACD physics preview removed; preview falls back to a bounds visualisation for both modes. Actual export still produces CoACD physics when the user picks that mode. Surfacing a plugin-contributed preview hook is a follow-up.

Empirically verified

  • CLI smoke: running against an empty test.psk, plugin log line Loaded plugin gmconverter.sourceengine v0.1.0 appears alongside Loaded plugin gmconverter.unrealengine v0.1.0, importer error surfaces as expected.
  • Plugin deploy: bin/Release/net10.0/plugins/sourceengine/ contains GMConverter.SourceEngine.dll, MdlCrowbar.dll, lib_coacd.dll, plugin.json and the rest of the runtime deps.
  • dotnet build GMConverter.slnx -c Release → 0 warnings, 0 errors
  • dotnet format --verify-no-changes → clean

Deferred to follow-up (explicit non-goals)

  • Generic schema-driven UI panel (ExporterOptionsPanel.axaml + DataTemplates + ExporterOptionsViewModel). The existing SourceEngineSettings.axaml panel still binds ConvertViewModel's typed Source properties; ConversionService pipes those into the ExportOptions bag at runtime. The schema is the source of truth for keys and defaults but rendering remains bespoke for Source until the generic panel lands.
  • CLI dynamic argument construction from the schema — the schema declares the options, but the CLI still has hand-rolled --studiomdl-path etc. arguments.
  • Manual runtime verification against real archives — same gap as refactor(unrealengine): extract UE plugin + wire DI into IPluginContext #25. PSKImporter and MDLExporter are the most-exercised real-world paths and I cannot validate them from CI. Before merge, please:
    • Open the UI, convert a UE4 asset to MDL, confirm the Source compile workspace generates
    • Convert an OPT or MOW asset to MDL too, confirm the existing non-plugin path still works

🤖 Generated with Claude Code

…ema-driven exporter contract

Source Engine support now lives in a standalone GMConverter.SourceEngine plugin
assembly. Core no longer contains MDL importer/exporter code, the Source/ directory,
the MdlCrowbar package reference, or the CoACD MSBuild target. The SDK exporter
contract also moves to a schema-driven shape that the generic UI panel (follow-up
PR) will render directly.

Plugin extraction
- New GMConverter.SourceEngine project (net10.0, library) referencing only
  GMConverter.SDK + MdlCrowbar + ImageSharp. 12 files moved from Core:
  MDLImporter, MDLExporter, all of Source/{GameInfo,MaterialOptimizationOptions,
  PhysicsOptions,SourceMaterialCompiler,SourceMaterialSurfaceProps,
  SourcePhongSettings,SourceToolPaths}, plus the UE-side-but-Source-specific
  helpers ProcessRunner, CoacdNative, CoacdDecompositionOptions.
- The plugin's CoACD MSBuild download target moved with it; lib_coacd is
  resolved at runtime via PluginLoadContext's sibling-folder fallback
  (introduced in #26 for CUE4Parse-Natives).
- SourceEnginePlugin : IPlugin registers MDLImporter and MDLExporter via DI;
  both receive ITextureFactory via constructor injection.
- plugin.json id is "gmconverter.sourceengine".
- Source-specific texture transforms (ToSourcePhongExponent, WithMaskInAlpha)
  reimplemented in the plugin against the SDK's new Texture.GetRgbaPixels()
  surface + ITextureFactory.FromRgba(). Core's TextureExtensions keeps the
  general-purpose transforms (ToGltfMetallicRoughness, WithOpenGlNormalMap,
  ToSpecularFactorMask).

Schema-driven exporter contract
- New SDK types: OptionType (enum), OptionDescriptor (data, with deferred-default
  factory hook), OptionGroup, ExporterOptionSchema, ExportOptions (typed-accessor
  bag). IExporter<TOptions> and IExporterDescriptor collapsed into a single
  non-generic IExporter exposing OptionSchema + Export(Model, string, string,
  ExportOptions). Plugins read what they need from the bag and assemble their
  own internal typed options.
- MDLExporter declares the full ~12-option schema across Tools / Materials /
  Physics groups. GLTFExporter declares a minimal {binary, bakeUvTransforms}
  schema; OBJExporter declares ExporterOptionSchema.Empty.

Host bundling
- GMConverter.CLI.csproj and GMConverter.UI.csproj both add a <BundledPlugin>
  entry for SourceEngine (same shape as the existing UnrealEngine entry). The
  CopyBundledPlugins target stages the plugin's bin into
  bin/.../plugins/sourceengine/, where PluginLoader picks it up at startup.
- The host's MdlCrowbar package ref is removed from GMConverter/GMConverter.csproj.

Host-side surface cleanup
- CLI Program.cs and UI ConversionService.cs no longer reference plugin-private
  Source types (PhysicsOptions, PhysicsMode, CoacdOptions, SourceToolPaths,
  MDLExporter, MDLImporter). Both build ExportOptions bags from primitive
  inputs and look up the exporter via PluginHost.Registry.GetExporter("mdl").
  Failing that, they surface a clear error pointing at the missing plugin.
- ConvertViewModel's auto-fill of StudioMDL / VTFCmd paths uses a small
  local copy of the path-discovery convention (./tools/<tool>/...) instead of
  calling into the plugin. The plugin keeps its own copy for the resolve
  path used at export time.
- UI's CoACD physics preview removed; preview falls back to a bounds
  visualisation for both modes. Actual export still produces CoACD physics
  when the user picks that mode. Surfacing a plugin-contributed preview hook
  is a follow-up.

Empirically verified
- CLI smoke: runs against an empty test.psk, plugin log line
  "Loaded plugin gmconverter.sourceengine v0.1.0" appears, importer error
  surfaces as expected.
- Plugin deploy: bin/Release/net10.0/plugins/sourceengine/ contains
  GMConverter.SourceEngine.dll, MdlCrowbar.dll, lib_coacd.dll, plugin.json
  and the rest of the runtime deps.
- dotnet build GMConverter.slnx -c Release → 0 warnings, 0 errors
- dotnet format --verify-no-changes → clean

Deferred to follow-up
- Generic schema-driven UI panel (ExporterOptionsPanel.axaml + DataTemplates
  + ExporterOptionsViewModel). The existing SourceEngineSettings.axaml panel
  still binds ConvertViewModel's typed Source properties; ConversionService
  pipes those into the ExportOptions bag at runtime. The schema is the source
  of truth for keys and defaults but rendering remains bespoke for Source
  until the generic panel work lands.
- CLI dynamic argument construction from the schema (the schema declares the
  options but the CLI still has hand-rolled --studiomdl-path etc. arguments).
- Manual UE4/UE5/Source runtime verification against real archives — same
  gap as #25.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread GMConverter.UI/Services/ConversionService.cs Dismissed
Comment thread GMConverter.UI/ViewModels/ConvertViewModel.cs Dismissed
@dhkatz dhkatz merged commit 914f764 into main May 25, 2026
4 checks passed
@dhkatz dhkatz deleted the refactor/source-plugin-and-options-schema branch May 25, 2026 23:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant