GltfScriptedImporter

This commit is contained in:
ousttrue
2021-02-22 18:07:30 +09:00
parent 10d17c2e13
commit b4f71fed84
14 changed files with 361 additions and 72 deletions

View File

@@ -1,4 +1,5 @@
using System.IO;
#if false
using System.IO;
using UnityEditor;
using UnityEngine;
@@ -51,3 +52,4 @@ namespace UniGLTF
}
}
}
#endif

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 552c42899d5f1664b8fba3faef53f3eb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,19 +1,15 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEditor.Experimental.AssetImporters;
using UnityEngine;
using VrmLib;
namespace UniVRM10
namespace UniGLTF
{
#if flase
[ScriptedImporter(1, "glb")]
#endif
public class GltfScriptedImporter : ScriptedImporter, IExternalUnityObject
public class GltfScriptedImporter : ScriptedImporter
{
const string TextureDirName = "Textures";
const string MaterialDirName = "Materials";
@@ -24,24 +20,25 @@ namespace UniVRM10
try
{
// Create model
VrmLib.Model model = CreateGlbModel(ctx.assetPath);
Debug.Log($"ModelLoader.Load: {model}");
// Parse
var parser = new GltfParser();
parser.ParsePath(ctx.assetPath);
// Build Unity Model
var assets = EditorUnityBuilder.ToUnityAsset(model, assetPath, this);
var context = new ImporterContext(parser);
context.Load();
context.ShowMeshes();
// Texture
var externalTextures = this.GetExternalUnityObjects<UnityEngine.Texture2D>();
foreach (var texture in assets.Textures)
foreach (var texture in context.Textures)
{
if (texture == null)
continue;
if (externalTextures.ContainsValue(texture))
{
throw new Exception();
}
else
if (!externalTextures.ContainsValue(texture))
{
ctx.AddObjectToAsset(texture.name, texture);
}
@@ -49,31 +46,28 @@ namespace UniVRM10
// Material
var externalMaterials = this.GetExternalUnityObjects<UnityEngine.Material>();
foreach (var material in assets.Materials)
foreach (var material in context.Materials)
{
if (material == null)
continue;
if (externalMaterials.ContainsValue(material))
{
throw new Exception();
}
else
if (!externalMaterials.ContainsValue(material))
{
ctx.AddObjectToAsset(material.name, material);
}
}
// Mesh
foreach (var mesh in assets.Meshes)
foreach (var mesh in context.Meshes.Select(x => x.Mesh))
{
ctx.AddObjectToAsset(mesh.name, mesh);
}
// Root
ctx.AddObjectToAsset(assets.Root.name, assets.Root);
ctx.SetMainObject(assets.Root);
ctx.AddObjectToAsset(context.Root.name, context.Root);
ctx.SetMainObject(context.Root);
}
catch (System.Exception ex)
{
@@ -81,26 +75,11 @@ namespace UniVRM10
}
}
private Model CreateGlbModel(string path)
{
var bytes = File.ReadAllBytes(path);
if (!UniGLTF.Glb.TryParse(bytes, out UniGLTF.Glb glb, out Exception ex))
{
throw ex;
}
VrmLib.Model model = null;
VrmLib.IVrmStorage storage;
storage = new Vrm10Storage(glb.Json.Bytes, glb.Binary.Bytes);
model = VrmLib.ModelLoader.Load(storage, Path.GetFileNameWithoutExtension(path));
model.ConvertCoordinate(VrmLib.Coordinates.Unity);
return model;
}
public void ExtractTextures()
{
this.ExtractTextures(TextureDirName, (path) => { return CreateGlbModel(path); });
// extract textures to files
this.ExtractTextures(TextureDirName);
// reimport
AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate);
}
@@ -112,7 +91,7 @@ namespace UniVRM10
public void ExtractMaterialsAndTextures()
{
this.ExtractTextures(TextureDirName, (path) => { return CreateGlbModel(path); }, () => { this.ExtractAssets<UnityEngine.Material>(MaterialDirName, ".mat"); });
this.ExtractTextures(TextureDirName, () => { this.ExtractAssets<UnityEngine.Material>(MaterialDirName, ".mat"); });
AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate);
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 5f9566aa8f690614f872fc63691397e9
guid: aecd2106718f2444db3ca21345da2cef
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -1,11 +1,9 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq;
using UnityEditor;
using UnityEditor.Experimental.AssetImporters;
using UnityEngine;
namespace UniVRM10
namespace UniGLTF
{
[CustomEditor(typeof(GltfScriptedImporter))]
public class GltfScriptedImporterEditorGUI : ScriptedImporterEditor
@@ -43,7 +41,7 @@ namespace UniVRM10
base.OnInspectorGUI();
}
private void DrawRemapGUI<T>(string title, GltfScriptedImporter importer) where T: UnityEngine.Object
private void DrawRemapGUI<T>(string title, GltfScriptedImporter importer) where T : UnityEngine.Object
{
EditorGUILayout.Foldout(_isOpen, title);
EditorGUI.indentLevel++;
@@ -53,7 +51,7 @@ namespace UniVRM10
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel(obj.Key.name);
var asset = EditorGUILayout.ObjectField(obj.Value, obj.Key.type, true) as T;
if(asset != obj.Value)
if (asset != obj.Value)
{
importer.SetExternalUnityObject(obj.Key, asset);
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: d4faa0a7a13c9c1489b5eed3a9575a01
guid: 706590752e82d004e99da97aff535f67
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -0,0 +1,242 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor.Experimental.AssetImporters;
using UnityEditor;
using System;
using UnityEngine;
using System.Text.RegularExpressions;
namespace UniGLTF
{
public static class ScriptedImporterExtension
{
public static void ClearExternalObjects<T>(this ScriptedImporter importer) where T : UnityEngine.Object
{
foreach (var extarnalObject in importer.GetExternalObjectMap().Where(x => x.Key.type == typeof(T)))
{
importer.RemoveRemap(extarnalObject.Key);
}
AssetDatabase.WriteImportSettingsIfDirty(importer.assetPath);
AssetDatabase.ImportAsset(importer.assetPath, ImportAssetOptions.ForceUpdate);
}
public static void ClearExtarnalObjects(this ScriptedImporter importer)
{
foreach (var extarnalObject in importer.GetExternalObjectMap())
{
importer.RemoveRemap(extarnalObject.Key);
}
AssetDatabase.WriteImportSettingsIfDirty(importer.assetPath);
AssetDatabase.ImportAsset(importer.assetPath, ImportAssetOptions.ForceUpdate);
}
private static T GetSubAsset<T>(this ScriptedImporter importer, string assetPath) where T : UnityEngine.Object
{
return importer.GetSubAssets<T>(assetPath)
.FirstOrDefault();
}
public static IEnumerable<T> GetSubAssets<T>(this ScriptedImporter importer, string assetPath) where T : UnityEngine.Object
{
return AssetDatabase
.LoadAllAssetsAtPath(assetPath)
.Where(x => AssetDatabase.IsSubAsset(x))
.Where(x => x is T)
.Select(x => x as T);
}
private static void ExtractFromAsset(UnityEngine.Object subAsset, string destinationPath, bool isForceUpdate)
{
string assetPath = AssetDatabase.GetAssetPath(subAsset);
var clone = UnityEngine.Object.Instantiate(subAsset);
AssetDatabase.CreateAsset(clone, destinationPath);
var assetImporter = AssetImporter.GetAtPath(assetPath);
assetImporter.AddRemap(new AssetImporter.SourceAssetIdentifier(subAsset), clone);
if (isForceUpdate)
{
AssetDatabase.WriteImportSettingsIfDirty(assetPath);
AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate);
}
}
public static void ExtractAssets<T>(this ScriptedImporter importer, string dirName, string extension) where T : UnityEngine.Object
{
if (string.IsNullOrEmpty(importer.assetPath))
return;
var subAssets = importer.GetSubAssets<T>(importer.assetPath);
var path = string.Format("{0}/{1}.{2}",
Path.GetDirectoryName(importer.assetPath),
Path.GetFileNameWithoutExtension(importer.assetPath),
dirName
);
var info = importer.SafeCreateDirectory(path);
foreach (var asset in subAssets)
{
ExtractFromAsset(asset, string.Format("{0}/{1}{2}", path, asset.name, extension), false);
}
}
struct TextureInfo
{
public string Path;
public bool sRGB;
public bool IsNormalMap;
}
class TextureExtractor
{
GltfParser m_parser;
public glTF GLTF => m_parser.GLTF;
public readonly List<TextureInfo> Textures = new List<TextureInfo>();
UnityEngine.Texture2D[] m_subAssets;
string m_path;
public TextureExtractor(ScriptedImporter importer)
{
// parse GLTF
m_parser = new GltfParser();
m_parser.ParsePath(importer.assetPath);
m_path = $"{Path.GetDirectoryName(importer.assetPath)}/{Path.GetFileNameWithoutExtension(importer.assetPath)}.Textures";
m_subAssets = importer.GetSubAssets<UnityEngine.Texture2D>(importer.assetPath).ToArray();
}
static Regex s_mimeTypeReg = new Regex("image/(?<mime>.*)$");
public void Extract(int? index, string prop = default)
{
if (!index.HasValue)
{
return;
}
var gltfTexture = GLTF.textures[index.Value];
var gltfImage = GLTF.images[gltfTexture.source];
var mimeType = s_mimeTypeReg.Match(gltfImage.mimeType);
var ext = "";
switch (mimeType.Groups["mime"].Value)
{
case "jpeg":
ext = ".jpg";
break;
case "png":
ext = ".png";
break;
default:
throw new NotImplementedException();
}
var assetName = gltfImage.name;
string targetPath = default;
switch (prop)
{
case GetTextureParam.NORMAL_PROP:
case GetTextureParam.METALLIC_GLOSS_PROP:
case GetTextureParam.OCCLUSION_PROP:
// File.WriteAllBytes(targetPath, subAsset.EncodeToPNG());
throw new NotImplementedException();
default:
{
var name = "";
var bytes = GLTF.GetImageBytes(m_parser.Storage, gltfTexture.source, out name);
targetPath = string.Format("{0}/{1}{2}",
m_path,
assetName,
ext
);
File.WriteAllBytes(targetPath, bytes.ToArray());
}
break;
}
AssetDatabase.ImportAsset(targetPath);
var subAsset = m_subAssets.FirstOrDefault(x => x.name == assetName);
Textures.Add(new TextureInfo
{
Path = targetPath,
sRGB = true,
});
}
}
public static void ExtractTextures(this ScriptedImporter importer, string dirName, Action onCompleted = null)
{
if (string.IsNullOrEmpty(importer.assetPath))
{
return;
}
var path = string.Format("{0}/{1}.{2}",
Path.GetDirectoryName(importer.assetPath),
Path.GetFileNameWithoutExtension(importer.assetPath),
dirName
);
importer.SafeCreateDirectory(path);
// Reload Model
var extractor = new TextureExtractor(importer);
foreach (var material in extractor.GLTF.materials)
{
// standard or unlit
extractor.Extract(material.pbrMetallicRoughness?.baseColorTexture?.index);
if (!glTF_KHR_materials_unlit.IsEnable(material))
{
// standard
}
}
EditorApplication.delayCall += () =>
{
foreach (var extracted in extractor.Textures)
{
// TextureImporter
var targetTextureImporter = AssetImporter.GetAtPath(extracted.Path) as TextureImporter;
targetTextureImporter.sRGBTexture = extracted.sRGB;
if (extracted.IsNormalMap)
{
targetTextureImporter.textureType = TextureImporterType.NormalMap;
}
targetTextureImporter.SaveAndReimport();
// remap
var externalObject = AssetDatabase.LoadAssetAtPath<UnityEngine.Texture2D>(extracted.Path);
importer.AddRemap(new AssetImporter.SourceAssetIdentifier(typeof(UnityEngine.Texture2D), externalObject.name), externalObject);
}
AssetDatabase.ImportAsset(importer.assetPath, ImportAssetOptions.ForceUpdate);
if (onCompleted != null)
{
onCompleted();
}
};
}
public static DirectoryInfo SafeCreateDirectory(this ScriptedImporter importer, string path)
{
if (Directory.Exists(path))
{
return null;
}
return Directory.CreateDirectory(path);
}
}
}

View File

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

View File

@@ -1,4 +1,5 @@
using System;
#if false
using System;
using System.IO;
using UnityEditor;
using UnityEngine;
@@ -85,3 +86,4 @@ namespace UniGLTF
}
}
}
#endif

