ImporterContextのAssetImport向けの機能を、EditorImporterContextに分離

EditorImporterContext
#758
This commit is contained in:
ousttrue
2021-02-26 19:00:48 +09:00
parent 553cd53719
commit 2e6ded5a7f
11 changed files with 383 additions and 314 deletions

View File

@@ -0,0 +1,245 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace UniGLTF
{
/// <summary>
/// Editor で Asset 化する場合専用
///
/// GameObject を prefab 化するので、prefab の元になった GameObject は破棄対象となる。
///
/// </summary>
public class EditorImporterContext : IDisposable
{
ImporterContext m_context;
public EditorImporterContext(ImporterContext context)
{
m_context = context;
}
public void Dispose()
{
m_context.Dispose();
if (m_context.Root != null)
{
// Destroy the GameObject that became the basis of Prefab
GameObject.DestroyImmediate(m_context.Root);
}
}
public virtual IEnumerable<UnityEngine.Object> ObjectsForSubAsset()
{
foreach (var x in m_context.TextureFactory.ObjectsForSubAsset())
{
yield return x;
}
foreach (var x in m_context.MaterialFactory.ObjectsForSubAsset())
{
yield return x;
}
foreach (var x in m_context.Meshes) { yield return x.Mesh; }
foreach (var x in m_context.AnimationClips) { yield return x; }
}
/// <summary>
/// Destroy assets that created ImporterContext. This function is clean up for importer error.
/// </summary>
public virtual void EditorDestroyRootAndAssets()
{
// Remove hierarchy
if (m_context.Root != null) GameObject.DestroyImmediate(m_context.Root);
// Remove resources. materials, textures meshes etc...
foreach (var o in ObjectsForSubAsset())
{
UnityEngine.Object.DestroyImmediate(o, true);
}
}
public virtual UnityPath GetAssetPath(UnityPath prefabPath, UnityEngine.Object o, bool meshAsSubAsset)
{
if (o is Material)
{
var materialDir = prefabPath.GetAssetFolder(".Materials");
var materialPath = materialDir.Child(o.name.EscapeFilePath() + ".asset");
return materialPath;
}
else if (o is Texture2D)
{
var textureDir = prefabPath.GetAssetFolder(".Textures");
var texturePath = textureDir.Child(o.name.EscapeFilePath() + ".asset");
return texturePath;
}
else if (o is Mesh && !meshAsSubAsset)
{
var meshDir = prefabPath.GetAssetFolder(".Meshes");
var meshPath = meshDir.Child(o.name.EscapeFilePath() + ".asset");
return meshPath;
}
else
{
return default(UnityPath);
}
}
public virtual bool AvoidOverwriteAndLoad(UnityPath assetPath, UnityEngine.Object o)
{
if (o is Material)
{
var loaded = assetPath.LoadAsset<Material>();
// replace component reference
foreach (var mesh in m_context.Meshes)
{
foreach (var r in mesh.Renderers)
{
for (int i = 0; i < r.sharedMaterials.Length; ++i)
{
if (r.sharedMaterials.Contains(o))
{
r.sharedMaterials = r.sharedMaterials.Select(x => x == o ? loaded : x).ToArray();
}
}
}
}
return true;
}
return false;
}
public void SaveAsAsset(UnityPath prefabPath, bool meshAsSubAsset = false)
{
m_context.ShowMeshes();
//var prefabPath = PrefabPath;
if (prefabPath.IsFileExists)
{
// clear SubAssets
foreach (var x in prefabPath.GetSubAssets().Where(x => !(x is GameObject) && !(x is Component)))
{
GameObject.DestroyImmediate(x, true);
}
}
//
// save sub assets
//
var paths = new List<UnityPath>(){
prefabPath
};
foreach (var o in ObjectsForSubAsset())
{
if (o == null) continue;
var assetPath = GetAssetPath(prefabPath, o, meshAsSubAsset);
if (!assetPath.IsNull)
{
if (assetPath.IsFileExists)
{
if (AvoidOverwriteAndLoad(assetPath, o))
{
// 上書きせずに既存のアセットからロードして置き換えた
continue;
}
}
// アセットとして書き込む
assetPath.Parent.EnsureFolder();
assetPath.CreateAsset(o);
paths.Add(assetPath);
}
else
{
// save as subasset
prefabPath.AddObjectToAsset(o);
}
}
// Create or update Main Asset
if (prefabPath.IsFileExists)
{
Debug.LogFormat("replace prefab: {0}", prefabPath);
var prefab = prefabPath.LoadAsset<GameObject>();
#if UNITY_2018_3_OR_NEWER
PrefabUtility.SaveAsPrefabAssetAndConnect(m_context.Root, prefabPath.Value, InteractionMode.AutomatedAction);
#else
PrefabUtility.ReplacePrefab(Root, prefab, ReplacePrefabOptions.ReplaceNameBased);
#endif
}
else
{
Debug.LogFormat("create prefab: {0}", prefabPath);
#if UNITY_2018_3_OR_NEWER
PrefabUtility.SaveAsPrefabAssetAndConnect(m_context.Root, prefabPath.Value, InteractionMode.AutomatedAction);
#else
PrefabUtility.CreatePrefab(prefabPath.Value, Root);
#endif
}
foreach (var x in paths)
{
x.ImportAsset();
}
}
/// <summary>
/// Extract images from glb or gltf out of Assets folder.
/// </summary>
/// <param name="prefabPath"></param>
public void ExtractImages(UnityPath prefabPath)
{
var prefabParentDir = prefabPath.Parent;
// glb buffer
var folder = prefabPath.GetAssetFolder(".Textures");
//
// https://answers.unity.com/questions/647615/how-to-update-import-settings-for-newly-created-as.html
//
int created = 0;
for (int i = 0; i < m_context.GLTF.textures.Count; ++i)
{
folder.EnsureFolder();
var gltfTexture = m_context.GLTF.textures[i];
var gltfImage = m_context.GLTF.images[gltfTexture.source];
var src = m_context.Storage.GetPath(gltfImage.uri);
if (UnityPath.FromFullpath(src).IsUnderAssetsFolder)
{
// asset is exists.
}
else
{
var byteSegment = m_context.GLTF.GetImageBytes(m_context.Storage, gltfTexture.source);
var textureName = gltfTexture.name;
// path
var dst = folder.Child(textureName + gltfImage.GetExt());
File.WriteAllBytes(dst.FullPath, byteSegment.ToArray());
dst.ImportAsset();
// make relative path from PrefabParentDir
gltfImage.uri = dst.Value.Substring(prefabParentDir.Value.Length + 1);
++created;
}
}
if (created > 0)
{
AssetDatabase.Refresh();
}
// texture will load from assets
m_context.TextureFactory.ImageBaseDir = prefabParentDir;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1c496f43ae12f9041a16af0489727587
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -2,11 +2,7 @@
using System.Linq;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using UniGLTF.AltTask;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace UniGLTF
{
@@ -230,237 +226,22 @@ namespace UniGLTF
public List<AnimationClip> AnimationClips = new List<AnimationClip>();
#endregion
protected virtual IEnumerable<UnityEngine.Object> ObjectsForSubAsset()
{
foreach (var x in TextureFactory.ObjectsForSubAsset())
{
yield return x;
}
foreach (var x in MaterialFactory.ObjectsForSubAsset())
{
yield return x;
}
foreach (var x in Meshes) { yield return x.Mesh; }
foreach (var x in AnimationClips) { yield return x; }
}
#if UNITY_EDITOR
#region Assets
public bool MeshAsSubAsset = false;
protected virtual UnityPath GetAssetPath(UnityPath prefabPath, UnityEngine.Object o)
{
if (o is Material)
{
var materialDir = prefabPath.GetAssetFolder(".Materials");
var materialPath = materialDir.Child(o.name.EscapeFilePath() + ".asset");
return materialPath;
}
else if (o is Texture2D)
{
var textureDir = prefabPath.GetAssetFolder(".Textures");
var texturePath = textureDir.Child(o.name.EscapeFilePath() + ".asset");
return texturePath;
}
else if (o is Mesh && !MeshAsSubAsset)
{
var meshDir = prefabPath.GetAssetFolder(".Meshes");
var meshPath = meshDir.Child(o.name.EscapeFilePath() + ".asset");
return meshPath;
}
else
{
return default(UnityPath);
}
}
public virtual bool AvoidOverwriteAndLoad(UnityPath assetPath, UnityEngine.Object o)
{
if (o is Material)
{
var loaded = assetPath.LoadAsset<Material>();
// replace component reference
foreach (var mesh in Meshes)
{
foreach (var r in mesh.Renderers)
{
for (int i = 0; i < r.sharedMaterials.Length; ++i)
{
if (r.sharedMaterials.Contains(o))
{
r.sharedMaterials = r.sharedMaterials.Select(x => x == o ? loaded : x).ToArray();
}
}
}
}
return true;
}
return false;
}
public void SaveAsAsset(UnityPath prefabPath)
{
ShowMeshes();
//var prefabPath = PrefabPath;
if (prefabPath.IsFileExists)
{
// clear SubAssets
foreach (var x in prefabPath.GetSubAssets().Where(x => !(x is GameObject) && !(x is Component)))
{
GameObject.DestroyImmediate(x, true);
}
}
//
// save sub assets
//
var paths = new List<UnityPath>(){
prefabPath
};
foreach (var o in ObjectsForSubAsset())
{
if (o == null) continue;
var assetPath = GetAssetPath(prefabPath, o);
if (!assetPath.IsNull)
{
if (assetPath.IsFileExists)
{
if (AvoidOverwriteAndLoad(assetPath, o))
{
// 上書きせずに既存のアセットからロードして置き換えた
continue;
}
}
// アセットとして書き込む
assetPath.Parent.EnsureFolder();
assetPath.CreateAsset(o);
paths.Add(assetPath);
}
else
{
// save as subasset
prefabPath.AddObjectToAsset(o);
}
}
// Create or update Main Asset
if (prefabPath.IsFileExists)
{
Debug.LogFormat("replace prefab: {0}", prefabPath);
var prefab = prefabPath.LoadAsset<GameObject>();
#if UNITY_2018_3_OR_NEWER
PrefabUtility.SaveAsPrefabAssetAndConnect(Root, prefabPath.Value, InteractionMode.AutomatedAction);
#else
PrefabUtility.ReplacePrefab(Root, prefab, ReplacePrefabOptions.ReplaceNameBased);
#endif
}
else
{
Debug.LogFormat("create prefab: {0}", prefabPath);
#if UNITY_2018_3_OR_NEWER
PrefabUtility.SaveAsPrefabAssetAndConnect(Root, prefabPath.Value, InteractionMode.AutomatedAction);
#else
PrefabUtility.CreatePrefab(prefabPath.Value, Root);
#endif
}
foreach (var x in paths)
{
x.ImportAsset();
}
}
/// <summary>
/// Extract images from glb or gltf out of Assets folder.
/// </summary>
/// <param name="prefabPath"></param>
public void ExtractImages(UnityPath prefabPath)
{
var prefabParentDir = prefabPath.Parent;
// glb buffer
var folder = prefabPath.GetAssetFolder(".Textures");
//
// https://answers.unity.com/questions/647615/how-to-update-import-settings-for-newly-created-as.html
//
int created = 0;
for (int i = 0; i < GLTF.textures.Count; ++i)
{
folder.EnsureFolder();
var gltfTexture = GLTF.textures[i];
var gltfImage = GLTF.images[gltfTexture.source];
var src = Storage.GetPath(gltfImage.uri);
if (UnityPath.FromFullpath(src).IsUnderAssetsFolder)
{
// asset is exists.
}
else
{
var byteSegment = GLTF.GetImageBytes(Storage, gltfTexture.source);
var textureName = gltfTexture.name;
// path
var dst = folder.Child(textureName + gltfImage.GetExt());
File.WriteAllBytes(dst.FullPath, byteSegment.ToArray());
dst.ImportAsset();
// make relative path from PrefabParentDir
gltfImage.uri = dst.Value.Substring(prefabParentDir.Value.Length + 1);
++created;
}
}
if (created > 0)
{
AssetDatabase.Refresh();
}
// texture will load from assets
m_textureFactory.ImageBaseDir = prefabParentDir;
}
#endregion
#endif
/// <summary>
/// Destroy the GameObject that became the basis of Prefab
/// </summary>
public void EditorDestroyRoot()
{
if (Root != null) GameObject.DestroyImmediate(Root);
}
/// <summary>
/// Destroy assets that created ImporterContext. This function is clean up for importer error.
/// </summary>
public void EditorDestroyRootAndAssets()
{
// Remove hierarchy
if (Root != null) GameObject.DestroyImmediate(Root);
// Remove resources. materials, textures meshes etc...
foreach (var o in ObjectsForSubAsset())
{
UnityEngine.Object.DestroyImmediate(o, true);
}
}
/// <summary>
/// Importに使った一時オブジェクトを破棄する
///
/// 変換のあるテクスチャで、変換前のもの
/// normal, occlusion, metallicRoughness
/// </summary>
public void Dispose()
public virtual void Dispose()
{
// m_textureFactory.Dispose();
foreach (var x in m_textureFactory.Textures)
{
if (!x.IsUsed)
{
// Destroy temporary texture object
UnityEngine.Object.Destroy(x.Texture);
}
}
}
/// <summary>

View File

@@ -1,7 +1,8 @@
{
"name": "UniGLTF.Tests",
"references": [
"UniGLTF"
"UniGLTF",
"UniGLTF.Editor"
],
"optionalUnityReferences": [
"TestAssemblies"

View File

@@ -133,7 +133,8 @@ namespace UniGLTF
GameObject.DestroyImmediate(go);
if (context != null)
{
context.EditorDestroyRootAndAssets();
var editor = new EditorImporterContext(context);
editor.EditorDestroyRootAndAssets();
}
}
}

View File

@@ -0,0 +1,91 @@
using System.Collections;
using System.Collections.Generic;
using UniGLTF;
using UnityEngine;
namespace VRM
{
public class VRMEditorImporterContext : EditorImporterContext
{
VRMImporterContext m_context;
public VRMEditorImporterContext(VRMImporterContext context) : base(context)
{
m_context = context;
}
public override IEnumerable<UnityEngine.Object> ObjectsForSubAsset()
{
foreach (var x in base.ObjectsForSubAsset())
{
yield return x;
}
yield return m_context.AvatarDescription;
yield return m_context.HumanoidAvatar;
if (m_context.BlendShapeAvatar != null && m_context.BlendShapeAvatar.Clips != null)
{
foreach (var x in m_context.BlendShapeAvatar.Clips)
{
yield return x;
}
}
yield return m_context.BlendShapeAvatar;
yield return m_context.Meta;
}
public override bool AvoidOverwriteAndLoad(UnityPath assetPath, UnityEngine.Object o)
{
if (o is BlendShapeAvatar)
{
var loaded = assetPath.LoadAsset<BlendShapeAvatar>();
var proxy = m_context.Root.GetComponent<VRMBlendShapeProxy>();
proxy.BlendShapeAvatar = loaded;
return true;
}
if (o is BlendShapeClip)
{
return true;
}
return base.AvoidOverwriteAndLoad(assetPath, o);
}
public override UnityPath GetAssetPath(UnityPath prefabPath, UnityEngine.Object o, bool meshAsSubAsset)
{
if (o is BlendShapeAvatar
|| o is BlendShapeClip)
{
var dir = prefabPath.GetAssetFolder(".BlendShapes");
var assetPath = dir.Child(o.name.EscapeFilePath() + ".asset");
return assetPath;
}
else if (o is Avatar)
{
var dir = prefabPath.GetAssetFolder(".Avatar");
var assetPath = dir.Child(o.name.EscapeFilePath() + ".asset");
return assetPath;
}
else if (o is VRMMetaObject)
{
var dir = prefabPath.GetAssetFolder(".MetaObject");
var assetPath = dir.Child(o.name.EscapeFilePath() + ".asset");
return assetPath;
}
else if (o is UniHumanoid.AvatarDescription)
{
var dir = prefabPath.GetAssetFolder(".AvatarDescription");
var assetPath = dir.Child(o.name.EscapeFilePath() + ".asset");
return assetPath;
}
else
{
return base.GetAssetPath(prefabPath, o, meshAsSubAsset);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 58b502cc8f926b74fadb536f3b033100
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -53,7 +53,8 @@ namespace VRM
var parser = new GltfParser();
parser.ParseGlb(File.ReadAllBytes(path));
var context = new VRMImporterContext(parser);
context.ExtractImages(prefabPath);
var editor = new VRMEditorImporterContext(context);
editor.ExtractImages(prefabPath);
EditorApplication.delayCall += () =>
{
@@ -61,8 +62,8 @@ namespace VRM
// after textures imported
//
context.Load();
context.SaveAsAsset(prefabPath);
context.EditorDestroyRoot();
editor.SaveAsAsset(prefabPath);
editor.Dispose();
};
}
}

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UniGLTF;
using UnityEditor;
using UnityEngine;
@@ -53,7 +52,8 @@ namespace VRM
// save texture assets !
var context = new VRMImporterContext(parser);
context.ExtractImages(prefabPath);
var editor = new VRMEditorImporterContext(context);
editor.ExtractImages(prefabPath);
EditorApplication.delayCall += () =>
{
@@ -61,8 +61,8 @@ namespace VRM
// after textures imported
//
context.Load();
context.SaveAsAsset(prefabPath);
context.EditorDestroyRoot();
editor.SaveAsAsset(prefabPath);
editor.Dispose();
};
}
}

View File

@@ -16,7 +16,7 @@ namespace VRM
private void OnEnable()
{
m_VRMMetaObjectProp = serializedObject.FindProperty("Meta");
m_VRMMetaObjectProp = serializedObject.FindProperty(nameof(VRMMeta.Meta));
m_Inspector = Editor.CreateEditor(m_VRMMetaObjectProp.objectReferenceValue);
}

View File

@@ -298,81 +298,8 @@ namespace VRM
return meta;
}
protected override IEnumerable<UnityEngine.Object> ObjectsForSubAsset()
{
foreach (var x in base.ObjectsForSubAsset())
{
yield return x;
}
yield return AvatarDescription;
yield return HumanoidAvatar;
if (BlendShapeAvatar != null && BlendShapeAvatar.Clips != null)
{
foreach (var x in BlendShapeAvatar.Clips)
{
yield return x;
}
}
yield return BlendShapeAvatar;
yield return Meta;
}
#if UNITY_EDITOR
public override bool AvoidOverwriteAndLoad(UnityPath assetPath, UnityEngine.Object o)
{
if (o is BlendShapeAvatar)
{
var loaded = assetPath.LoadAsset<BlendShapeAvatar>();
var proxy = Root.GetComponent<VRMBlendShapeProxy>();
proxy.BlendShapeAvatar = loaded;
return true;
}
if (o is BlendShapeClip)
{
return true;
}
return base.AvoidOverwriteAndLoad(assetPath, o);
}
protected override UnityPath GetAssetPath(UnityPath prefabPath, UnityEngine.Object o)
{
if (o is BlendShapeAvatar
|| o is BlendShapeClip)
{
var dir = prefabPath.GetAssetFolder(".BlendShapes");
var assetPath = dir.Child(o.name.EscapeFilePath() + ".asset");
return assetPath;
}
else if (o is Avatar)
{
var dir = prefabPath.GetAssetFolder(".Avatar");
var assetPath = dir.Child(o.name.EscapeFilePath() + ".asset");
return assetPath;
}
else if (o is VRMMetaObject)
{
var dir = prefabPath.GetAssetFolder(".MetaObject");
var assetPath = dir.Child(o.name.EscapeFilePath() + ".asset");
return assetPath;
}
else if (o is UniHumanoid.AvatarDescription)
{
var dir = prefabPath.GetAssetFolder(".AvatarDescription");
var assetPath = dir.Child(o.name.EscapeFilePath() + ".asset");
return assetPath;
}
else
{
return base.GetAssetPath(prefabPath, o);
}
}
#endif
}
}