Merge pull request #759 from ousttrue/feature/gltf_export_window

Feature/gltf export window
This commit is contained in:
ousttrue 2021-03-01 14:37:38 +09:00 committed by GitHub
commit ead45c9640
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 403 additions and 165 deletions

View File

@ -2,6 +2,7 @@
"name": "UniGLTF.Editor",
"references": [
"UniGLTF",
"MeshUtility",
"MeshUtility.Editor"
],
"optionalUnityReferences": [],

View File

@ -0,0 +1,18 @@
using UnityEngine;
namespace UniGLTF
{
public class GltfExportSettings : ScriptableObject
{
public GameObject Root { get; set; }
public Axises InverseAxis;
[Header("MorphTarget(BlendShape)")]
public bool Sparse;
public bool DropNormal;
}
}

View File

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

View File

@ -0,0 +1,248 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using MeshUtility.M17N;
using UnityEditor;
using UnityEngine;
namespace UniGLTF
{
public class GltfExportWindow : EditorWindow
{
const string MENU_KEY = UniGLTFVersion.MENU + "/Export " + UniGLTFVersion.UNIGLTF_VERSION;
[MenuItem(MENU_KEY, false, 0)]
private static void ExportFromMenu()
{
var window = (GltfExportWindow)GetWindow(typeof(GltfExportWindow));
window.titleContent = new GUIContent("Gltf Exporter");
window.Show();
}
private static void Export(GameObject go, string path, MeshExportSettings settings, Axises inverseAxis)
{
var ext = Path.GetExtension(path).ToLower();
var isGlb = false;
switch (ext)
{
case ".glb": isGlb = true; break;
case ".gltf": isGlb = false; break;
default: throw new System.Exception();
}
var gltf = new glTF();
using (var exporter = new gltfExporter(gltf, inverseAxis))
{
exporter.Prepare(go);
exporter.Export(settings);
}
if (isGlb)
{
var bytes = gltf.ToGlbBytes();
File.WriteAllBytes(path, bytes);
}
else
{
var (json, buffers) = gltf.ToGltf(path);
// without BOM
var encoding = new System.Text.UTF8Encoding(false);
File.WriteAllText(path, json, encoding);
// write to local folder
var dir = Path.GetDirectoryName(path);
foreach (var b in buffers)
{
var bufferPath = Path.Combine(dir, b.uri);
File.WriteAllBytes(bufferPath, b.GetBytes().ToArray());
}
}
if (path.StartsWithUnityAssetPath())
{
AssetDatabase.ImportAsset(path.ToUnityRelativePath());
AssetDatabase.Refresh();
}
}
MeshUtility.ExporterDialogState m_state;
GltfExportSettings m_settings;
Editor m_settingsInspector;
void OnEnable()
{
// Debug.Log("OnEnable");
Undo.willFlushUndoRecord += Repaint;
Selection.selectionChanged += Repaint;
m_settings = ScriptableObject.CreateInstance<GltfExportSettings>();
m_settingsInspector = Editor.CreateEditor(m_settings);
m_state = new MeshUtility.ExporterDialogState();
m_state.ExportRootChanged += (root) =>
{
Repaint();
};
m_state.ExportRoot = Selection.activeObject as GameObject;
}
void OnDisable()
{
m_state.Dispose();
// Debug.Log("OnDisable");
Selection.selectionChanged -= Repaint;
Undo.willFlushUndoRecord -= Repaint;
// m_settingsInspector
UnityEditor.Editor.DestroyImmediate(m_settingsInspector);
m_settingsInspector = null;
// m_settings
ScriptableObject.DestroyImmediate(m_settings);
m_settings = null;
}
public delegate Vector2 BeginVerticalScrollViewFunc(Vector2 scrollPosition, bool alwaysShowVertical, GUIStyle verticalScrollbar, GUIStyle background, params GUILayoutOption[] options);
static BeginVerticalScrollViewFunc s_func;
static BeginVerticalScrollViewFunc BeginVerticalScrollView
{
get
{
if (s_func == null)
{
var methods = typeof(EditorGUILayout).GetMethods(BindingFlags.Static | BindingFlags.NonPublic).Where(x => x.Name == "BeginVerticalScrollView").ToArray();
var method = methods.First(x => x.GetParameters()[1].ParameterType == typeof(bool));
s_func = (BeginVerticalScrollViewFunc)method.CreateDelegate(typeof(BeginVerticalScrollViewFunc));
}
return s_func;
}
}
private Vector2 m_ScrollPosition;
IEnumerable<MeshUtility.Validator> ValidatorFactory()
{
yield return MeshUtility.Validators.HierarchyValidator.ValidateRoot;
if (!m_state.ExportRoot)
{
yield break;
}
}
private void OnGUI()
{
// ArgumentException: Getting control 1's position in a group with only 1 controls when doing repaint Aborting
// Validation により GUI の表示項目が変わる場合があるので、
// EventType.Layout と EventType.Repaint 間で内容が変わらないようしている。
if (Event.current.type == EventType.Layout)
{
m_state.Validate(ValidatorFactory());
}
EditorGUIUtility.labelWidth = 150;
// lang
MeshUtility.M17N.Getter.OnGuiSelectLang();
EditorGUILayout.LabelField("ExportRoot");
{
m_state.ExportRoot = (GameObject)EditorGUILayout.ObjectField(m_state.ExportRoot, typeof(GameObject), true);
}
// Render contents using Generic Inspector GUI
m_ScrollPosition = BeginVerticalScrollView(m_ScrollPosition, false, GUI.skin.verticalScrollbar, "OL Box");
GUIUtility.GetControlID(645789, FocusType.Passive);
bool modified = ScrollArea();
EditorGUILayout.EndScrollView();
// Create and Other Buttons
{
// errors
GUILayout.BeginVertical();
// GUILayout.FlexibleSpace();
{
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
GUI.enabled = m_state.Validations.All(x => x.CanExport);
if (GUILayout.Button("Export", GUILayout.MinWidth(100)))
{
OnExportClicked(m_state.ExportRoot, m_settings);
Close();
GUIUtility.ExitGUI();
}
GUI.enabled = true;
GUILayout.EndHorizontal();
}
GUILayout.EndVertical();
}
GUILayout.Space(8);
if (modified)
{
m_state.Invalidate();
}
}
bool ScrollArea()
{
//
// Validation
//
foreach (var v in m_state.Validations)
{
v.DrawGUI();
if (v.ErrorLevel == MeshUtility.ErrorLevels.Critical)
{
// Export UI を表示しない
return false;
}
}
//
// GUI
//
m_settings.Root = m_state.ExportRoot;
m_settingsInspector.OnInspectorGUI();
return true;
}
private static string m_lastExportDir;
static void OnExportClicked(GameObject root, GltfExportSettings settings)
{
string directory;
if (string.IsNullOrEmpty(m_lastExportDir))
{
directory = Directory.GetParent(Application.dataPath).ToString();
}
else
{
directory = m_lastExportDir;
}
// save dialog
var path = EditorUtility.SaveFilePanel(
"Save vrm",
directory,
root.name + ".glb",
"glb,gltf");
if (string.IsNullOrEmpty(path))
{
return;
}
m_lastExportDir = Path.GetDirectoryName(path).Replace("\\", "/");
// export
Export(root, path, new MeshExportSettings
{
ExportOnlyBlendShapePosition = settings.DropNormal,
UseSparseAccessorForMorphTarget = settings.Sparse,
}, settings.InverseAxis);
}
}
}

