Merge pull request #755 from ousttrue/feature/glb_coordinate

gltf の読み書きで反転軸を指定できるようにする
This commit is contained in:
ousttrue
2021-02-25 21:14:03 +09:00
committed by GitHub
49 changed files with 1579 additions and 915 deletions

View File

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

View File

@@ -1,4 +1,5 @@
using System.IO;
#if false
using System.IO;
using UnityEditor;
using UnityEngine;
@@ -21,8 +22,10 @@ namespace UniGLTF
//
// load into scene
//
var context = new ImporterContext();
context.Load(path);
var parser = new GltfParser();
parser.ParsePath(path);
var context = new ImporterContext(parser);
context.Load();
context.ShowMeshes();
Selection.activeGameObject = context.Root;
}
@@ -49,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

@@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.Experimental.AssetImporters;
using UnityEngine;
namespace UniGLTF
{
[ScriptedImporter(1, "glb")]
public class GltfScriptedImporter : ScriptedImporter
{
[SerializeField]
Axises m_reverseAxis = default;
const string TextureDirName = "Textures";
const string MaterialDirName = "Materials";
public override void OnImportAsset(AssetImportContext ctx)
{
Debug.Log("OnImportAsset to " + ctx.assetPath);
try
{
// Parse
var parser = new GltfParser();
parser.ParsePath(ctx.assetPath);
// Build Unity Model
var externalObjectMap = GetExternalObjectMap()
.Select(kv => (kv.Key.name, kv.Value))
;
var context = new ImporterContext(parser, externalObjectMap);
context.InvertAxis = m_reverseAxis;
context.Load();
context.ShowMeshes();
// Texture
foreach (var info in context.TextureFactory.Textures)
{
if (!info.IsUsed)
{
continue;
}
if (!info.IsExternal)
{
var texture = info.Texture;
ctx.AddObjectToAsset(texture.name, texture);
}
}
// Material
foreach (var info in context.MaterialFactory.Materials)
{
if (!info.UseExternal)
{
var material = info.Asset;
ctx.AddObjectToAsset(material.name, material);
}
}
// Mesh
foreach (var mesh in context.Meshes.Select(x => x.Mesh))
{
ctx.AddObjectToAsset(mesh.name, mesh);
}
// Animation
foreach (var clip in context.AnimationClips)
{
ctx.AddObjectToAsset(clip.name, clip);
}
// Root
ctx.AddObjectToAsset(context.Root.name, context.Root);
ctx.SetMainObject(context.Root);
}
catch (System.Exception ex)
{
Debug.LogError(ex);
}
}
public void ExtractMaterialsAndTextures()
{
this.ExtractTextures(TextureDirName, () =>
{
this.ExtractAssets<UnityEngine.Material>(MaterialDirName, ".mat");
AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate);
});
}
public void SetExternalUnityObject<T>(UnityEditor.AssetImporter.SourceAssetIdentifier sourceAssetIdentifier, T obj) where T : UnityEngine.Object
{
this.AddRemap(sourceAssetIdentifier, obj);
AssetDatabase.WriteImportSettingsIfDirty(this.assetPath);
AssetDatabase.ImportAsset(this.assetPath, ImportAssetOptions.ForceUpdate);
}
}
}

View File

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

View File

@@ -0,0 +1,137 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.Experimental.AssetImporters;
using UnityEngine;
namespace UniGLTF
{
[CustomEditor(typeof(GltfScriptedImporter))]
public class GltfScriptedImporterEditorGUI : ScriptedImporterEditor
{
GltfScriptedImporter m_importer;
GltfParser m_parser;
public override void OnEnable()
{
m_importer = target as GltfScriptedImporter;
m_parser = new GltfParser();
m_parser.ParsePath(m_importer.assetPath);
}
enum Tabs
{
Model,
Animation,
Materials,
}
static Tabs s_currentTab;
public override void OnInspectorGUI()
{
s_currentTab = MeshUtility.TabBar.OnGUI(s_currentTab);
GUILayout.Space(10);
switch (s_currentTab)
{
case Tabs.Model:
base.OnInspectorGUI();
break;
case Tabs.Animation:
OnGUIAnimation(m_importer, m_parser);
break;
case Tabs.Materials:
OnGUIMaterial(m_importer, m_parser);
break;
}
}
static bool s_foldMaterials;
static bool s_foldTextures;
class TmpGuiEnable : IDisposable
{
bool m_backup;
public TmpGuiEnable(bool enable)
{
m_backup = GUI.enabled;
GUI.enabled = enable;
}
public void Dispose()
{
GUI.enabled = m_backup;
}
}
static void OnGUIMaterial(GltfScriptedImporter importer, GltfParser parser)
{
var canExtract = !importer.GetExternalObjectMap().Any(x => x.Value is Material || x.Value is Texture2D);
using (new TmpGuiEnable(canExtract))
{
if (GUILayout.Button("Extract Materials And Textures ..."))
{
importer.ExtractMaterialsAndTextures();
}
}
// ObjectMap
s_foldMaterials = EditorGUILayout.Foldout(s_foldMaterials, "Remapped Materials");
if (s_foldMaterials)
{
DrawRemapGUI<UnityEngine.Material>(importer, parser.GLTF.materials.Select(x => x.name));
}
s_foldTextures = EditorGUILayout.Foldout(s_foldTextures, "Remapped Textures");
if (s_foldTextures)
{
DrawRemapGUI<UnityEngine.Texture2D>(importer, parser.EnumerateTextures().Select(x => x.Name));
}
if (GUILayout.Button("Clear"))
{
importer.ClearExternalObjects<UnityEngine.Material>();
importer.ClearExternalObjects<UnityEngine.Texture2D>();
}
}
static void DrawRemapGUI<T>(GltfScriptedImporter importer, IEnumerable<string> names) where T : UnityEngine.Object
{
EditorGUI.indentLevel++;
var map = importer.GetExternalObjectMap()
.Select(x => (x.Key.name, x.Value as T))
.Where(x => x.Item2 != null)
.ToDictionary(x => x.Item1, x => x.Item2)
;
foreach (var name in names)
{
if (string.IsNullOrEmpty(name))
{
throw new System.ArgumentNullException();
}
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel(name);
map.TryGetValue(name, out T value);
var asset = EditorGUILayout.ObjectField(value, typeof(T), true) as T;
if (asset != value)
{
importer.SetExternalUnityObject(new AssetImporter.SourceAssetIdentifier(value), asset);
}
EditorGUILayout.EndHorizontal();
}
EditorGUI.indentLevel--;
}
static void OnGUIAnimation(GltfScriptedImporter importer, GltfParser parser)
{
foreach (var a in parser.GLTF.animations)
{
GUILayout.Label(a.name);
}
}
}
}

View File

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

View File

@@ -0,0 +1,200 @@
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 GltfParser Parser => 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(GetTextureParam param)
{
var subAsset = m_subAssets.FirstOrDefault(x => x.name == param.Name);
var targetPath = string.Format("{0}/{1}{2}",
m_path,
param.Name,
".png"
);
File.WriteAllBytes(targetPath, subAsset.EncodeToPNG().ToArray());
AssetDatabase.ImportAsset(targetPath);
Textures.Add(new TextureInfo
{
Path = targetPath,
sRGB = true,
IsNormalMap = param.TextureType == GetTextureParam.NORMAL_PROP,
});
}
}
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)
{
foreach (var x in extractor.Parser.EnumerateTextures(material))
{
extractor.Extract(x);
}
}
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

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: db36517c11c455e4b94d9cbb8c1cc3ff
guid: 1c57d58453713684bb3888c33177ed78
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -1,4 +1,5 @@
using System;
#if false
using System;
using System.IO;
using UnityEditor;
using UnityEngine;
@@ -44,8 +45,9 @@ namespace UniGLTF
return;
}
var context = new ImporterContext();
context.Parse(src);
var parser = new GltfParser();
parser.ParsePath(src);
var context = new ImporterContext(parser);
// Extract textures to assets folder
context.ExtractImages(prefabPath);
@@ -84,3 +86,4 @@ namespace UniGLTF
}
}
}
#endif

View File

