diff --git a/Editor/Scripts/GLTFExportMenu.cs b/Editor/Scripts/GLTFExportMenu.cs
index 924429fca..135aa6763 100644
--- a/Editor/Scripts/GLTFExportMenu.cs
+++ b/Editor/Scripts/GLTFExportMenu.cs
@@ -108,7 +108,7 @@ private static void ExportSceneGLB()
private static void Export(Transform[] transforms, bool binary, string sceneName)
{
var settings = GLTFSettings.GetOrCreateSettings();
- var exportOptions = new ExportOptions { TexturePathRetriever = RetrieveTexturePath };
+ var exportOptions = new ExportContext { TexturePathRetriever = RetrieveTexturePath };
var exporter = new GLTFSceneExporter(transforms, exportOptions);
var invokedByShortcut = Event.current?.type == EventType.KeyDown;
diff --git a/Editor/Scripts/GLTFImporter.cs b/Editor/Scripts/GLTFImporter.cs
index 1ebadab32..ddf2eb28f 100644
--- a/Editor/Scripts/GLTFImporter.cs
+++ b/Editor/Scripts/GLTFImporter.cs
@@ -46,7 +46,7 @@ namespace UnityGLTF
#else
[ScriptedImporter(ImporterVersion, new[] { "glb" })]
#endif
- public class GLTFImporter : ScriptedImporter, IGLTFImportRemap
+ public class GLTFImporter : ScriptedImporter
{
private const int ImporterVersion = 9;
@@ -236,7 +236,7 @@ public override void OnImportAsset(AssetImportContext ctx)
if (plugin != null && plugin.Enabled)
{
var instance = plugin.CreateInstance(context);
- if(instance != null) plugins.Add(instance);
+ if (instance != null) plugins.Add(instance);
}
}
diff --git a/Editor/Scripts/Plugins/GltfPluginEditor.cs b/Editor/Scripts/Plugins/GltfPluginEditor.cs
new file mode 100644
index 000000000..77b1987fd
--- /dev/null
+++ b/Editor/Scripts/Plugins/GltfPluginEditor.cs
@@ -0,0 +1,21 @@
+using UnityEditor;
+using UnityGLTF.Plugins;
+
+namespace UnityGLTF
+{
+ [CustomEditor(typeof(GltfPlugin), true)]
+ public class GltfPluginEditor: Editor
+ {
+ // Follows the default implementation of OnInspectorGUI, but skips the script field
+ public override void OnInspectorGUI()
+ {
+ serializedObject.Update();
+ var iterator = serializedObject.GetIterator();
+ // skip script field
+ iterator.NextVisible(true);
+ while (iterator.NextVisible(false))
+ EditorGUILayout.PropertyField(iterator, true);
+ serializedObject.ApplyModifiedProperties();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Editor/Scripts/Plugins/GltfPluginEditor.cs.meta b/Editor/Scripts/Plugins/GltfPluginEditor.cs.meta
new file mode 100644
index 000000000..c3ab18f28
--- /dev/null
+++ b/Editor/Scripts/Plugins/GltfPluginEditor.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 5df1106545064293aebd7efc2fc87905
+timeCreated: 1703883677
\ No newline at end of file
diff --git a/Editor/Scripts/ShaderGraph/MaterialEditorBridge.cs b/Editor/Scripts/ShaderGraph/MaterialEditorBridge.cs
index e3d093cd5..fc1d6993b 100644
--- a/Editor/Scripts/ShaderGraph/MaterialEditorBridge.cs
+++ b/Editor/Scripts/ShaderGraph/MaterialEditorBridge.cs
@@ -23,7 +23,7 @@ private static void OnImmutableMaterialChanged(Material material)
// var mainAsset = AssetDatabase.LoadMainAssetAtPath(assetPath);
// Transform[] rootTransforms = null;
- var exporter = new GLTFSceneExporter((Transform[]) null, new ExportOptions());
+ var exporter = new GLTFSceneExporter((Transform[]) null, new ExportContext());
// load all materials from mainAsset
var importer = AssetImporter.GetAtPath(assetPath) as GLTFImporter;
if (!importer) return;
diff --git a/Runtime/Scripts/Extensions/MaterialExtensions.cs b/Runtime/Scripts/Extensions/MaterialExtensions.cs
index 7547a56ec..d5f05936d 100644
--- a/Runtime/Scripts/Extensions/MaterialExtensions.cs
+++ b/Runtime/Scripts/Extensions/MaterialExtensions.cs
@@ -2,23 +2,57 @@
using GLTF.Schema;
using UnityEngine;
using UnityGLTF.Extensions;
+using UnityGLTF.Plugins;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace UnityGLTF
{
- public static class MaterialExtensions
+ // When a plugin is registered with the default settings (the scriptable object in the project),
+ // it will be active "by default" when someone uses those default settings.
+ // e.g. it's used when someone uses the built-in editor methods for exporting objects.
+ // When using the API, one needs to manually register wanted plugins and configure them
+ // (can get the default settings and modify them).
+
+ // Plugins can contain any number of extensions, but are encouraged to specify in the description
+ // which extensions are imported/exported with that plugin.
+ // Theoretically there could be multiple plugins operating on the same extension in different ways, in
+ // which case we currently can't warn about conflicts; they would all run.
+ // If plugins were required to list the extensions they operate on, we could warn about conflicts.
+
+ // Plugins are ScriptableObjects which are added to the default GLTFSettings scriptable object.
+ // Their public serialized fields are exposed in the inspector, and they can be enabled/disabled.
+ // Plugins replace both GLTFSceneExporter.* static callbacks and GLTFSceneExporter.ExportOptions callbacks
+ // to allow for more control.
+
+ // Example cases where separate plugins operate on the same data:
+ // - exporting UI as custom extension vs. baking UI to mesh
+ // - exporting Audio in a custom extension vs. using KHR_audio
+ // - exporting LODs as custom extension vs. using MSFT_lod
+ // - exporting particle systems as custom extension vs. baking to mesh
+
+ // Plugins can either be added manually to ExportOptions.plugins / ImportContext.plugins
+ // or advertise themselves via a static callback which allows configuring their settings in the inspector.
+ // For each new instance of GLTFSceneExporter, new instances of plugins are created.
+ // For each new instance of GLTFSceneImporter, new instances of plugins are created.
+
+ public class MaterialExtensionsPlugin: GltfExportPlugin
{
-#if UNITY_EDITOR
- [InitializeOnLoadMethod]
-#endif
- [RuntimeInitializeOnLoadMethod]
- static void InitExt()
+ public bool KHR_materials_ior = true;
+ public bool KHR_materials_transmission = true;
+ public bool KHR_materials_volume = true;
+
+ public override GltfExportPluginContext CreateInstance(ExportContext context)
{
- GLTFSceneExporter.AfterMaterialExport += GLTFSceneExporterOnAfterMaterialExport;
+ return new MaterialExtensions();
}
+ public override string DisplayName => "Material Extensions";
+ }
+
+ public class MaterialExtensions: GltfExportPluginContext
+ {
private static readonly int thicknessTexture = Shader.PropertyToID("thicknessTexture");
private static readonly int thicknessFactor = Shader.PropertyToID("thicknessFactor");
private static readonly int attenuationDistance = Shader.PropertyToID("attenuationDistance");
@@ -45,8 +79,7 @@ static void InitExt()
private static readonly int clearcoatRoughnessTexture = Shader.PropertyToID("clearcoatRoughnessTexture");
private static readonly int clearcoatNormalTexture = Shader.PropertyToID("clearcoatNormalTexture");
-
- private static void GLTFSceneExporterOnAfterMaterialExport(GLTFSceneExporter exporter, GLTFRoot gltfroot, Material material, GLTFMaterial materialnode)
+ public override void AfterMaterialExport(GLTFSceneExporter exporter, GLTFRoot gltfroot, Material material, GLTFMaterial materialnode)
{
if (!material) return;
@@ -193,8 +226,6 @@ private static void GLTFSceneExporterOnAfterMaterialExport(GLTFSceneExporter exp
cc.clearcoatRoughnessTexture = exporter.ExportTextureInfoWithTextureTransform(material, material.GetTexture(clearcoatRoughnessTexture), nameof(clearcoatRoughnessTexture));
if (material.HasProperty(clearcoatNormalTexture))
cc.clearcoatNormalTexture = exporter.ExportTextureInfoWithTextureTransform(material, material.GetTexture(clearcoatNormalTexture), nameof(clearcoatNormalTexture));
-
-
}
}
}
diff --git a/Runtime/Scripts/GLTFSceneExporter.cs b/Runtime/Scripts/GLTFSceneExporter.cs
index 3c294161c..58ec40c74 100644
--- a/Runtime/Scripts/GLTFSceneExporter.cs
+++ b/Runtime/Scripts/GLTFSceneExporter.cs
@@ -16,11 +16,11 @@
using Unity.Profiling;
using UnityEngine;
using UnityGLTF.Extensions;
-using WrapMode = GLTF.Schema.WrapMode;
+using UnityGLTF.Plugins;
namespace UnityGLTF
{
- public class ExportOptions
+ public class ExportContext
{
public bool TreatEmptyRootAsScene = false;
public bool MergeClipsWithMatchingNames = false;
@@ -28,9 +28,9 @@ public class ExportOptions
public ILogger logger;
internal readonly GLTFSettings settings;
- public ExportOptions() : this(GLTFSettings.GetOrCreateSettings()) { }
+ public ExportContext() : this(GLTFSettings.GetOrCreateSettings()) { }
- public ExportOptions(GLTFSettings settings)
+ public ExportContext(GLTFSettings settings)
{
if (!settings) settings = GLTFSettings.GetOrCreateSettings();
if (settings.UseMainCameraVisibility)
@@ -39,16 +39,71 @@ public ExportOptions(GLTFSettings settings)
}
public GLTFSceneExporter.RetrieveTexturePathDelegate TexturePathRetriever = (texture) => texture.name;
+
+ [Obsolete("Register export plugins with GLTFSettings instead")]
public GLTFSceneExporter.AfterSceneExportDelegate AfterSceneExport;
+ [Obsolete("Register export plugins with GLTFSettings instead")]
public GLTFSceneExporter.BeforeSceneExportDelegate BeforeSceneExport;
+ [Obsolete("Register export plugins with GLTFSettings instead")]
public GLTFSceneExporter.AfterNodeExportDelegate AfterNodeExport;
+ [Obsolete("Register export plugins with GLTFSettings instead")]
public GLTFSceneExporter.BeforeMaterialExportDelegate BeforeMaterialExport;
+ [Obsolete("Register export plugins with GLTFSettings instead")]
public GLTFSceneExporter.AfterMaterialExportDelegate AfterMaterialExport;
+ [Obsolete("Register export plugins with GLTFSettings instead")]
public GLTFSceneExporter.BeforeTextureExportDelegate BeforeTextureExport;
+ [Obsolete("Register export plugins with GLTFSettings instead")]
public GLTFSceneExporter.AfterTextureExportDelegate AfterTextureExport;
+ [Obsolete("Register export plugins with GLTFSettings instead")]
public GLTFSceneExporter.AfterPrimitiveExportDelegate AfterPrimitiveExport;
+
+ internal GltfExportPluginContext GetImplicitPlugin()
+ {
+ return new ImplicitPlugin(this);
+ }
+
+#pragma warning disable CS0618 // Type or member is obsolete
+ internal class ImplicitPlugin : GltfExportPluginContext
+ {
+ private readonly ExportContext _exportContext;
+
+ internal ImplicitPlugin(ExportContext context)
+ {
+ _exportContext = context;
+ }
+
+ public override void BeforeSceneExport(GLTFSceneExporter exporter, GLTFRoot gltfRoot) => _exportContext.BeforeSceneExport?.Invoke(exporter, gltfRoot);
+ public override void AfterSceneExport(GLTFSceneExporter exporter, GLTFRoot gltfRoot) => _exportContext.AfterSceneExport?.Invoke(exporter, gltfRoot);
+ public override void AfterNodeExport(GLTFSceneExporter exporter, GLTFRoot gltfRoot, Transform transform, Node node) => _exportContext.AfterNodeExport?.Invoke(exporter, gltfRoot, transform, node);
+ public override bool BeforeMaterialExport(GLTFSceneExporter exporter, GLTFRoot gltfRoot, Material material, GLTFMaterial materialNode)
+ {
+ // static callback, run after options callback
+ // we're iterating here because we want to stop calling any once we hit one that can export this material.
+ if (_exportContext.BeforeMaterialExport != null)
+ {
+ var list = _exportContext.BeforeMaterialExport.GetInvocationList();
+ foreach (var entry in list)
+ {
+ var cb = (GLTFSceneExporter.BeforeMaterialExportDelegate) entry;
+ if (cb != null && cb.Invoke(exporter, gltfRoot, material, materialNode))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ public override void AfterMaterialExport(GLTFSceneExporter exporter, GLTFRoot gltfRoot, Material material, GLTFMaterial materialNode) => _exportContext.AfterMaterialExport?.Invoke(exporter, gltfRoot, material, materialNode);
+ public override void BeforeTextureExport(GLTFSceneExporter exporter, ref GLTFSceneExporter.UniqueTexture texture, string textureSlot) => _exportContext.BeforeTextureExport?.Invoke(exporter, ref texture, textureSlot);
+ public override void AfterTextureExport(GLTFSceneExporter exporter, GLTFSceneExporter.UniqueTexture texture, int index, GLTFTexture tex) => _exportContext.AfterTextureExport?.Invoke(exporter, texture, index, tex);
+ public override void AfterPrimitiveExport(GLTFSceneExporter exporter, Mesh mesh, MeshPrimitive primitive, int index) => _exportContext.AfterPrimitiveExport?.Invoke(exporter, mesh, primitive, index);
+ }
+#pragma warning restore CS0618 // Type or member is obsolete
}
+
+ [Obsolete("Use ExportContext instead")]
+ public class ExportOptions: ExportContext {}
public partial class GLTFSceneExporter
{
@@ -59,11 +114,43 @@ public partial class GLTFSceneExporter
public delegate void BeforeSceneExportDelegate(GLTFSceneExporter exporter, GLTFRoot gltfRoot);
public delegate void AfterSceneExportDelegate(GLTFSceneExporter exporter, GLTFRoot gltfRoot);
public delegate void AfterNodeExportDelegate(GLTFSceneExporter exporter, GLTFRoot gltfRoot, Transform transform, Node node);
+ /// True: material export is complete. False: continue regular export.
+ public delegate bool BeforeMaterialExportDelegate(GLTFSceneExporter exporter, GLTFRoot gltfRoot, Material material, GLTFMaterial materialNode);
+ public delegate void AfterMaterialExportDelegate(GLTFSceneExporter exporter, GLTFRoot gltfRoot, Material material, GLTFMaterial materialNode);
public delegate void BeforeTextureExportDelegate(GLTFSceneExporter exporter, ref UniqueTexture texture, string textureSlot);
public delegate void AfterTextureExportDelegate(GLTFSceneExporter exporter, UniqueTexture texture, int index, GLTFTexture tex);
public delegate void AfterPrimitiveExportDelegate(GLTFSceneExporter exporter, Mesh mesh, MeshPrimitive primitive, int index);
+
+ private class LegacyCallbacksPlugin : GltfExportPluginContext
+ {
+ public override void AfterSceneExport(GLTFSceneExporter exporter, GLTFRoot gltfRoot) => GLTFSceneExporter.AfterSceneExport?.Invoke(exporter, gltfRoot);
+ public override void BeforeSceneExport(GLTFSceneExporter exporter, GLTFRoot gltfRoot) => GLTFSceneExporter.BeforeSceneExport?.Invoke(exporter, gltfRoot);
+ public override void AfterNodeExport(GLTFSceneExporter exporter, GLTFRoot gltfRoot, Transform transform, Node node) => GLTFSceneExporter.AfterNodeExport?.Invoke(exporter, gltfRoot, transform, node);
+
+ public override bool BeforeMaterialExport(GLTFSceneExporter exporter, GLTFRoot gltfRoot, Material material, GLTFMaterial materialNode)
+ {
+ // static callback, run after options callback
+ // we're iterating here because we want to stop calling any once we hit one that can export this material.
+ if (GLTFSceneExporter.BeforeMaterialExport != null)
+ {
+ var list = GLTFSceneExporter.BeforeMaterialExport.GetInvocationList();
+ foreach (var entry in list)
+ {
+ var cb = (BeforeMaterialExportDelegate) entry;
+ if (cb != null && cb.Invoke(exporter, gltfRoot, material, materialNode))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ public override void AfterMaterialExport(GLTFSceneExporter exporter, GLTFRoot gltfRoot, Material material, GLTFMaterial materialNode) => GLTFSceneExporter.AfterMaterialExport?.Invoke(exporter, gltfRoot, material, materialNode);
+ }
+
private static ILogger Debug = UnityEngine.Debug.unityLogger;
+ private List _plugins = new List();
public struct TextureMapType
{
@@ -278,7 +365,7 @@ public struct ExportFileResult
private List _animatedNodes;
private int _exportLayerMask;
- private ExportOptions _exportOptions;
+ private ExportContext _exportContext;
private Material _metalGlossChannelSwapMaterial;
private Material _normalChannelMaterial;
@@ -396,7 +483,7 @@ public override int GetHashCode()
#region Settings
- private GLTFSettings settings => _exportOptions.settings;
+ private GLTFSettings settings => _exportContext.settings;
private bool ExportNames => settings.ExportNames;
private bool RequireExtensions => settings.RequireExtensions;
private bool ExportAnimations => settings.ExportAnimations;
@@ -463,11 +550,11 @@ public override int GetHashCode()
/// Root transform of object to export
[Obsolete("Please switch to GLTFSceneExporter(Transform[] rootTransforms, ExportOptions options). This constructor is deprecated and will be removed in a future release.")]
public GLTFSceneExporter(Transform[] rootTransforms, RetrieveTexturePathDelegate texturePathRetriever)
- : this(rootTransforms, new ExportOptions { TexturePathRetriever = texturePathRetriever })
+ : this(rootTransforms, new ExportContext { TexturePathRetriever = texturePathRetriever })
{
}
- public GLTFSceneExporter(Transform rootTransform, ExportOptions options) : this(new [] { rootTransform }, options)
+ public GLTFSceneExporter(Transform rootTransform, ExportContext context) : this(new [] { rootTransform }, context)
{
}
@@ -475,16 +562,31 @@ public GLTFSceneExporter(Transform rootTransform, ExportOptions options) : this(
/// Create a GLTFExporter that exports out a transform
///
/// Root transform of object to export
- /// Export Settings
- public GLTFSceneExporter(Transform[] rootTransforms, ExportOptions options)
+ /// Export Settings
+ public GLTFSceneExporter(Transform[] rootTransforms, ExportContext context)
{
- _exportOptions = options;
- if (options.logger != null)
- Debug = options.logger;
+ _exportContext = context;
+ if (context.logger != null)
+ Debug = context.logger;
else
Debug = UnityEngine.Debug.unityLogger;
+
+ // legacy: implicit plugin for all the static methods on GLTFSceneExporter
+ _plugins.Add(new LegacyCallbacksPlugin());
+ // legacy: implicit plugin for all the methods on ExportContext
+ _plugins.Add(context.GetImplicitPlugin());
+
+ // create export plugin instances
+ foreach (var plugin in settings.ExportPlugins)
+ {
+ if (plugin != null && plugin.Enabled)
+ {
+ var instance = plugin.CreateInstance(context);
+ if (instance != null) _plugins.Add(instance);
+ }
+ }
- _exportLayerMask = _exportOptions.ExportLayers;
+ _exportLayerMask = _exportContext.ExportLayers;
var metalGlossChannelSwapShader = Resources.Load("MetalGlossChannelSwap", typeof(Shader)) as Shader;
_metalGlossChannelSwapMaterial = new Material(metalGlossChannelSwapShader);
@@ -607,8 +709,8 @@ public void SaveGLBToStream(Stream stream, string sceneName)
exportGltfInitMarker.End();
beforeSceneExportMarker.Begin();
- _exportOptions.BeforeSceneExport?.Invoke(this, _root);
- BeforeSceneExport?.Invoke(this, _root);
+ foreach (var plugin in _plugins)
+ plugin.BeforeSceneExport(this, _root);
beforeSceneExportMarker.End();
_root.Scene = ExportScene(sceneName, _rootTransforms);
@@ -626,11 +728,8 @@ public void SaveGLBToStream(Stream stream, string sceneName)
}
afterSceneExportMarker.Begin();
- if (_exportOptions.AfterSceneExport != null)
- _exportOptions.AfterSceneExport(this, _root);
-
- if (AfterSceneExport != null)
- AfterSceneExport.Invoke(this, _root);
+ foreach (var plugin in _plugins)
+ plugin.AfterSceneExport(this, _root);
afterSceneExportMarker.End();
animationPointerResolver?.Resolve(this);
@@ -711,8 +810,8 @@ public void SaveGLTFandBin(string path, string fileName, bool exportTextures = t
exportGltfInitMarker.End();
beforeSceneExportMarker.Begin();
- _exportOptions.BeforeSceneExport?.Invoke(this, _root);
- BeforeSceneExport?.Invoke(this, _root);
+ foreach (var plugin in _plugins)
+ plugin.BeforeSceneExport(this, _root);
beforeSceneExportMarker.End();
if (_rootTransforms != null)
@@ -729,10 +828,8 @@ public void SaveGLTFandBin(string path, string fileName, bool exportTextures = t
}
afterSceneExportMarker.Begin();
- if (_exportOptions.AfterSceneExport != null)
- _exportOptions.AfterSceneExport(this, _root);
- if (AfterSceneExport != null)
- AfterSceneExport.Invoke(this, _root);
+ foreach (var plugin in _plugins)
+ plugin.AfterSceneExport(this, _root);
afterSceneExportMarker.End();
animationPointerResolver?.Resolve(this);
@@ -874,7 +971,7 @@ private SceneId ExportScene(string name, Transform[] rootObjTransforms)
scene.Name = name;
}
- if(_exportOptions.TreatEmptyRootAsScene)
+ if(_exportContext.TreatEmptyRootAsScene)
{
// if we're exporting with a single object selected, that object can be the scene root, no need for an extra root node.
if (rootObjTransforms.Length == 1 && rootObjTransforms[0].GetComponents().Length == 1) // single root with a single transform
@@ -1019,8 +1116,8 @@ private NodeId ExportNode(Transform nodeTransform)
// node export callback
afterNodeExportMarker.Begin();
- _exportOptions.AfterNodeExport?.Invoke(this, _root, nodeTransform, node);
- AfterNodeExport?.Invoke(this, _root, nodeTransform, node);
+ foreach (var plugin in _plugins)
+ plugin.AfterNodeExport(this, _root, nodeTransform, node);
afterNodeExportMarker.End();
return id;
diff --git a/Runtime/Scripts/GLTFSettings.cs b/Runtime/Scripts/GLTFSettings.cs
index 09931348d..8785fcdc5 100644
--- a/Runtime/Scripts/GLTFSettings.cs
+++ b/Runtime/Scripts/GLTFSettings.cs
@@ -33,8 +33,7 @@ public override void OnActivate(string searchContext, VisualElement rootElement)
base.OnActivate(searchContext, rootElement);
CalculateCacheStats();
}
-
-
+
[SettingsProvider]
public static SettingsProvider CreateGltfSettingsProvider()
{
@@ -51,8 +50,7 @@ private static void CalculateCacheStats()
var files = new List();
exportCacheByteLength = ExportCache.CalculateCacheSize(files);
}
-
-
+
private static GLTFSettings settings;
private static SerializedObject m_SerializedObject;
@@ -94,42 +92,60 @@ internal static void DrawGLTFSettingsGUI()
}
GUILayout.Space(10);
+
EditorGUILayout.LabelField("Import Plugins", EditorStyles.boldLabel);
- OnPluginsGUI();
+ OnPluginsGUI(settings.ImportPlugins);
+
+ EditorGUILayout.LabelField("Export Plugins", EditorStyles.boldLabel);
+ OnPluginsGUI(settings.ExportPlugins);
+
+ EditorGUILayout.LabelField("Supported Extensions (Import)", EditorStyles.boldLabel);
+ // All plugins in the extension factory are supported for import.
+ // TODO Some of them have extra package requirements (e.g. meshopt/draco), could be shown here
+ // TODO help buttons and docs/tooltip would be great
+ foreach (var ext in GLTFProperty.RegisteredExtensions)
+ {
+ EditorGUILayout.ToggleLeft(ext, true);
+ }
OnAfterGUI?.Invoke(settings);
}
- internal static void OnPluginsGUI()
+ private static Dictionary editorCache = new Dictionary();
+ internal static void OnPluginsGUI(IEnumerable plugins)
{
- foreach (var plugin in settings.ImportPlugins)
+ EditorGUI.indentLevel++;
+ foreach (var plugin in plugins)
{
if (!plugin) continue;
var displayName = plugin.DisplayName ?? plugin.name;
if (string.IsNullOrEmpty(displayName))
displayName = ObjectNames.NicifyVariableName(plugin.GetType().Name);
+ var key = plugin.GetType().FullName + "_SettingsExpanded";
+ var expanded = SessionState.GetBool(key, false);
using (new GUILayout.HorizontalScope())
{
var label = new GUIContent(displayName);
- plugin.Expanded = EditorGUILayout.BeginFoldoutHeaderGroup(plugin.Expanded, label);
+ var expanded2 = EditorGUILayout.Foldout(expanded, label);
+ if (expanded2 != expanded)
+ {
+ expanded = expanded2;
+ SessionState.SetBool(key, expanded2);
+ }
plugin.Enabled = GUILayout.Toggle(plugin.Enabled, "", GUILayout.Width(20));
}
- if (plugin.Expanded)
+ if (expanded)
{
- EditorGUI.indentLevel += 1;
- plugin.OnGUI();
- EditorGUI.indentLevel -= 1;
+ EditorGUI.indentLevel++;
+ editorCache.TryGetValue(plugin.GetType(), out var editor);
+ Editor.CreateCachedEditor(plugin, null, ref editor);
+ editorCache[plugin.GetType()] = editor;
+ editor.OnInspectorGUI();
+ EditorGUI.indentLevel--;
}
EditorGUILayout.EndFoldoutHeaderGroup();
}
-
- EditorGUILayout.LabelField("Supported Extensions (Import)", EditorStyles.boldLabel);
- // All plugins in the extension factory are supported for import.
- // TODO Some of them have extra package requirements (e.g. meshopt/draco), could be shown here
- // TODO help buttons and docs/tooltip would be great
- foreach (var ext in GLTFProperty.RegisteredExtensions)
- {
- EditorGUILayout.ToggleLeft(ext, true);
- }
+
+ EditorGUI.indentLevel--;
}
}
@@ -161,9 +177,13 @@ public enum BlendShapeExportPropertyFlags
All = ~0
}
+ // Plugins
[SerializeField, HideInInspector]
public List ImportPlugins;
-
+
+ [SerializeField, HideInInspector]
+ public List ExportPlugins;
+
[Header("Export Settings")]
[SerializeField]
private bool exportNames = true;
@@ -273,6 +293,7 @@ public static GLTFSettings GetOrCreateSettings()
settings = ScriptableObject.CreateInstance();
#endif
}
+
#if UNITY_EDITOR
RegisterPlugins(settings);
#endif
@@ -312,20 +333,29 @@ public static bool TryGetSettings(out GLTFSettings settings)
#if UNITY_EDITOR
private static void RegisterPlugins(GLTFSettings settings)
{
- if(settings.ImportPlugins == null) settings.ImportPlugins = new List();
+ if (settings.ImportPlugins == null) settings.ImportPlugins = new List();
+ if (settings.ExportPlugins == null) settings.ExportPlugins = new List();
- foreach (var pluginType in TypeCache.GetTypesDerivedFrom())
+ void FindAndRegisterPlugins(List plugins) where T : GltfPlugin
{
- if (pluginType.IsAbstract) continue;
- // If the plugin already exists we dont want to add it again
- if (settings.ImportPlugins.Any(p => p != null && p.GetType() == pluginType))
- continue;
- if (typeof(ScriptableObject).IsAssignableFrom(pluginType))
+ foreach (var pluginType in TypeCache.GetTypesDerivedFrom())
{
- var newInstance = ScriptableObject.CreateInstance(pluginType) as GltfImportPlugin;
- settings.ImportPlugins.Add(newInstance);
+ if (pluginType.IsAbstract) continue;
+ if (plugins.Any(p => p != null && p.GetType() == pluginType))
+ continue;
+
+ if (typeof(ScriptableObject).IsAssignableFrom(pluginType))
+ {
+ var newInstance = CreateInstance(pluginType) as T;
+ plugins.Add(newInstance);
+ Debug.Log("added plugin " + newInstance);
+ EditorUtility.SetDirty(settings);
+ }
}
}
+
+ FindAndRegisterPlugins(settings.ImportPlugins);
+ FindAndRegisterPlugins(settings.ExportPlugins);
}
#endif
}
diff --git a/Runtime/Scripts/Plugins/GltfExportPlugin.cs b/Runtime/Scripts/Plugins/GltfExportPlugin.cs
new file mode 100644
index 000000000..f112386b6
--- /dev/null
+++ b/Runtime/Scripts/Plugins/GltfExportPlugin.cs
@@ -0,0 +1,26 @@
+using GLTF.Schema;
+using UnityEngine;
+
+namespace UnityGLTF.Plugins
+{
+ public abstract class GltfExportPlugin: GltfPlugin
+ {
+ ///
+ /// Return the Plugin Instance that receives the import callbacks
+ ///
+ public abstract GltfExportPluginContext CreateInstance(ExportContext context);
+ }
+
+ public abstract class GltfExportPluginContext
+ {
+ public virtual void BeforeSceneExport(GLTFSceneExporter exporter, GLTFRoot gltfRoot) {}
+ public virtual void AfterSceneExport(GLTFSceneExporter exporter, GLTFRoot gltfRoot) {}
+ public virtual void AfterNodeExport(GLTFSceneExporter exporter, GLTFRoot gltfRoot, Transform transform, Node node) {}
+ public virtual bool BeforeMaterialExport(GLTFSceneExporter exporter, GLTFRoot gltfRoot, Material material, GLTFMaterial materialNode) => false;
+ public virtual void AfterMaterialExport(GLTFSceneExporter exporter, GLTFRoot gltfRoot, Material material, GLTFMaterial materialNode) {}
+ public virtual void BeforeTextureExport(GLTFSceneExporter exporter, ref GLTFSceneExporter.UniqueTexture texture, string textureSlot) {}
+ public virtual void AfterTextureExport(GLTFSceneExporter exporter, GLTFSceneExporter.UniqueTexture texture, int index, GLTFTexture tex) {}
+ public virtual void AfterPrimitiveExport(GLTFSceneExporter exporter, Mesh mesh, MeshPrimitive primitive, int index) {}
+
+ }
+}
\ No newline at end of file
diff --git a/Runtime/Scripts/Plugins/GltfExportPlugin.cs.meta b/Runtime/Scripts/Plugins/GltfExportPlugin.cs.meta
new file mode 100644
index 000000000..04e03ae7a
--- /dev/null
+++ b/Runtime/Scripts/Plugins/GltfExportPlugin.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 201693f491d641c3a7bf53d98828d59c
+timeCreated: 1703879501
\ No newline at end of file
diff --git a/Runtime/Scripts/Plugins/IGltfImporterPlugin.cs b/Runtime/Scripts/Plugins/GltfImportPlugin.cs
similarity index 86%
rename from Runtime/Scripts/Plugins/IGltfImporterPlugin.cs
rename to Runtime/Scripts/Plugins/GltfImportPlugin.cs
index 2a3aa5921..6f534a8b2 100644
--- a/Runtime/Scripts/Plugins/IGltfImporterPlugin.cs
+++ b/Runtime/Scripts/Plugins/GltfImportPlugin.cs
@@ -4,12 +4,8 @@
namespace UnityGLTF.Plugins
{
- public abstract class GltfImportPlugin : ScriptableObject
+ public abstract class GltfImportPlugin : GltfPlugin
{
- internal bool Expanded = true;
- public abstract string DisplayName { get; }
- public bool Enabled { get; set; } = true;
- public abstract void OnGUI();
///
/// Return the Plugin Instance that receives the import callbacks
///
diff --git a/Runtime/Scripts/Plugins/IGltfImporterPlugin.cs.meta b/Runtime/Scripts/Plugins/GltfImportPlugin.cs.meta
similarity index 100%
rename from Runtime/Scripts/Plugins/IGltfImporterPlugin.cs.meta
rename to Runtime/Scripts/Plugins/GltfImportPlugin.cs.meta
diff --git a/Runtime/Scripts/Plugins/GltfPlugin.cs b/Runtime/Scripts/Plugins/GltfPlugin.cs
new file mode 100644
index 000000000..bdabedfe0
--- /dev/null
+++ b/Runtime/Scripts/Plugins/GltfPlugin.cs
@@ -0,0 +1,10 @@
+using UnityEngine;
+
+namespace UnityGLTF.Plugins
+{
+ public abstract class GltfPlugin: ScriptableObject
+ {
+ public abstract string DisplayName { get; }
+ public bool Enabled { get; set; } = true;
+ }
+}
\ No newline at end of file
diff --git a/Runtime/Scripts/Plugins/GltfPlugin.cs.meta b/Runtime/Scripts/Plugins/GltfPlugin.cs.meta
new file mode 100644
index 000000000..426962e5a
--- /dev/null
+++ b/Runtime/Scripts/Plugins/GltfPlugin.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: faaa3712025f4258a16c9afa9e8d1fc6
+timeCreated: 1703880672
\ No newline at end of file
diff --git a/Runtime/Scripts/Plugins/ImportContext.cs b/Runtime/Scripts/Plugins/ImportContext.cs
index 1d9fd409b..dde79f3a5 100644
--- a/Runtime/Scripts/Plugins/ImportContext.cs
+++ b/Runtime/Scripts/Plugins/ImportContext.cs
@@ -35,9 +35,4 @@ internal GLTFImportContext(IReadOnlyList plugins)
Plugins = plugins;
}
}
-
- public interface IGLTFImportRemap
- {
-
- }
}
diff --git a/Runtime/Scripts/SceneExporter/ExporterAnimation.cs b/Runtime/Scripts/SceneExporter/ExporterAnimation.cs
index 3722fd979..0b220cd15 100644
--- a/Runtime/Scripts/SceneExporter/ExporterAnimation.cs
+++ b/Runtime/Scripts/SceneExporter/ExporterAnimation.cs
@@ -135,7 +135,7 @@ private GLTFAnimation GetOrCreateAnimation(AnimationClip clip, string searchForD
return existingAnim;
}
- if (_exportOptions.MergeClipsWithMatchingNames)
+ if (_exportContext.MergeClipsWithMatchingNames)
{
// Check if we already exported an animation with exactly that name. If yes, we want to append to the previous one instead of making a new one.
// This allows to merge multiple animations into one if required (e.g. a character and an instrument that should play at the same time but have individual clips).
diff --git a/Runtime/Scripts/SceneExporter/ExporterMaterials.cs b/Runtime/Scripts/SceneExporter/ExporterMaterials.cs
index a41e11ac4..198bdb2a4 100644
--- a/Runtime/Scripts/SceneExporter/ExporterMaterials.cs
+++ b/Runtime/Scripts/SceneExporter/ExporterMaterials.cs
@@ -12,18 +12,19 @@ namespace UnityGLTF
{
public partial class GLTFSceneExporter
{
- /// True: material export is complete. False: continue regular export.
- public delegate bool BeforeMaterialExportDelegate(GLTFSceneExporter exporter, GLTFRoot gltfRoot, Material material, GLTFMaterial materialNode);
- public delegate void AfterMaterialExportDelegate(GLTFSceneExporter exporter, GLTFRoot gltfRoot, Material material, GLTFMaterial materialNode);
-
// Static callbacks
+ [Obsolete("Use ExportPlugins on GLTFSettings instead")]
public static event BeforeSceneExportDelegate BeforeSceneExport;
+ [Obsolete("Use ExportPlugins on GLTFSettings instead")]
public static event AfterSceneExportDelegate AfterSceneExport;
+ [Obsolete("Use ExportPlugins on GLTFSettings instead")]
public static event AfterNodeExportDelegate AfterNodeExport;
/// True: material export is complete. False: continue regular export.
+ [Obsolete("Use ExportPlugins on GLTFSettings instead")]
public static event BeforeMaterialExportDelegate BeforeMaterialExport;
+ [Obsolete("Use ExportPlugins on GLTFSettings instead")]
public static event AfterMaterialExportDelegate AfterMaterialExport;
-
+
// mock for Default material
private static Material _defaultMaterial = null;
public static Material DefaultMaterial
@@ -77,11 +78,10 @@ public MaterialId ExportMaterial(Material materialObj)
material.Name = materialObj.name;
}
- // before material export: only continue with regular export if that didn't succeed.
- if (_exportOptions.BeforeMaterialExport != null)
+ foreach (var plugin in _plugins)
{
beforeMaterialExportMarker.Begin();
- if (_exportOptions.BeforeMaterialExport.Invoke(this, _root, materialObj, material))
+ if (plugin.BeforeMaterialExport(this, _root, materialObj, material))
{
beforeMaterialExportMarker.End();
return CreateAndAddMaterialId(materialObj, material);
@@ -92,28 +92,6 @@ public MaterialId ExportMaterial(Material materialObj)
}
}
-
- // static callback, run after options callback
- // we're iterating here because we want to stop calling any once we hit one that can export this material.
- if (BeforeMaterialExport != null)
- {
- var list = BeforeMaterialExport.GetInvocationList();
- foreach (var entry in list)
- {
- beforeMaterialExportMarker.Begin();
- var cb = (BeforeMaterialExportDelegate) entry;
- if (cb != null && cb.Invoke(this, _root, materialObj, material))
- {
- beforeMaterialExportMarker.End();
- return CreateAndAddMaterialId(materialObj, material);
- }
- else
- {
- beforeMaterialExportMarker.End();
- }
- }
- }
-
exportMaterialMarker.Begin();
var isBirp =
#if UNITY_2019_3_OR_NEWER
@@ -356,13 +334,13 @@ private MaterialId CreateAndAddMaterialId(Material materialObj, GLTFMaterial mat
_root.Materials.Add(material);
// after material export
- afterMaterialExportMarker.Begin();
if (materialObj)
{
- _exportOptions.AfterMaterialExport?.Invoke(this, _root, materialObj, material);
- AfterMaterialExport?.Invoke(this, _root, materialObj, material);
+ afterMaterialExportMarker.Begin();
+ foreach (var plugin in _plugins)
+ plugin.AfterMaterialExport(this, _root, materialObj, material);
+ afterMaterialExportMarker.End();
}
- afterMaterialExportMarker.End();
return id;
}
diff --git a/Runtime/Scripts/SceneExporter/ExporterMeshes.cs b/Runtime/Scripts/SceneExporter/ExporterMeshes.cs
index d08c73d01..a97001836 100644
--- a/Runtime/Scripts/SceneExporter/ExporterMeshes.cs
+++ b/Runtime/Scripts/SceneExporter/ExporterMeshes.cs
@@ -343,7 +343,8 @@ private MeshPrimitive[] ExportPrimitive(UniquePrimitive primKey, GLTFMesh mesh)
// remove any prims that have empty triangles
if (EmptyPrimitive(prim)) continue;
// invoke pre export event
- _exportOptions.AfterPrimitiveExport?.Invoke(this, meshObj, prim, i);
+ foreach (var plugin in _plugins)
+ plugin.AfterPrimitiveExport(this, meshObj, prim, i);
nonEmptyPrims.Add(prim);
}
prims = nonEmptyPrims.ToArray();
diff --git a/Runtime/Scripts/SceneExporter/ExporterTextures.cs b/Runtime/Scripts/SceneExporter/ExporterTextures.cs
index 29c0883ca..c102d50b6 100644
--- a/Runtime/Scripts/SceneExporter/ExporterTextures.cs
+++ b/Runtime/Scripts/SceneExporter/ExporterTextures.cs
@@ -179,7 +179,8 @@ public TextureId ExportTexture(Texture textureObj, string textureSlot, TextureEx
new UniqueTexture(textureObj, exportSettings) :
new UniqueTexture(textureObj, textureSlot, this);
- _exportOptions.BeforeTextureExport?.Invoke(this, ref uniqueTexture, textureSlot);
+ foreach (var plugin in _plugins)
+ plugin.BeforeTextureExport(this, ref uniqueTexture, textureSlot);
TextureId id = GetTextureId(_root, uniqueTexture);
if (id != null)
@@ -229,7 +230,8 @@ public TextureId ExportTexture(Texture textureObj, string textureSlot, TextureEx
DeclareExtensionUsage(EXT_texture_exr.EXTENSION_NAME);
}
- _exportOptions.AfterTextureExport?.Invoke(this, uniqueTexture, id.Id, texture);
+ foreach (var plugin in _plugins)
+ plugin.AfterTextureExport(this, uniqueTexture, id.Id, texture);
return id;
}
@@ -243,7 +245,7 @@ public TextureId ExportTexture(Texture textureObj, string textureSlot, TextureEx
/// The relative texture output path on disk, including extension
private string GetImageOutputPath(Texture texture, TextureExportSettings textureMapType, string textureSlot, out bool ableToExportFromDisk)
{
- var imagePath = _exportOptions.TexturePathRetriever(texture);
+ var imagePath = _exportContext.TexturePathRetriever(texture);
if (string.IsNullOrEmpty(imagePath))
{
imagePath = texture.name;
diff --git a/Runtime/Scripts/Timeline/GLTFRecorder.cs b/Runtime/Scripts/Timeline/GLTFRecorder.cs
index f64be413a..33315ac1e 100644
--- a/Runtime/Scripts/Timeline/GLTFRecorder.cs
+++ b/Runtime/Scripts/Timeline/GLTFRecorder.cs
@@ -350,7 +350,7 @@ public void EndRecording(Stream stream, string sceneName = "scene", GLTFSettings
var logHandler = new StringBuilderLogHandler();
- var exporter = new GLTFSceneExporter(new Transform[] { root }, new ExportOptions(settings)
+ var exporter = new GLTFSceneExporter(new Transform[] { root }, new ExportContext(settings)
{
AfterSceneExport = PostExport,
logger = new Logger(logHandler),