View File

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

View File

@ -62,6 +62,21 @@ namespace MeshUtility.Validators
return false;
}
public static IEnumerable<Validation> ValidateRoot(GameObject ExportRoot)
{
if (ExportRoot == null)
{
yield return Validation.Critical(ExportValidatorMessages.ROOT_EXISTS.Msg());
yield break;
}
if (ExportRoot.transform.parent != null)
{
yield return Validation.Critical(ExportValidatorMessages.NO_PARENT.Msg());
yield break;
}
}
public static IEnumerable<Validation> Validate(GameObject ExportRoot)
{
if (ExportRoot == null)

View File

@ -286,14 +286,14 @@ namespace UniGLTF
return new float[] { c.r, c.g, c.b, c.a };
}
public static void ReverseZRecursive(this Transform root)
public static void ReverseRecursive(this Transform root, IAxisInverter axisInverter)
{
var globalMap = root.Traverse().ToDictionary(x => x, x => PosRot.FromGlobalTransform(x));
foreach (var x in root.Traverse())
{
x.position = globalMap[x].Position.ReverseZ();
x.rotation = globalMap[x].Rotation.ReverseZ();
x.position = axisInverter.InvertVector3(globalMap[x].Position);
x.rotation = axisInverter.InvertQuaternion(globalMap[x].Rotation);
}
}

View File