@@ -45,11 +45,21 @@ namespace UniGLTF
return new Vector4(v.x, v.y, -v.z, v.w);
}
public static Vector4 ReverseX(this Vector4 v)
{
return new Vector4(-v.x, v.y, v.z, v.w);
}
public static Vector3 ReverseZ(this Vector3 v)
{
return new Vector3(v.x, v.y, -v.z);
}
public static Vector3 ReverseX(this Vector3 v)
{
return new Vector3(-v.x, v.y, v.z);
}
[Obsolete]
public static Vector2 ReverseY(this Vector2 v)
{
@@ -69,6 +79,14 @@ namespace UniGLTF
return Quaternion.AngleAxis(-angle, ReverseZ(axis));
}
public static Quaternion ReverseX(this Quaternion q)
{
float angle;
Vector3 axis;
q.ToAngleAxis(out angle, out axis);
return Quaternion.AngleAxis(-angle, ReverseX(axis));
}
public static Matrix4x4 Matrix4x4FromColumns(Vector4 c0, Vector4 c1, Vector4 c2, Vector4 c3)
{
#if UNITY_2017_1_OR_NEWER
@@ -100,6 +118,12 @@ namespace UniGLTF
return m;
}
public static Matrix4x4 ReverseX(this Matrix4x4 m)
{
m.SetTRS(m.ExtractPosition().ReverseX(), m.ExtractRotation().ReverseX(), m.ExtractScale());
return m;
}
public static Matrix4x4 MatrixFromArray(float[] values)
{
var m = new Matrix4x4();

View File

@@ -325,7 +325,8 @@ namespace UniGLTF
var bufferCount = vertexAccessor.count * vertexAccessor.TypeCount;
float[] result = null;
if(vertexAccessor.bufferView != -1){
if (vertexAccessor.bufferView != -1)
{
var attrib = new float[vertexAccessor.count * vertexAccessor.TypeCount];
var view = self.bufferViews[vertexAccessor.bufferView];
var segment = self.buffers[view.buffer].GetBytes();
@@ -333,8 +334,9 @@ namespace UniGLTF
bytes.MarshalCopyTo(attrib);
result = attrib;
}
else{
result = new float[bufferCount];
else
{
result = new float[bufferCount];
}
var sparse = vertexAccessor.sparse;
@@ -354,28 +356,15 @@ namespace UniGLTF
return result;
}
public static ArraySegment<Byte> GetImageBytes(this glTF self, IStorage storage, int imageIndex, out string textureName)
public static ArraySegment<Byte> GetImageBytes(this glTF self, IStorage storage, int imageIndex)
{
var image = self.images[imageIndex];
if (string.IsNullOrEmpty(image.uri))
{
//
// use buffer view (GLB)
//
//m_imageBytes = ToArray(byteSegment);
textureName = !string.IsNullOrEmpty(image.name) ? image.name : string.Format("{0:00}#GLB", imageIndex);
return self.GetViewBytes(image.bufferView);
}
else
{
if (image.uri.FastStartsWith("data:"))
{
textureName = !string.IsNullOrEmpty(image.name) ? image.name : string.Format("{0:00}#Base64Embedded", imageIndex);
}
else
{
textureName = !string.IsNullOrEmpty(image.name) ? image.name : Path.GetFileNameWithoutExtension(image.uri);
}
return storage.Get(image.uri);
}
}
@@ -441,7 +430,7 @@ namespace UniGLTF
// remove unused extenions
var json = f.ToString().ParseAsJson().ToString(" ");
self.RemoveUnusedExtensions(json);
return Glb.Create(json, self.buffers[0].GetBytes()).ToBytes();
}

View File

@@ -67,16 +67,6 @@ namespace UniGLTF
[JsonSchema(MinItems = 1, ExplicitIgnorableItemLength = 0)]
public List<glTFImage> images = new List<glTFImage>();
public int GetImageIndexFromTextureIndex(int textureIndex)
{
return textures[textureIndex].source;
}
public glTFImage GetImageFromTextureIndex(int textureIndex)
{
return images[GetImageIndexFromTextureIndex(textureIndex)];
}
public glTFTextureSampler GetSamplerFromTextureIndex(int textureIndex)
{
var samplerIndex = textures[textureIndex].sampler;

View File

@@ -79,10 +79,16 @@ namespace UniGLTF.AltTask
public Awaitable(Task task)
{
_task = task;
if (_task.Exception != null)
{
throw _task.Exception;
}
}
public bool IsCompleted => _task.IsCompleted;
public Exception Exception => _task.Exception;
public IAwaiter GetAwaiter()
{
return new Awaiter(this);
@@ -126,6 +132,10 @@ namespace UniGLTF.AltTask
public void GetResult()
{
if (m_task.Exception != null)
{
throw m_task.Exception;
}
}
public void OnCompleted(Action continuation)

View File

@@ -70,11 +70,15 @@ namespace UniGLTF.AltTask
public Awaitable(Task<T> task)
{
_task = task;
if (_task.Exception != null)
{
throw _task.Exception;
}
}
public bool IsCompleted => _task.IsCompleted;
public T Result => _task.Result;
public Exception Exception => _task.Exception;
public IAwaiter<T> GetAwaiter()
{
@@ -110,6 +114,10 @@ namespace UniGLTF.AltTask
public T GetResult()
{
if (m_task.Exception != null)
{
throw m_task.Exception;
}
return m_task.Result;
}

View File

@@ -2,7 +2,7 @@ using System;
namespace UniGLTF.AltTask
{
public static class LoopAwaitable
public static class NextFrameAwaitable
{
// TODO
// loop スレッド使わないようにしたい

View File

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

View File

@@ -64,7 +64,7 @@ namespace UniGLTF
}
public delegate float[] ReverseZ(float[] current, float[] last);
public delegate float[] ReverseFunc(float[] current, float[] last);
public static void SetAnimationCurve(
AnimationClip targetClip,
string relativePath,
@@ -73,7 +73,7 @@ namespace UniGLTF
float[] output,
string interpolation,
Type curveType,
ReverseZ reverse)
ReverseFunc reverse)
{
var tangentMode = GetTangentMode(interpolation);
@@ -164,18 +164,18 @@ namespace UniGLTF
private static string RelativePathFrom(List<glTFNode> nodes, glTFNode root, glTFNode target, List<string> path)
{
if(path.Count == 0) path.Add(target.name);
if (path.Count == 0) path.Add(target.name);
var targetIndex = nodes.IndexOf(target);
foreach (var parent in nodes)
{
if(parent.children == null || parent.children.Length == 0) continue;
if (parent.children == null || parent.children.Length == 0) continue;
foreach(var child in parent.children)
foreach (var child in parent.children)
{
if(child != targetIndex) continue;
if (child != targetIndex) continue;
if(parent == root) return string.Join("/", path);
if (parent == root) return string.Join("/", path);
path.Insert(0, parent.name);
return RelativePathFrom(nodes, root, parent, path);
@@ -185,7 +185,7 @@ namespace UniGLTF
return string.Join("/", path);
}
public static AnimationClip ConvertAnimationClip(glTF gltf, glTFAnimation animation, glTFNode root = null)
public static AnimationClip ConvertAnimationClip(glTF gltf, glTFAnimation animation, AxisInverter inverter, glTFNode root = null)
{
var clip = new AnimationClip();
clip.ClearCurves();
@@ -215,7 +215,7 @@ namespace UniGLTF
(values, last) =>
{
Vector3 temp = new Vector3(values[0], values[1], values[2]);
return temp.ReverseZ().ToArray();
return inverter.InvertVector3(temp).ToArray();
}
);
}
@@ -239,7 +239,7 @@ namespace UniGLTF
{
Quaternion currentQuaternion = new Quaternion(values[0], values[1], values[2], values[3]);
Quaternion lastQuaternion = new Quaternion(last[0], last[1], last[2], last[3]);
return AnimationImporterUtil.GetShortest(lastQuaternion, currentQuaternion.ReverseZ()).ToArray();
return AnimationImporterUtil.GetShortest(lastQuaternion, inverter.InvertQuaternion(currentQuaternion)).ToArray();
}
);

View File

@@ -0,0 +1,35 @@
using System;
using UnityEngine;
namespace UniGLTF
{
public enum Axises
{
Z,
X,
}
public struct AxisInverter
{
public Func<Vector3, Vector3> InvertVector3;
public Func<Vector4, Vector4> InvertVector4;
public Func<Quaternion, Quaternion> InvertQuaternion;
public Func<Matrix4x4, Matrix4x4> InvertMat4;
public static AxisInverter ReverseZ => new AxisInverter
{
InvertVector3 = x => x.ReverseZ(),
InvertVector4 = x => x.ReverseZ(),
InvertQuaternion = x => x.ReverseZ(),
InvertMat4 = x => x.ReverseZ(),
};
public static AxisInverter ReverseX => new AxisInverter
{
InvertVector3 = x => x.ReverseX(),
InvertVector4 = x => x.ReverseX(),
InvertQuaternion = x => x.ReverseX(),
InvertMat4 = x => x.ReverseX(),
};
}
}

View File

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

View File

@@ -0,0 +1,436 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UniJSON;
using UnityEngine;
namespace UniGLTF
{
public class GltfParser
{
/// <summary>
/// JSON source
/// </summary>
public String Json;
/// <summary>
/// GLTF parsed from JSON
/// </summary>
public glTF GLTF;
/// <summary>
/// URI access
/// </summary>
public IStorage Storage;
public static bool IsGeneratedUniGLTFAndOlderThan(string generatorVersion, int major, int minor)
{
if (string.IsNullOrEmpty(generatorVersion)) return false;
if (generatorVersion == "UniGLTF") return true;
if (!generatorVersion.FastStartsWith("UniGLTF-")) return false;
try
{
var splitted = generatorVersion.Substring(8).Split('.');
var generatorMajor = int.Parse(splitted[0]);
var generatorMinor = int.Parse(splitted[1]);
if (generatorMajor < major)
{
return true;
}
else if (generatorMajor > major)
{
return false;
}
else
{
if (generatorMinor >= minor)
{
return false;
}
else
{
return true;
}
}
}
catch (Exception ex)
{
Debug.LogWarningFormat("{0}: {1}", generatorVersion, ex);
return false;
}
}
public bool IsGeneratedUniGLTFAndOlder(int major, int minor)
{
if (GLTF == null) return false;
if (GLTF.asset == null) return false;
return IsGeneratedUniGLTFAndOlderThan(GLTF.asset.generator, major, minor);
}
#region Parse
public void ParsePath(string path)
{
Parse(path, File.ReadAllBytes(path));
}
/// <summary>
/// Parse gltf json or Parse json chunk of glb
/// </summary>
/// <param name="path"></param>
/// <param name="bytes"></param>
public virtual void Parse(string path, Byte[] bytes)
{
var ext = Path.GetExtension(path).ToLower();
switch (ext)
{
case ".gltf":
ParseJson(Encoding.UTF8.GetString(bytes), new FileSystemStorage(Path.GetDirectoryName(path)));
break;
case ".zip":
{
var zipArchive = Zip.ZipArchiveStorage.Parse(bytes);
var gltf = zipArchive.Entries.FirstOrDefault(x => x.FileName.ToLower().EndsWith(".gltf"));
if (gltf == null)
{
throw new Exception("no gltf in archive");
}
var jsonBytes = zipArchive.Extract(gltf);
var json = Encoding.UTF8.GetString(jsonBytes);
ParseJson(json, zipArchive);
}
break;
default:
ParseGlb(bytes);
break;
}
}
/// <summary>
///
/// </summary>
/// <param name="bytes"></param>
public void ParseGlb(Byte[] bytes)
{
var chunks = glbImporter.ParseGlbChunks(bytes);
if (chunks.Count != 2)
{
throw new Exception("unknown chunk count: " + chunks.Count);
}
if (chunks[0].ChunkType != GlbChunkType.JSON)
{
throw new Exception("chunk 0 is not JSON");
}
if (chunks[1].ChunkType != GlbChunkType.BIN)
{
throw new Exception("chunk 1 is not BIN");
}
try
{
var jsonBytes = chunks[0].Bytes;
ParseJson(Encoding.UTF8.GetString(jsonBytes.Array, jsonBytes.Offset, jsonBytes.Count),
new SimpleStorage(chunks[1].Bytes));
}
catch (StackOverflowException ex)
{
throw new Exception("[UniVRM Import Error] json parsing failed, nesting is too deep.\n" + ex);
}
catch
{
throw;
}
}
public virtual void ParseJson(string json, IStorage storage)
{
Json = json;
Storage = storage;
GLTF = GltfDeserializer.Deserialize(json.ParseAsJson());
if (GLTF.asset.version != "2.0")
{
throw new UniGLTFException("unknown gltf version {0}", GLTF.asset.version);
}
// Version Compatibility
RestoreOlderVersionValues();
FixMeshNameUnique();
FixTextureNameUnique();
FixMaterialNameUnique();
FixNodeName();
// parepare byte buffer
//GLTF.baseDir = System.IO.Path.GetDirectoryName(Path);
foreach (var buffer in GLTF.buffers)
{
buffer.OpenStorage(storage);
}
}
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 lower = mesh.name.ToLower();
if (used.Contains(lower))
{
// rename
var uname = lower + "_" + Guid.NewGuid().ToString("N");
Debug.LogWarning($"same name: {lower} => {uname}");
mesh.name = uname;
lower = uname;
}
used.Add(lower);
}
}
}
void RenameImageFromTexture(int i)
{
foreach (var texture in GLTF.textures)
{
if (texture.source == i)
{
if (!string.IsNullOrEmpty(texture.name))
{
GLTF.images[i].name = texture.name;
return;
}
}
}
}
/// <summary>
/// gltfTexture.name を Unity Asset 名として運用する。
/// ユニークである必要がある。
/// </summary>
void FixTextureNameUnique()
{
var used = new HashSet<string>();
for (int i = 0; i < GLTF.textures.Count; ++i)
{
var gltfTexture = GLTF.textures[i];
if (string.IsNullOrEmpty(gltfTexture.name))
{
// use image name
gltfTexture.name = GLTF.images[gltfTexture.source].name;
}
if (string.IsNullOrEmpty(gltfTexture.name))
{
var newName = $"texture_{i}";
if (!used.Add(newName))
{
newName = "texture_" + Guid.NewGuid().ToString("N");
if (!used.Add(newName))
{
throw new Exception();
}
}
gltfTexture.name = newName;
}
else
{
var lower = gltfTexture.name.ToLower();
if (used.Contains(lower))
{
// rename
var uname = lower + "_" + Guid.NewGuid().ToString("N");
Debug.LogWarning($"same name: {lower} => {uname}");
gltfTexture.name = uname;
lower = uname;
}
used.Add(lower);
}
}
}
public void FixMaterialNameUnique()
{
foreach (var material in GLTF.materials)
{
var originalName = material.name;
int j = 2;
while (GLTF.materials.Any(x => x != material && x.name == material.name))
{
material.name = string.Format("{0}({1})", originalName, j++);
}
}
}
/// <summary>
/// rename empty name to $"{index}"
/// </summary>
void FixNodeName()
{
for (var i = 0; i < GLTF.nodes.Count; ++i)
{
var node = GLTF.nodes[i];
if (string.IsNullOrWhiteSpace(node.name))
{
node.name = $"{i}";
}
}
}
void RestoreOlderVersionValues()
{
var parsed = UniJSON.JsonParser.Parse(Json);
for (int i = 0; i < GLTF.images.Count; ++i)
{
if (string.IsNullOrEmpty(GLTF.images[i].name))
{
try
{
var extraName = parsed["images"][i]["extra"]["name"].Value.GetString();
if (!string.IsNullOrEmpty(extraName))
{
//Debug.LogFormat("restore texturename: {0}", extraName);
GLTF.images[i].name = extraName;
}
}
catch (Exception)
{
// do nothing
}
}
}
}
#endregion
public static void AppendImageExtension(glTFImage texture, string extension)
{
if (!texture.name.EndsWith(extension))
{
texture.name = texture.name + extension;
}
}
public string GetTextureExtension(int imageIndex)
{
foreach (var m in GLTF.materials)
{
if (m.pbrMetallicRoughness != null)
{
// base color
if (m.pbrMetallicRoughness?.baseColorTexture != null)
{
if (m.pbrMetallicRoughness.baseColorTexture.index == imageIndex)
{
return "";
}
}
// metallic roughness
if (m.pbrMetallicRoughness?.metallicRoughnessTexture != null)
{
if (m.pbrMetallicRoughness.metallicRoughnessTexture.index == imageIndex)
{
return ".metallicRoughness";
}
}
}
// emission
if (m.emissiveTexture != null)
{
if (m.emissiveTexture.index == imageIndex)
{
return "";
}
}
// normal
if (m.normalTexture != null)
{
if (m.normalTexture.index == imageIndex)
{
return "";
}
}
// occlusion
if (m.occlusionTexture != null)
{
if (m.occlusionTexture.index == imageIndex)
{
return ".occlusion";
}
}
}
return "";
}
public IEnumerable<GetTextureParam> EnumerateTextures(glTFMaterial m)
{
if (m.pbrMetallicRoughness != null)
{
// base color
if (m.pbrMetallicRoughness?.baseColorTexture != null)
{
yield return PBRMaterialItem.BaseColorTexture(GLTF, m);
}
// metallic roughness
if (m.pbrMetallicRoughness?.metallicRoughnessTexture != null)
{
yield return PBRMaterialItem.MetallicRoughnessTexture(GLTF, m);
}
}
// emission
if (m.emissiveTexture != null)
{
yield return GetTextureParam.Create(GLTF, m.emissiveTexture.index);
}
// normal
if (m.normalTexture != null)
{
yield return PBRMaterialItem.NormalTexture(GLTF, m);
}
// occlusion
if (m.occlusionTexture != null)
{
yield return PBRMaterialItem.OcclusionTexture(GLTF, m);
}
}
public IEnumerable<GetTextureParam> EnumerateTextures()
{
var used = new HashSet<string>();
for (int i = 0; i < GLTF.materials.Count; ++i)
{
var m = GLTF.materials[i];
foreach (var x in EnumerateTextures(m))
{
if (used.Add(x.Name))
{
yield return x;
}
}
}
}
}
}

View File

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

View File

@@ -3,8 +3,6 @@ using System.Linq;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.Text;
using UniJSON;
using UniGLTF.AltTask;
#if UNITY_EDITOR
using UnityEditor;
@@ -12,76 +10,11 @@ using UnityEditor;
namespace UniGLTF
{
/// <summary>
/// GLTF importer
/// </summary>
public class ImporterContext : IDisposable
{
#region MeasureTime
bool m_showSpeedLog
#if VRM_DEVELOP
= true
#endif
;
public bool ShowSpeedLog
{
set { m_showSpeedLog = value; }
}
public struct KeyElapsed
{
public string Key;
public TimeSpan Elapsed;
public KeyElapsed(string key, TimeSpan elapsed)
{
Key = key;
Elapsed = elapsed;
}
}
public struct MeasureScope : IDisposable
{
Action m_onDispose;
public MeasureScope(Action onDispose)
{
m_onDispose = onDispose;
}
public void Dispose()
{
m_onDispose();
}
}
public List<KeyElapsed> m_speedReports = new List<KeyElapsed>();
public IDisposable MeasureTime(string key)
{
var sw = System.Diagnostics.Stopwatch.StartNew();
return new MeasureScope(() =>
{
m_speedReports.Add(new KeyElapsed(key, sw.Elapsed));
});
}
public string GetSpeedLog()
{
var total = TimeSpan.Zero;
var sb = new StringBuilder();
sb.AppendLine("【SpeedLog】");
foreach (var kv in m_speedReports)
{
sb.AppendLine(string.Format("{0}: {1}ms", kv.Key, (int)kv.Elapsed.TotalMilliseconds));
total += kv.Elapsed;
}
sb.AppendLine(string.Format("total: {0}ms", (int)total.TotalMilliseconds));
return sb.ToString();
}
#endregion
#region Animation
protected IAnimationImporter m_animationImporter;
public void SetAnimationImporter(IAnimationImporter animationImporter)
@@ -108,258 +41,54 @@ namespace UniGLTF
TextureFactory m_textureFactory;
public TextureFactory TextureFactory => m_textureFactory;
public ImporterContext()
public ImporterContext(GltfParser parser, IEnumerable<(string, UnityEngine.Object)> externalObjectMap = null)
{
m_parser = parser;
m_textureFactory = new TextureFactory(GLTF, Storage, externalObjectMap);
m_materialFactory = new MaterialFactory(GLTF, Storage, externalObjectMap);
}
#region Source
/// <summary>
/// JSON source
/// </summary>
public String Json;
/// <summary>
/// GLTF parsed from JSON
/// </summary>
public glTF GLTF; // parsed
public static bool IsGeneratedUniGLTFAndOlderThan(string generatorVersion, int major, int minor)
{
if (string.IsNullOrEmpty(generatorVersion)) return false;
if (generatorVersion == "UniGLTF") return true;
if (!generatorVersion.FastStartsWith("UniGLTF-")) return false;
try
{
var splitted = generatorVersion.Substring(8).Split('.');
var generatorMajor = int.Parse(splitted[0]);
var generatorMinor = int.Parse(splitted[1]);
if (generatorMajor < major)
{
return true;
}
else if (generatorMajor > major)
{
return false;
}
else
{
if (generatorMinor >= minor)
{
return false;
}
else
{
return true;
}
}
}
catch (Exception ex)
{
Debug.LogWarningFormat("{0}: {1}", generatorVersion, ex);
return false;
}
}
public bool IsGeneratedUniGLTFAndOlder(int major, int minor)
{
if (GLTF == null) return false;
if (GLTF.asset == null) return false;
return IsGeneratedUniGLTFAndOlderThan(GLTF.asset.generator, major, minor);
}
/// <summary>
/// URI access
/// </summary>
public IStorage Storage;
GltfParser m_parser;
public GltfParser Parser => m_parser;
public String Json => m_parser.Json;
public glTF GLTF => m_parser.GLTF;
public IStorage Storage => m_parser.Storage;
#endregion
#region Parse
public void Parse(string path)
{
Parse(path, File.ReadAllBytes(path));
}
// configuration
/// <summary>
/// Parse gltf json or Parse json chunk of glb
/// GLTF から Unity に変換するときに反転させる軸
/// </summary>
/// <param name="path"></param>
/// <param name="bytes"></param>
public virtual void Parse(string path, Byte[] bytes)
public Axises InvertAxis = Axises.Z;
#region Load. Build unity objects
public virtual async Awaitable LoadAsync(Func<string, IDisposable> MeasureTime = null)
{
var ext = Path.GetExtension(path).ToLower();
switch (ext)
if (MeasureTime == null)
{
case ".gltf":
ParseJson(Encoding.UTF8.GetString(bytes), new FileSystemStorage(Path.GetDirectoryName(path)));
MeasureTime = new ImporterContextSpeedLog().MeasureTime;
}
AxisInverter inverter = default;
switch (InvertAxis)
{
case Axises.Z:
inverter = AxisInverter.ReverseZ;
break;
case ".zip":
{
var zipArchive = Zip.ZipArchiveStorage.Parse(bytes);
var gltf = zipArchive.Entries.FirstOrDefault(x => x.FileName.ToLower().EndsWith(".gltf"));
if (gltf == null)
{
throw new Exception("no gltf in archive");
}
var jsonBytes = zipArchive.Extract(gltf);
var json = Encoding.UTF8.GetString(jsonBytes);
ParseJson(json, zipArchive);
}
break;
case ".glb":
ParseGlb(bytes);
case Axises.X:
inverter = AxisInverter.ReverseX;
break;
default:
throw new NotImplementedException();
}
}
/// <summary>
///
/// </summary>
/// <param name="bytes"></param>
public void ParseGlb(Byte[] bytes)
{
var chunks = glbImporter.ParseGlbChunks(bytes);
if (chunks.Count != 2)
{
throw new Exception("unknown chunk count: " + chunks.Count);
}
if (chunks[0].ChunkType != GlbChunkType.JSON)
{
throw new Exception("chunk 0 is not JSON");
}
if (chunks[1].ChunkType != GlbChunkType.BIN)
{
throw new Exception("chunk 1 is not BIN");
}
try
{
var jsonBytes = chunks[0].Bytes;
ParseJson(Encoding.UTF8.GetString(jsonBytes.Array, jsonBytes.Offset, jsonBytes.Count),
new SimpleStorage(chunks[1].Bytes));
}
catch (StackOverflowException ex)
{
throw new Exception("[UniVRM Import Error] json parsing failed, nesting is too deep.\n" + ex);
}
catch
{
throw;
}
}
public virtual void ParseJson(string json, IStorage storage)
{
Json = json;
Storage = storage;
GLTF = GltfDeserializer.Deserialize(json.ParseAsJson());
if (GLTF.asset.version != "2.0")
{
throw new UniGLTFException("unknown gltf version {0}", GLTF.asset.version);
}
m_textureFactory = new TextureFactory(GLTF, Storage);
m_materialFactory = new MaterialFactory(GLTF, Storage);
// Version Compatibility
RestoreOlderVersionValues();
FixUnique();
FixNodeName();
// parepare byte buffer
//GLTF.baseDir = System.IO.Path.GetDirectoryName(Path);
foreach (var buffer in GLTF.buffers)
{
buffer.OpenStorage(storage);
}
}
void FixUnique()
{
var used = new HashSet<string>();
foreach (var mesh in GLTF.meshes)
{
if (string.IsNullOrEmpty(mesh.name))
{
mesh.name = "mesh_" + Guid.NewGuid().ToString("N");
used.Add(mesh.name);
}
else
{
var lname = mesh.name.ToLower();
if (used.Contains(lname))
{
// rename
var uname = lname + "_" + Guid.NewGuid().ToString("N");
Debug.LogWarning($"same name: {lname} => {uname}");
mesh.name = uname;
lname = uname;
}
used.Add(lname);
}
}
}
/// <summary>
/// rename empty name to $"{index}"
/// </summary>
void FixNodeName()
{
for (var i = 0; i < GLTF.nodes.Count; ++i)
{
var node = GLTF.nodes[i];
if (string.IsNullOrWhiteSpace(node.name))
{
node.name = $"{i}";
}
}
}
void RestoreOlderVersionValues()
{
var parsed = UniJSON.JsonParser.Parse(Json);
for (int i = 0; i < GLTF.images.Count; ++i)
{
if (string.IsNullOrEmpty(GLTF.images[i].name))
{
try
{
var extraName = parsed["images"][i]["extra"]["name"].Value.GetString();
if (!string.IsNullOrEmpty(extraName))
{
//Debug.LogFormat("restore texturename: {0}", extraName);
GLTF.images[i].name = extraName;
}
}
catch (Exception)
{
// do nothing
}
}
}
}
#endregion
#region Load. Build unity objects
public bool EnableLoadBalancing;
public virtual async Awaitable LoadAsync()
{
if (Root == null)
{
Root = new GameObject("_root_");
Root = new GameObject("GLTF");
}
// UniGLTF does not support draco
@@ -369,17 +98,30 @@ namespace UniGLTF
throw new UniGLTFNotSupportedException("draco is not supported");
}
await m_materialFactory.LoadMaterialsAsync(m_textureFactory.GetTextureAsync);
// using (MeasureTime("LoadTextures"))
// {
// for (int i = 0; i < GLTF.materials.Count; ++i)
// {
// foreach (var param in MaterialFactory.EnumerateGetTextureparam(i))
// {
// await m_textureFactory.GetTextureAsync(GLTF, param);
// }
// }
// }
using (MeasureTime("LoadMaterials"))
{
await m_materialFactory.LoadMaterialsAsync(m_textureFactory.GetTextureAsync);
}
// meshes
var meshImporter = new MeshImporter();
for (int i = 0; i < GLTF.meshes.Count; ++i)
{
var index = i;
using (MeasureTime("ReadMesh"))
{
var x = meshImporter.ReadMesh(this, index);
var y = await BuildMeshAsync(x, index);
var x = meshImporter.ReadMesh(this, index, inverter);
var y = await BuildMeshAsync(MeasureTime, x, index);
Meshes.Add(y);
}
}
@@ -391,7 +133,7 @@ namespace UniGLTF
Nodes.Add(NodeImporter.ImportNode(GLTF.nodes[i], i).transform);
}
}
await LoopAwaitable.Create();
await NextFrameAwaitable.Create();
using (MeasureTime("BuildHierarchy"))
{
@@ -401,12 +143,12 @@ namespace UniGLTF
nodes.Add(NodeImporter.BuildHierarchy(this, i));
}
NodeImporter.FixCoordinate(this, nodes);
NodeImporter.FixCoordinate(this, nodes, inverter);
// skinning
for (int i = 0; i < nodes.Count; ++i)
{
NodeImporter.SetupSkinning(this, nodes, i);
NodeImporter.SetupSkinning(this, nodes, i, inverter);
}
// connect root
@@ -416,41 +158,26 @@ namespace UniGLTF
t.SetParent(Root.transform, false);
}
}
await LoopAwaitable.Create();
await NextFrameAwaitable.Create();
using (MeasureTime("AnimationImporter"))
{
AnimationImporter.Import(this);
}
await OnLoadModel();
if (m_showSpeedLog)
{
Debug.Log(GetSpeedLog());
}
await OnLoadModel(MeasureTime);
}
protected virtual async Awaitable OnLoadModel()
protected virtual async Awaitable OnLoadModel(Func<string, IDisposable> MeasureTime)
{
Root.name = "GLTF";
await LoopAwaitable.Create();
// do nothing
}
async Awaitable<MeshWithMaterials> BuildMeshAsync(MeshImporter.MeshContext x, int i)
async Awaitable<MeshWithMaterials> BuildMeshAsync(Func<string, IDisposable> MeasureTime, MeshImporter.MeshContext x, int i)
{
using (MeasureTime("BuildMesh"))
{
MeshWithMaterials meshWithMaterials;
if (EnableLoadBalancing)
{
meshWithMaterials = await MeshImporter.BuildMeshAsync(MaterialFactory, x);
}
else
{
meshWithMaterials = MeshImporter.BuildMesh(MaterialFactory, x);
}
var meshWithMaterials = await MeshImporter.BuildMeshAsync(MaterialFactory, x);
var mesh = meshWithMaterials.Mesh;
// mesh name
@@ -664,30 +391,29 @@ namespace UniGLTF
// 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)
for (int i = 0; i < GLTF.images.Count; ++i)
for (int i = 0; i < GLTF.textures.Count; ++i)
{
folder.EnsureFolder();
//var x = GLTF.textures[i];
var image = GLTF.images[i];
var src = Storage.GetPath(image.uri);
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
{
string textureName;
var byteSegment = GLTF.GetImageBytes(Storage, i, out textureName);
var byteSegment = GLTF.GetImageBytes(Storage, gltfTexture.source);
var textureName = gltfTexture.name;
// path
var dst = folder.Child(textureName + image.GetExt());
var dst = folder.Child(textureName + gltfImage.GetExt());
File.WriteAllBytes(dst.FullPath, byteSegment.ToArray());
dst.ImportAsset();
// make relative path from PrefabParentDir
image.uri = dst.Value.Substring(prefabParentDir.Value.Length + 1);
gltfImage.uri = dst.Value.Substring(prefabParentDir.Value.Length + 1);
++created;
}
}

View File

@@ -1,40 +1,20 @@
using System.IO;
using UniGLTF.AltTask;
using UnityEngine;
namespace UniGLTF
{
public static class ImporterContextExtensions
{
/// <summary>
/// ReadAllBytes, Parse, Create GameObject
/// </summary>
/// <param name="path">allbytes</param>
public static void Load(this ImporterContext self, string path)
{
var bytes = File.ReadAllBytes(path);
self.Load(path, bytes);
}
/// <summary>
/// Parse, Create GameObject
/// </summary>
/// <param name="path">gltf or glb path</param>
/// <param name="bytes">allbytes</param>
public static void Load(this ImporterContext self, string path, byte[] bytes)
{
self.Parse(path, bytes);
self.Load();
self.Root.name = Path.GetFileNameWithoutExtension(path);
}
/// <summary>
/// Build unity objects from parsed gltf
/// </summary>
public static void Load(this ImporterContext self)
{
var meassureTime = new ImporterContextSpeedLog();
using (var queue = TaskQueue.Create())
{
var task = self.LoadAsync();
var task = self.LoadAsync(meassureTime.MeasureTime);
// 中断された await を消化する
while (!task.IsCompleted)
@@ -43,6 +23,10 @@ namespace UniGLTF
queue.ExecuteOneCallback();
}
}
#if VRM_DEVELOP
Debug.Log(meassureTime.GetSpeedLog());
#endif
}
}
}

View File

@@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace UniGLTF
{
public class ImporterContextSpeedLog
{
public struct KeyElapsed
{
public string Key;
public TimeSpan Elapsed;
public KeyElapsed(string key, TimeSpan elapsed)
{
Key = key;
Elapsed = elapsed;
}
}
public struct MeasureScope : IDisposable
{
Action m_onDispose;
public MeasureScope(Action onDispose)
{
m_onDispose = onDispose;
}
public void Dispose()
{
m_onDispose();
}
}
public List<KeyElapsed> m_speedReports = new List<KeyElapsed>();
public IDisposable MeasureTime(string key)
{
var sw = System.Diagnostics.Stopwatch.StartNew();
return new MeasureScope(() =>
{
m_speedReports.Add(new KeyElapsed(key, sw.Elapsed));
});
}
public string GetSpeedLog()
{
var total = TimeSpan.Zero;
var sb = new StringBuilder();
sb.AppendLine("【SpeedLog】");
foreach (var kv in m_speedReports)
{
sb.AppendLine(string.Format("{0}: {1}ms", kv.Key, (int)kv.Elapsed.TotalMilliseconds));
total += kv.Elapsed;
}
sb.AppendLine(string.Format("total: {0}ms", (int)total.TotalMilliseconds));
return sb.ToString();
}
}
}

View File

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

View File

@@ -10,13 +10,38 @@ namespace UniGLTF
{
glTF m_gltf;
IStorage m_storage;
public MaterialFactory(glTF gltf, IStorage storage)
Dictionary<string, Material> m_externalMap;
public bool TryGetExternal(int index, out Material external)
{
if (m_externalMap != null)
{
var gltfMaterial = m_gltf.materials[index];
if (m_externalMap.TryGetValue(gltfMaterial.name, out external))
{
return true;
}
}
external = default;
return false;
}
public MaterialFactory(glTF gltf, IStorage storage,
IEnumerable<(string, UnityEngine.Object)> externalMap)
{
m_gltf = gltf;
m_storage = storage;
if (externalMap != null)
{
m_externalMap = externalMap
.Select(kv => (kv.Item1, kv.Item2 as Material))
.Where(kv => kv.Item2 != null)
.ToDictionary(kv => kv.Item1, kv => kv.Item2)
;
}
}
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
{
@@ -34,8 +59,20 @@ namespace UniGLTF
}
}
List<Material> m_materials = new List<Material>();
public IReadOnlyList<Material> Materials => m_materials;
public struct MaterialLoadInfo
{
public readonly Material Asset;
public readonly bool UseExternal;
public MaterialLoadInfo(Material asset, bool useExternal)
{
Asset = asset;
UseExternal = useExternal;
}
}
List<MaterialLoadInfo> m_materials = new List<MaterialLoadInfo>();
public IReadOnlyList<MaterialLoadInfo> Materials => m_materials;
public void Dispose()
{
foreach (var x in ObjectsForSubAsset())
@@ -48,41 +85,42 @@ namespace UniGLTF
{
foreach (var x in m_materials)
{
yield return x;
yield return x.Asset;
}
}
public void AddMaterial(Material material)
{
var originalName = material.name;
int j = 2;
while (m_materials.Any(x => x.name == material.name))
{
material.name = string.Format("{0}({1})", originalName, j++);
}
m_materials.Add(material);
}
public Material GetMaterial(int index)
{
if (index < 0) return null;
if (index >= m_materials.Count) return null;
return m_materials[index];
return m_materials[index].Asset;
}
/// <summary>
/// テクスチャ生成
/// </summary>
/// <param name="getTexture"></param>
/// <returns></returns>
public async Awaitable LoadMaterialsAsync(GetTextureAsyncFunc getTexture)
{
if (m_gltf.materials == null || m_gltf.materials.Count == 0)
{
// no material. work around.
var material = await CreateMaterialAsync(m_gltf, 0, getTexture);
AddMaterial(material);
m_materials.Add(new MaterialLoadInfo(material, false));
return;
}
else
for (int i = 0; i < m_gltf.materials.Count; ++i)
{
for (int i = 0; i < m_gltf.materials.Count; ++i)
if (TryGetExternal(i, out Material material))
{
var material = await CreateMaterialAsync(m_gltf, i, getTexture);
AddMaterial(material);
m_materials.Add(new MaterialLoadInfo(material, true));
continue;
}
material = await CreateMaterialAsync(m_gltf, i, getTexture);
m_materials.Add(new MaterialLoadInfo(material, false));
}
}
@@ -125,21 +163,20 @@ 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");
return PBRMaterialItem.CreateAsync(i, null, getTexture);
return PBRMaterialItem.CreateAsync(gltf, i, getTexture);
}
var x = gltf.materials[i];
if (glTF_KHR_materials_unlit.IsEnable(x))
{
var hasVertexColor = gltf.MaterialHasVertexColor(i);
return UnlitMaterialItem.CreateAsync(i, x, getTexture, hasVertexColor);
return UnlitMaterialItem.CreateAsync(gltf, i, getTexture, hasVertexColor);
}
return PBRMaterialItem.CreateAsync(i, x, getTexture);
return PBRMaterialItem.CreateAsync(gltf, i, getTexture);
}
/// <summary>
@@ -158,5 +195,22 @@ namespace UniGLTF
var task = DefaultCreateMaterialAsync(gltf, i, null);
return task.Result;
}
public IEnumerable<GetTextureParam> EnumerateGetTextureparam(int i)
{
var m = m_gltf.materials[i];
// color texture
var colorIndex = m.pbrMetallicRoughness?.baseColorTexture?.index;
if (colorIndex.HasValue)
{
yield return GetTextureParam.Create(m_gltf, i);
}
if (!glTF_KHR_materials_unlit.IsEnable(m))
{
// PBR
}
}
}
}

