mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-07-15 07:34:25 -05:00
Merge pull request #759 from ousttrue/feature/gltf_export_window
Feature/gltf export window
This commit is contained in:
commit
ead45c9640
|
|
@ -2,6 +2,7 @@
|
|||
"name": "UniGLTF.Editor",
|
||||
"references": [
|
||||
"UniGLTF",
|
||||
"MeshUtility",
|
||||
"MeshUtility.Editor"
|
||||
],
|
||||
"optionalUnityReferences": [],
|
||||
|
|
|
|||
18
Assets/UniGLTF/Editor/UniGLTF/GltfExportSettings.cs
Normal file
18
Assets/UniGLTF/Editor/UniGLTF/GltfExportSettings.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
11
Assets/UniGLTF/Editor/UniGLTF/GltfExportSettings.cs.meta
Normal file
11
Assets/UniGLTF/Editor/UniGLTF/GltfExportSettings.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6e6d0977c916e05438394a6ea65b6a1c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
248
Assets/UniGLTF/Editor/UniGLTF/GltfExportWindow.cs
Normal file
248
Assets/UniGLTF/Editor/UniGLTF/GltfExportWindow.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/UniGLTF/Editor/UniGLTF/GltfExportWindow.cs.meta
Normal file
11
Assets/UniGLTF/Editor/UniGLTF/GltfExportWindow.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fd4010850f4b6bf4e8c3cc58a5af9fc9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
||||
|
|
|
|||
|
|
@ -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>();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user