@ -185,7 +185,7 @@ namespace UniGLTF
return string.Join("/", path);
}
public static AnimationClip ConvertAnimationClip(glTF gltf, glTFAnimation animation, AxisInverter inverter, glTFNode root = null)
public static AnimationClip ConvertAnimationClip(glTF gltf, glTFAnimation animation, IAxisInverter inverter, glTFNode root = null)
{
var clip = new AnimationClip();
clip.ClearCurves();

View File

@ -9,27 +9,70 @@ namespace UniGLTF
X,
}
public struct AxisInverter
public interface IAxisInverter
{
public Func<Vector3, Vector3> InvertVector3;
public Func<Vector4, Vector4> InvertVector4;
public Func<Quaternion, Quaternion> InvertQuaternion;
public Func<Matrix4x4, Matrix4x4> InvertMat4;
Vector3 InvertVector3(Vector3 src);
Vector4 InvertVector4(Vector4 src);
Quaternion InvertQuaternion(Quaternion src);
Matrix4x4 InvertMat4(Matrix4x4 src);
}
public static AxisInverter ReverseZ => new AxisInverter
public struct ReverseZ : IAxisInverter
{
public Matrix4x4 InvertMat4(Matrix4x4 src)
{
InvertVector3 = x => x.ReverseZ(),
InvertVector4 = x => x.ReverseZ(),
InvertQuaternion = x => x.ReverseZ(),
InvertMat4 = x => x.ReverseZ(),
};
return src.ReverseZ();
}
public static AxisInverter ReverseX => new AxisInverter
public Quaternion InvertQuaternion(Quaternion src)
{
InvertVector3 = x => x.ReverseX(),
InvertVector4 = x => x.ReverseX(),
InvertQuaternion = x => x.ReverseX(),
InvertMat4 = x => x.ReverseX(),
};
return src.ReverseZ();
}
public Vector3 InvertVector3(Vector3 src)
{
return src.ReverseZ();
}
public Vector4 InvertVector4(Vector4 src)
{
return src.ReverseZ();
}
}
public struct ReverseX : IAxisInverter
{
public Matrix4x4 InvertMat4(Matrix4x4 src)
{
return src.ReverseX();
}
public Quaternion InvertQuaternion(Quaternion src)
{
return src.ReverseX();
}
public Vector3 InvertVector3(Vector3 src)
{
return src.ReverseX();
}
public Vector4 InvertVector4(Vector4 src)
{
return src.ReverseX();
}
}
public static class AxisesExtensions
{
public static IAxisInverter Create(this Axises axis)
{
switch (axis)
{
case Axises.Z: return new ReverseZ();
case Axises.X: return new ReverseX();
default: throw new NotImplementedException();
}
}
}
}

View File