View File

@@ -43,12 +43,35 @@ namespace UniGLTF
Transparent
}
public static async Awaitable<Material> CreateAsync(int i, glTFMaterial src, GetTextureAsyncFunc getTexture)
public static GetTextureParam BaseColorTexture(glTF gltf, glTFMaterial src)
{
return GetTextureParam.Create(gltf, src.pbrMetallicRoughness.baseColorTexture.index);
}
public static GetTextureParam MetallicRoughnessTexture(glTF gltf, glTFMaterial src)
{
return GetTextureParam.CreateMetallic(gltf,
src.pbrMetallicRoughness.metallicRoughnessTexture.index,
src.pbrMetallicRoughness.metallicFactor);
}
public static GetTextureParam OcclusionTexture(glTF gltf, glTFMaterial src)
{
return GetTextureParam.CreateOcclusion(gltf, src.occlusionTexture.index);
}
public static GetTextureParam NormalTexture(glTF gltf, glTFMaterial src)
{
return GetTextureParam.CreateNormal(gltf, src.normalTexture.index);
}
public static async Awaitable<Material> CreateAsync(glTF gltf, int i, GetTextureAsyncFunc getTexture)
{
if (getTexture == null)
{
getTexture = _ => Awaitable.FromResult<Texture2D>(null);
getTexture = (_x, _y) => Awaitable.FromResult<Texture2D>(null);
}
var src = gltf.materials[i];
var material = MaterialFactory.CreateMaterial(i, src, ShaderName);
@@ -65,7 +88,7 @@ namespace UniGLTF
if (src.pbrMetallicRoughness.baseColorTexture != null && src.pbrMetallicRoughness.baseColorTexture.index != -1)
{
material.mainTexture = await getTexture(GetTextureParam.Create(src.pbrMetallicRoughness.baseColorTexture.index));
material.mainTexture = await getTexture(gltf, BaseColorTexture(gltf, src));
// Texture Offset and Scale
MaterialFactory.SetTextureOffsetAndScale(material, src.pbrMetallicRoughness.baseColorTexture, "_MainTex");
@@ -75,9 +98,7 @@ namespace UniGLTF
{
material.EnableKeyword("_METALLICGLOSSMAP");
var texture = await getTexture(GetTextureParam.CreateMetallic(
src.pbrMetallicRoughness.metallicRoughnessTexture.index,
src.pbrMetallicRoughness.metallicFactor));
var texture = await getTexture(gltf, MetallicRoughnessTexture(gltf, src));
if (texture != null)
{
material.SetTexture(GetTextureParam.METALLIC_GLOSS_PROP, texture);
@@ -100,7 +121,7 @@ namespace UniGLTF
if (src.normalTexture != null && src.normalTexture.index != -1)
{
material.EnableKeyword("_NORMALMAP");
var texture = await getTexture(GetTextureParam.CreateNormal(src.normalTexture.index));
var texture = await getTexture(gltf, NormalTexture(gltf, src));
if (texture != null)
{
material.SetTexture(GetTextureParam.NORMAL_PROP, texture);
@@ -113,7 +134,7 @@ namespace UniGLTF
if (src.occlusionTexture != null && src.occlusionTexture.index != -1)
{
var texture = await getTexture(GetTextureParam.CreateOcclusion(src.occlusionTexture.index));
var texture = await getTexture(gltf, OcclusionTexture(gltf, src));
if (texture != null)
{
material.SetTexture(GetTextureParam.OCCLUSION_PROP, texture);
@@ -137,7 +158,7 @@ namespace UniGLTF
if (src.emissiveTexture != null && src.emissiveTexture.index != -1)
{
var texture = await getTexture(GetTextureParam.Create(src.emissiveTexture.index));
var texture = await getTexture(gltf, GetTextureParam.Create(gltf, src.emissiveTexture.index));
if (texture != null)
{
material.SetTexture("_EmissionMap", texture);

View File

@@ -7,19 +7,20 @@ namespace UniGLTF
{
public const string ShaderName = "UniGLTF/UniUnlit";
public static async Awaitable<Material> CreateAsync(int i, glTFMaterial src, GetTextureAsyncFunc getTexture, bool hasVertexColor)
public static async Awaitable<Material> CreateAsync(glTF gltf, int i, GetTextureAsyncFunc getTexture, bool hasVertexColor)
{
if (getTexture == null)
{
getTexture = _ => Awaitable.FromResult<Texture2D>(null);
getTexture = (_x, _y) => Awaitable.FromResult<Texture2D>(default);
}
var src = gltf.materials[i];
var material = MaterialFactory.CreateMaterial(i, src, ShaderName);
// texture
if (src.pbrMetallicRoughness.baseColorTexture != null)
{
material.mainTexture = await getTexture(GetTextureParam.Create(src.pbrMetallicRoughness.baseColorTexture.index));
material.mainTexture = await getTexture(gltf, GetTextureParam.Create(gltf, src.pbrMetallicRoughness.baseColorTexture.index));
// Texture Offset and Scale
MaterialFactory.SetTextureOffsetAndScale(material, src.pbrMetallicRoughness.baseColorTexture, "_MainTex");

View File

@@ -8,8 +8,6 @@ using UnityEngine;
namespace UniGLTF
{
public class MeshImporter
{
const float FRAME_WEIGHT = 100.0f;
@@ -109,7 +107,7 @@ namespace UniGLTF
/// <param name="ctx"></param>
/// <param name="gltfMesh"></param>
/// <returns></returns>
public void ImportMeshIndependentVertexBuffer(ImporterContext ctx, glTFMesh gltfMesh)
public void ImportMeshIndependentVertexBuffer(ImporterContext ctx, glTFMesh gltfMesh, AxisInverter inverter)
{
foreach (var prim in gltfMesh.primitives)
{
@@ -119,7 +117,7 @@ namespace UniGLTF
// position は必ずある
var positions = ctx.GLTF.GetArrayFromAccessor<Vector3>(prim.attributes.POSITION);
var fillLength = m_positions.Count;
m_positions.AddRange(positions.Select(x => x.ReverseZ()));
m_positions.AddRange(positions.Select(inverter.InvertVector3));
// normal
if (prim.attributes.NORMAL != -1)
@@ -130,7 +128,7 @@ namespace UniGLTF
throw new Exception("different length");
}
FillZero(m_normals, fillLength);
m_normals.AddRange(normals.Select(x => x.ReverseZ()));
m_normals.AddRange(normals.Select(inverter.InvertVector3));
}
#if false
@@ -142,7 +140,7 @@ namespace UniGLTF
throw new Exception("different length");
}
FillZero(tangetns, fillLength);
tangents.AddRange(.Select(x => x.ReverseZ()));
tangents.AddRange(.Select(inverter.InvertVector4));
}
#endif
@@ -154,7 +152,7 @@ namespace UniGLTF
{
throw new Exception("different length");
}
if (ctx.IsGeneratedUniGLTFAndOlder(1, 16))
if (ctx.Parser.IsGeneratedUniGLTFAndOlder(1, 16))
{
#pragma warning disable 0612
// backward compatibility
@@ -242,7 +240,7 @@ namespace UniGLTF
throw new Exception("different length");
}
FillZero(blendShape.Positions, fillLength);
blendShape.Positions.AddRange(array.Select(x => x.ReverseZ()).ToArray());
blendShape.Positions.AddRange(array.Select(inverter.InvertVector3).ToArray());
}
if (primTarget.NORMAL != -1)
{
@@ -252,7 +250,7 @@ namespace UniGLTF
throw new Exception("different length");
}
FillZero(blendShape.Normals, fillLength);
blendShape.Normals.AddRange(array.Select(x => x.ReverseZ()).ToArray());
blendShape.Normals.AddRange(array.Select(inverter.InvertVector3).ToArray());
}
if (primTarget.TANGENT != -1)
{
@@ -262,7 +260,7 @@ namespace UniGLTF
throw new Exception("different length");
}
FillZero(blendShape.Tangents, fillLength);
blendShape.Tangents.AddRange(array.Select(x => x.ReverseZ()).ToArray());
blendShape.Tangents.AddRange(array.Select(inverter.InvertVector3).ToArray());
}
m_blendShapes.Add(blendShape);
}
@@ -293,31 +291,31 @@ namespace UniGLTF
/// <param name="ctx"></param>
/// <param name="gltfMesh"></param>
/// <returns></returns>
public void ImportMeshSharingVertexBuffer(ImporterContext ctx, glTFMesh gltfMesh)
public void ImportMeshSharingVertexBuffer(ImporterContext ctx, glTFMesh gltfMesh, AxisInverter inverter)
{
{
// 同じVertexBufferを共有しているので先頭のモを使う
var prim = gltfMesh.primitives.First();
m_positions.AddRange(ctx.GLTF.GetArrayFromAccessor<Vector3>(prim.attributes.POSITION).SelectInplace(x => x.ReverseZ()));
m_positions.AddRange(ctx.GLTF.GetArrayFromAccessor<Vector3>(prim.attributes.POSITION).SelectInplace(inverter.InvertVector3));
// normal
if (prim.attributes.NORMAL != -1)
{
m_normals.AddRange(ctx.GLTF.GetArrayFromAccessor<Vector3>(prim.attributes.NORMAL).SelectInplace(x => x.ReverseZ()));
m_normals.AddRange(ctx.GLTF.GetArrayFromAccessor<Vector3>(prim.attributes.NORMAL).SelectInplace(inverter.InvertVector3));
}
#if false
// tangent
if (prim.attributes.TANGENT != -1)
{
tangents.AddRange(ctx.GLTF.GetArrayFromAccessor<Vector4>(prim.attributes.TANGENT).SelectInplace(x => x.ReverseZ()));
tangents.AddRange(ctx.GLTF.GetArrayFromAccessor<Vector4>(prim.attributes.TANGENT).SelectInplace(inverter.InvertVector4));
}
#endif
// uv
if (prim.attributes.TEXCOORD_0 != -1)
{
if (ctx.IsGeneratedUniGLTFAndOlder(1, 16))
if (ctx.Parser.IsGeneratedUniGLTFAndOlder(1, 16))
{
#pragma warning disable 0612
// backward compatibility
@@ -403,17 +401,17 @@ namespace UniGLTF
if (primTarget.POSITION != -1)
{
blendShape.Positions.Assign(
ctx.GLTF.GetArrayFromAccessor<Vector3>(primTarget.POSITION), x => x.ReverseZ());
ctx.GLTF.GetArrayFromAccessor<Vector3>(primTarget.POSITION), inverter.InvertVector3);
}
if (primTarget.NORMAL != -1)
{
blendShape.Normals.Assign(
ctx.GLTF.GetArrayFromAccessor<Vector3>(primTarget.NORMAL), x => x.ReverseZ());
ctx.GLTF.GetArrayFromAccessor<Vector3>(primTarget.NORMAL), inverter.InvertVector3);
}
if (primTarget.TANGENT != -1)
{
blendShape.Tangents.Assign(
ctx.GLTF.GetArrayFromAccessor<Vector3>(primTarget.TANGENT), x => x.ReverseZ());
ctx.GLTF.GetArrayFromAccessor<Vector3>(primTarget.TANGENT), inverter.InvertVector3);
}
}
}
@@ -509,18 +507,18 @@ namespace UniGLTF
return sharedAttributes;
}
public MeshContext ReadMesh(ImporterContext ctx, int meshIndex)
public MeshContext ReadMesh(ImporterContext ctx, int meshIndex, AxisInverter inverter)
{
var gltfMesh = ctx.GLTF.meshes[meshIndex];
var meshContext = new MeshContext(gltfMesh.name, meshIndex);
if (HasSharedVertexBuffer(gltfMesh))
{
meshContext.ImportMeshSharingVertexBuffer(ctx, gltfMesh);
meshContext.ImportMeshSharingVertexBuffer(ctx, gltfMesh, inverter);
}
else
{
meshContext.ImportMeshIndependentVertexBuffer(ctx, gltfMesh);
meshContext.ImportMeshIndependentVertexBuffer(ctx, gltfMesh, inverter);
}
meshContext.RenameBlendShape(gltfMesh);
@@ -627,42 +625,15 @@ namespace UniGLTF
}
}
public static MeshWithMaterials BuildMesh(MaterialFactory ctx, MeshImporter.MeshContext meshContext)
{
var (mesh, recalculateTangents) = _BuildMesh(meshContext);
if (recalculateTangents)
{
mesh.RecalculateTangents();
}
// 先にすべてのマテリアルを作成済みなのでテクスチャーは生成済み。Resultを使ってよい
var result = new MeshWithMaterials
{
Mesh = mesh,
Materials = meshContext.MaterialIndices.Select(x => ctx.GetMaterial(x)).ToArray()
};
if (meshContext.BlendShapes.Count > 0)
{
var emptyVertices = new Vector3[mesh.vertexCount];
foreach (var blendShape in meshContext.BlendShapes)
{
BuildBlendShape(mesh, meshContext, blendShape, emptyVertices);
}
}
return result;
}
public static async Awaitable<MeshWithMaterials> BuildMeshAsync(MaterialFactory ctx, MeshImporter.MeshContext meshContext)
{
var (mesh, recalculateTangents) = _BuildMesh(meshContext);
if (recalculateTangents)
{
await LoopAwaitable.Create();
await NextFrameAwaitable.Create();
mesh.RecalculateTangents();
await LoopAwaitable.Create();
await NextFrameAwaitable.Create();
}
// 先にすべてのマテリアルを作成済みなのでテクスチャーは生成済み。Resultを使ってよい
@@ -672,7 +643,7 @@ namespace UniGLTF
Materials = meshContext.MaterialIndices.Select(x => ctx.GetMaterial(x)).ToArray()
};
await LoopAwaitable.Create();
await NextFrameAwaitable.Create();
if (meshContext.BlendShapes.Count > 0)
{
var emptyVertices = new Vector3[mesh.vertexCount];

View File

@@ -16,7 +16,7 @@ namespace UniGLTF
Debug.LogWarningFormat("node {0} contains /. replace _", node.name);
nodeName = nodeName.Replace("/", "_");
}
if(string.IsNullOrEmpty(nodeName))
if (string.IsNullOrEmpty(nodeName))
{
nodeName = string.Format("nodeIndex_{0}", nodeIndex);
}
@@ -131,7 +131,7 @@ namespace UniGLTF
//
// fix node's coordinate. z-back to z-forward
//
public static void FixCoordinate(ImporterContext context, List<TransformWithSkin> nodes)
public static void FixCoordinate(ImporterContext context, List<TransformWithSkin> nodes, AxisInverter inverter)
{
var globalTransformMap = nodes.ToDictionary(x => x.Transform, x => new PosRot
{
@@ -148,13 +148,13 @@ namespace UniGLTF
foreach (var transform in t.Traverse())
{
var g = globalTransformMap[transform];
transform.position = g.Position.ReverseZ();
transform.rotation = g.Rotation.ReverseZ();
transform.position = inverter.InvertVector3(g.Position);
transform.rotation = inverter.InvertQuaternion(g.Rotation);
}
}
}
public static void SetupSkinning(ImporterContext context, List<TransformWithSkin> nodes, int i)
public static void SetupSkinning(ImporterContext context, List<TransformWithSkin> nodes, int i, AxisInverter inverter)
{
var x = nodes[i];
var skinnedMeshRenderer = x.Transform.GetComponent<SkinnedMeshRenderer>();
@@ -181,7 +181,7 @@ namespace UniGLTF
if (skin.inverseBindMatrices != -1)
{
var bindPoses = context.GLTF.GetArrayFromAccessor<Matrix4x4>(skin.inverseBindMatrices)
.Select(y => y.ReverseZ())
.Select(inverter.InvertMat4)
.ToArray()
;
mesh.bindposes = bindPoses;

View File

@@ -12,7 +12,7 @@ namespace UniGLTF
if (context.GLTF.animations != null && context.GLTF.animations.Any())
{
var animation = context.Root.AddComponent<Animation>();
context.AnimationClips = ImportAnimationClips(context.GLTF);
context.AnimationClips = ImportAnimationClips(context.GLTF, context.InvertAxis);
foreach (var clip in context.AnimationClips)
{
@@ -25,7 +25,7 @@ namespace UniGLTF
}
}
private List<AnimationClip> ImportAnimationClips(glTF gltf)
private List<AnimationClip> ImportAnimationClips(glTF gltf, Axises invertAxis)
{
var animationClips = new List<AnimationClip>();
for (var i = 0; i < gltf.animations.Count; ++i)
@@ -46,7 +46,23 @@ namespace UniGLTF
animation.name = $"animation:{i}";
}
animationClips.Add(AnimationImporterUtil.ConvertAnimationClip(gltf, animation));
AxisInverter inverter = default;
switch (invertAxis)
{
case Axises.X:
inverter = AxisInverter.ReverseX;
break;
case Axises.Z:
inverter = AxisInverter.ReverseZ;
break;
default:
throw new System.Exception();
}
animationClips.Add(AnimationImporterUtil.ConvertAnimationClip(gltf, animation, inverter));
}
return animationClips;

View File

@@ -31,22 +31,6 @@ namespace UniGLTF
return copyTexture;
}
public static void AppendTextureExtension(Texture texture, string extension)
{
if (!texture.name.EndsWith(extension))
{
texture.name = texture.name + extension;
}
}
public static void RemoveTextureExtension(Texture texture, string extension)
{
if (texture.name.EndsWith(extension))
{
texture.name = texture.name.Replace(extension, "");
}
}
struct ColorSpaceScope : IDisposable
{
bool m_sRGBWrite;
@@ -194,8 +178,6 @@ namespace UniGLTF
public class MetallicRoughnessConverter : ITextureConverter
{
private const string m_extension = ".metallicRoughness";
private float _smoothnessOrRoughness;
public MetallicRoughnessConverter(float smoothnessOrRoughness)
@@ -206,14 +188,12 @@ namespace UniGLTF
public Texture2D GetImportTexture(Texture2D texture)
{
var converted = TextureConverter.Convert(texture, glTFTextureTypes.Metallic, Import, null);
TextureConverter.AppendTextureExtension(converted, m_extension);
return converted;
}
public Texture2D GetExportTexture(Texture2D texture)
{
var converted = TextureConverter.Convert(texture, glTFTextureTypes.Metallic, Export, null);
TextureConverter.RemoveTextureExtension(converted, m_extension);
return converted;
}
@@ -260,8 +240,6 @@ namespace UniGLTF
public class NormalConverter : ITextureConverter
{
private const string m_extension = ".normal";
private Material m_decoder;
private Material GetDecoder()
{
@@ -288,7 +266,6 @@ namespace UniGLTF
{
var mat = GetEncoder();
var converted = TextureConverter.Convert(texture, glTFTextureTypes.Normal, null, mat);
TextureConverter.AppendTextureExtension(converted, m_extension);
return converted;
}
@@ -298,26 +275,22 @@ namespace UniGLTF
{
var mat = GetDecoder();
var converted = TextureConverter.Convert(texture, glTFTextureTypes.Normal, null, mat);
TextureConverter.RemoveTextureExtension(converted, m_extension);
return converted;
}
}
public class OcclusionConverter : ITextureConverter
{
private const string m_extension = ".occlusion";
public Texture2D GetImportTexture(Texture2D texture)
{
var converted = TextureConverter.Convert(texture, glTFTextureTypes.Occlusion, Import, null);
TextureConverter.AppendTextureExtension(converted, m_extension);
return converted;
}
public Texture2D GetExportTexture(Texture2D texture)
{
var converted = TextureConverter.Convert(texture, glTFTextureTypes.Occlusion, Export, null);
TextureConverter.RemoveTextureExtension(converted, m_extension);
return converted;
}

View File

@@ -1,5 +1,4 @@
using System.Threading.Tasks;
using UnityEngine;
using System;
namespace UniGLTF
{
@@ -9,6 +8,7 @@ namespace UniGLTF
public const string METALLIC_GLOSS_PROP = "_MetallicGlossMap";
public const string OCCLUSION_PROP = "_OcclusionMap";
public readonly string Name;
public readonly string TextureType;
public readonly float MetallicFactor;
public readonly ushort? Index0;
@@ -18,8 +18,14 @@ namespace UniGLTF
public readonly ushort? Index4;
public readonly ushort? Index5;
public GetTextureParam(string textureType, float metallicFactor, int i0, int i1, int i2, int i3, int i4, int i5)
public GetTextureParam(string name, string textureType, float metallicFactor, int i0, int i1, int i2, int i3, int i4, int i5)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentNullException();
}
Name = name;
TextureType = textureType;
MetallicFactor = metallicFactor;
Index0 = (ushort)i0;
@@ -30,25 +36,46 @@ namespace UniGLTF
Index5 = (ushort)i5;
}
public static GetTextureParam Create(int index)
public static GetTextureParam Create(glTF gltf, int textureIndex)
{
return new GetTextureParam(default, default, index, default, default, default, default, default);
var name = gltf.textures[textureIndex].name;
return new GetTextureParam(name, default, default, textureIndex, default, default, default, default, default);
}
public static GetTextureParam CreateNormal(int index)
public static GetTextureParam Create(glTF gltf, int index, string prop)
{
return new GetTextureParam(NORMAL_PROP, default, index, default, default, default, default, default);
switch (prop)
{
case NORMAL_PROP:
return CreateNormal(gltf, index);
case OCCLUSION_PROP:
return CreateOcclusion(gltf, index);
case METALLIC_GLOSS_PROP:
return CreateMetallic(gltf, index, 1);
default:
return Create(gltf, index);
}
}
public static GetTextureParam CreateMetallic(int index, float metallicFactor)
public static GetTextureParam CreateNormal(glTF gltf, int textureIndex)
{
return new GetTextureParam(METALLIC_GLOSS_PROP, metallicFactor, index, default, default, default, default, default);
var name = gltf.textures[textureIndex].name;
return new GetTextureParam(name, NORMAL_PROP, default, textureIndex, default, default, default, default, default);
}
public static GetTextureParam CreateOcclusion(int index)
public static GetTextureParam CreateMetallic(glTF gltf, int textureIndex, float metallicFactor)
{
return new GetTextureParam(OCCLUSION_PROP, default, index, default, default, default, default, default);
var name = gltf.textures[textureIndex].name;
return new GetTextureParam(name + ".metallicRoughness", METALLIC_GLOSS_PROP, metallicFactor, textureIndex, default, default, default, default, default);
}
public static GetTextureParam CreateOcclusion(glTF gltf, int textureIndex)
{
var name = gltf.textures[textureIndex].name;
return new GetTextureParam(name + ".occlusion", OCCLUSION_PROP, default, textureIndex, default, default, default, default, default);
}
}
}

View File

@@ -24,27 +24,25 @@ namespace UniGLTF
}
}
public static async Awaitable<Texture2D> LoadTextureAsync(glTF gltf, IStorage storage, int index)
public static async Awaitable<Texture2D> LoadTextureAsync(glTF gltf, IStorage storage, int textureIndex)
{
string m_textureName = default;
var imageBytes = await Awaitable.Run(() =>
{
var imageIndex = gltf.GetImageIndexFromTextureIndex(index);
var segments = gltf.GetImageBytes(storage, imageIndex, out m_textureName);
var imageIndex = gltf.textures[textureIndex].source;
var segments = gltf.GetImageBytes(storage, imageIndex);
return ToArray(segments);
});
//
// texture from image(png etc) bytes
//
var textureType = TextureIO.GetglTFTextureType(gltf, index);
var textureType = TextureIO.GetglTFTextureType(gltf, textureIndex);
var colorSpace = TextureIO.GetColorSpace(textureType);
var isLinear = colorSpace == RenderTextureReadWrite.Linear;
var sampler = gltf.GetSamplerFromTextureIndex(index);
var sampler = gltf.GetSamplerFromTextureIndex(textureIndex);
var texture = new Texture2D(2, 2, TextureFormat.ARGB32, false, isLinear);
texture.name = m_textureName;
texture.name = gltf.textures[textureIndex].name;
if (imageBytes != null)
{
texture.LoadImage(imageBytes);

View File

@@ -1,28 +1,82 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UniGLTF.AltTask;
using UnityEditor;
using UnityEngine;
using System.Linq;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace UniGLTF
{
public delegate Awaitable<Texture2D> GetTextureAsyncFunc(GetTextureParam param);
public class TextureFactory : IDisposable
[Flags]
public enum TextureLoadFlags
{
None = 0,
Used = 1,
External = 1 << 1,
}
public struct TextureLoadInfo
{
public readonly Texture2D Texture;
public readonly TextureLoadFlags Flags;
public bool IsUsed => Flags.HasFlag(TextureLoadFlags.Used);
public bool IsExternal => Flags.HasFlag(TextureLoadFlags.External);
public TextureLoadInfo(Texture2D texture, bool used, bool isExternal)
{
Texture = texture;
var flags = TextureLoadFlags.None;
if (used)
{
flags |= TextureLoadFlags.Used;
}
if (isExternal)
{
flags |= TextureLoadFlags.External;
}
Flags = flags;
}
}
public delegate Awaitable<Texture2D> GetTextureAsyncFunc(glTF gltf, GetTextureParam param);
public class TextureFactory : IDisposable
{
glTF m_gltf;
IStorage m_storage;
Dictionary<string, Texture2D> m_externalMap;
public bool TryGetExternal(GetTextureParam param, bool used, out Texture2D external)
{
if (param.Index0.HasValue && m_externalMap != null)
{
if (m_externalMap.TryGetValue(param.Name, out external))
{
// Debug.Log($"use external: {param.Name}");
m_textureCache.Add(param.Name, new TextureLoadInfo(external, used, true));
return external;
}
}
external = default;
return false;
}
public UnityPath ImageBaseDir { get; set; }
public TextureFactory(glTF gltf, IStorage storage)
public TextureFactory(glTF gltf, IStorage storage,
IEnumerable<(string, UnityEngine.Object)> externalMap)
{
m_gltf = gltf;
m_storage = storage;
if (externalMap != null)
{
m_externalMap = externalMap
.Select(kv => (kv.Item1, kv.Item2 as Texture2D))
.Where(kv => kv.Item2 != null)
.ToDictionary(kv => kv.Item1, kv => kv.Item2);
}
}
List<Texture2D> m_textuers = new List<Texture2D>();
public void Dispose()
{
foreach (var x in ObjectsForSubAsset())
@@ -35,21 +89,35 @@ namespace UniGLTF
{
foreach (var kv in m_textureCache)
{
yield return kv.Value;
yield return kv.Value.Texture;
}
}
Dictionary<GetTextureParam, Texture2D> m_textureCache = new Dictionary<GetTextureParam, Texture2D>();
Dictionary<string, TextureLoadInfo> m_textureCache = new Dictionary<string, TextureLoadInfo>();
public virtual Awaitable<Texture2D> LoadTextureAsync(int index)
public IEnumerable<TextureLoadInfo> Textures => m_textureCache.Values;
public virtual async Awaitable<TextureLoadInfo> LoadTextureAsync(int index, bool used)
{
#if UNIGLTF_USE_WEBREQUEST_TEXTURELOADER
return UnityWebRequestTextureLoader.LoadTextureAsync(index);
#else
return GltfTextureLoader.LoadTextureAsync(m_gltf, m_storage, index);
var texture = await GltfTextureLoader.LoadTextureAsync(m_gltf, m_storage, index);
return new TextureLoadInfo(texture, used, false);
#endif
}
async Awaitable<TextureLoadInfo> GetOrCreateBaseTexture(glTF gltf, int textureIndex, bool used)
{
var name = gltf.textures[textureIndex].name;
if (!m_textureCache.TryGetValue(name, out TextureLoadInfo cacheInfo))
{
cacheInfo = await LoadTextureAsync(textureIndex, used);
m_textureCache.Add(name, cacheInfo);
}
return cacheInfo;
}
/// <summary>
/// テクスチャーをロード、必要であれば変換して返す。
/// 同じものはキャッシュを返す
@@ -58,20 +126,15 @@ namespace UniGLTF
/// <param name="roughnessFactor">METALLIC_GLOSS_PROPの追加パラメーター</param>
/// <param name="indices">gltf の texture index</param>
/// <returns></returns>
public async Awaitable<Texture2D> GetTextureAsync(GetTextureParam param)
public async Awaitable<Texture2D> GetTextureAsync(glTF gltf, GetTextureParam param)
{
if (m_textureCache.TryGetValue(param, out Texture2D texture))
if (m_textureCache.TryGetValue(param.Name, out TextureLoadInfo cacheInfo))
{
return texture;
return cacheInfo.Texture;
}
if (TryGetExternal(param, true, out Texture2D external))
{
var defaultParam = GetTextureParam.Create(param.Index0.Value);
if (!m_textureCache.TryGetValue(defaultParam, out texture))
{
texture = await LoadTextureAsync(param.Index0.Value);
m_textureCache.Add(defaultParam, texture);
}
return external;
}
switch (param.TextureType)
@@ -80,48 +143,55 @@ namespace UniGLTF
{
if (Application.isPlaying)
{
var converted = new NormalConverter().GetImportTexture(texture);
m_textureCache.Add(param, converted);
return converted;
var baseTexture = await GetOrCreateBaseTexture(gltf, param.Index0.Value, false);
var converted = new NormalConverter().GetImportTexture(baseTexture.Texture);
var info = new TextureLoadInfo(converted, true, false);
m_textureCache.Add(param.Name, info);
return info.Texture;
}
else
{
#if UNITY_EDITOR
var textureAssetPath = AssetDatabase.GetAssetPath(texture);
if (!string.IsNullOrEmpty(textureAssetPath))
{
TextureIO.MarkTextureAssetAsNormalMap(textureAssetPath);
}
else
{
Debug.LogWarningFormat("no asset for {0}", texture);
}
var info = await LoadTextureAsync(param.Index0.Value, true);
var name = gltf.textures[param.Index0.Value].name;
m_textureCache.Add(name, info);
var textureAssetPath = AssetDatabase.GetAssetPath(info.Texture);
TextureIO.MarkTextureAssetAsNormalMap(textureAssetPath);
#endif
m_textureCache.Add(param, texture);
return texture;
return info.Texture;
}
}
case GetTextureParam.METALLIC_GLOSS_PROP:
{
// Bake roughnessFactor values into a texture.
var converted = new MetallicRoughnessConverter(param.MetallicFactor).GetImportTexture(texture);
m_textureCache.Add(param, converted);
return converted;
var baseTexture = await GetOrCreateBaseTexture(gltf, param.Index0.Value, false);
var converted = new MetallicRoughnessConverter(param.MetallicFactor).GetImportTexture(baseTexture.Texture);
converted.name = param.Name;
var info = new TextureLoadInfo(converted, true, false);
m_textureCache.Add(param.Name, info);
return info.Texture;
}
case GetTextureParam.OCCLUSION_PROP:
{
var converted = new OcclusionConverter().GetImportTexture(texture);
m_textureCache.Add(param, converted);
return converted;
var baseTexture = await GetOrCreateBaseTexture(gltf, param.Index0.Value, false);
var converted = new OcclusionConverter().GetImportTexture(baseTexture.Texture);
converted.name = param.Name;
var info = new TextureLoadInfo(converted, true, false);
m_textureCache.Add(param.Name, info);
return info.Texture;
}
default:
return texture;
}
{
var baseTexture = await GetOrCreateBaseTexture(gltf, param.Index0.Value, true);
return baseTexture.Texture;
}
throw new NotImplementedException();
throw new NotImplementedException();
}
}
}
}

View File

@@ -55,8 +55,8 @@ namespace UniGLTF
public IEnumerator ProcessOnMainThread(glTF gltf, IStorage storage, bool isLinear, glTFTextureSampler sampler)
{
var imageIndex = gltf.GetImageIndexFromTextureIndex(m_textureIndex);
var bytes = gltf.GetImageBytes(storage, imageIndex, out m_textureName);
var gltfTexture = gltf.textures[m_textureIndex];
var bytes = gltf.GetImageBytes(storage, gltfTexture.source);
// tmp file
var tmp = Path.GetTempFileName();

View File

@@ -98,7 +98,7 @@ namespace UniGLTF
public void UniGLTFSimpleSceneTest()
{
var go = CreateSimpleScene();
var context = new ImporterContext();
ImporterContext context = default;
try
{
@@ -117,9 +117,12 @@ namespace UniGLTF
json = gltf.ToJson();
}
// parse
var parser = new GltfParser();
parser.ParseJson(json, new SimpleStorage(new ArraySegment<byte>()));
// import
context.ParseJson(json, new SimpleStorage(new ArraySegment<byte>()));
//Debug.LogFormat("{0}", context.Json);
context = new ImporterContext(parser);
context.Load();
AssertAreEqual(go.transform, context.Root.transform);
@@ -128,7 +131,10 @@ namespace UniGLTF
{
//Debug.LogFormat("Destroy, {0}", go.name);
GameObject.DestroyImmediate(go);
context.EditorDestroyRootAndAssets();
if (context != null)
{
context.EditorDestroyRootAndAssets();
}
}
}
@@ -191,12 +197,12 @@ namespace UniGLTF
[Test]
public void VersionChecker()
{
Assert.False(ImporterContext.IsGeneratedUniGLTFAndOlderThan("hoge", 1, 16));
Assert.False(ImporterContext.IsGeneratedUniGLTFAndOlderThan("UniGLTF-1.16", 1, 16));
Assert.True(ImporterContext.IsGeneratedUniGLTFAndOlderThan("UniGLTF-1.15", 1, 16));
Assert.False(ImporterContext.IsGeneratedUniGLTFAndOlderThan("UniGLTF-11.16", 1, 16));
Assert.True(ImporterContext.IsGeneratedUniGLTFAndOlderThan("UniGLTF-0.16", 1, 16));
Assert.True(ImporterContext.IsGeneratedUniGLTFAndOlderThan("UniGLTF", 1, 16));
Assert.False(GltfParser.IsGeneratedUniGLTFAndOlderThan("hoge", 1, 16));
Assert.False(GltfParser.IsGeneratedUniGLTFAndOlderThan("UniGLTF-1.16", 1, 16));
Assert.True(GltfParser.IsGeneratedUniGLTFAndOlderThan("UniGLTF-1.15", 1, 16));
Assert.False(GltfParser.IsGeneratedUniGLTFAndOlderThan("UniGLTF-11.16", 1, 16));
Assert.True(GltfParser.IsGeneratedUniGLTFAndOlderThan("UniGLTF-0.16", 1, 16));
Assert.True(GltfParser.IsGeneratedUniGLTFAndOlderThan("UniGLTF", 1, 16));
}
[Test]
@@ -439,7 +445,7 @@ namespace UniGLTF
public void SkinTestEmptyName()
{
var model = new glTFSkin()
{
{
name = "",
inverseBindMatrices = 4,
joints = new int[] { 1 },
@@ -559,8 +565,9 @@ namespace UniGLTF
// import
{
var context = new ImporterContext();
context.ParseJson(json, new SimpleStorage(new ArraySegment<byte>(new byte[1024 * 1024])));
var parser = new GltfParser();
parser.ParseJson(json, new SimpleStorage(new ArraySegment<byte>(new byte[1024 * 1024])));
var context = new ImporterContext(parser);
//Debug.LogFormat("{0}", context.Json);
context.Load();
@@ -577,9 +584,10 @@ namespace UniGLTF
// import new version
{
var context = new ImporterContext();
context.ParseJson(json, new SimpleStorage(new ArraySegment<byte>(new byte[1024 * 1024])));
var parser = new GltfParser();
parser.ParseJson(json, new SimpleStorage(new ArraySegment<byte>(new byte[1024 * 1024])));
//Debug.LogFormat("{0}", context.Json);
var context = new ImporterContext(parser);
context.Load();
var importedRed = context.Root.transform.GetChild(0);

View File

@@ -42,8 +42,10 @@ namespace VRM.Samples
public void ImportExportTest()
{
var path = AliciaPath;
var context = new VRMImporterContext();
context.ParseGlb(File.ReadAllBytes(path));
var parser = new GltfParser();
parser.ParseGlb(File.ReadAllBytes(path));
var context = new VRMImporterContext(parser);
context.Load();
context.ShowMeshes();
context.EnableUpdateWhenOffscreen();
@@ -114,8 +116,9 @@ namespace VRM.Samples
public void MeshCopyTest()
{
var path = AliciaPath;
var context = new VRMImporterContext();
context.ParseGlb(File.ReadAllBytes(path));
var parser = new GltfParser();
parser.ParseGlb(File.ReadAllBytes(path));
var context = new VRMImporterContext(parser);
context.Load();
context.ShowMeshes();
context.EnableUpdateWhenOffscreen();
@@ -133,8 +136,9 @@ namespace VRM.Samples
{
// Aliciaを古いデシリアライザでロードする
var path = AliciaPath;
var context = new VRMImporterContext();
context.ParseGlb(File.ReadAllBytes(path));
var parser = new GltfParser();
parser.ParseGlb(File.ReadAllBytes(path));
var context = new VRMImporterContext(parser);
var oldJson = context.GLTF.ToJson().ParseAsJson().ToString(" ");
// 生成シリアライザでJSON化する

View File

@@ -46,11 +46,11 @@ namespace VRM.Samples
var bytes = File.ReadAllBytes(path);
// なんらかの方法でByte列を得た
var context = new VRMImporterContext();
// GLB形式でJSONを取得しParseします
context.ParseGlb(bytes);
var parser = new GltfParser();
parser.ParseGlb(bytes);
var context = new VRMImporterContext(parser);
// metaを取得(todo: thumbnailテクスチャのロード)
var meta = await context.ReadMetaAsync();

View File

@@ -1,14 +1,11 @@
#pragma warning disable 0414
using System.IO;
using UniGLTF;
using UniGLTF.AltTask;
using UnityEngine;
namespace VRM.Samples
{
public class VRMRuntimeLoader : MonoBehaviour
{
[SerializeField]
@@ -89,33 +86,33 @@ namespace VRM.Samples
var bytes = File.ReadAllBytes(path);
// なんらかの方法でByte列を得た
var context = new VRMImporterContext();
// GLB形式でJSONを取得しParseします
context.ParseGlb(bytes);
var parser = new GltfParser();
parser.Parse(path, bytes);
var context = new VRMImporterContext(parser);
// metaを取得(todo: thumbnailテクスチャのロード)
var meta = await context.ReadMetaAsync();
Debug.LogFormat("meta: title:{0}", meta.Title);
// ParseしたJSONをシーンオブジェクトに変換していく
if (m_loadAsync)
{
await LoadAsync(context);
await context.LoadAsync();
}
else
{
context.LoadAsync();
OnLoaded(context);
context.Load();
}
OnLoaded(context);
}
/// <summary>
/// メタが不要な場合のローダー
/// </summary>
void LoadVRMClicked_without_meta()
async void LoadVRMClicked_without_meta()
{
#if UNITY_STANDALONE_WIN
var path = FileDialogForWindows.FileDialog("open VRM", ".vrm");
@@ -129,55 +126,23 @@ namespace VRM.Samples
return;
}
#if true
var bytes = File.ReadAllBytes(path);
// なんらかの方法でByte列を得た
var context = new VRMImporterContext();
// GLB形式でJSONを取得しParseします
context.ParseGlb(bytes);
var parser = new GltfParser();
parser.ParseGlb(bytes);
var context = new VRMImporterContext(parser);
if (m_loadAsync)
{
// ローカルファイルシステムからロードします
LoadAsync(context);
await context.LoadAsync();
}
else
{
context.Load();
OnLoaded(context);
}
#else
// ParseしたJSONをシーンオブジェクトに変換していく
if (m_loadAsync)
{
// ローカルファイルシステムからロードします
VRMImporter.LoadVrmAsync(path, OnLoaded);
}
else
{
var root=VRMImporter.LoadFromPath(path);
OnLoaded(root);
}
#endif
}
async Awaitable LoadAsync(VRMImporterContext context)
{
#if true
var now = Time.time;
await context.LoadAsync();
var delta = Time.time - now;
Debug.LogFormat("LoadAsync {0:0.0} seconds", delta);
OnLoaded(context);
#else
// ローカルファイルシステムからロードします
VRMImporter.LoadVrmAsync(path, OnLoaded);
#endif
}
void LoadBVHClicked()

View File

@@ -309,15 +309,15 @@ namespace VRM.Samples
{
case ".vrm":
{
var context = new VRMImporterContext();
var file = File.ReadAllBytes(path);
context.ParseGlb(file);
var parser = new GltfParser();
parser.ParseGlb(file);
var context = new VRMImporterContext(parser);
await m_texts.UpdateMetaAsync(context);
#if true
await context.LoadAsync();
#else
context.Load();
#endif
context.ShowMeshes();
context.EnableUpdateWhenOffscreen();
context.ShowMeshes();
@@ -327,9 +327,11 @@ namespace VRM.Samples
case ".glb":
{
var context = new UniGLTF.ImporterContext();
var file = File.ReadAllBytes(path);
context.ParseGlb(file);
var parser = new GltfParser();
parser.ParseGlb(file);
var context = new UniGLTF.ImporterContext(parser);
context.Load();
context.ShowMeshes();
context.EnableUpdateWhenOffscreen();
@@ -341,8 +343,10 @@ namespace VRM.Samples
case ".gltf":
case ".zip":
{
var context = new UniGLTF.ImporterContext();
context.Parse(path);
var parser = new GltfParser();
parser.ParsePath(path);
var context = new UniGLTF.ImporterContext(parser);
context.Load();
context.ShowMeshes();
context.EnableUpdateWhenOffscreen();

View File

@@ -20,8 +20,10 @@ namespace VRM
if (Application.isPlaying)
{
// load into scene
var context = new VRMImporterContext();
context.Load(path);
var parser = new GltfParser();
parser.ParsePath(path);
var context = new VRMImporterContext(parser);
context.Load();
context.ShowMeshes();
context.EnableUpdateWhenOffscreen();
Selection.activeGameObject = context.Root;
@@ -48,8 +50,9 @@ namespace VRM
// import as asset
var prefabPath = UnityPath.FromUnityPath(assetPath);
var context = new VRMImporterContext();
context.ParseGlb(File.ReadAllBytes(path));
var parser = new GltfParser();
parser.ParseGlb(File.ReadAllBytes(path));
var context = new VRMImporterContext(parser);
context.ExtractImages(prefabPath);
EditorApplication.delayCall += () =>

View File

@@ -36,11 +36,11 @@ namespace VRM
{
throw new Exception();
}
var context = new VRMImporterContext();
var parser = new GltfParser();
try
{
context.ParseGlb(File.ReadAllBytes(path.FullPath));
parser.ParseGlb(File.ReadAllBytes(path.FullPath));
}
catch (KeyNotFoundException)
{
@@ -52,6 +52,7 @@ namespace VRM
var prefabPath = path.Parent.Child(path.FileNameWithoutExtension + ".prefab");
// save texture assets !
var context = new VRMImporterContext(parser);
context.ExtractImages(prefabPath);
EditorApplication.delayCall += () =>

View File

@@ -15,30 +15,8 @@ namespace VRM
{
public VRM.glTF_VRM_extensions VRM { get; private set; }
public VRMImporterContext()
public VRMImporterContext(GltfParser parser) : base(parser)
{
}
public override void Parse(string path, byte[] bytes)
{
var ext = Path.GetExtension(path).ToLower();
switch (ext)
{
case ".vrm":
ParseGlb(bytes);
break;
default:
base.Parse(path, bytes);
break;
}
}
public override void ParseJson(string json, IStorage storage)
{
// parse GLTF part(core + unlit, textureTransform, targetNames)
base.ParseJson(json, storage);
// parse VRM part
if (glTF_VRM_extensions.TryDeserilize(GLTF.extensions, out glTF_VRM_extensions vrm))
{
@@ -53,7 +31,7 @@ namespace VRM
}
#region OnLoad
protected override async Awaitable OnLoadModel()
protected override async Awaitable OnLoadModel(Func<string, IDisposable> MeasureTime)
{
Root.name = "VRM";
@@ -61,26 +39,26 @@ namespace VRM
{
await LoadMetaAsync();
}
await LoopAwaitable.Create();
await NextFrameAwaitable.Create();
using (MeasureTime("VRM LoadHumanoid"))
{
LoadHumanoid();
}
await LoopAwaitable.Create();
await NextFrameAwaitable.Create();
using (MeasureTime("VRM LoadBlendShapeMaster"))
{
LoadBlendShapeMaster();
}
await LoopAwaitable.Create();
await NextFrameAwaitable.Create();
using (MeasureTime("VRM LoadSecondary"))
{
VRMSpringUtility.LoadSecondary(Root.transform, Nodes,
VRM.secondaryAnimation);
}
await LoopAwaitable.Create();
await NextFrameAwaitable.Create();
using (MeasureTime("VRM LoadFirstPerson"))
{
@@ -196,6 +174,7 @@ namespace VRM
}
var material = MaterialFactory.Materials
.Select(y => y.Asset)
.FirstOrDefault(y => y.name == x.materialName);
var propertyName = x.propertyName;
if (x.propertyName.FastEndsWith("_ST_S")
@@ -307,7 +286,7 @@ namespace VRM
meta.ContactInformation = gltfMeta.contactInformation;
meta.Reference = gltfMeta.reference;
meta.Title = gltfMeta.title;
meta.Thumbnail = await TextureFactory.GetTextureAsync(GetTextureParam.Create(gltfMeta.texture));
meta.Thumbnail = await TextureFactory.GetTextureAsync(GLTF, GetTextureParam.Create(GLTF, gltfMeta.texture));
meta.AllowedUser = gltfMeta.allowedUser;
meta.ViolentUssage = gltfMeta.violentUssage;
meta.SexualUssage = gltfMeta.sexualUssage;

View File

@@ -71,7 +71,8 @@ namespace VRM
}
foreach (var kv in item.textureProperties)
{
var texture = await getTexture(new GetTextureParam(kv.Key, default, kv.Value, default, default, default, default, default));
var param = GetTextureParam.Create(gltf, kv.Value, kv.Key);
var texture = await getTexture(gltf, param);
if (texture != null)
{
material.SetTexture(kv.Key, texture);

View File

@@ -333,9 +333,11 @@ namespace UniVRM10.Samples
case ".glb":
{
var context = new UniGLTF.ImporterContext();
var file = File.ReadAllBytes(path);
context.ParseGlb(file);
var parser = new GltfParser();
parser.ParseGlb(file);
var context = new UniGLTF.ImporterContext(parser);
context.Load();
context.ShowMeshes();
context.EnableUpdateWhenOffscreen();
@@ -347,8 +349,10 @@ namespace UniVRM10.Samples
case ".gltf":
case ".zip":
{
var context = new UniGLTF.ImporterContext();
context.Parse(path);
var parser = new GltfParser();
parser.ParsePath(path);
var context = new UniGLTF.ImporterContext(parser);
context.Load();
context.ShowMeshes();
context.EnableUpdateWhenOffscreen();

View File

@@ -1,131 +0,0 @@
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
{
#if flase
[ScriptedImporter(1, "glb")]
#endif
public class GltfScriptedImporter : ScriptedImporter, IExternalUnityObject
{
const string TextureDirName = "Textures";
const string MaterialDirName = "Materials";
public override void OnImportAsset(AssetImportContext ctx)
{
Debug.Log("OnImportAsset to " + ctx.assetPath);
try
{
// Create model
VrmLib.Model model = CreateGlbModel(ctx.assetPath);
Debug.Log($"ModelLoader.Load: {model}");
// Build Unity Model
var assets = EditorUnityBuilder.ToUnityAsset(model, assetPath, this);
// Texture
var externalTextures = this.GetExternalUnityObjects<UnityEngine.Texture2D>();
foreach (var texture in assets.Textures)
{
if (texture == null)
continue;
if (externalTextures.ContainsValue(texture))
{
}
else
{
ctx.AddObjectToAsset(texture.name, texture);
}
}
// Material
var externalMaterials = this.GetExternalUnityObjects<UnityEngine.Material>();
foreach (var material in assets.Materials)
{
if (material == null)
continue;
if (externalMaterials.ContainsValue(material))
{
}
else
{
ctx.AddObjectToAsset(material.name, material);
}
}
// Mesh
foreach (var mesh in assets.Meshes)
{
ctx.AddObjectToAsset(mesh.name, mesh);
}
// Root
ctx.AddObjectToAsset(assets.Root.name, assets.Root);
ctx.SetMainObject(assets.Root);
}
catch (System.Exception ex)
{
Debug.LogError(ex);
}
}
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); });
AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate);
}
public void ExtractMaterials()
{
this.ExtractAssets<UnityEngine.Material>(MaterialDirName, ".mat");
AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate);
}
public void ExtractMaterialsAndTextures()
{
this.ExtractTextures(TextureDirName, (path) => { return CreateGlbModel(path); }, () => { this.ExtractAssets<UnityEngine.Material>(MaterialDirName, ".mat"); });
AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate);
}
public Dictionary<string, T> GetExternalUnityObjects<T>() where T : UnityEngine.Object
{
return this.GetExternalObjectMap().Where(x => x.Key.type == typeof(T)).ToDictionary(x => x.Key.name, x => (T)x.Value);
}
public void SetExternalUnityObject<T>(UnityEditor.AssetImporter.SourceAssetIdentifier sourceAssetIdentifier, T obj) where T : UnityEngine.Object
{
this.AddRemap(sourceAssetIdentifier, obj);
AssetDatabase.WriteImportSettingsIfDirty(this.assetPath);
AssetDatabase.ImportAsset(this.assetPath, ImportAssetOptions.ForceUpdate);
}
}
}

View File

@@ -1,65 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.Experimental.AssetImporters;
using UnityEngine;
namespace UniVRM10
{
[CustomEditor(typeof(GltfScriptedImporter))]
public class GltfScriptedImporterEditorGUI : ScriptedImporterEditor
{
private bool _isOpen = true;
public override void OnInspectorGUI()
{
var importer = target as GltfScriptedImporter;
EditorGUILayout.LabelField("Extract settings");
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel("Materials And Textures");
GUI.enabled = !(importer.GetExternalUnityObjects<UnityEngine.Material>().Any()
&& importer.GetExternalUnityObjects<UnityEngine.Texture2D>().Any());
if (GUILayout.Button("Extract"))
{
importer.ExtractMaterialsAndTextures();
}
GUI.enabled = !GUI.enabled;
if (GUILayout.Button("Clear"))
{
importer.ClearExternalObjects<UnityEngine.Material>();
importer.ClearExternalObjects<UnityEngine.Texture2D>();
}
GUI.enabled = true;
EditorGUILayout.EndHorizontal();
// ObjectMap
DrawRemapGUI<UnityEngine.Material>("Material Remap", importer);
DrawRemapGUI<UnityEngine.Texture2D>("Texture Remap", importer);
base.OnInspectorGUI();
}
private void DrawRemapGUI<T>(string title, GltfScriptedImporter importer) where T: UnityEngine.Object
{
EditorGUILayout.Foldout(_isOpen, title);
EditorGUI.indentLevel++;
var objects = importer.GetExternalObjectMap().Where(x => x.Key.type == typeof(T));
foreach (var obj in objects)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel(obj.Key.name);
var asset = EditorGUILayout.ObjectField(obj.Value, obj.Key.type, true) as T;
if(asset != obj.Value)
{
importer.SetExternalUnityObject(obj.Key, asset);
}
EditorGUILayout.EndHorizontal();
}
EditorGUI.indentLevel--;
}
}
}