View File

@@ -163,7 +163,8 @@ namespace UniGLTF
// Version Compatibility
RestoreOlderVersionValues();
FixUnique();
FixMeshNameUnique();
FixImageNameUnique();
FixNodeName();
// parepare byte buffer
@@ -174,29 +175,66 @@ namespace UniGLTF
}
}
void FixUnique()
void FixMeshNameUnique()
{
var used = new HashSet<string>();
foreach (var mesh in GLTF.meshes)
{
if (string.IsNullOrEmpty(mesh.name))
{
// empty
mesh.name = "mesh_" + Guid.NewGuid().ToString("N");
Debug.LogWarning($"no name: => {mesh.name}");
used.Add(mesh.name);
}
else
{
var lname = mesh.name.ToLower();
if (used.Contains(lname))
var lower = mesh.name.ToLower();
if (used.Contains(lower))
{
// rename
var uname = lname + "_" + Guid.NewGuid().ToString("N");
Debug.LogWarning($"same name: {lname} => {uname}");
var uname = lower + "_" + Guid.NewGuid().ToString("N");
Debug.LogWarning($"same name: {lower} => {uname}");
mesh.name = uname;
lname = uname;
lower = uname;
}
used.Add(lower);
}
}
}
used.Add(lname);
void FixImageNameUnique()
{
var used = new HashSet<string>();
for (int i = 0; i < GLTF.images.Count; ++i)
{
var image = GLTF.images[i];
if (string.IsNullOrEmpty(image.name))
{
var newName = $"image_{i}";
if (!used.Add(newName))
{
newName = "image_" + Guid.NewGuid().ToString("N");
if (!used.Add(newName))
{
throw new Exception();
}
}
image.name = newName;
// Debug.LogWarning($"no name: => {image.name}");
}
else
{
var lower = image.name.ToLower();
if (used.Contains(lower))
{
// rename
var uname = lower + "_" + Guid.NewGuid().ToString("N");
Debug.LogWarning($"same name: {lower} => {uname}");
image.name = uname;
lower = uname;
}
used.Add(lower);
}
}
}