@ -71,20 +71,7 @@ namespace UniGLTF
MeasureTime = new ImporterContextSpeedLog().MeasureTime;
}
AxisInverter inverter = default;
switch (InvertAxis)
{
case Axises.Z:
inverter = AxisInverter.ReverseZ;
break;
case Axises.X:
inverter = AxisInverter.ReverseX;
break;
default:
throw new NotImplementedException();
}
var inverter = InvertAxis.Create();
if (Root == null)
{

View File

@ -5,6 +5,7 @@ using UnityEngine;
namespace UniGLTF
{
[Serializable]
public struct MeshExportSettings
{
// MorphTarget に Sparse Accessor を使う
@ -23,18 +24,19 @@ namespace UniGLTF
public static class MeshExporter
{
static glTFMesh ExportPrimitives(glTF gltf, int bufferIndex,
MeshWithRenderer unityMesh, List<Material> unityMaterials)
MeshWithRenderer unityMesh, List<Material> unityMaterials,
IAxisInverter axisInverter)
{
var mesh = unityMesh.Mesh;
var materials = unityMesh.Renderer.sharedMaterials;
var positions = mesh.vertices.Select(y => y.ReverseZ()).ToArray();
var positions = mesh.vertices.Select(axisInverter.InvertVector3).ToArray();
var positionAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, positions, glBufferTarget.ARRAY_BUFFER);
gltf.accessors[positionAccessorIndex].min = positions.Aggregate(positions[0], (a, b) => new Vector3(Mathf.Min(a.x, b.x), Math.Min(a.y, b.y), Mathf.Min(a.z, b.z))).ToArray();
gltf.accessors[positionAccessorIndex].max = positions.Aggregate(positions[0], (a, b) => new Vector3(Mathf.Max(a.x, b.x), Math.Max(a.y, b.y), Mathf.Max(a.z, b.z))).ToArray();
var normalAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, mesh.normals.Select(y => y.normalized.ReverseZ()).ToArray(), glBufferTarget.ARRAY_BUFFER);
var normalAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, mesh.normals.Select(y => axisInverter.InvertVector3(y.normalized)).ToArray(), glBufferTarget.ARRAY_BUFFER);
#if GLTF_EXPORT_TANGENTS
var tangentAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, mesh.tangents.Select(y => y.ReverseZ()).ToArray(), glBufferTarget.ARRAY_BUFFER);
var tangentAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, mesh.tangents.Select(axisInverter.InvertVector4()).ToArray(), glBufferTarget.ARRAY_BUFFER);
#endif
var uvAccessorIndex0 = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, mesh.uv.Select(y => y.ReverseUV()).ToArray(), glBufferTarget.ARRAY_BUFFER);
var uvAccessorIndex1 = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, mesh.uv2.Select(y => y.ReverseUV()).ToArray(), glBufferTarget.ARRAY_BUFFER);
@ -161,7 +163,8 @@ namespace UniGLTF
static gltfMorphTarget ExportMorphTarget(glTF gltf, int bufferIndex,
Mesh mesh, int j,
bool useSparseAccessorForMorphTarget,
bool exportOnlyBlendShapePosition)
bool exportOnlyBlendShapePosition,
IAxisInverter axisInverter)
{
var blendShapeVertices = mesh.vertices;
var usePosition = blendShapeVertices != null && blendShapeVertices.Length > 0;
@ -217,7 +220,7 @@ namespace UniGLTF
{
sparseIndicesViewIndex = gltf.ExtendBufferAndGetViewIndex(bufferIndex, sparseIndices);
blendShapeVertices = sparseIndices.Select(x => blendShapeVertices[x].ReverseZ()).ToArray();
blendShapeVertices = sparseIndices.Select(x => axisInverter.InvertVector3(blendShapeVertices[x])).ToArray();
blendShapePositionAccessorIndex = gltf.ExtendSparseBufferAndGetAccessorIndex(bufferIndex, accessorCount,
blendShapeVertices,
sparseIndices, sparseIndicesViewIndex,
@ -226,7 +229,7 @@ namespace UniGLTF
if (useNormal)
{
blendShapeNormals = sparseIndices.Select(x => blendShapeNormals[x].ReverseZ()).ToArray();
blendShapeNormals = sparseIndices.Select(x => axisInverter.InvertVector3(blendShapeNormals[x])).ToArray();
blendShapeNormalAccessorIndex = gltf.ExtendSparseBufferAndGetAccessorIndex(bufferIndex, accessorCount,
blendShapeNormals,
sparseIndices, sparseIndicesViewIndex,
@ -235,7 +238,7 @@ namespace UniGLTF
if (useTangent)
{
blendShapeTangents = sparseIndices.Select(x => blendShapeTangents[x].ReverseZ()).ToArray();
blendShapeTangents = sparseIndices.Select(x => axisInverter.InvertVector3(blendShapeTangents[x])).ToArray();
blendShapeTangentAccessorIndex = gltf.ExtendSparseBufferAndGetAccessorIndex(bufferIndex, accessorCount,
blendShapeTangents, sparseIndices, sparseIndicesViewIndex,
glBufferTarget.NONE);
@ -243,7 +246,7 @@ namespace UniGLTF
}
else
{
for (int i = 0; i < blendShapeVertices.Length; ++i) blendShapeVertices[i] = blendShapeVertices[i].ReverseZ();
for (int i = 0; i < blendShapeVertices.Length; ++i) blendShapeVertices[i] = axisInverter.InvertVector3(blendShapeVertices[i]);
if (usePosition)
{
blendShapePositionAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex,
@ -253,7 +256,7 @@ namespace UniGLTF
if (useNormal)
{
for (int i = 0; i < blendShapeNormals.Length; ++i) blendShapeNormals[i] = blendShapeNormals[i].ReverseZ();
for (int i = 0; i < blendShapeNormals.Length; ++i) blendShapeNormals[i] = axisInverter.InvertVector3(blendShapeNormals[i]);
blendShapeNormalAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex,
blendShapeNormals,
glBufferTarget.ARRAY_BUFFER);
@ -261,7 +264,7 @@ namespace UniGLTF
if (useTangent)
{
for (int i = 0; i < blendShapeTangents.Length; ++i) blendShapeTangents[i] = blendShapeTangents[i].ReverseZ();
for (int i = 0; i < blendShapeTangents.Length; ++i) blendShapeTangents[i] = axisInverter.InvertVector3(blendShapeTangents[i]);
blendShapeTangentAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex,
blendShapeTangents,
glBufferTarget.ARRAY_BUFFER);
@ -284,12 +287,12 @@ namespace UniGLTF
public static IEnumerable<(Mesh, glTFMesh, Dictionary<int, int>)> ExportMeshes(glTF gltf, int bufferIndex,
List<MeshWithRenderer> unityMeshes, List<Material> unityMaterials,
MeshExportSettings settings)
MeshExportSettings settings, IAxisInverter axisInverter)
{
foreach (var unityMesh in unityMeshes)
{
var gltfMesh = ExportPrimitives(gltf, bufferIndex,
unityMesh, unityMaterials);
unityMesh, unityMaterials, axisInverter);
var targetNames = new List<string>();
@ -300,7 +303,7 @@ namespace UniGLTF
var morphTarget = ExportMorphTarget(gltf, bufferIndex,
unityMesh.Mesh, j,
settings.UseSparseAccessorForMorphTarget,
settings.ExportOnlyBlendShapePosition);
settings.ExportOnlyBlendShapePosition, axisInverter);
if (morphTarget.POSITION < 0 && morphTarget.NORMAL < 0 && morphTarget.TANGENT < 0)
{
continue;

View File

@ -107,7 +107,7 @@ namespace UniGLTF
/// <param name="ctx"></param>
/// <param name="gltfMesh"></param>
/// <returns></returns>
public void ImportMeshIndependentVertexBuffer(ImporterContext ctx, glTFMesh gltfMesh, AxisInverter inverter)
public void ImportMeshIndependentVertexBuffer(ImporterContext ctx, glTFMesh gltfMesh, IAxisInverter inverter)
{
foreach (var prim in gltfMesh.primitives)
{
@ -291,7 +291,7 @@ namespace UniGLTF
/// <param name="ctx"></param>
/// <param name="gltfMesh"></param>
/// <returns></returns>
public void ImportMeshSharingVertexBuffer(ImporterContext ctx, glTFMesh gltfMesh, AxisInverter inverter)
public void ImportMeshSharingVertexBuffer(ImporterContext ctx, glTFMesh gltfMesh, IAxisInverter inverter)
{
{
// 同じVertexBufferを共有しているので先頭のモを使う
@ -507,7 +507,7 @@ namespace UniGLTF
return sharedAttributes;
}
public MeshContext ReadMesh(ImporterContext ctx, int meshIndex, AxisInverter inverter)
public MeshContext ReadMesh(ImporterContext ctx, int meshIndex, IAxisInverter inverter)
{
var gltfMesh = ctx.GLTF.meshes[meshIndex];

View File

@ -131,7 +131,7 @@ namespace UniGLTF
//
// fix node's coordinate. z-back to z-forward
//
public static void FixCoordinate(ImporterContext context, List<TransformWithSkin> nodes, AxisInverter inverter)
public static void FixCoordinate(ImporterContext context, List<TransformWithSkin> nodes, IAxisInverter inverter)
{
var globalTransformMap = nodes.ToDictionary(x => x.Transform, x => new PosRot
{
@ -154,7 +154,7 @@ namespace UniGLTF
}
}
public static void SetupSkinning(ImporterContext context, List<TransformWithSkin> nodes, int i, AxisInverter inverter)
public static void SetupSkinning(ImporterContext context, List<TransformWithSkin> nodes, int i, IAxisInverter inverter)
{
var x = nodes[i];
var skinnedMeshRenderer = x.Transform.GetComponent<SkinnedMeshRenderer>();

View File

@ -46,26 +46,10 @@ namespace UniGLTF
animation.name = $"animation:{i}";
}
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));
animationClips.Add(AnimationImporterUtil.ConvertAnimationClip(gltf, animation, invertAxis.Create()));
}
return animationClips;
}
}
}
}

View File

@ -1,100 +1,13 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace UniGLTF
{
public class gltfExporter : IDisposable
{
const string MENU_EXPORT_GLB_KEY = UniGLTFVersion.MENU + "/Export(glb)";
const string MENU_EXPORT_GLTF_KEY = UniGLTFVersion.MENU + "/Export(gltf)";
#if UNITY_EDITOR
[MenuItem(MENU_EXPORT_GLTF_KEY, true, 1)]
[MenuItem(MENU_EXPORT_GLB_KEY, true, 1)]
private static bool ExportValidate()
{
return Selection.activeObject != null && Selection.activeObject is GameObject;
}
[MenuItem(MENU_EXPORT_GLTF_KEY, priority = 0)]
private static void ExportGltfFromMenu()
{
ExportFromMenu(false, new MeshExportSettings
{
ExportOnlyBlendShapePosition = false,
UseSparseAccessorForMorphTarget = true,
});
}
[MenuItem(MENU_EXPORT_GLB_KEY, priority = 10)]
private static void ExportGlbFromMenu()
{
ExportFromMenu(true, MeshExportSettings.Default);
}
private static void ExportFromMenu(bool isGlb, MeshExportSettings settings)
{
var go = Selection.activeObject as GameObject;
var ext = isGlb ? "glb" : "gltf";
if (go.transform.position == Vector3.zero &&
go.transform.rotation == Quaternion.identity &&
go.transform.localScale == Vector3.one)
{
var path = EditorUtility.SaveFilePanel(
$"Save {ext}", "", go.name + $".{ext}", $"{ext}");
if (string.IsNullOrEmpty(path))
{
return;
}
var gltf = new glTF();
using (var exporter = new gltfExporter(gltf))
{
exporter.Prepare(go);
exporter.Export(settings);
}
if (isGlb)
{
var bytes = gltf.ToGlbBytes();
File.WriteAllBytes(path, bytes);
}
else
{
var (json, buffers) = gltf.ToGltf(path);
// without BOM
var encoding = new System.Text.UTF8Encoding(false);
File.WriteAllText(path, json, encoding);
// write to local folder
var dir = Path.GetDirectoryName(path);
foreach (var b in buffers)
{
var bufferPath = Path.Combine(dir, b.uri);
File.WriteAllBytes(bufferPath, b.GetBytes().ToArray());
}
}
if (path.StartsWithUnityAssetPath())
{
AssetDatabase.ImportAsset(path.ToUnityRelativePath());
AssetDatabase.Refresh();
}
}
else
{
EditorUtility.DisplayDialog("Error", "The Root transform should have Default translation, rotation and scale.", "ok");
}
}
#endif
protected glTF glTF;
@ -175,7 +88,9 @@ namespace UniGLTF
}
}
public gltfExporter(glTF gltf)
IAxisInverter m_axisInverter;
public gltfExporter(glTF gltf, Axises invertAxis = Axises.Z)
{
glTF = gltf;
@ -186,15 +101,17 @@ namespace UniGLTF
generator = "UniGLTF-" + UniGLTFVersion.VERSION,
version = "2.0",
};
m_axisInverter = invertAxis.Create();
}
GameObject m_tmpParent = null;
public virtual void Prepare(GameObject go)
{
// コピーを作って、Z軸を反転することで左手系を右手系に変換する
// コピーを作って左手系を右手系に変換する
Copy = GameObject.Instantiate(go);
Copy.transform.ReverseZRecursive();
Copy.transform.ReverseRecursive(m_axisInverter);
// Export の root は gltf の scene になるので、
// エクスポート対象が単一の GameObject の場合に、
@ -303,7 +220,7 @@ namespace UniGLTF
MeshBlendShapeIndexMap = new Dictionary<Mesh, Dictionary<int, int>>();
foreach (var (mesh, gltfMesh, blendShapeIndexMap) in MeshExporter.ExportMeshes(
glTF, bufferIndex, unityMeshes, Materials, meshExportSettings))
glTF, bufferIndex, unityMeshes, Materials, meshExportSettings, m_axisInverter))
{
glTF.meshes.Add(gltfMesh);
if (!MeshBlendShapeIndexMap.ContainsKey(mesh))
@ -330,7 +247,7 @@ namespace UniGLTF
foreach (var x in unitySkins)
{
var matrices = x.GetBindPoses().Select(y => y.ReverseZ()).ToArray();
var matrices = x.GetBindPoses().Select(m_axisInverter.InvertMat4).ToArray();
var accessor = glTF.ExtendBufferAndGetAccessorIndex(bufferIndex, matrices, glBufferTarget.NONE);
var renderer = x.Renderer as SkinnedMeshRenderer;

View File

@ -28,7 +28,7 @@ namespace VRM
public readonly VRM.glTF_VRM_extensions VRM = new glTF_VRM_extensions();
public VRMExporter(glTF gltf) : base(gltf)
public VRMExporter(glTF gltf) : base(gltf, Axises.Z)
{
gltf.extensionsUsed.Add(glTF_VRM_extensions.ExtensionName);
}