View File

@@ -37,9 +37,11 @@ namespace UniGLTF
MaterialFactory m_materialFactory;
public MaterialFactory MaterialFactory => m_materialFactory;
public IEnumerable<Material> Materials => m_materialFactory.Materials;
TextureFactory m_textureFactory;
public TextureFactory TextureFactory => m_textureFactory;
public IEnumerable<Texture2D> Textures => m_textureFactory.Textures;
public ImporterContext(GltfParser parser)
{

View File

@@ -16,7 +16,7 @@ namespace UniGLTF
m_storage = storage;
}
public delegate Awaitable<Material> CreateMaterialAsyncFunc(glTF glTF, int i, GetTextureAsyncFunc getTexture);
public delegate Awaitable<Material> CreateMaterialAsyncFunc(glTF gltf, int i, GetTextureAsyncFunc getTexture);
CreateMaterialAsyncFunc m_createMaterialAsync;
public CreateMaterialAsyncFunc CreateMaterialAsync
{
@@ -78,6 +78,13 @@ namespace UniGLTF
}
else
{
// 先に m_gltf.textures を作成
for (int i = 0; i < m_gltf.textures.Count; ++i)
{
await getTexture(GetTextureParam.Create(i));
}
// 後に material を作成。
// 必用に応じてテクスチャを変換。
for (int i = 0; i < m_gltf.materials.Count; ++i)
{
var material = await CreateMaterialAsync(m_gltf, i, getTexture);
@@ -125,7 +132,6 @@ namespace UniGLTF
public static Awaitable<Material> DefaultCreateMaterialAsync(glTF gltf, int i, GetTextureAsyncFunc getTexture)
{
if (i < 0 || i >= gltf.materials.Count)
{
UnityEngine.Debug.LogWarning("glTFMaterial is empty");

View File

@@ -26,12 +26,11 @@ namespace UniGLTF
public static async Awaitable<Texture2D> LoadTextureAsync(glTF gltf, IStorage storage, int index)
{
string m_textureName = default;
string textureName = default;
var imageBytes = await Awaitable.Run(() =>
{
var imageIndex = gltf.GetImageIndexFromTextureIndex(index);
var segments = gltf.GetImageBytes(storage, imageIndex, out m_textureName);
var segments = gltf.GetImageBytes(storage, imageIndex, out textureName);
return ToArray(segments);
});
@@ -44,7 +43,7 @@ namespace UniGLTF
var sampler = gltf.GetSamplerFromTextureIndex(index);
var texture = new Texture2D(2, 2, TextureFormat.ARGB32, false, isLinear);
texture.name = m_textureName;
texture.name = textureName;
if (imageBytes != null)
{
texture.LoadImage(imageBytes);

View File

@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UniGLTF.AltTask;
using UnityEditor;
using UnityEngine;
@@ -9,7 +8,6 @@ namespace UniGLTF
{
public delegate Awaitable<Texture2D> GetTextureAsyncFunc(GetTextureParam param);
public class TextureFactory : IDisposable
{
glTF m_gltf;
IStorage m_storage;
@@ -22,7 +20,6 @@ namespace UniGLTF
m_storage = storage;
}
List<Texture2D> m_textuers = new List<Texture2D>();
public void Dispose()
{
foreach (var x in ObjectsForSubAsset())
@@ -40,6 +37,7 @@ namespace UniGLTF
}
Dictionary<GetTextureParam, Texture2D> m_textureCache = new Dictionary<GetTextureParam, Texture2D>();
public IEnumerable<Texture2D> Textures => m_textureCache.Values;
public virtual Awaitable<Texture2D> LoadTextureAsync(int index)
{
@@ -82,6 +80,7 @@ namespace UniGLTF
{
var converted = new NormalConverter().GetImportTexture(texture);
m_textureCache.Add(param, converted);
converted.name = $"{converted.name}.{GetTextureParam.NORMAL_PROP}";
return converted;
}
else
@@ -91,6 +90,7 @@ namespace UniGLTF
if (!string.IsNullOrEmpty(textureAssetPath))
{
TextureIO.MarkTextureAssetAsNormalMap(textureAssetPath);
texture.name = $"{texture.name}.{GetTextureParam.NORMAL_PROP}";
}
else
{
@@ -106,6 +106,7 @@ namespace UniGLTF
{
// Bake roughnessFactor values into a texture.
var converted = new MetallicRoughnessConverter(param.MetallicFactor).GetImportTexture(texture);
converted.name = $"{converted.name}.{GetTextureParam.METALLIC_GLOSS_PROP}";
m_textureCache.Add(param, converted);
return converted;
}
@@ -113,6 +114,7 @@ namespace UniGLTF
case GetTextureParam.OCCLUSION_PROP:
{
var converted = new OcclusionConverter().GetImportTexture(texture);
converted.name = $"{converted.name}.{GetTextureParam.OCCLUSION_PROP}";
m_textureCache.Add(param, converted);
return converted;
}