mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-05-15 15:11:43 -05:00
Merge commit '2a19c831f8440eed1279b9930ef33115c61d7d82' as 'UniGLTF'
Co-authored-by: Akihiko Odaki <nekomanma@pixiv.co.jp> Co-authored-by: Emiliana <vtemiliana@gmail.com> Co-authored-by: junichi_hirose <junichi_hirose@dwango.co.jp> Co-authored-by: Masataka SUMI <santarh@gmail.com> Co-authored-by: ousttrue <oustrrue@gmail.com> Co-authored-by: ousttrue <ousttrue@gmail.com> Co-authored-by: TORISOUP <tori.birdstrike@gmail.com> Co-authored-by: Yuki Shimada <emadurandal@gmail.com> Co-authored-by: yutopp <yutopp@gmail.com>
This commit is contained in:
commit
1d108052de
40
UniGLTF/.gitignore
vendored
Normal file
40
UniGLTF/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
[Ll]ibrary/
|
||||
[Tt]emp/
|
||||
[Oo]bj/
|
||||
[Bb]uild/
|
||||
[Bb]uilds/
|
||||
Assets/AssetStoreTools*
|
||||
|
||||
# Visual Studio 2015 cache directory
|
||||
/.vs/
|
||||
|
||||
# Autogenerated VS/MD/Consulo solution and project files
|
||||
ExportedObj/
|
||||
.consulo/
|
||||
*.csproj
|
||||
*.unityproj
|
||||
*.sln
|
||||
*.suo
|
||||
*.tmp
|
||||
*.user
|
||||
*.userprefs
|
||||
*.pidb
|
||||
*.booproj
|
||||
*.svd
|
||||
*.pdb
|
||||
|
||||
# Unity3D generated meta files
|
||||
*.pidb.meta
|
||||
*.pdb.meta
|
||||
|
||||
# Unity3D Generated File On Crash Reports
|
||||
sysinfo.txt
|
||||
|
||||
# Builds
|
||||
*.apk
|
||||
*.unitypackage
|
||||
|
||||
/Assets/Unity Technologies
|
||||
/Assets/Unity Technologies.meta
|
||||
|
||||
/Assets/glTF-Sample-Models.meta
|
||||
9
UniGLTF/.gitmodules
vendored
Normal file
9
UniGLTF/.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[submodule "UniJSON"]
|
||||
path = UniJSON
|
||||
url = https://github.com/ousttrue/UniJSON.git
|
||||
[submodule "UniHumanoid"]
|
||||
path = UniHumanoid
|
||||
url = https://github.com/ousttrue/UniHumanoid.git
|
||||
[submodule "DepthFirstScheduler"]
|
||||
path = DepthFirstScheduler
|
||||
url = https://github.com/ousttrue/DepthFirstScheduler.git
|
||||
10
UniGLTF/Core.meta
Normal file
10
UniGLTF/Core.meta
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d2d37db807d5f0946b5d264a870ac053
|
||||
folderAsset: yes
|
||||
timeCreated: 1517139118
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
9
UniGLTF/Core/Editor.meta
Normal file
9
UniGLTF/Core/Editor.meta
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d3f12e850b80b874da5050c9599b8abc
|
||||
folderAsset: yes
|
||||
timeCreated: 1521096862
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
180
UniGLTF/Core/Editor/MaterialTests.cs
Normal file
180
UniGLTF/Core/Editor/MaterialTests.cs
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
using NUnit.Framework;
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public class MaterialTests
|
||||
{
|
||||
[Test]
|
||||
public void UnlitShaderImportTest()
|
||||
{
|
||||
var shaderStore = new ShaderStore(null);
|
||||
|
||||
{
|
||||
// OPAQUE/Color
|
||||
var shader = shaderStore.GetShader(new glTFMaterial
|
||||
{
|
||||
alphaMode = "OPAQUE",
|
||||
pbrMetallicRoughness = new glTFPbrMetallicRoughness
|
||||
{
|
||||
baseColorFactor = new float[] { 1, 0, 0, 1 },
|
||||
},
|
||||
extensions = new glTFMaterial_extensions
|
||||
{
|
||||
KHR_materials_unlit = new glTF_KHR_materials_unlit { }
|
||||
}
|
||||
});
|
||||
Assert.AreEqual("UniGLTF/UniUnlit", shader.name);
|
||||
}
|
||||
|
||||
{
|
||||
// OPAQUE/Texture
|
||||
var shader = shaderStore.GetShader(new glTFMaterial
|
||||
{
|
||||
alphaMode = "OPAQUE",
|
||||
pbrMetallicRoughness = new glTFPbrMetallicRoughness
|
||||
{
|
||||
baseColorTexture = new glTFMaterialBaseColorTextureInfo(),
|
||||
},
|
||||
extensions = new glTFMaterial_extensions
|
||||
{
|
||||
KHR_materials_unlit = new glTF_KHR_materials_unlit { }
|
||||
}
|
||||
});
|
||||
Assert.AreEqual("UniGLTF/UniUnlit", shader.name);
|
||||
}
|
||||
|
||||
{
|
||||
// OPAQUE/Color/Texture
|
||||
var shader = shaderStore.GetShader(new glTFMaterial
|
||||
{
|
||||
alphaMode = "OPAQUE",
|
||||
pbrMetallicRoughness = new glTFPbrMetallicRoughness
|
||||
{
|
||||
baseColorFactor = new float[] { 1, 0, 0, 1 },
|
||||
baseColorTexture = new glTFMaterialBaseColorTextureInfo(),
|
||||
},
|
||||
extensions = new glTFMaterial_extensions
|
||||
{
|
||||
KHR_materials_unlit = new glTF_KHR_materials_unlit { }
|
||||
}
|
||||
});
|
||||
Assert.AreEqual("UniGLTF/UniUnlit", shader.name);
|
||||
}
|
||||
|
||||
{
|
||||
// BLEND/Color
|
||||
var shader = shaderStore.GetShader(new glTFMaterial
|
||||
{
|
||||
alphaMode = "BLEND",
|
||||
pbrMetallicRoughness = new glTFPbrMetallicRoughness
|
||||
{
|
||||
baseColorFactor = new float[] { 1, 0, 0, 1 },
|
||||
},
|
||||
extensions = new glTFMaterial_extensions
|
||||
{
|
||||
KHR_materials_unlit = new glTF_KHR_materials_unlit { }
|
||||
}
|
||||
});
|
||||
Assert.AreEqual("UniGLTF/UniUnlit", shader.name);
|
||||
}
|
||||
|
||||
{
|
||||
// BLEND/Texture
|
||||
var shader = shaderStore.GetShader(new glTFMaterial
|
||||
{
|
||||
alphaMode = "BLEND",
|
||||
pbrMetallicRoughness = new glTFPbrMetallicRoughness
|
||||
{
|
||||
baseColorTexture = new glTFMaterialBaseColorTextureInfo(),
|
||||
},
|
||||
extensions = new glTFMaterial_extensions
|
||||
{
|
||||
KHR_materials_unlit = new glTF_KHR_materials_unlit { }
|
||||
}
|
||||
});
|
||||
Assert.AreEqual("UniGLTF/UniUnlit", shader.name);
|
||||
}
|
||||
|
||||
{
|
||||
// BLEND/Color/Texture
|
||||
var shader = shaderStore.GetShader(new glTFMaterial
|
||||
{
|
||||
alphaMode = "BLEND",
|
||||
pbrMetallicRoughness = new glTFPbrMetallicRoughness
|
||||
{
|
||||
baseColorFactor = new float[] { 1, 0, 0, 1 },
|
||||
baseColorTexture = new glTFMaterialBaseColorTextureInfo(),
|
||||
},
|
||||
extensions = new glTFMaterial_extensions
|
||||
{
|
||||
KHR_materials_unlit = new glTF_KHR_materials_unlit { }
|
||||
}
|
||||
});
|
||||
Assert.AreEqual("UniGLTF/UniUnlit", shader.name);
|
||||
}
|
||||
|
||||
{
|
||||
// MASK/Texture
|
||||
var shader = shaderStore.GetShader(new glTFMaterial
|
||||
{
|
||||
alphaMode = "MASK",
|
||||
pbrMetallicRoughness = new glTFPbrMetallicRoughness
|
||||
{
|
||||
baseColorTexture = new glTFMaterialBaseColorTextureInfo(),
|
||||
},
|
||||
extensions = new glTFMaterial_extensions
|
||||
{
|
||||
KHR_materials_unlit = new glTF_KHR_materials_unlit { }
|
||||
}
|
||||
});
|
||||
Assert.AreEqual("UniGLTF/UniUnlit", shader.name);
|
||||
}
|
||||
|
||||
{
|
||||
// MASK/Color/Texture
|
||||
var shader = shaderStore.GetShader(new glTFMaterial
|
||||
{
|
||||
alphaMode = "MASK",
|
||||
pbrMetallicRoughness = new glTFPbrMetallicRoughness
|
||||
{
|
||||
baseColorFactor = new float[] { 1, 0, 0, 1 },
|
||||
baseColorTexture = new glTFMaterialBaseColorTextureInfo(),
|
||||
},
|
||||
extensions = new glTFMaterial_extensions
|
||||
{
|
||||
KHR_materials_unlit = new glTF_KHR_materials_unlit { }
|
||||
}
|
||||
});
|
||||
Assert.AreEqual("UniGLTF/UniUnlit", shader.name);
|
||||
}
|
||||
|
||||
{
|
||||
// default
|
||||
var shader = shaderStore.GetShader(new glTFMaterial
|
||||
{
|
||||
extensions = new glTFMaterial_extensions
|
||||
{
|
||||
KHR_materials_unlit = new glTF_KHR_materials_unlit { }
|
||||
}
|
||||
});
|
||||
Assert.AreEqual("UniGLTF/UniUnlit", shader.name);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MaterialImportTest()
|
||||
{
|
||||
var shaderStore = new ShaderStore(null);
|
||||
var materialImporter = new MaterialImporter(shaderStore, null);
|
||||
|
||||
{
|
||||
var material = materialImporter.CreateMaterial(0, new glTFMaterial
|
||||
{
|
||||
|
||||
});
|
||||
Assert.AreEqual("Standard", material.shader.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
UniGLTF/Core/Editor/MaterialTests.cs.meta
Normal file
12
UniGLTF/Core/Editor/MaterialTests.cs.meta
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 37dbc1bc37728a44f8e33ad44e407c91
|
||||
timeCreated: 1533626339
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
195
UniGLTF/Core/Editor/UniGLTFTests.cs
Normal file
195
UniGLTF/Core/Editor/UniGLTFTests.cs
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UniJSON;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public class UniGLTFTests
|
||||
{
|
||||
static GameObject CreateSimpelScene()
|
||||
{
|
||||
var root = new GameObject("gltfRoot").transform;
|
||||
|
||||
var scene = new GameObject("scene0").transform;
|
||||
scene.SetParent(root, false);
|
||||
scene.localPosition = new Vector3(1, 2, 3);
|
||||
|
||||
return root.gameObject;
|
||||
}
|
||||
|
||||
void AssertAreEqual(Transform go, Transform other)
|
||||
{
|
||||
var lt = go.Traverse().GetEnumerator();
|
||||
var rt = go.Traverse().GetEnumerator();
|
||||
|
||||
while (lt.MoveNext())
|
||||
{
|
||||
if (!rt.MoveNext())
|
||||
{
|
||||
throw new Exception("rt shorter");
|
||||
}
|
||||
|
||||
MonoBehaviourComparator.AssertAreEquals(lt.Current.gameObject, rt.Current.gameObject);
|
||||
}
|
||||
|
||||
if (rt.MoveNext())
|
||||
{
|
||||
throw new Exception("rt longer");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UniGLTFSimpleSceneTest()
|
||||
{
|
||||
var go = CreateSimpelScene();
|
||||
var context = new ImporterContext();
|
||||
|
||||
try
|
||||
{
|
||||
// export
|
||||
var gltf = new glTF();
|
||||
|
||||
string json = null;
|
||||
using (var exporter = new gltfExporter(gltf))
|
||||
{
|
||||
exporter.Prepare(go);
|
||||
exporter.Export();
|
||||
|
||||
// remove empty buffer
|
||||
gltf.buffers.Clear();
|
||||
|
||||
json = gltf.ToJson();
|
||||
}
|
||||
|
||||
// import
|
||||
context.ParseJson(json, new SimpleStorage(new ArraySegment<byte>()));
|
||||
//Debug.LogFormat("{0}", context.Json);
|
||||
context.Load();
|
||||
|
||||
AssertAreEqual(go.transform, context.Root.transform);
|
||||
}
|
||||
finally
|
||||
{
|
||||
//Debug.LogFormat("Destory, {0}", go.name);
|
||||
GameObject.DestroyImmediate(go);
|
||||
context.EditorDestroyRootAndAssets();
|
||||
}
|
||||
}
|
||||
|
||||
void BufferTest(int init, params int[] size)
|
||||
{
|
||||
var initBytes = init == 0 ? null : new byte[init];
|
||||
var storage = new ArrayByteBuffer(initBytes);
|
||||
var buffer = new glTFBuffer(storage);
|
||||
|
||||
var values = new List<byte>();
|
||||
int offset = 0;
|
||||
foreach (var x in size)
|
||||
{
|
||||
var nums = Enumerable.Range(offset, x).Select(y => (Byte)y).ToArray();
|
||||
values.AddRange(nums);
|
||||
var bytes = new ArraySegment<Byte>(nums);
|
||||
offset += x;
|
||||
buffer.Append(bytes, glBufferTarget.NONE);
|
||||
}
|
||||
|
||||
Assert.AreEqual(values.Count, buffer.byteLength);
|
||||
Assert.True(Enumerable.SequenceEqual(values, buffer.GetBytes().ToArray()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BufferTest()
|
||||
{
|
||||
BufferTest(0, 0, 100, 200);
|
||||
BufferTest(0, 128);
|
||||
BufferTest(0, 256);
|
||||
|
||||
BufferTest(1024, 0);
|
||||
BufferTest(1024, 128);
|
||||
BufferTest(1024, 2048);
|
||||
BufferTest(1024, 900, 900);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UnityPathTest()
|
||||
{
|
||||
var root = UnityPath.FromUnityPath(".");
|
||||
Assert.IsFalse(root.IsNull);
|
||||
Assert.IsFalse(root.IsUnderAssetsFolder);
|
||||
Assert.AreEqual(UnityPath.FromUnityPath("."), root);
|
||||
|
||||
var assets = UnityPath.FromUnityPath("Assets");
|
||||
Assert.IsFalse(assets.IsNull);
|
||||
Assert.IsTrue(assets.IsUnderAssetsFolder);
|
||||
|
||||
var rootChild = root.Child("Assets");
|
||||
Assert.AreEqual(assets, rootChild);
|
||||
|
||||
var assetsChild = assets.Child("Hoge");
|
||||
var hoge = UnityPath.FromUnityPath("Assets/Hoge");
|
||||
Assert.AreEqual(assetsChild, hoge);
|
||||
|
||||
//var children = root.TravserseDir().ToArray();
|
||||
}
|
||||
|
||||
[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));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MeshTest()
|
||||
{
|
||||
var mesh = new glTFMesh("mesh")
|
||||
{
|
||||
primitives = new List<glTFPrimitives>
|
||||
{
|
||||
new glTFPrimitives
|
||||
{
|
||||
attributes=new glTFAttributes
|
||||
{
|
||||
POSITION=0,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var f = new JsonFormatter();
|
||||
f.Serialize(mesh);
|
||||
|
||||
var json = new Utf8String(f.GetStoreBytes()).ToString();
|
||||
Debug.Log(json);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PrimitiveTest()
|
||||
{
|
||||
var prims = new List<glTFPrimitives> {
|
||||
new glTFPrimitives
|
||||
{
|
||||
attributes = new glTFAttributes
|
||||
{
|
||||
POSITION = 0,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var f = new JsonFormatter();
|
||||
f.Serialize(prims);
|
||||
|
||||
var json = new Utf8String(f.GetStoreBytes()).ToString();
|
||||
Debug.Log(json);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
12
UniGLTF/Core/Editor/UniGLTFTests.cs.meta
Normal file
12
UniGLTF/Core/Editor/UniGLTFTests.cs.meta
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 164dff9806a684447ae2c9d679ae730b
|
||||
timeCreated: 1521096875
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
10
UniGLTF/Core/Scripts.meta
Normal file
10
UniGLTF/Core/Scripts.meta
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d0989add64612874d9cb36855d07677d
|
||||
folderAsset: yes
|
||||
timeCreated: 1517139165
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
9
UniGLTF/Core/Scripts/Editor.meta
Normal file
9
UniGLTF/Core/Scripts/Editor.meta
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 37a5b86b0d204ea49b66dba21b239b22
|
||||
folderAsset: yes
|
||||
timeCreated: 1517119647
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
51
UniGLTF/Core/Scripts/Editor/ImporterMenu.cs
Normal file
51
UniGLTF/Core/Scripts/Editor/ImporterMenu.cs
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public static class ImporterMenu
|
||||
{
|
||||
[MenuItem(UniGLTFVersion.UNIGLTF_VERSION + "/Import", priority = 1)]
|
||||
public static void ImportMenu()
|
||||
{
|
||||
var path = EditorUtility.OpenFilePanel("open gltf", "", "gltf,glb,zip");
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
//
|
||||
// load into scene
|
||||
//
|
||||
var context = new ImporterContext();
|
||||
context.Load(path);
|
||||
context.ShowMeshes();
|
||||
Selection.activeGameObject = context.Root;
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// save as asset
|
||||
//
|
||||
if (path.StartsWithUnityAssetPath())
|
||||
{
|
||||
Debug.LogWarningFormat("disallow import from folder under the Assets");
|
||||
return;
|
||||
}
|
||||
|
||||
var assetPath = EditorUtility.SaveFilePanel("save prefab", "Assets", Path.GetFileNameWithoutExtension(path), "prefab");
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// import as asset
|
||||
gltfAssetPostprocessor.ImportAsset(path, Path.GetExtension(path).ToLower(), UnityPath.FromFullpath(assetPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13
UniGLTF/Core/Scripts/Editor/ImporterMenu.cs.meta
Normal file
13
UniGLTF/Core/Scripts/Editor/ImporterMenu.cs.meta
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ae2833922b06981439883d1e924b4cd0
|
||||
timeCreated: 1517153624
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
45
UniGLTF/Core/Scripts/Editor/UniGLTFVersionMenu.cs
Normal file
45
UniGLTF/Core/Scripts/Editor/UniGLTFVersionMenu.cs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#if UNIGLTF_DEVELOP
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public static class UniGLTFVersionMenu
|
||||
{
|
||||
public const int MENU_PRIORITY = 99;
|
||||
static string path = "Assets/UniGLTF/Core/Scripts/UniGLTFVersion.cs";
|
||||
|
||||
const string template = @"
|
||||
namespace UniGLTF
|
||||
{{
|
||||
public static partial class UniGLTFVersion
|
||||
{{
|
||||
public const int MAJOR = {0};
|
||||
public const int MINOR = {1};
|
||||
|
||||
public const string VERSION = ""{0}.{1}"";
|
||||
}}
|
||||
}}
|
||||
";
|
||||
|
||||
[MenuItem(UniGLTFVersion.UNIGLTF_VERSION + "/Increment", priority = MENU_PRIORITY)]
|
||||
public static void IncrementVersion()
|
||||
{
|
||||
var source = string.Format(template, UniGLTFVersion.MAJOR, UniGLTFVersion.MINOR + 1);
|
||||
File.WriteAllText(path, source);
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
[MenuItem(UniGLTFVersion.UNIGLTF_VERSION + "/Decrement", priority = MENU_PRIORITY)]
|
||||
public static void DecrementVersion()
|
||||
{
|
||||
var source = string.Format(template, UniGLTFVersion.MAJOR, UniGLTFVersion.MINOR - 1);
|
||||
File.WriteAllText(path, source);
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
13
UniGLTF/Core/Scripts/Editor/UniGLTFVersionMenu.cs.meta
Normal file
13
UniGLTF/Core/Scripts/Editor/UniGLTFVersionMenu.cs.meta
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 49d23365a29018d4cb142227a01e662d
|
||||
timeCreated: 1522130550
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
80
UniGLTF/Core/Scripts/Editor/gltfAssetPostprocessor.cs
Normal file
80
UniGLTF/Core/Scripts/Editor/gltfAssetPostprocessor.cs
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public class gltfAssetPostprocessor : AssetPostprocessor
|
||||
{
|
||||
static void OnPostprocessAllAssets(string[] importedAssets,
|
||||
string[] deletedAssets,
|
||||
string[] movedAssets,
|
||||
string[] movedFromAssetPaths)
|
||||
{
|
||||
foreach (string path in importedAssets)
|
||||
{
|
||||
var ext = Path.GetExtension(path).ToLower();
|
||||
switch (ext)
|
||||
{
|
||||
case ".gltf":
|
||||
case ".glb":
|
||||
{
|
||||
var gltfPath = UnityPath.FromUnityPath(path);
|
||||
var prefabPath = gltfPath.Parent.Child(gltfPath.FileNameWithoutExtension + ".prefab");
|
||||
ImportAsset(UnityPath.FromUnityPath(path).FullPath, ext, prefabPath);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void ImportAsset(string src, string ext, UnityPath prefabPath)
|
||||
{
|
||||
if (!prefabPath.IsUnderAssetsFolder)
|
||||
{
|
||||
Debug.LogWarningFormat("out of asset path: {0}", prefabPath);
|
||||
return;
|
||||
}
|
||||
|
||||
var context = new ImporterContext();
|
||||
context.Parse(src);
|
||||
|
||||
// Extract textures to assets folder
|
||||
context.ExtranctImages(prefabPath);
|
||||
|
||||
ImportDelayed(src, prefabPath, context);
|
||||
}
|
||||
|
||||
static void ImportDelayed(string src, UnityPath prefabPath, ImporterContext context)
|
||||
{
|
||||
EditorApplication.delayCall += () =>
|
||||
{
|
||||
//
|
||||
// After textures imported(To ensure TextureImporter be accessible).
|
||||
//
|
||||
try
|
||||
{
|
||||
context.Load();
|
||||
context.SaveAsAsset(prefabPath);
|
||||
context.EditorDestroyRoot();
|
||||
}
|
||||
catch (UniGLTFNotSupportedException ex)
|
||||
{
|
||||
Debug.LogWarningFormat("{0}: {1}",
|
||||
src,
|
||||
ex.Message
|
||||
);
|
||||
context.EditorDestroyRootAndAssets();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogErrorFormat("import error: {0}", src);
|
||||
Debug.LogErrorFormat("{0}", ex);
|
||||
context.EditorDestroyRootAndAssets();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
12
UniGLTF/Core/Scripts/Editor/gltfAssetPostprocessor.cs.meta
Normal file
12
UniGLTF/Core/Scripts/Editor/gltfAssetPostprocessor.cs.meta
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7bbb264b34e75dd438622e1f29f0f46c
|
||||
timeCreated: 1517119659
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
10
UniGLTF/Core/Scripts/Extensions.meta
Normal file
10
UniGLTF/Core/Scripts/Extensions.meta
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bb1523b753d4a6444935951652053804
|
||||
folderAsset: yes
|
||||
timeCreated: 1514252348
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
127
UniGLTF/Core/Scripts/Extensions/ArrayExtensions.cs
Normal file
127
UniGLTF/Core/Scripts/Extensions/ArrayExtensions.cs
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public static class Pin
|
||||
{
|
||||
public static Pin<T> Create<T>(ArraySegment<T> src) where T : struct
|
||||
{
|
||||
return new Pin<T>(src);
|
||||
}
|
||||
public static Pin<T> Create<T>(T[] src) where T : struct
|
||||
{
|
||||
return Create(new ArraySegment<T>(src));
|
||||
}
|
||||
}
|
||||
public class Pin<T> : IDisposable
|
||||
where T : struct
|
||||
{
|
||||
GCHandle m_pinnedArray;
|
||||
|
||||
ArraySegment<T> m_src;
|
||||
|
||||
public int Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_src.Count * Marshal.SizeOf(typeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
public Pin(ArraySegment<T> src)
|
||||
{
|
||||
m_src = src;
|
||||
m_pinnedArray = GCHandle.Alloc(src.Array, GCHandleType.Pinned);
|
||||
}
|
||||
|
||||
public IntPtr Ptr
|
||||
{
|
||||
get
|
||||
{
|
||||
var ptr = m_pinnedArray.AddrOfPinnedObject();
|
||||
return new IntPtr(ptr.ToInt64() + m_src.Offset);
|
||||
}
|
||||
}
|
||||
|
||||
#region IDisposable Support
|
||||
private bool disposedValue = false; // 重複する呼び出しを検出するには
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// TODO: マネージ状態を破棄します (マネージ オブジェクト)。
|
||||
}
|
||||
|
||||
// TODO: アンマネージ リソース (アンマネージ オブジェクト) を解放し、下のファイナライザーをオーバーライドします。
|
||||
// TODO: 大きなフィールドを null に設定します。
|
||||
if (m_pinnedArray.IsAllocated)
|
||||
{
|
||||
m_pinnedArray.Free();
|
||||
}
|
||||
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: 上の Dispose(bool disposing) にアンマネージ リソースを解放するコードが含まれる場合にのみ、ファイナライザーをオーバーライドします。
|
||||
// ~Pin() {
|
||||
// // このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
|
||||
// Dispose(false);
|
||||
// }
|
||||
|
||||
// このコードは、破棄可能なパターンを正しく実装できるように追加されました。
|
||||
public void Dispose()
|
||||
{
|
||||
// このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
|
||||
Dispose(true);
|
||||
// TODO: 上のファイナライザーがオーバーライドされる場合は、次の行のコメントを解除してください。
|
||||
// GC.SuppressFinalize(this);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
public static class ArrayExtensions
|
||||
{
|
||||
public static int MarshalCoyTo<T>(this ArraySegment<byte> src, T[] dst) where T : struct
|
||||
{
|
||||
var size = dst.Length * Marshal.SizeOf(typeof(T));
|
||||
using (var pin = Pin.Create(dst))
|
||||
{
|
||||
Marshal.Copy(src.Array, src.Offset, pin.Ptr, size);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
public static Byte[] ToArray(this ArraySegment<byte> src)
|
||||
{
|
||||
var dst = new byte[src.Count];
|
||||
Array.Copy(src.Array, src.Offset, dst, 0, src.Count);
|
||||
return dst;
|
||||
}
|
||||
|
||||
public static T[] SelectInplace<T>(this T[] src, Func<T, T> pred)
|
||||
{
|
||||
for (int i = 0; i < src.Length; ++i)
|
||||
{
|
||||
src[i] = pred(src[i]);
|
||||
}
|
||||
return src;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ListExtensions
|
||||
{
|
||||
public static void Assign<T>(this List<T> dst, T[] src, Func<T, T> pred)
|
||||
{
|
||||
dst.Capacity = src.Length;
|
||||
dst.AddRange(src.Select(pred));
|
||||
}
|
||||
}
|
||||
}
|
||||
13
UniGLTF/Core/Scripts/Extensions/ArrayExtensions.cs.meta
Normal file
13
UniGLTF/Core/Scripts/Extensions/ArrayExtensions.cs.meta
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e843bb7270bc9f54f8495e1e35f82e8d
|
||||
timeCreated: 1514252357
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
19
UniGLTF/Core/Scripts/Extensions/JsonParserExtensions.cs
Normal file
19
UniGLTF/Core/Scripts/Extensions/JsonParserExtensions.cs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
namespace UniJSON
|
||||
{
|
||||
public static class JsonParserExtensions
|
||||
{
|
||||
public static List<T> DeserializeList<T>(this ListTreeNode<JsonValue> jsonList)
|
||||
{
|
||||
return jsonList.ArrayItems().Select(x => {
|
||||
|
||||
return JsonUtility.FromJson<T>(new Utf8String(x.Value.Bytes).ToString());
|
||||
|
||||
}).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
13
UniGLTF/Core/Scripts/Extensions/JsonParserExtensions.cs.meta
Normal file
13
UniGLTF/Core/Scripts/Extensions/JsonParserExtensions.cs.meta
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d0a724a200cc85b4887efa87292c6626
|
||||
timeCreated: 1515608626
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
73
UniGLTF/Core/Scripts/Extensions/StringExtensions.cs
Normal file
73
UniGLTF/Core/Scripts/Extensions/StringExtensions.cs
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public static class StringExtensions
|
||||
{
|
||||
public static string ToLowerCamelCase(this string lower)
|
||||
{
|
||||
return lower.Substring(0, 1).ToLower() + lower.Substring(1);
|
||||
}
|
||||
public static string ToUpperCamelCase(this string lower)
|
||||
{
|
||||
return lower.Substring(0, 1).ToUpper() + lower.Substring(1);
|
||||
}
|
||||
|
||||
static string m_unityBasePath;
|
||||
public static string UnityBasePath
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_unityBasePath == null)
|
||||
{
|
||||
m_unityBasePath = Path.GetFullPath(Application.dataPath + "/..").Replace("\\", "/");
|
||||
}
|
||||
return m_unityBasePath;
|
||||
}
|
||||
}
|
||||
|
||||
public static string AssetPathToFullPath(this string path)
|
||||
{
|
||||
return UnityBasePath + "/" + path;
|
||||
}
|
||||
|
||||
public static bool StartsWithUnityAssetPath(this string path)
|
||||
{
|
||||
return path.Replace("\\", "/").StartsWith(UnityBasePath + "/Assets");
|
||||
}
|
||||
|
||||
public static string ToUnityRelativePath(this string path)
|
||||
{
|
||||
path = path.Replace("\\", "/");
|
||||
if (path.StartsWith(UnityBasePath))
|
||||
{
|
||||
return path.Substring(UnityBasePath.Length + 1);
|
||||
}
|
||||
|
||||
//Debug.LogWarningFormat("{0} is starts with {1}", path, basePath);
|
||||
return path;
|
||||
}
|
||||
|
||||
static readonly char[] EscapeChars = new char[]
|
||||
{
|
||||
'\\',
|
||||
'/',
|
||||
':',
|
||||
'*',
|
||||
'?',
|
||||
'"',
|
||||
'<',
|
||||
'>',
|
||||
'|',
|
||||
};
|
||||
public static string EscapeFilePath(this string path)
|
||||
{
|
||||
foreach(var x in EscapeChars)
|
||||
{
|
||||
path = path.Replace(x, '+');
|
||||
}
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
13
UniGLTF/Core/Scripts/Extensions/StringExtensions.cs.meta
Normal file
13
UniGLTF/Core/Scripts/Extensions/StringExtensions.cs.meta
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 54b33d3e5f599eb419fae2bbf00f1613
|
||||
timeCreated: 1517471530
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
313
UniGLTF/Core/Scripts/Extensions/UnityExtensions.cs
Normal file
313
UniGLTF/Core/Scripts/Extensions/UnityExtensions.cs
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public struct PosRot
|
||||
{
|
||||
public Vector3 Position;
|
||||
public Quaternion Rotation;
|
||||
|
||||
public static PosRot FromGlobalTransform(Transform t)
|
||||
{
|
||||
return new PosRot
|
||||
{
|
||||
Position = t.position,
|
||||
Rotation = t.rotation,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class BlendShape
|
||||
{
|
||||
public string Name;
|
||||
|
||||
public BlendShape(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public List<Vector3> Positions = new List<Vector3>();
|
||||
public List<Vector3> Normals = new List<Vector3>();
|
||||
public List<Vector3> Tangents = new List<Vector3>();
|
||||
}
|
||||
|
||||
public static class UnityExtensions
|
||||
{
|
||||
public static Vector4 ReverseZ(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);
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public static Vector2 ReverseY(this Vector2 v)
|
||||
{
|
||||
return new Vector2(v.x, -v.y);
|
||||
}
|
||||
|
||||
public static Vector2 ReverseUV(this Vector2 v)
|
||||
{
|
||||
return new Vector2(v.x, 1.0f - v.y);
|
||||
}
|
||||
|
||||
public static Quaternion ReverseZ(this Quaternion q)
|
||||
{
|
||||
float angle;
|
||||
Vector3 axis;
|
||||
q.ToAngleAxis(out angle, out axis);
|
||||
return Quaternion.AngleAxis(-angle, ReverseZ(axis));
|
||||
}
|
||||
|
||||
public static Matrix4x4 Matrix4x4FromColumns(Vector4 c0, Vector4 c1, Vector4 c2, Vector4 c3)
|
||||
{
|
||||
#if UNITY_2017_1_OR_NEWER
|
||||
return new Matrix4x4(c0, c1, c2, c3);
|
||||
#else
|
||||
var m = default(Matrix4x4);
|
||||
m.SetColumn(0, c0);
|
||||
m.SetColumn(1, c1);
|
||||
m.SetColumn(2, c2);
|
||||
m.SetColumn(3, c3);
|
||||
return m;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static Matrix4x4 Matrix4x4FromRotation(Quaternion q)
|
||||
{
|
||||
#if UNITY_2017_1_OR_NEWER
|
||||
return Matrix4x4.Rotate(q);
|
||||
#else
|
||||
var m = default(Matrix4x4);
|
||||
m.SetTRS(Vector3.zero, q, Vector3.one);
|
||||
return m;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static Matrix4x4 ReverseZ(this Matrix4x4 m)
|
||||
{
|
||||
m.SetTRS(m.ExtractPosition().ReverseZ(), m.ExtractRotation().ReverseZ(), m.ExtractScale());
|
||||
return m;
|
||||
}
|
||||
|
||||
public static Matrix4x4 MatrixFromArray(float[] values)
|
||||
{
|
||||
var m = new Matrix4x4();
|
||||
m.m00 = values[0];
|
||||
m.m10 = values[1];
|
||||
m.m20 = values[2];
|
||||
m.m30 = values[3];
|
||||
m.m01 = values[4];
|
||||
m.m11 = values[5];
|
||||
m.m21 = values[6];
|
||||
m.m31 = values[7];
|
||||
m.m02 = values[8];
|
||||
m.m12 = values[9];
|
||||
m.m22 = values[10];
|
||||
m.m32 = values[11];
|
||||
m.m03 = values[12];
|
||||
m.m13 = values[13];
|
||||
m.m23 = values[14];
|
||||
m.m33 = values[15];
|
||||
return m;
|
||||
}
|
||||
|
||||
// https://forum.unity.com/threads/how-to-assign-matrix4x4-to-transform.121966/
|
||||
public static Quaternion ExtractRotation(this Matrix4x4 matrix)
|
||||
{
|
||||
Vector3 forward;
|
||||
forward.x = matrix.m02;
|
||||
forward.y = matrix.m12;
|
||||
forward.z = matrix.m22;
|
||||
|
||||
Vector3 upwards;
|
||||
upwards.x = matrix.m01;
|
||||
upwards.y = matrix.m11;
|
||||
upwards.z = matrix.m21;
|
||||
|
||||
return Quaternion.LookRotation(forward, upwards);
|
||||
}
|
||||
|
||||
public static Vector3 ExtractPosition(this Matrix4x4 matrix)
|
||||
{
|
||||
Vector3 position;
|
||||
position.x = matrix.m03;
|
||||
position.y = matrix.m13;
|
||||
position.z = matrix.m23;
|
||||
return position;
|
||||
}
|
||||
|
||||
public static Vector3 ExtractScale(this Matrix4x4 matrix)
|
||||
{
|
||||
Vector3 scale;
|
||||
scale.x = new Vector4(matrix.m00, matrix.m10, matrix.m20, matrix.m30).magnitude;
|
||||
scale.y = new Vector4(matrix.m01, matrix.m11, matrix.m21, matrix.m31).magnitude;
|
||||
scale.z = new Vector4(matrix.m02, matrix.m12, matrix.m22, matrix.m32).magnitude;
|
||||
return scale;
|
||||
}
|
||||
|
||||
public static string RelativePathFrom(this Transform self, Transform root)
|
||||
{
|
||||
var path = new List<String>();
|
||||
for (var current = self; current != null; current = current.parent)
|
||||
{
|
||||
if (current == root)
|
||||
{
|
||||
return String.Join("/", path.ToArray());
|
||||
}
|
||||
|
||||
path.Insert(0, current.name);
|
||||
}
|
||||
|
||||
throw new Exception("no RelativePath");
|
||||
}
|
||||
|
||||
public static Transform GetChildByName(this Transform self, string childName)
|
||||
{
|
||||
foreach (Transform child in self)
|
||||
{
|
||||
if (child.name == childName)
|
||||
{
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
throw new KeyNotFoundException();
|
||||
}
|
||||
|
||||
public static Transform GetFromPath(this Transform self, string path)
|
||||
{
|
||||
var current = self;
|
||||
|
||||
var splited = path.Split('/');
|
||||
|
||||
foreach (var childName in splited)
|
||||
{
|
||||
current = current.GetChildByName(childName);
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
public static IEnumerable<Transform> GetChildren(this Transform self)
|
||||
{
|
||||
foreach (Transform child in self)
|
||||
{
|
||||
yield return child;
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<Transform> Traverse(this Transform t)
|
||||
{
|
||||
yield return t;
|
||||
foreach (Transform x in t)
|
||||
{
|
||||
foreach (Transform y in x.Traverse())
|
||||
{
|
||||
yield return y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Transform FindDescenedant(this Transform t, string name)
|
||||
{
|
||||
return t.Traverse().First(x => x.name == name);
|
||||
}
|
||||
|
||||
public static IEnumerable<Transform> Ancestors(this Transform t)
|
||||
{
|
||||
yield return t;
|
||||
if (t.parent != null)
|
||||
{
|
||||
foreach (Transform x in t.parent.Ancestors())
|
||||
{
|
||||
yield return x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static float[] ToArray(this Quaternion q)
|
||||
{
|
||||
return new float[] { q.x, q.y, q.z, q.w };
|
||||
}
|
||||
|
||||
public static float[] ToArray(this Vector3 v)
|
||||
{
|
||||
return new float[] { v.x, v.y, v.z };
|
||||
}
|
||||
|
||||
public static float[] ToArray(this Vector4 v)
|
||||
{
|
||||
return new float[] { v.x, v.y, v.z, v.w };
|
||||
}
|
||||
|
||||
public static float[] ToArray(this Color c)
|
||||
{
|
||||
return new float[] { c.r, c.g, c.b, c.a };
|
||||
}
|
||||
|
||||
public static void ReverseZRecursive(this Transform root)
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
public static Mesh GetSharedMesh(this Transform t)
|
||||
{
|
||||
var meshFilter = t.GetComponent<MeshFilter>();
|
||||
if (meshFilter != null)
|
||||
{
|
||||
return meshFilter.sharedMesh;
|
||||
}
|
||||
|
||||
var skinnedMeshRenderer = t.GetComponent<SkinnedMeshRenderer>();
|
||||
if (skinnedMeshRenderer != null)
|
||||
{
|
||||
return skinnedMeshRenderer.sharedMesh;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Material[] GetSharedMaterials(this Transform t)
|
||||
{
|
||||
var renderer = t.GetComponent<Renderer>();
|
||||
if (renderer != null)
|
||||
{
|
||||
return renderer.sharedMaterials;
|
||||
}
|
||||
|
||||
return new Material[] { };
|
||||
}
|
||||
|
||||
public static bool Has<T>(this Transform transform, T t) where T : Component
|
||||
{
|
||||
return transform.GetComponent<T>() == t;
|
||||
}
|
||||
|
||||
public static T GetOrAddComponent<T>(this GameObject go) where T : Component
|
||||
{
|
||||
var c = go.GetComponent<T>();
|
||||
if (c != null)
|
||||
{
|
||||
return c;
|
||||
}
|
||||
return go.AddComponent<T>();
|
||||
}
|
||||
}
|
||||
}
|
||||
13
UniGLTF/Core/Scripts/Extensions/UnityExtensions.cs.meta
Normal file
13
UniGLTF/Core/Scripts/Extensions/UnityExtensions.cs.meta
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5294813527b3278458026afc820dd63d
|
||||
timeCreated: 1515606586
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
203
UniGLTF/Core/Scripts/Extensions/glTFExtensions.cs
Normal file
203
UniGLTF/Core/Scripts/Extensions/glTFExtensions.cs
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
[Serializable, StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
struct UShort4
|
||||
{
|
||||
public ushort x;
|
||||
public ushort y;
|
||||
public ushort z;
|
||||
public ushort w;
|
||||
|
||||
public UShort4(ushort _x, ushort _y, ushort _z, ushort _w)
|
||||
{
|
||||
x = _x;
|
||||
y = _y;
|
||||
z = _z;
|
||||
w = _w;
|
||||
}
|
||||
}
|
||||
|
||||
public static class glTFExtensions
|
||||
{
|
||||
struct ComponentVec
|
||||
{
|
||||
public glComponentType ComponentType;
|
||||
public int ElementCount;
|
||||
|
||||
public ComponentVec(glComponentType componentType, int elementCount)
|
||||
{
|
||||
ComponentType = componentType;
|
||||
ElementCount = elementCount;
|
||||
}
|
||||
}
|
||||
|
||||
static Dictionary<Type, ComponentVec> ComponentTypeMap = new Dictionary<Type, ComponentVec>
|
||||
{
|
||||
{ typeof(Vector2), new ComponentVec(glComponentType.FLOAT, 2) },
|
||||
{ typeof(Vector3), new ComponentVec(glComponentType.FLOAT, 3) },
|
||||
{ typeof(Vector4), new ComponentVec(glComponentType.FLOAT, 4) },
|
||||
{ typeof(UShort4), new ComponentVec(glComponentType.UNSIGNED_SHORT, 4) },
|
||||
{ typeof(Matrix4x4), new ComponentVec(glComponentType.FLOAT, 16) },
|
||||
{ typeof(Color), new ComponentVec(glComponentType.FLOAT, 4) },
|
||||
};
|
||||
|
||||
static glComponentType GetComponentType<T>()
|
||||
{
|
||||
var cv = default(ComponentVec);
|
||||
if (ComponentTypeMap.TryGetValue(typeof(T), out cv))
|
||||
{
|
||||
return cv.ComponentType;
|
||||
}
|
||||
else if (typeof(T) == typeof(uint))
|
||||
{
|
||||
return glComponentType.UNSIGNED_INT;
|
||||
}
|
||||
else if (typeof(T) == typeof(float))
|
||||
{
|
||||
return glComponentType.FLOAT;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException(typeof(T).Name);
|
||||
}
|
||||
}
|
||||
|
||||
static string GetAccessorType<T>()
|
||||
{
|
||||
var cv = default(ComponentVec);
|
||||
if (ComponentTypeMap.TryGetValue(typeof(T), out cv))
|
||||
{
|
||||
switch (cv.ElementCount)
|
||||
{
|
||||
case 2: return "VEC2";
|
||||
case 3: return "VEC3";
|
||||
case 4: return "VEC4";
|
||||
case 16: return "MAT4";
|
||||
default: throw new Exception();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return "SCALAR";
|
||||
}
|
||||
}
|
||||
|
||||
static int GetAccessorElementCount<T>()
|
||||
{
|
||||
var cv = default(ComponentVec);
|
||||
if (ComponentTypeMap.TryGetValue(typeof(T), out cv))
|
||||
{
|
||||
return cv.ElementCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
public static int ExtendBufferAndGetAccessorIndex<T>(this glTF gltf, int bufferIndex, T[] array,
|
||||
glBufferTarget target = glBufferTarget.NONE) where T : struct
|
||||
{
|
||||
return gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, new ArraySegment<T>(array), target);
|
||||
}
|
||||
|
||||
public static int ExtendBufferAndGetAccessorIndex<T>(this glTF gltf, int bufferIndex,
|
||||
ArraySegment<T> array,
|
||||
glBufferTarget target = glBufferTarget.NONE) where T : struct
|
||||
{
|
||||
if (array.Count == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
var viewIndex = ExtendBufferAndGetViewIndex(gltf, bufferIndex, array, target);
|
||||
|
||||
// index buffer's byteStride is unnecessary
|
||||
gltf.bufferViews[viewIndex].byteStride = 0;
|
||||
|
||||
var accessorIndex = gltf.accessors.Count;
|
||||
gltf.accessors.Add(new glTFAccessor
|
||||
{
|
||||
bufferView = viewIndex,
|
||||
byteOffset = 0,
|
||||
componentType = GetComponentType<T>(),
|
||||
type = GetAccessorType<T>(),
|
||||
count = array.Count,
|
||||
});
|
||||
return accessorIndex;
|
||||
}
|
||||
|
||||
public static int ExtendBufferAndGetViewIndex<T>(this glTF gltf, int bufferIndex,
|
||||
T[] array,
|
||||
glBufferTarget target = glBufferTarget.NONE) where T : struct
|
||||
{
|
||||
return ExtendBufferAndGetViewIndex(gltf, bufferIndex, new ArraySegment<T>(array), target);
|
||||
}
|
||||
|
||||
public static int ExtendBufferAndGetViewIndex<T>(this glTF gltf, int bufferIndex,
|
||||
ArraySegment<T> array,
|
||||
glBufferTarget target = glBufferTarget.NONE) where T : struct
|
||||
{
|
||||
if (array.Count == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
var view = gltf.buffers[bufferIndex].Append(array, target);
|
||||
var viewIndex = gltf.bufferViews.Count;
|
||||
gltf.bufferViews.Add(view);
|
||||
return viewIndex;
|
||||
}
|
||||
|
||||
public static int ExtendSparseBufferAndGetAccessorIndex<T>(this glTF gltf, int bufferIndex,
|
||||
int accessorCount,
|
||||
T[] sparseValues, int[] sparseIndices, int sparseViewIndex,
|
||||
glBufferTarget target = glBufferTarget.NONE) where T : struct
|
||||
{
|
||||
return ExtendSparseBufferAndGetAccessorIndex(gltf, bufferIndex,
|
||||
accessorCount,
|
||||
new ArraySegment<T>(sparseValues), sparseIndices, sparseViewIndex,
|
||||
target);
|
||||
}
|
||||
|
||||
public static int ExtendSparseBufferAndGetAccessorIndex<T>(this glTF gltf, int bufferIndex,
|
||||
int accessorCount,
|
||||
ArraySegment<T> sparseValues, int[] sparseIndices, int sparseIndicesViewIndex,
|
||||
glBufferTarget target = glBufferTarget.NONE) where T : struct
|
||||
{
|
||||
if (sparseValues.Count == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
var sparseValuesViewIndex = ExtendBufferAndGetViewIndex(gltf, bufferIndex, sparseValues, target);
|
||||
var accessorIndex = gltf.accessors.Count;
|
||||
gltf.accessors.Add(new glTFAccessor
|
||||
{
|
||||
byteOffset = 0,
|
||||
componentType = GetComponentType<T>(),
|
||||
type = GetAccessorType<T>(),
|
||||
count = accessorCount,
|
||||
|
||||
sparse = new glTFSparse
|
||||
{
|
||||
count=sparseIndices.Length,
|
||||
indices = new glTFSparseIndices
|
||||
{
|
||||
bufferView = sparseIndicesViewIndex,
|
||||
componentType = glComponentType.UNSIGNED_INT
|
||||
},
|
||||
values = new glTFSparseValues
|
||||
{
|
||||
bufferView = sparseValuesViewIndex,
|
||||
}
|
||||
}
|
||||
});
|
||||
return accessorIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
13
UniGLTF/Core/Scripts/Extensions/glTFExtensions.cs.meta
Normal file
13
UniGLTF/Core/Scripts/Extensions/glTFExtensions.cs.meta
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 870cb47398900534994d90eb1cecace8
|
||||
timeCreated: 1517038417
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
10
UniGLTF/Core/Scripts/Format.meta
Normal file
10
UniGLTF/Core/Scripts/Format.meta
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 91b71b4caaefeae4fa5ca31908b35a86
|
||||
folderAsset: yes
|
||||
timeCreated: 1516616861
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
187
UniGLTF/Core/Scripts/Format/BytesBuffer.cs
Normal file
187
UniGLTF/Core/Scripts/Format/BytesBuffer.cs
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public interface IBytesBuffer
|
||||
{
|
||||
string Uri { get; }
|
||||
ArraySegment<Byte> GetBytes();
|
||||
glTFBufferView Extend<T>(ArraySegment<T> array, glBufferTarget target) where T : struct;
|
||||
}
|
||||
|
||||
public static class IBytesBufferExtensions
|
||||
{
|
||||
public static glTFBufferView Extend<T>(this IBytesBuffer buffer, T[] array, glBufferTarget target) where T : struct
|
||||
{
|
||||
return buffer.Extend(new ArraySegment<T>(array), target);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// for buffer with uri read
|
||||
/// </summary>
|
||||
public class UriByteBuffer : IBytesBuffer
|
||||
{
|
||||
public string Uri
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
Byte[] m_bytes;
|
||||
public ArraySegment<byte> GetBytes()
|
||||
{
|
||||
return new ArraySegment<byte>(m_bytes);
|
||||
}
|
||||
|
||||
public UriByteBuffer(string baseDir, string uri)
|
||||
{
|
||||
Uri = uri;
|
||||
m_bytes = ReadFromUri(baseDir, uri);
|
||||
}
|
||||
|
||||
const string DataPrefix = "data:application/octet-stream;base64,";
|
||||
|
||||
const string DataPrefix2 = "data:application/gltf-buffer;base64,";
|
||||
|
||||
const string DataPrefix3 = "data:image/jpeg;base64,";
|
||||
|
||||
public static Byte[] ReadEmbeded(string uri)
|
||||
{
|
||||
var pos = uri.IndexOf(";base64,");
|
||||
if (pos < 0)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
else
|
||||
{
|
||||
return Convert.FromBase64String(uri.Substring(pos + 8));
|
||||
}
|
||||
}
|
||||
|
||||
Byte[] ReadFromUri(string baseDir, string uri)
|
||||
{
|
||||
var bytes = ReadEmbeded(uri);
|
||||
if (bytes != null)
|
||||
{
|
||||
return bytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
// as local file path
|
||||
return File.ReadAllBytes(Path.Combine(baseDir, uri));
|
||||
}
|
||||
}
|
||||
|
||||
public glTFBufferView Extend<T>(ArraySegment<T> array, glBufferTarget target) where T : struct
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// for glb chunk buffer read
|
||||
/// </summary>
|
||||
public class ArraySegmentByteBuffer : IBytesBuffer
|
||||
{
|
||||
ArraySegment<Byte> m_bytes;
|
||||
|
||||
public ArraySegmentByteBuffer(ArraySegment<Byte> bytes)
|
||||
{
|
||||
m_bytes = bytes;
|
||||
}
|
||||
|
||||
public string Uri
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public glTFBufferView Extend<T>(ArraySegment<T> array, glBufferTarget target) where T : struct
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ArraySegment<byte> GetBytes()
|
||||
{
|
||||
return m_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// for exporter
|
||||
/// </summary>
|
||||
public class ArrayByteBuffer : IBytesBuffer
|
||||
{
|
||||
public string Uri
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
Byte[] m_bytes;
|
||||
int m_used;
|
||||
|
||||
public ArrayByteBuffer(Byte[] bytes = null)
|
||||
{
|
||||
Uri = "";
|
||||
m_bytes = bytes;
|
||||
}
|
||||
|
||||
public glTFBufferView Extend<T>(ArraySegment<T> array, glBufferTarget target) where T : struct
|
||||
{
|
||||
using (var pin = Pin.Create(array))
|
||||
{
|
||||
var elementSize = Marshal.SizeOf(typeof(T));
|
||||
var view = Extend(pin.Ptr, array.Count * elementSize, elementSize, target);
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
public glTFBufferView Extend(IntPtr p, int bytesLength, int stride, glBufferTarget target)
|
||||
{
|
||||
var tmp = m_bytes;
|
||||
// alignment
|
||||
var padding = m_used % stride == 0 ? 0 : stride - m_used % stride;
|
||||
|
||||
if (m_bytes == null || m_used + padding + bytesLength > m_bytes.Length)
|
||||
{
|
||||
// recreate buffer
|
||||
m_bytes = new Byte[m_used + padding + bytesLength];
|
||||
if (m_used > 0)
|
||||
{
|
||||
Buffer.BlockCopy(tmp, 0, m_bytes, 0, m_used);
|
||||
}
|
||||
}
|
||||
if (m_used + padding + bytesLength > m_bytes.Length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
Marshal.Copy(p, m_bytes, m_used + padding, bytesLength);
|
||||
var result=new glTFBufferView
|
||||
{
|
||||
buffer = 0,
|
||||
byteLength = bytesLength,
|
||||
byteOffset = m_used + padding,
|
||||
byteStride = stride,
|
||||
target = target,
|
||||
};
|
||||
m_used = m_used + padding + bytesLength;
|
||||
return result;
|
||||
}
|
||||
|
||||
public ArraySegment<byte> GetBytes()
|
||||
{
|
||||
if (m_bytes == null)
|
||||
{
|
||||
return new ArraySegment<byte>();
|
||||
}
|
||||
|
||||
return new ArraySegment<byte>(m_bytes, 0, m_used);
|
||||
}
|
||||
}
|
||||
}
|
||||
13
UniGLTF/Core/Scripts/Format/BytesBuffer.cs.meta
Normal file
13
UniGLTF/Core/Scripts/Format/BytesBuffer.cs.meta
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 33b0000c5446b7547bcad1da1e9768ed
|
||||
timeCreated: 1516730619
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
9
UniGLTF/Core/Scripts/Format/ExtensionsAndExtras.meta
Normal file
9
UniGLTF/Core/Scripts/Format/ExtensionsAndExtras.meta
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ba47ab6d00c723348b9ac86141cb7bfd
|
||||
folderAsset: yes
|
||||
timeCreated: 1533605176
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
using System;
|
||||
using UniJSON;
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
[Serializable]
|
||||
public class glTF_KHR_draco_mesh_compression : JsonSerializableBase
|
||||
{
|
||||
[JsonSchema(Required = true, Minimum = 0)]
|
||||
public int bufferView = -1;
|
||||
public glTFAttributes attributes;
|
||||
|
||||
protected override void SerializeMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
//throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public partial class glTFPrimitives_extensions : ExtensionsBase<glTFPrimitives_extensions>
|
||||
{
|
||||
[JsonSchema(Required = true)]
|
||||
public glTF_KHR_draco_mesh_compression KHR_draco_mesh_compression;
|
||||
|
||||
[JsonSerializeMembers]
|
||||
void SerializeMembers_draco(GLTFJsonFormatter f)
|
||||
{
|
||||
//throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2d21c7591557c7744a6fa34a7acace91
|
||||
timeCreated: 1533605595
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
using System;
|
||||
using UniJSON;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
[Serializable]
|
||||
public class glTF_KHR_materials_unlit : JsonSerializableBase
|
||||
{
|
||||
public static string ExtensionName
|
||||
{
|
||||
get
|
||||
{
|
||||
return "KHR_materials_unlit";
|
||||
}
|
||||
}
|
||||
|
||||
protected override void SerializeMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
//throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public static glTFMaterial CreateDefault()
|
||||
{
|
||||
return new glTFMaterial
|
||||
{
|
||||
pbrMetallicRoughness = new glTFPbrMetallicRoughness
|
||||
{
|
||||
baseColorFactor = new float[] { 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
roughnessFactor = 0.9f,
|
||||
metallicFactor = 0.0f,
|
||||
},
|
||||
extensions = new glTFMaterial_extensions
|
||||
{
|
||||
KHR_materials_unlit = new glTF_KHR_materials_unlit(),
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public partial class glTFMaterial_extensions : ExtensionsBase<glTFMaterial_extensions>
|
||||
{
|
||||
[JsonSchema(Required = true)]
|
||||
public glTF_KHR_materials_unlit KHR_materials_unlit;
|
||||
|
||||
[JsonSerializeMembers]
|
||||
void SerializeMembers_unlit(GLTFJsonFormatter f)
|
||||
{
|
||||
if (KHR_materials_unlit != null)
|
||||
{
|
||||
f.KeyValue(() => KHR_materials_unlit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2c95b5ebe597e924fa2e7fcce3a65f76
|
||||
timeCreated: 1533606191
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
using UniJSON;
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
[Serializable]
|
||||
[ItemJsonSchema(ValueType = ValueNodeType.Object)]
|
||||
public partial class glTFOrthographic_extensions : ExtensionsBase<glTFOrthographic_extensions> { }
|
||||
|
||||
[Serializable]
|
||||
public partial class glTFOrthographic_extras : ExtraBase<glTFOrthographic_extras> { }
|
||||
|
||||
[Serializable]
|
||||
[ItemJsonSchema(ValueType = ValueNodeType.Object)]
|
||||
public partial class glTFPerspective_extensions : ExtensionsBase<glTFPerspective_extensions> { }
|
||||
|
||||
[Serializable]
|
||||
public partial class glTFPerspective_extras : ExtraBase<glTFPerspective_extras> { }
|
||||
|
||||
[Serializable]
|
||||
[ItemJsonSchema(ValueType = ValueNodeType.Object)]
|
||||
public partial class glTFCamera_extensions : ExtensionsBase<glTFCamera_extensions> { }
|
||||
|
||||
[Serializable]
|
||||
public partial class glTFCamera_extras : ExtraBase<glTFCamera_extras> { }
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 38f59c64e05855b41be64595c570e366
|
||||
timeCreated: 1533605499
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UniJSON;
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
#region Base
|
||||
public class JsonSerializeMembersAttribute : Attribute { }
|
||||
|
||||
public class PartialExtensionBase<T> : JsonSerializableBase
|
||||
{
|
||||
protected override void SerializeMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
foreach (var method in this.GetType().GetMethods(BindingFlags.Instance |
|
||||
BindingFlags.Public | BindingFlags.NonPublic))
|
||||
{
|
||||
if (method.GetCustomAttributes(typeof(JsonSerializeMembersAttribute), true).Any())
|
||||
{
|
||||
method.Invoke(this, new[] { f });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int __count
|
||||
{
|
||||
get
|
||||
{
|
||||
return typeof(T).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
|
||||
.Where(x => x.GetCustomAttributes(typeof(JsonSerializeMembersAttribute), true).Any())
|
||||
.Count();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[ItemJsonSchema(ValueType = ValueNodeType.Object)]
|
||||
[JsonSchema(MinProperties = 1)]
|
||||
public partial class ExtensionsBase<T> : PartialExtensionBase<T>
|
||||
{
|
||||
}
|
||||
|
||||
[JsonSchema(MinProperties = 1)]
|
||||
public partial class ExtraBase<T> : PartialExtensionBase<T>
|
||||
{
|
||||
}
|
||||
#endregion
|
||||
|
||||
[Serializable]
|
||||
public partial class glTF_extensions : ExtensionsBase<glTF_extensions> { }
|
||||
|
||||
[Serializable]
|
||||
public partial class gltf_extras : ExtraBase<gltf_extras> { }
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 356f989283db8c848b851b82c34086d1
|
||||
timeCreated: 1532101527
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UniJSON;
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
/// <summary>
|
||||
/// https://github.com/KhronosGroup/glTF/issues/1036
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public partial class glTFPrimitives_extras : ExtraBase<glTFPrimitives_extras>
|
||||
{
|
||||
[JsonSchema(Required = true, MinItems = 1)]
|
||||
public List<string> targetNames = new List<string>();
|
||||
|
||||
[JsonSerializeMembers]
|
||||
void PrimitiveMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
if (targetNames.Count > 0)
|
||||
{
|
||||
f.Key("targetNames");
|
||||
f.BeginList();
|
||||
foreach (var x in targetNames)
|
||||
{
|
||||
f.Value(x);
|
||||
}
|
||||
f.EndList();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e95762d0b18d11243b32a56f21cef862
|
||||
timeCreated: 1533605545
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
11
UniGLTF/Core/Scripts/Format/ExtensionsAndExtras/glTFNode.cs
Normal file
11
UniGLTF/Core/Scripts/Format/ExtensionsAndExtras/glTFNode.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
[Serializable]
|
||||
public partial class glTFNode_extensions : ExtensionsBase<glTFNode_extensions> { }
|
||||
|
||||
[Serializable]
|
||||
public partial class glTFNode_extra : ExtraBase<glTFNode_extra> { }
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: eef0405c0c9670947ab6fa2034f0343f
|
||||
timeCreated: 1533605847
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
35
UniGLTF/Core/Scripts/Format/GLTFJsonFormatter.cs
Normal file
35
UniGLTF/Core/Scripts/Format/GLTFJsonFormatter.cs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
using System.Collections.Generic;
|
||||
using UniJSON;
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public class GLTFJsonFormatter: UniJSON.JsonFormatter
|
||||
{
|
||||
public void GLTFValue(JsonSerializableBase s)
|
||||
{
|
||||
CommaCheck();
|
||||
Store.Write(s.ToJson());
|
||||
}
|
||||
|
||||
public void GLTFValue<T>(IEnumerable<T> values) where T : JsonSerializableBase
|
||||
{
|
||||
BeginList();
|
||||
foreach (var value in values)
|
||||
{
|
||||
GLTFValue(value);
|
||||
}
|
||||
EndList();
|
||||
}
|
||||
|
||||
public void GLTFValue(List<string> values)
|
||||
{
|
||||
BeginList();
|
||||
foreach (var value in values)
|
||||
{
|
||||
this.Value(value);
|
||||
}
|
||||
EndList();
|
||||
}
|
||||
}
|
||||
}
|
||||
12
UniGLTF/Core/Scripts/Format/GLTFJsonFormatter.cs.meta
Normal file
12
UniGLTF/Core/Scripts/Format/GLTFJsonFormatter.cs.meta
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b80a7ee38e762de44a90d60d9d4ff4dd
|
||||
timeCreated: 1531806259
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
22
UniGLTF/Core/Scripts/Format/JsonSerializableBase.cs
Normal file
22
UniGLTF/Core/Scripts/Format/JsonSerializableBase.cs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
[Serializable]
|
||||
public abstract class JsonSerializableBase
|
||||
{
|
||||
protected abstract void SerializeMembers(GLTFJsonFormatter f);
|
||||
|
||||
public string ToJson()
|
||||
{
|
||||
var f = new GLTFJsonFormatter();
|
||||
f.BeginMap();
|
||||
|
||||
SerializeMembers(f);
|
||||
|
||||
f.EndMap();
|
||||
return f.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
12
UniGLTF/Core/Scripts/Format/JsonSerializableBase.cs.meta
Normal file
12
UniGLTF/Core/Scripts/Format/JsonSerializableBase.cs.meta
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d169186adbfc59b4b882611d37f0a282
|
||||
timeCreated: 1532078625
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
68
UniGLTF/Core/Scripts/Format/MonoBehaviourComparator.cs
Normal file
68
UniGLTF/Core/Scripts/Format/MonoBehaviourComparator.cs
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public static class MonoBehaviourComparator
|
||||
{
|
||||
public static bool AssertAreEquals(GameObject l, GameObject r)
|
||||
{
|
||||
return
|
||||
l.name == r.name
|
||||
&& AssertAreEquals<Transform>(l, r,
|
||||
(x, y) => AssertAreEquals(x[0], y[0]))
|
||||
&& AssertAreEquals<MeshFilter>(l, r,
|
||||
(x, y) => AssertAreEquals(x[0], y[0]))
|
||||
&& AssertAreEquals<MeshRenderer>(l, r,
|
||||
(x, y) => AssertAreEquals(x[0], y[0]))
|
||||
&& AssertAreEquals<SkinnedMeshRenderer>(l, r,
|
||||
(x, y) => AssertAreEquals(x[0], y[0]))
|
||||
;
|
||||
}
|
||||
|
||||
public static bool AssertAreEquals<T>(GameObject l, GameObject r, Func<T[], T[], bool> pred) where T : Component
|
||||
{
|
||||
var ll = l.GetComponents<T>();
|
||||
var rr = r.GetComponents<T>();
|
||||
if (ll.Length != rr.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (ll.Length == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return pred(ll, rr);
|
||||
}
|
||||
|
||||
public static bool AssertAreEquals(Transform l, Transform r)
|
||||
{
|
||||
return
|
||||
(l.localPosition == r.localPosition)
|
||||
&& (l.localRotation == r.localRotation)
|
||||
&& (l.localScale == r.localScale)
|
||||
;
|
||||
}
|
||||
|
||||
public static bool AssertAreEquals(MeshFilter l, MeshFilter r)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static bool AssertAreEquals(MeshRenderer l, MeshRenderer r)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static bool AssertAreEquals(SkinnedMeshRenderer l, SkinnedMeshRenderer r)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static bool AssetAreEquals(Texture2D l, Texture2D r)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
12
UniGLTF/Core/Scripts/Format/MonoBehaviourComparator.cs.meta
Normal file
12
UniGLTF/Core/Scripts/Format/MonoBehaviourComparator.cs.meta
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7cee53236ca4f234ab42ba8e92afd178
|
||||
timeCreated: 1521104277
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
48
UniGLTF/Core/Scripts/Format/glEnum.cs
Normal file
48
UniGLTF/Core/Scripts/Format/glEnum.cs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/// <summary>
|
||||
/// https://gist.github.com/szimek/763999
|
||||
/// </summary>
|
||||
namespace UniGLTF
|
||||
{
|
||||
public enum glComponentType : int
|
||||
{
|
||||
BYTE = 5120, // signed ?
|
||||
UNSIGNED_BYTE = 5121,
|
||||
|
||||
SHORT = 5122,
|
||||
UNSIGNED_SHORT = 5123,
|
||||
|
||||
//INT = 5124,
|
||||
UNSIGNED_INT = 5125,
|
||||
|
||||
FLOAT = 5126,
|
||||
}
|
||||
|
||||
public enum glBufferTarget : int
|
||||
{
|
||||
NONE = 0,
|
||||
ARRAY_BUFFER = 34962,
|
||||
ELEMENT_ARRAY_BUFFER = 34963,
|
||||
}
|
||||
|
||||
public enum glFilter : int
|
||||
{
|
||||
NONE = 0,
|
||||
NEAREST = 9728,
|
||||
LINEAR = 9729,
|
||||
|
||||
#region for minFilter only
|
||||
NEAREST_MIPMAP_NEAREST = 9984,
|
||||
LINEAR_MIPMAP_NEAREST = 9985,
|
||||
NEAREST_MIPMAP_LINEAR = 9986,
|
||||
LINEAR_MIPMAP_LINEAR = 9987,
|
||||
#endregion
|
||||
}
|
||||
|
||||
public enum glWrap : int
|
||||
{
|
||||
NONE = 0,
|
||||
CLAMP_TO_EDGE = 33071,
|
||||
REPEAT = 10497,
|
||||
MIRRORED_REPEAT = 33648,
|
||||
}
|
||||
}
|
||||
13
UniGLTF/Core/Scripts/Format/glEnum.cs.meta
Normal file
13
UniGLTF/Core/Scripts/Format/glEnum.cs.meta
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 378358a997cd2e9418f180593fb04a6f
|
||||
timeCreated: 1516212452
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
518
UniGLTF/Core/Scripts/Format/glTF.cs
Normal file
518
UniGLTF/Core/Scripts/Format/glTF.cs
Normal file
|
|
@ -0,0 +1,518 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UniJSON;
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
[Serializable]
|
||||
public class gltfScene : JsonSerializableBase
|
||||
{
|
||||
[JsonSchema(MinItems = 1)]
|
||||
[ItemJsonSchema(Minimum = 0)]
|
||||
public int[] nodes;
|
||||
|
||||
public object extensions;
|
||||
public object extras;
|
||||
public string name;
|
||||
|
||||
protected override void SerializeMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
f.KeyValue(() => nodes);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class glTF : JsonSerializableBase, IEquatable<glTF>
|
||||
{
|
||||
[JsonSchema(Required = true)]
|
||||
public glTFAssets asset = new glTFAssets();
|
||||
|
||||
#region Buffer
|
||||
[JsonSchema(MinItems = 1)]
|
||||
public List<glTFBuffer> buffers = new List<glTFBuffer>();
|
||||
public int AddBuffer(IBytesBuffer bytesBuffer)
|
||||
{
|
||||
var index = buffers.Count;
|
||||
buffers.Add(new glTFBuffer(bytesBuffer));
|
||||
return index;
|
||||
}
|
||||
|
||||
[JsonSchema(MinItems = 1)]
|
||||
public List<glTFBufferView> bufferViews = new List<glTFBufferView>();
|
||||
public int AddBufferView(glTFBufferView view)
|
||||
{
|
||||
var index = bufferViews.Count;
|
||||
bufferViews.Add(view);
|
||||
return index;
|
||||
}
|
||||
|
||||
[JsonSchema(MinItems = 1)]
|
||||
public List<glTFAccessor> accessors = new List<glTFAccessor>();
|
||||
|
||||
T[] GetAttrib<T>(glTFAccessor accessor, glTFBufferView view) where T : struct
|
||||
{
|
||||
return GetAttrib<T>(accessor.count, accessor.byteOffset, view);
|
||||
}
|
||||
T[] GetAttrib<T>(int count, int byteOffset, glTFBufferView view) where T : struct
|
||||
{
|
||||
var attrib = new T[count];
|
||||
//
|
||||
var segment = buffers[view.buffer].GetBytes();
|
||||
var bytes = new ArraySegment<Byte>(segment.Array, segment.Offset + view.byteOffset + byteOffset, count * view.byteStride);
|
||||
bytes.MarshalCoyTo(attrib);
|
||||
return attrib;
|
||||
}
|
||||
|
||||
public ArraySegment<Byte> GetViewBytes(int bufferView)
|
||||
{
|
||||
var view = bufferViews[bufferView];
|
||||
var segment = buffers[view.buffer].GetBytes();
|
||||
return new ArraySegment<byte>(segment.Array, segment.Offset + view.byteOffset, view.byteLength);
|
||||
}
|
||||
|
||||
IEnumerable<int> _GetIndices(glTFAccessor accessor, out int count)
|
||||
{
|
||||
count = accessor.count;
|
||||
var view = bufferViews[accessor.bufferView];
|
||||
switch ((glComponentType)accessor.componentType)
|
||||
{
|
||||
case glComponentType.UNSIGNED_BYTE:
|
||||
{
|
||||
return GetAttrib<Byte>(accessor, view).Select(x => (int)(x));
|
||||
}
|
||||
|
||||
case glComponentType.UNSIGNED_SHORT:
|
||||
{
|
||||
return GetAttrib<UInt16>(accessor, view).Select(x => (int)(x));
|
||||
}
|
||||
|
||||
case glComponentType.UNSIGNED_INT:
|
||||
{
|
||||
return GetAttrib<UInt32>(accessor, view).Select(x => (int)(x));
|
||||
}
|
||||
}
|
||||
throw new NotImplementedException("GetIndices: unknown componenttype: " + accessor.componentType);
|
||||
}
|
||||
|
||||
IEnumerable<int> _GetIndices(glTFBufferView view, int count, int byteOffset, glComponentType componentType)
|
||||
{
|
||||
switch (componentType)
|
||||
{
|
||||
case glComponentType.UNSIGNED_BYTE:
|
||||
{
|
||||
return GetAttrib<Byte>(count, byteOffset, view).Select(x => (int)(x));
|
||||
}
|
||||
|
||||
case glComponentType.UNSIGNED_SHORT:
|
||||
{
|
||||
return GetAttrib<UInt16>(count, byteOffset, view).Select(x => (int)(x));
|
||||
}
|
||||
|
||||
case glComponentType.UNSIGNED_INT:
|
||||
{
|
||||
return GetAttrib<UInt32>(count, byteOffset, view).Select(x => (int)(x));
|
||||
}
|
||||
}
|
||||
throw new NotImplementedException("GetIndices: unknown componenttype: " + componentType);
|
||||
}
|
||||
|
||||
public int[] GetIndices(int accessorIndex)
|
||||
{
|
||||
int count;
|
||||
var result = _GetIndices(accessors[accessorIndex], out count);
|
||||
var indices = new int[count];
|
||||
|
||||
// flip triangles
|
||||
var it = result.GetEnumerator();
|
||||
{
|
||||
for (int i = 0; i < count; i += 3)
|
||||
{
|
||||
it.MoveNext(); indices[i + 2] = it.Current;
|
||||
it.MoveNext(); indices[i + 1] = it.Current;
|
||||
it.MoveNext(); indices[i] = it.Current;
|
||||
}
|
||||
}
|
||||
|
||||
return indices;
|
||||
}
|
||||
|
||||
public T[] GetArrayFromAccessor<T>(int accessorIndex) where T : struct
|
||||
{
|
||||
var vertexAccessor = accessors[accessorIndex];
|
||||
|
||||
if (vertexAccessor.count <= 0) return new T[] { };
|
||||
|
||||
var result = (vertexAccessor.bufferView != -1)
|
||||
? GetAttrib<T>(vertexAccessor, bufferViews[vertexAccessor.bufferView])
|
||||
: new T[vertexAccessor.count]
|
||||
;
|
||||
|
||||
var sparse = vertexAccessor.sparse;
|
||||
if (sparse != null && sparse.count > 0)
|
||||
{
|
||||
// override sparse values
|
||||
var indices = _GetIndices(bufferViews[sparse.indices.bufferView], sparse.count, sparse.indices.byteOffset, sparse.indices.componentType);
|
||||
var values = GetAttrib<T>(sparse.count, sparse.values.byteOffset, bufferViews[sparse.values.bufferView]);
|
||||
|
||||
var it = indices.GetEnumerator();
|
||||
for (int i = 0; i < sparse.count; ++i)
|
||||
{
|
||||
it.MoveNext();
|
||||
result[it.Current] = values[i];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public float[] GetArrayFromAccessorAsFloat(int accessorIndex)
|
||||
{
|
||||
var vertexAccessor = accessors[accessorIndex];
|
||||
|
||||
if (vertexAccessor.count <= 0) return new float[] { };
|
||||
|
||||
var bufferCount = vertexAccessor.count * vertexAccessor.TypeCount;
|
||||
var result = (vertexAccessor.bufferView != -1)
|
||||
? GetAttrib<float>(bufferCount, vertexAccessor.byteOffset, bufferViews[vertexAccessor.bufferView])
|
||||
: new float[bufferCount]
|
||||
;
|
||||
|
||||
var sparse = vertexAccessor.sparse;
|
||||
if (sparse != null && sparse.count > 0)
|
||||
{
|
||||
// override sparse values
|
||||
var indices = _GetIndices(bufferViews[sparse.indices.bufferView], sparse.count, sparse.indices.byteOffset, sparse.indices.componentType);
|
||||
var values = GetAttrib<float>(sparse.count * vertexAccessor.TypeCount, sparse.values.byteOffset, bufferViews[sparse.values.bufferView]);
|
||||
|
||||
var it = indices.GetEnumerator();
|
||||
for (int i = 0; i < sparse.count; ++i)
|
||||
{
|
||||
it.MoveNext();
|
||||
result[it.Current] = values[i];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
[JsonSchema(MinItems = 1)]
|
||||
public List<glTFTexture> textures = new List<glTFTexture>();
|
||||
|
||||
[JsonSchema(MinItems = 1)]
|
||||
public List<glTFTextureSampler> samplers = new List<glTFTextureSampler>();
|
||||
public glTFTextureSampler GetSampler(int index)
|
||||
{
|
||||
if (samplers.Count == 0)
|
||||
{
|
||||
samplers.Add(new glTFTextureSampler()); // default sampler
|
||||
}
|
||||
|
||||
return samplers[index];
|
||||
}
|
||||
|
||||
[JsonSchema(MinItems = 1)]
|
||||
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;
|
||||
return GetSampler(samplerIndex);
|
||||
}
|
||||
|
||||
public ArraySegment<Byte> GetImageBytes(IStorage storage, int imageIndex, out string textureName)
|
||||
{
|
||||
var image = 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 GetViewBytes(image.bufferView);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (image.uri.StartsWith("data:"))
|
||||
{
|
||||
textureName = !string.IsNullOrEmpty(image.name) ? image.name : string.Format("{0:00}#Base64Embeded", imageIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
textureName = !string.IsNullOrEmpty(image.name) ? image.name : Path.GetFileNameWithoutExtension(image.uri);
|
||||
}
|
||||
return storage.Get(image.uri);
|
||||
}
|
||||
}
|
||||
|
||||
[JsonSchema(MinItems = 1)]
|
||||
public List<glTFMaterial> materials = new List<glTFMaterial>();
|
||||
public string GetUniqueMaterialName(int index)
|
||||
{
|
||||
if (materials.Any(x => string.IsNullOrEmpty(x.name))
|
||||
|| materials.Select(x => x.name).Distinct().Count() != materials.Count)
|
||||
{
|
||||
return String.Format("{0:00}_{1}", index, materials[index].name);
|
||||
}
|
||||
else
|
||||
{
|
||||
return materials[index].name;
|
||||
}
|
||||
}
|
||||
|
||||
public bool MaterialHasVertexColor(glTFMaterial material)
|
||||
{
|
||||
if (material == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var materialIndex = materials.IndexOf(material);
|
||||
if (materialIndex == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return MaterialHasVertexColor(materialIndex);
|
||||
}
|
||||
|
||||
[JsonSchema(MinItems = 1)]
|
||||
public List<glTFMesh> meshes = new List<glTFMesh>();
|
||||
|
||||
public bool MaterialHasVertexColor(int materialIndex)
|
||||
{
|
||||
if (materialIndex < 0 || materialIndex >= materials.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var hasVertexColor = meshes.SelectMany(x => x.primitives).Any(x => x.material == materialIndex && x.HasVertexColor);
|
||||
return hasVertexColor;
|
||||
}
|
||||
|
||||
[JsonSchema(MinItems = 1)]
|
||||
public List<glTFNode> nodes = new List<glTFNode>();
|
||||
|
||||
[JsonSchema(MinItems = 1)]
|
||||
public List<glTFSkin> skins = new List<glTFSkin>();
|
||||
|
||||
[JsonSchema(Dependencies = new string[] { "scenes" }, Minimum = 0)]
|
||||
public int scene;
|
||||
|
||||
[JsonSchema(MinItems = 1)]
|
||||
public List<gltfScene> scenes = new List<gltfScene>();
|
||||
public int[] rootnodes
|
||||
{
|
||||
get
|
||||
{
|
||||
return scenes[scene].nodes;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonSchema(MinItems = 1)]
|
||||
public List<glTFAnimation> animations = new List<glTFAnimation>();
|
||||
|
||||
[JsonSchema(MinItems = 1)]
|
||||
public List<glTFCamera> cameras = new List<glTFCamera>();
|
||||
|
||||
[JsonSchema(MinItems = 1)]
|
||||
public List<string> extensionsUsed = new List<string>();
|
||||
|
||||
[JsonSchema(MinItems = 1)]
|
||||
public List<string> extensionsRequired = new List<string>();
|
||||
|
||||
public glTF_extensions extensions = new glTF_extensions();
|
||||
public gltf_extras extras = new gltf_extras();
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0}", asset);
|
||||
}
|
||||
|
||||
protected override void SerializeMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
if (extensionsUsed.Count > 0)
|
||||
{
|
||||
f.KeyValue(() => extensionsUsed);
|
||||
}
|
||||
if (extensions.__count > 0)
|
||||
{
|
||||
f.KeyValue(() => extensions);
|
||||
}
|
||||
if (extras.__count > 0)
|
||||
{
|
||||
f.KeyValue(() => extras);
|
||||
}
|
||||
|
||||
f.KeyValue(() => asset);
|
||||
|
||||
// buffer
|
||||
if (buffers.Any())
|
||||
{
|
||||
f.KeyValue(() => buffers);
|
||||
}
|
||||
if (bufferViews.Any())
|
||||
{
|
||||
f.Key("bufferViews"); f.GLTFValue(bufferViews);
|
||||
}
|
||||
if (accessors.Any())
|
||||
{
|
||||
f.Key("accessors"); f.GLTFValue(accessors);
|
||||
}
|
||||
|
||||
// materials
|
||||
if (images.Any())
|
||||
{
|
||||
f.Key("images"); f.GLTFValue(images);
|
||||
if (samplers.Count == 0)
|
||||
{
|
||||
samplers.Add(new glTFTextureSampler());
|
||||
}
|
||||
}
|
||||
|
||||
if (samplers.Any())
|
||||
{
|
||||
f.Key("samplers"); f.GLTFValue(samplers);
|
||||
}
|
||||
|
||||
if (textures.Any())
|
||||
{
|
||||
f.Key("textures"); f.GLTFValue(textures);
|
||||
}
|
||||
if (materials.Any())
|
||||
{
|
||||
f.Key("materials"); f.GLTFValue(materials);
|
||||
}
|
||||
|
||||
// meshes
|
||||
if (meshes.Any())
|
||||
{
|
||||
f.KeyValue(() => meshes);
|
||||
}
|
||||
if (skins.Any())
|
||||
{
|
||||
f.KeyValue(() => skins);
|
||||
}
|
||||
|
||||
// scene
|
||||
if (nodes.Any())
|
||||
{
|
||||
f.KeyValue(() => nodes);
|
||||
}
|
||||
if (scenes.Any())
|
||||
{
|
||||
f.KeyValue(() => scenes);
|
||||
if (scene >= 0)
|
||||
{
|
||||
f.KeyValue(() => scene);
|
||||
}
|
||||
}
|
||||
|
||||
// animations
|
||||
if (animations.Any())
|
||||
{
|
||||
f.Key("animations"); f.GLTFValue(animations);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Equals(glTF other)
|
||||
{
|
||||
return
|
||||
textures.SequenceEqual(other.textures)
|
||||
&& samplers.SequenceEqual(other.samplers)
|
||||
&& images.SequenceEqual(other.images)
|
||||
&& materials.SequenceEqual(other.materials)
|
||||
&& meshes.SequenceEqual(other.meshes)
|
||||
&& nodes.SequenceEqual(other.nodes)
|
||||
&& skins.SequenceEqual(other.skins)
|
||||
&& scene == other.scene
|
||||
&& scenes.SequenceEqual(other.scenes)
|
||||
&& animations.SequenceEqual(other.animations)
|
||||
;
|
||||
}
|
||||
|
||||
bool UsedExtension(string key)
|
||||
{
|
||||
if (extensionsUsed.Contains(key))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static Utf8String s_extensions = Utf8String.From("extensions");
|
||||
|
||||
void Traverse(ListTreeNode<JsonValue> node, JsonFormatter f, Utf8String parentKey)
|
||||
{
|
||||
if(node.IsMap())
|
||||
{
|
||||
f.BeginMap();
|
||||
foreach(var kv in node.ObjectItems())
|
||||
{
|
||||
if (parentKey == s_extensions)
|
||||
{
|
||||
if (!UsedExtension(kv.Key.GetString()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
f.Key(kv.Key.GetUtf8String());
|
||||
Traverse(kv.Value, f, kv.Key.GetUtf8String());
|
||||
}
|
||||
f.EndMap();
|
||||
}
|
||||
else if(node.IsArray())
|
||||
{
|
||||
f.BeginList();
|
||||
foreach(var x in node.ArrayItems())
|
||||
{
|
||||
Traverse(x, f, default(Utf8String));
|
||||
}
|
||||
f.EndList();
|
||||
}
|
||||
else
|
||||
{
|
||||
f.Value(node);
|
||||
}
|
||||
}
|
||||
|
||||
string RemoveUnusedExtensions(string json)
|
||||
{
|
||||
var f = new JsonFormatter();
|
||||
|
||||
Traverse(JsonParser.Parse(json), f, default(Utf8String));
|
||||
|
||||
return f.ToString();
|
||||
}
|
||||
|
||||
public byte[] ToGlbBytes(bool UseUniJSONSerializer = false)
|
||||
{
|
||||
string json;
|
||||
if (UseUniJSONSerializer)
|
||||
{
|
||||
json = JsonSchema.FromType(GetType()).Serialize(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
json = ToJson();
|
||||
}
|
||||
|
||||
RemoveUnusedExtensions(json);
|
||||
|
||||
return Glb.ToBytes(json, buffers[0].GetBytes());
|
||||
}
|
||||
}
|
||||
}
|
||||
13
UniGLTF/Core/Scripts/Format/glTF.cs.meta
Normal file
13
UniGLTF/Core/Scripts/Format/glTF.cs.meta
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 317adeaa75a118a4f857721c90946933
|
||||
timeCreated: 1516618885
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
210
UniGLTF/Core/Scripts/Format/glTFAnimation.cs
Normal file
210
UniGLTF/Core/Scripts/Format/glTFAnimation.cs
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using UniJSON;
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
[Serializable]
|
||||
public class glTFAnimationTarget : JsonSerializableBase
|
||||
{
|
||||
[JsonSchema(Minimum = 0)]
|
||||
public int node;
|
||||
|
||||
[JsonSchema(Required = true, EnumValues = new object[] { "translation", "rotation", "scale", "weights" }, EnumSerializationType = EnumSerializationType.AsString)]
|
||||
public string path;
|
||||
|
||||
// empty schemas
|
||||
public object extensions;
|
||||
public object extras;
|
||||
|
||||
protected override void SerializeMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
f.KeyValue(() => node);
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
f.KeyValue(() => path);
|
||||
}
|
||||
}
|
||||
|
||||
public enum Interpolations
|
||||
{
|
||||
LINEAR,
|
||||
STEP,
|
||||
CUBICSPLINE
|
||||
}
|
||||
|
||||
public const string PATH_TRANSLATION = "translation";
|
||||
public const string PATH_EULER_ROTATION = "rotation";
|
||||
public const string PATH_ROTATION = "rotation";
|
||||
public const string PATH_SCALE = "scale";
|
||||
public const string PATH_WEIGHT = "weights";
|
||||
public const string NOT_IMPLEMENTED = "NotImplemented";
|
||||
|
||||
public enum AnimationPropertys
|
||||
{
|
||||
Translation,
|
||||
EulerRotation,
|
||||
Rotation,
|
||||
Scale,
|
||||
Weight,
|
||||
BlendShape,
|
||||
|
||||
NotImplemented
|
||||
}
|
||||
|
||||
public static string GetPathName(AnimationPropertys property)
|
||||
{
|
||||
switch (property)
|
||||
{
|
||||
case AnimationPropertys.Translation:
|
||||
return PATH_TRANSLATION;
|
||||
case AnimationPropertys.EulerRotation:
|
||||
case AnimationPropertys.Rotation:
|
||||
return PATH_ROTATION;
|
||||
case AnimationPropertys.Scale:
|
||||
return PATH_SCALE;
|
||||
case AnimationPropertys.BlendShape:
|
||||
return PATH_WEIGHT;
|
||||
default: throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public static AnimationPropertys GetAnimationProperty(string path)
|
||||
{
|
||||
switch (path)
|
||||
{
|
||||
case PATH_TRANSLATION:
|
||||
return AnimationPropertys.Translation;
|
||||
case PATH_ROTATION:
|
||||
return AnimationPropertys.Rotation;
|
||||
case PATH_SCALE:
|
||||
return AnimationPropertys.Scale;
|
||||
case PATH_WEIGHT:
|
||||
return AnimationPropertys.BlendShape;
|
||||
default: throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public static int GetElementCount(AnimationPropertys property)
|
||||
{
|
||||
switch (property)
|
||||
{
|
||||
case AnimationPropertys.Translation: return 3;
|
||||
case AnimationPropertys.EulerRotation: return 3;
|
||||
case AnimationPropertys.Rotation: return 4;
|
||||
case AnimationPropertys.Scale: return 3;
|
||||
case AnimationPropertys.BlendShape: return 1;
|
||||
default: throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public static int GetElementCount(string path)
|
||||
{
|
||||
return GetElementCount(GetAnimationProperty(path));
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class glTFAnimationChannel : JsonSerializableBase
|
||||
{
|
||||
[JsonSchema(Required = true, Minimum = 0)]
|
||||
public int sampler = -1;
|
||||
|
||||
[JsonSchema(Required = true)]
|
||||
public glTFAnimationTarget target;
|
||||
|
||||
// empty schemas
|
||||
public object extensions;
|
||||
public object extras;
|
||||
|
||||
protected override void SerializeMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
f.KeyValue(() => sampler);
|
||||
f.KeyValue(() => target);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class glTFAnimationSampler : JsonSerializableBase
|
||||
{
|
||||
[JsonSchema(Required = true, Minimum = 0)]
|
||||
public int input = -1;
|
||||
|
||||
[JsonSchema(EnumValues = new object[] { "LINEAR", "STEP", "CUBICSPLINE" }, EnumSerializationType = EnumSerializationType.AsString)]
|
||||
public string interpolation;
|
||||
|
||||
[JsonSchema(Required = true, Minimum = 0)]
|
||||
public int output = -1;
|
||||
|
||||
// empty schemas
|
||||
public object extensions;
|
||||
public object extras;
|
||||
|
||||
protected override void SerializeMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
f.KeyValue(() => input);
|
||||
if (!string.IsNullOrEmpty(interpolation))
|
||||
{
|
||||
f.KeyValue(() => interpolation);
|
||||
}
|
||||
f.KeyValue(() => output);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class glTFAnimation : JsonSerializableBase
|
||||
{
|
||||
public string name = "";
|
||||
|
||||
[JsonSchema(Required = true, MinItems = 1)]
|
||||
public List<glTFAnimationChannel> channels = new List<glTFAnimationChannel>();
|
||||
|
||||
[JsonSchema(Required = true, MinItems = 1)]
|
||||
public List<glTFAnimationSampler> samplers = new List<glTFAnimationSampler>();
|
||||
|
||||
// empty schemas
|
||||
public object extensions;
|
||||
public object extras;
|
||||
|
||||
protected override void SerializeMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
{
|
||||
f.KeyValue(() => name);
|
||||
}
|
||||
|
||||
f.KeyValue(() => channels);
|
||||
f.KeyValue(() => samplers);
|
||||
}
|
||||
|
||||
public int AddChannelAndGetSampler(int nodeIndex, glTFAnimationTarget.AnimationPropertys property)
|
||||
{
|
||||
// find channel
|
||||
var channel = channels.FirstOrDefault(x => x.target.node == nodeIndex && x.target.path == glTFAnimationTarget.GetPathName(property));
|
||||
if (channel != null)
|
||||
{
|
||||
return channel.sampler;
|
||||
}
|
||||
|
||||
// not found. create new
|
||||
var samplerIndex = samplers.Count;
|
||||
var sampler = new glTFAnimationSampler();
|
||||
samplers.Add(sampler);
|
||||
|
||||
channel = new glTFAnimationChannel
|
||||
{
|
||||
sampler = samplerIndex,
|
||||
target = new glTFAnimationTarget
|
||||
{
|
||||
node = nodeIndex,
|
||||
path = glTFAnimationTarget.GetPathName(property),
|
||||
},
|
||||
};
|
||||
channels.Add(channel);
|
||||
|
||||
return samplerIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
13
UniGLTF/Core/Scripts/Format/glTFAnimation.cs.meta
Normal file
13
UniGLTF/Core/Scripts/Format/glTFAnimation.cs.meta
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f571aa160b9de354c8f5fcc6a38e41f1
|
||||
timeCreated: 1515833098
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
34
UniGLTF/Core/Scripts/Format/glTFAssets.cs
Normal file
34
UniGLTF/Core/Scripts/Format/glTFAssets.cs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
using UniJSON;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
[Serializable]
|
||||
public class glTFAssets : JsonSerializableBase
|
||||
{
|
||||
public string generator;
|
||||
|
||||
[JsonSchema(Required = true, Pattern = "^[0-9]+\\.[0-9]+$")]
|
||||
public string version;
|
||||
|
||||
public string copyright;
|
||||
|
||||
[JsonSchema(Pattern = "^[0-9]+\\.[0-9]+$")]
|
||||
public string minVersion;
|
||||
|
||||
// empty schemas
|
||||
public object extensions;
|
||||
public object extras;
|
||||
|
||||
protected override void SerializeMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
f.Key("generator"); f.Value(generator);
|
||||
f.Key("version"); f.Value(version);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("GLTF-{0} generated by {1}", version, generator);
|
||||
}
|
||||
}
|
||||
}
|
||||
13
UniGLTF/Core/Scripts/Format/glTFAssets.cs.meta
Normal file
13
UniGLTF/Core/Scripts/Format/glTFAssets.cs.meta
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1b759a27457c3cb49adea3fba807e447
|
||||
timeCreated: 1516618004
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
261
UniGLTF/Core/Scripts/Format/glTFBuffer.cs
Normal file
261
UniGLTF/Core/Scripts/Format/glTFBuffer.cs
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using UniJSON;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
[Serializable]
|
||||
public class glTFBuffer : JsonSerializableBase
|
||||
{
|
||||
IBytesBuffer Storage;
|
||||
|
||||
public void OpenStorage(IStorage storage)
|
||||
{
|
||||
Storage = new ArraySegmentByteBuffer(storage.Get(uri));
|
||||
/*
|
||||
if (string.IsNullOrEmpty(uri))
|
||||
{
|
||||
Storage = (glbDataBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
Storage = new UriByteBuffer(baseDir, uri);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
public glTFBuffer(IBytesBuffer storage)
|
||||
{
|
||||
Storage = storage;
|
||||
}
|
||||
|
||||
public string uri;
|
||||
|
||||
[JsonSchema(Required = true, Minimum = 1)]
|
||||
public int byteLength;
|
||||
|
||||
// empty schemas
|
||||
public object extensions;
|
||||
public object extras;
|
||||
public string name;
|
||||
|
||||
public glTFBufferView Append<T>(T[] array, glBufferTarget target) where T : struct
|
||||
{
|
||||
return Append(new ArraySegment<T>(array), target);
|
||||
}
|
||||
public glTFBufferView Append<T>(ArraySegment<T> segment, glBufferTarget target) where T : struct
|
||||
{
|
||||
var view = Storage.Extend(segment, target);
|
||||
byteLength = Storage.GetBytes().Count;
|
||||
return view;
|
||||
}
|
||||
|
||||
public ArraySegment<Byte> GetBytes()
|
||||
{
|
||||
return Storage.GetBytes();
|
||||
}
|
||||
|
||||
protected override void SerializeMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(uri))
|
||||
{
|
||||
f.KeyValue(() => uri);
|
||||
}
|
||||
f.KeyValue(() => byteLength);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class glTFBufferView : JsonSerializableBase
|
||||
{
|
||||
[JsonSchema(Required = true, Minimum = 0)]
|
||||
public int buffer;
|
||||
|
||||
[JsonSchema(Minimum = 0)]
|
||||
public int byteOffset;
|
||||
|
||||
[JsonSchema(Required = true, Minimum = 1)]
|
||||
public int byteLength;
|
||||
|
||||
[JsonSchema(Minimum = 4, Maximum = 252, MultipleOf = 4)]
|
||||
public int byteStride;
|
||||
|
||||
[JsonSchema(EnumSerializationType = EnumSerializationType.AsInt, EnumExcludes = new object[] { glBufferTarget.NONE })]
|
||||
public glBufferTarget target;
|
||||
|
||||
// empty schemas
|
||||
public object extensions;
|
||||
public object extras;
|
||||
public string name;
|
||||
|
||||
protected override void SerializeMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
f.KeyValue(() => buffer);
|
||||
f.KeyValue(() => byteOffset);
|
||||
f.KeyValue(() => byteLength);
|
||||
if (target != glBufferTarget.NONE)
|
||||
{
|
||||
f.Key("target"); f.Value((int)target);
|
||||
}
|
||||
/* When this is not defined, data is tightly packed. When two or more accessors use the same bufferView, this field must be defined.
|
||||
if (byteStride >= 4)
|
||||
{
|
||||
f.KeyValue(() => byteStride);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class glTFSparseIndices : JsonSerializableBase
|
||||
{
|
||||
[JsonSchema(Required = true, Minimum = 0)]
|
||||
public int bufferView = -1;
|
||||
|
||||
[JsonSchema(Minimum = 0)]
|
||||
public int byteOffset;
|
||||
|
||||
[JsonSchema(Required = true, EnumValues = new object[] { 5121, 5123, 5125 })]
|
||||
public glComponentType componentType;
|
||||
|
||||
// empty schemas
|
||||
public object extensions;
|
||||
public object extras;
|
||||
|
||||
protected override void SerializeMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
f.KeyValue(() => bufferView);
|
||||
f.KeyValue(() => byteOffset);
|
||||
f.Key("componentType"); f.Value((int)componentType);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class glTFSparseValues : JsonSerializableBase
|
||||
{
|
||||
[JsonSchema(Required = true, Minimum = 0)]
|
||||
public int bufferView = -1;
|
||||
|
||||
[JsonSchema(Minimum = 0)]
|
||||
public int byteOffset;
|
||||
|
||||
// empty schemas
|
||||
public object extensions;
|
||||
public object extras;
|
||||
|
||||
protected override void SerializeMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
f.KeyValue(() => bufferView);
|
||||
f.KeyValue(() => byteOffset);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class glTFSparse : JsonSerializableBase
|
||||
{
|
||||
[JsonSchema(Required = true, Minimum = 1)]
|
||||
public int count;
|
||||
|
||||
[JsonSchema(Required = true)]
|
||||
public glTFSparseIndices indices;
|
||||
|
||||
[JsonSchema(Required = true)]
|
||||
public glTFSparseValues values;
|
||||
|
||||
// empty schemas
|
||||
public object extensions;
|
||||
public object extras;
|
||||
|
||||
protected override void SerializeMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
f.KeyValue(() => count);
|
||||
f.KeyValue(() => indices);
|
||||
f.KeyValue(() => values);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class glTFAccessor : JsonSerializableBase
|
||||
{
|
||||
[JsonSchema(Minimum = 0)]
|
||||
public int bufferView = -1;
|
||||
|
||||
[JsonSchema(Minimum = 0, Dependencies = new string[] { "bufferView" })]
|
||||
public int byteOffset;
|
||||
|
||||
[JsonSchema(Required = true, EnumValues = new object[] { "SCALAR", "VEC2", "VEC3", "VEC4", "MAT2", "MAT3", "MAT4" }, EnumSerializationType = EnumSerializationType.AsString)]
|
||||
public string type;
|
||||
|
||||
public int TypeCount
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case "SCALAR":
|
||||
return 1;
|
||||
case "VEC2":
|
||||
return 2;
|
||||
case "VEC3":
|
||||
return 3;
|
||||
case "VEC4":
|
||||
case "MAT2":
|
||||
return 4;
|
||||
case "MAT3":
|
||||
return 9;
|
||||
case "MAT4":
|
||||
return 16;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[JsonSchema(Required = true, EnumSerializationType = EnumSerializationType.AsInt)]
|
||||
public glComponentType componentType;
|
||||
|
||||
[JsonSchema(Required = true, Minimum = 1)]
|
||||
public int count;
|
||||
|
||||
[JsonSchema(MinItems = 1, MaxItems = 16)]
|
||||
public float[] max;
|
||||
|
||||
[JsonSchema(MinItems = 1, MaxItems = 16)]
|
||||
public float[] min;
|
||||
|
||||
public bool normalized;
|
||||
public glTFSparse sparse;
|
||||
|
||||
// empty schemas
|
||||
public string name;
|
||||
|
||||
public object extensions;
|
||||
|
||||
public object extras;
|
||||
|
||||
protected override void SerializeMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
f.KeyValue(() => bufferView);
|
||||
f.KeyValue(() => byteOffset);
|
||||
f.KeyValue(() => type);
|
||||
f.Key("componentType"); f.Value((int)componentType);
|
||||
f.KeyValue(() => count);
|
||||
if (max != null && max.Any())
|
||||
{
|
||||
f.KeyValue(() => max);
|
||||
}
|
||||
if (min != null && min.Any())
|
||||
{
|
||||
f.KeyValue(() => min);
|
||||
}
|
||||
|
||||
if (sparse != null && sparse.count > 0)
|
||||
{
|
||||
f.KeyValue(() => sparse);
|
||||
}
|
||||
|
||||
f.KeyValue(() => normalized);
|
||||
f.KeyValue(() => name);
|
||||
}
|
||||
}
|
||||
}
|
||||
13
UniGLTF/Core/Scripts/Format/glTFBuffer.cs.meta
Normal file
13
UniGLTF/Core/Scripts/Format/glTFBuffer.cs.meta
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 50888f361f18c3140a23e9bfdcf43557
|
||||
timeCreated: 1515832638
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
60
UniGLTF/Core/Scripts/Format/glTFCamera.cs
Normal file
60
UniGLTF/Core/Scripts/Format/glTFCamera.cs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
using System;
|
||||
using UniJSON;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public enum ProjectionType
|
||||
{
|
||||
Perspective,
|
||||
Orthographic
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class glTFOrthographic
|
||||
{
|
||||
[JsonSchema(Required = true)]
|
||||
public float xmag;
|
||||
[JsonSchema(Required = true)]
|
||||
public float ymag;
|
||||
[JsonSchema(Required = true, Minimum = 0.0f, ExclusiveMinimum = true)]
|
||||
public float zfar;
|
||||
[JsonSchema(Required = true, Minimum = 0.0f)]
|
||||
public float znear;
|
||||
|
||||
[JsonSchema(MinProperties = 1)]
|
||||
public glTFOrthographic_extensions extensions;
|
||||
[JsonSchema(MinProperties = 1)]
|
||||
public glTFOrthographic_extras extras;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class glTFPerspective
|
||||
{
|
||||
[JsonSchema(Minimum = 0.0f, ExclusiveMinimum = true)]
|
||||
public float aspectRatio;
|
||||
[JsonSchema(Required = true, Minimum = 0.0f, ExclusiveMinimum = true)]
|
||||
public float yfov;
|
||||
[JsonSchema(Minimum = 0.0f, ExclusiveMinimum = true)]
|
||||
public float zfar;
|
||||
[JsonSchema(Required = true, Minimum = 0.0f, ExclusiveMinimum = true)]
|
||||
public float znear;
|
||||
|
||||
public glTFPerspective_extensions extensions;
|
||||
public glTFPerspective_extras extras;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class glTFCamera
|
||||
{
|
||||
public glTFOrthographic orthographic;
|
||||
public glTFPerspective perspective;
|
||||
|
||||
[JsonSchema(Required = true, EnumSerializationType = EnumSerializationType.AsLowerString)]
|
||||
public ProjectionType type;
|
||||
|
||||
public string name;
|
||||
|
||||
public glTFCamera_extensions extensions;
|
||||
public glTFCamera_extras extras;
|
||||
}
|
||||
}
|
||||
12
UniGLTF/Core/Scripts/Format/glTFCamera.cs.meta
Normal file
12
UniGLTF/Core/Scripts/Format/glTFCamera.cs.meta
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a4ff02eb93400b142a5ff47d1067bd8b
|
||||
timeCreated: 1531635880
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
225
UniGLTF/Core/Scripts/Format/glTFMaterial.cs
Normal file
225
UniGLTF/Core/Scripts/Format/glTFMaterial.cs
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
using System;
|
||||
using UniJSON;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public enum glTFTextureTypes
|
||||
{
|
||||
BaseColor,
|
||||
Metallic,
|
||||
Normal,
|
||||
Occlusion,
|
||||
Emissive,
|
||||
Unknown
|
||||
}
|
||||
|
||||
public interface IglTFTextureinfo
|
||||
{
|
||||
glTFTextureTypes TextreType { get; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public abstract class glTFTextureInfo : JsonSerializableBase, IglTFTextureinfo
|
||||
{
|
||||
[JsonSchema(Required = true, Minimum = 0)]
|
||||
public int index = -1;
|
||||
|
||||
[JsonSchema(Minimum = 0)]
|
||||
public int texCoord;
|
||||
|
||||
// empty schemas
|
||||
public object extensions;
|
||||
public object extras;
|
||||
|
||||
protected override void SerializeMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
f.KeyValue(() => index);
|
||||
f.KeyValue(() => texCoord);
|
||||
}
|
||||
|
||||
public abstract glTFTextureTypes TextreType { get; }
|
||||
}
|
||||
|
||||
|
||||
[Serializable]
|
||||
public class glTFMaterialBaseColorTextureInfo : glTFTextureInfo
|
||||
{
|
||||
public override glTFTextureTypes TextreType
|
||||
{
|
||||
get { return glTFTextureTypes.BaseColor; }
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class glTFMaterialMetallicRoughnessTextureInfo : glTFTextureInfo
|
||||
{
|
||||
public override glTFTextureTypes TextreType
|
||||
{
|
||||
get { return glTFTextureTypes.Metallic; }
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class glTFMaterialNormalTextureInfo : glTFTextureInfo
|
||||
{
|
||||
public float scale = 1.0f;
|
||||
|
||||
protected override void SerializeMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
f.KeyValue(() => scale);
|
||||
base.SerializeMembers(f);
|
||||
}
|
||||
|
||||
public override glTFTextureTypes TextreType
|
||||
{
|
||||
get { return glTFTextureTypes.Normal; }
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class glTFMaterialOcclusionTextureInfo : glTFTextureInfo
|
||||
{
|
||||
[JsonSchema(Minimum = 0.0, Maximum = 1.0)]
|
||||
public float strength = 1.0f;
|
||||
|
||||
protected override void SerializeMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
f.KeyValue(() => strength);
|
||||
base.SerializeMembers(f);
|
||||
}
|
||||
|
||||
public override glTFTextureTypes TextreType
|
||||
{
|
||||
get { return glTFTextureTypes.Occlusion; }
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class glTFMaterialEmissiveTextureInfo : glTFTextureInfo
|
||||
{
|
||||
public override glTFTextureTypes TextreType
|
||||
{
|
||||
get { return glTFTextureTypes.Emissive; }
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class glTFPbrMetallicRoughness : JsonSerializableBase
|
||||
{
|
||||
public glTFMaterialBaseColorTextureInfo baseColorTexture = null;
|
||||
|
||||
[JsonSchema(MinItems = 4, MaxItems = 4)]
|
||||
[ItemJsonSchema(Minimum = 0.0, Maximum = 1.0)]
|
||||
public float[] baseColorFactor;
|
||||
|
||||
public glTFMaterialMetallicRoughnessTextureInfo metallicRoughnessTexture = null;
|
||||
|
||||
[JsonSchema(Minimum = 0.0, Maximum = 1.0)]
|
||||
public float metallicFactor = 1.0f;
|
||||
|
||||
[JsonSchema(Minimum = 0.0, Maximum = 1.0)]
|
||||
public float roughnessFactor = 1.0f;
|
||||
|
||||
// empty schemas
|
||||
public object extensions;
|
||||
public object extras;
|
||||
|
||||
protected override void SerializeMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
if (baseColorTexture != null)
|
||||
{
|
||||
f.KeyValue(() => baseColorTexture);
|
||||
}
|
||||
if (baseColorFactor != null)
|
||||
{
|
||||
f.KeyValue(() => baseColorFactor);
|
||||
}
|
||||
if (metallicRoughnessTexture != null)
|
||||
{
|
||||
f.KeyValue(() => metallicRoughnessTexture);
|
||||
}
|
||||
f.KeyValue(() => metallicFactor);
|
||||
f.KeyValue(() => roughnessFactor);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class glTFMaterial : JsonSerializableBase
|
||||
{
|
||||
public string name;
|
||||
public glTFPbrMetallicRoughness pbrMetallicRoughness;
|
||||
public glTFMaterialNormalTextureInfo normalTexture = null;
|
||||
|
||||
public glTFMaterialOcclusionTextureInfo occlusionTexture = null;
|
||||
|
||||
public glTFMaterialEmissiveTextureInfo emissiveTexture = null;
|
||||
|
||||
[JsonSchema(MinItems = 3, MaxItems = 3)]
|
||||
[ItemJsonSchema(Minimum = 0.0, Maximum = 1.0)]
|
||||
public float[] emissiveFactor;
|
||||
|
||||
[JsonSchema(EnumValues = new object[] { "OPAQUE", "MASK", "BLEND" }, EnumSerializationType = EnumSerializationType.AsUpperString)]
|
||||
public string alphaMode;
|
||||
|
||||
[JsonSchema(Dependencies = new string[] { "alphaMode" }, Minimum = 0.0)]
|
||||
public float alphaCutoff = 0.5f;
|
||||
|
||||
public bool doubleSided;
|
||||
|
||||
[JsonSchema(SkipSchemaComparison = true)]
|
||||
public glTFMaterial_extensions extensions;
|
||||
public object extras;
|
||||
|
||||
protected override void SerializeMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
if (!String.IsNullOrEmpty(name))
|
||||
{
|
||||
f.Key("name"); f.Value(name);
|
||||
}
|
||||
if (pbrMetallicRoughness != null)
|
||||
{
|
||||
f.Key("pbrMetallicRoughness"); f.GLTFValue(pbrMetallicRoughness);
|
||||
}
|
||||
if (normalTexture != null)
|
||||
{
|
||||
f.Key("normalTexture"); f.GLTFValue(normalTexture);
|
||||
}
|
||||
if (occlusionTexture != null)
|
||||
{
|
||||
f.Key("occlusionTexture"); f.GLTFValue(occlusionTexture);
|
||||
}
|
||||
if (emissiveTexture != null)
|
||||
{
|
||||
f.Key("emissiveTexture"); f.GLTFValue(emissiveTexture);
|
||||
}
|
||||
if (emissiveFactor != null)
|
||||
{
|
||||
f.Key("emissiveFactor"); f.Serialize(emissiveFactor);
|
||||
}
|
||||
|
||||
f.KeyValue(() => doubleSided);
|
||||
|
||||
if (!string.IsNullOrEmpty(alphaMode))
|
||||
{
|
||||
f.KeyValue(() => alphaMode);
|
||||
}
|
||||
|
||||
if (extensions != null)
|
||||
{
|
||||
f.KeyValue(() => extensions);
|
||||
}
|
||||
}
|
||||
|
||||
public glTFTextureInfo[] GetTextures()
|
||||
{
|
||||
return new glTFTextureInfo[]
|
||||
{
|
||||
pbrMetallicRoughness.baseColorTexture,
|
||||
pbrMetallicRoughness.metallicRoughnessTexture,
|
||||
normalTexture,
|
||||
occlusionTexture,
|
||||
emissiveTexture
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
13
UniGLTF/Core/Scripts/Format/glTFMaterial.cs.meta
Normal file
13
UniGLTF/Core/Scripts/Format/glTFMaterial.cs.meta
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f6315a9155238724c9b221c155c73f68
|
||||
timeCreated: 1515601872
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
168
UniGLTF/Core/Scripts/Format/glTFMesh.cs
Normal file
168
UniGLTF/Core/Scripts/Format/glTFMesh.cs
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UniJSON;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
[Serializable]
|
||||
public class glTFAttributes : JsonSerializableBase
|
||||
{
|
||||
[JsonSchema(Minimum = 0)]
|
||||
public int POSITION = -1;
|
||||
|
||||
[JsonSchema(Minimum = 0)]
|
||||
public int NORMAL = -1;
|
||||
|
||||
[JsonSchema(Minimum = 0)]
|
||||
public int TANGENT = -1;
|
||||
|
||||
[JsonSchema(Minimum = 0)]
|
||||
public int TEXCOORD_0 = -1;
|
||||
|
||||
[JsonSchema(Minimum = 0)]
|
||||
public int COLOR_0 = -1;
|
||||
|
||||
[JsonSchema(Minimum = 0)]
|
||||
public int JOINTS_0 = -1;
|
||||
|
||||
[JsonSchema(Minimum = 0)]
|
||||
public int WEIGHTS_0 = -1;
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return base.GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
var rhs = obj as glTFAttributes;
|
||||
if (rhs == null)
|
||||
{
|
||||
return base.Equals(obj);
|
||||
}
|
||||
|
||||
return POSITION == rhs.POSITION
|
||||
&& NORMAL == rhs.NORMAL
|
||||
&& TANGENT == rhs.TANGENT
|
||||
&& TEXCOORD_0 == rhs.TEXCOORD_0
|
||||
&& COLOR_0 == rhs.COLOR_0
|
||||
&& JOINTS_0 == rhs.JOINTS_0
|
||||
&& WEIGHTS_0 == rhs.WEIGHTS_0
|
||||
;
|
||||
}
|
||||
|
||||
protected override void SerializeMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
f.KeyValue(() => POSITION);
|
||||
if (NORMAL != -1) f.KeyValue(() => NORMAL);
|
||||
if (TANGENT != -1) f.KeyValue(() => TANGENT);
|
||||
if (TEXCOORD_0 != -1) f.KeyValue(() => TEXCOORD_0);
|
||||
if (COLOR_0 != -1) f.KeyValue(() => COLOR_0);
|
||||
if (JOINTS_0 != -1) f.KeyValue(() => JOINTS_0);
|
||||
if (WEIGHTS_0 != -1) f.KeyValue(() => WEIGHTS_0);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class gltfMorphTarget : JsonSerializableBase
|
||||
{
|
||||
public int POSITION = -1;
|
||||
public int NORMAL = -1;
|
||||
public int TANGENT = -1;
|
||||
|
||||
protected override void SerializeMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
f.KeyValue(() => POSITION);
|
||||
if (NORMAL >= 0) f.KeyValue(() => NORMAL);
|
||||
if (TANGENT >= 0) f.KeyValue(() => TANGENT);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/schema/mesh.primitive.schema.json
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class glTFPrimitives : JsonSerializableBase
|
||||
{
|
||||
[JsonSchema(EnumValues = new object[] { 0, 1, 2, 3, 4, 5, 6 })]
|
||||
public int mode;
|
||||
|
||||
[JsonSchema(Minimum = 0)]
|
||||
public int indices = -1;
|
||||
|
||||
[JsonSchema(Required = true, SkipSchemaComparison = true)]
|
||||
public glTFAttributes attributes;
|
||||
|
||||
public bool HasVertexColor
|
||||
{
|
||||
get
|
||||
{
|
||||
return attributes.COLOR_0 != -1;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonSchema(Minimum = 0)]
|
||||
public int material;
|
||||
|
||||
[JsonSchema(MinItems = 1)]
|
||||
[ItemJsonSchema(SkipSchemaComparison = true)]
|
||||
public List<gltfMorphTarget> targets = new List<gltfMorphTarget>();
|
||||
|
||||
public glTFPrimitives_extras extras = new glTFPrimitives_extras();
|
||||
|
||||
[JsonSchema(SkipSchemaComparison = true)]
|
||||
public glTFPrimitives_extensions extensions = new glTFPrimitives_extensions();
|
||||
|
||||
protected override void SerializeMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
f.KeyValue(() => mode);
|
||||
f.KeyValue(() => indices);
|
||||
f.Key("attributes"); f.GLTFValue(attributes);
|
||||
f.KeyValue(() => material);
|
||||
if (targets != null && targets.Count > 0)
|
||||
{
|
||||
f.Key("targets"); f.GLTFValue(targets);
|
||||
}
|
||||
if (extensions.KHR_draco_mesh_compression != null)
|
||||
{
|
||||
f.KeyValue(() => extensions);
|
||||
}
|
||||
if (extras.targetNames.Count > 0)
|
||||
{
|
||||
f.KeyValue(() => extras);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class glTFMesh : JsonSerializableBase
|
||||
{
|
||||
public string name;
|
||||
|
||||
[JsonSchema(Required = true, MinItems = 1)]
|
||||
public List<glTFPrimitives> primitives;
|
||||
|
||||
[JsonSchema(MinItems = 1)]
|
||||
public float[] weights;
|
||||
|
||||
// empty schemas
|
||||
public object extensions;
|
||||
public object extras;
|
||||
|
||||
public glTFMesh(string _name)
|
||||
{
|
||||
name = _name;
|
||||
primitives = new List<glTFPrimitives>();
|
||||
}
|
||||
|
||||
protected override void SerializeMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
f.KeyValue(() => name);
|
||||
f.Key("primitives"); f.GLTFValue(primitives);
|
||||
if (weights != null && weights.Length > 0)
|
||||
{
|
||||
f.KeyValue(() => weights);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13
UniGLTF/Core/Scripts/Format/glTFMesh.cs.meta
Normal file
13
UniGLTF/Core/Scripts/Format/glTFMesh.cs.meta
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 20634c83ea30c99449cd85ef46368b13
|
||||
timeCreated: 1516622346
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
76
UniGLTF/Core/Scripts/Format/glTFNode.cs
Normal file
76
UniGLTF/Core/Scripts/Format/glTFNode.cs
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using UniJSON;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
[Serializable]
|
||||
public class glTFNode : JsonSerializableBase
|
||||
{
|
||||
public string name = "";
|
||||
|
||||
[JsonSchema(MinItems = 1)]
|
||||
[ItemJsonSchema(Minimum = 0)]
|
||||
public int[] children;
|
||||
|
||||
[JsonSchema(MinItems = 16, MaxItems = 16)]
|
||||
public float[] matrix;
|
||||
|
||||
[JsonSchema(MinItems = 3, MaxItems = 3)]
|
||||
public float[] translation;
|
||||
|
||||
[JsonSchema(MinItems = 4, MaxItems = 4)]
|
||||
[ItemJsonSchema(Minimum = -1.0, Maximum = 1.0)]
|
||||
public float[] rotation;
|
||||
|
||||
[JsonSchema(MinItems = 3, MaxItems = 3)]
|
||||
public float[] scale;
|
||||
|
||||
[JsonSchema(Minimum = 0)]
|
||||
public int mesh = -1;
|
||||
|
||||
[JsonSchema(Dependencies = new string[] { "mesh" }, Minimum = 0)]
|
||||
public int skin = -1;
|
||||
|
||||
[JsonSchema(Dependencies = new string[] { "mesh" }, MinItems = 1)]
|
||||
public float[] weights;
|
||||
|
||||
[JsonSchema(Minimum = 0)]
|
||||
public int camera = -1;
|
||||
|
||||
// empty schemas
|
||||
public glTFNode_extensions extensions;
|
||||
public glTFNode_extra extras = new glTFNode_extra();
|
||||
|
||||
protected override void SerializeMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
if (children != null && children.Any())
|
||||
{
|
||||
f.Key("children"); f.BeginList();
|
||||
foreach (var child in children)
|
||||
{
|
||||
f.Value(child);
|
||||
}
|
||||
f.EndList();
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(name)) f.KeyValue(() => name);
|
||||
if (matrix != null) f.KeyValue(() => matrix);
|
||||
if (translation != null) f.KeyValue(() => translation);
|
||||
if (rotation != null) f.KeyValue(() => rotation);
|
||||
if (scale != null) f.KeyValue(() => scale);
|
||||
|
||||
if (mesh >= 0) f.KeyValue(() => mesh);
|
||||
if (camera >= 0) f.KeyValue(() => camera);
|
||||
if (skin >= 0)
|
||||
{
|
||||
f.KeyValue(() => skin);
|
||||
|
||||
if (extras.__count > 0)
|
||||
{
|
||||
f.KeyValue(() => extras);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13
UniGLTF/Core/Scripts/Format/glTFNode.cs.meta
Normal file
13
UniGLTF/Core/Scripts/Format/glTFNode.cs.meta
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 655775a9435baed4281c569519829362
|
||||
timeCreated: 1515833011
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
34
UniGLTF/Core/Scripts/Format/glTFSkin.cs
Normal file
34
UniGLTF/Core/Scripts/Format/glTFSkin.cs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
using UniJSON;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
[Serializable]
|
||||
public class glTFSkin : JsonSerializableBase
|
||||
{
|
||||
[JsonSchema(Minimum = 0)]
|
||||
public int inverseBindMatrices = -1;
|
||||
|
||||
[JsonSchema(Required = true, MinItems = 1)]
|
||||
[ItemJsonSchema(Minimum = 0)]
|
||||
public int[] joints;
|
||||
|
||||
[JsonSchema(Minimum = 0)]
|
||||
public int skeleton = -1;
|
||||
|
||||
// empty schemas
|
||||
public object extensions;
|
||||
public object extras;
|
||||
public string name;
|
||||
|
||||
protected override void SerializeMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
f.KeyValue(() => inverseBindMatrices);
|
||||
f.KeyValue(() => joints);
|
||||
if (skeleton >= 0)
|
||||
{
|
||||
f.KeyValue(() => skeleton);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13
UniGLTF/Core/Scripts/Format/glTFSkin.cs.meta
Normal file
13
UniGLTF/Core/Scripts/Format/glTFSkin.cs.meta
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 592c03928131f494f99dca5ef46a8ba0
|
||||
timeCreated: 1516722509
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
123
UniGLTF/Core/Scripts/Format/glTFTexture.cs
Normal file
123
UniGLTF/Core/Scripts/Format/glTFTexture.cs
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using UniJSON;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
[Serializable]
|
||||
public class glTFTextureSampler : JsonSerializableBase
|
||||
{
|
||||
[JsonSchema(EnumSerializationType = EnumSerializationType.AsInt,
|
||||
EnumExcludes = new object[] {
|
||||
glFilter.NONE,
|
||||
glFilter.NEAREST_MIPMAP_NEAREST,
|
||||
glFilter.LINEAR_MIPMAP_NEAREST,
|
||||
glFilter.NEAREST_MIPMAP_LINEAR,
|
||||
glFilter.LINEAR_MIPMAP_LINEAR,
|
||||
})]
|
||||
public glFilter magFilter = glFilter.NEAREST;
|
||||
|
||||
[JsonSchema(EnumSerializationType = EnumSerializationType.AsInt,
|
||||
EnumExcludes = new object[] { glFilter.NONE })]
|
||||
public glFilter minFilter = glFilter.NEAREST;
|
||||
|
||||
[JsonSchema(EnumSerializationType = EnumSerializationType.AsInt,
|
||||
EnumExcludes = new object[] { glWrap.NONE })]
|
||||
public glWrap wrapS = glWrap.REPEAT;
|
||||
|
||||
[JsonSchema(EnumSerializationType = EnumSerializationType.AsInt,
|
||||
EnumExcludes = new object[] { glWrap.NONE })]
|
||||
public glWrap wrapT = glWrap.REPEAT;
|
||||
|
||||
// empty schemas
|
||||
public object extensions;
|
||||
public object extras;
|
||||
public string name;
|
||||
|
||||
protected override void SerializeMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
f.Key("magFilter"); f.Value((int)magFilter);
|
||||
f.Key("minFilter"); f.Value((int)minFilter);
|
||||
f.Key("wrapS"); f.Value((int)wrapS);
|
||||
f.Key("wrapT"); f.Value((int)wrapT);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class glTFImage : JsonSerializableBase
|
||||
{
|
||||
public string name;
|
||||
public string uri;
|
||||
|
||||
[JsonSchema(Dependencies = new string[] { "mimeType" }, Minimum = 0)]
|
||||
public int bufferView;
|
||||
|
||||
[JsonSchema(EnumValues = new object[] { "image/jpeg", "image/png" }, EnumSerializationType =EnumSerializationType.AsString)]
|
||||
public string mimeType;
|
||||
|
||||
public string GetExt()
|
||||
{
|
||||
switch (mimeType)
|
||||
{
|
||||
case "image/png":
|
||||
return ".png";
|
||||
|
||||
case "image/jpeg":
|
||||
return ".jpg";
|
||||
|
||||
default:
|
||||
if (uri.StartsWith("data:image/jpeg;"))
|
||||
{
|
||||
return ".jpg";
|
||||
}
|
||||
else if (uri.StartsWith("data:image/png;"))
|
||||
{
|
||||
return ".png";
|
||||
}
|
||||
else
|
||||
{
|
||||
return Path.GetExtension(uri).ToLower();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// empty schemas
|
||||
public object extensions;
|
||||
public object extras;
|
||||
|
||||
protected override void SerializeMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
f.KeyValue(() => name);
|
||||
if (!string.IsNullOrEmpty(uri))
|
||||
{
|
||||
f.KeyValue(() => uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
f.KeyValue(() => bufferView);
|
||||
f.KeyValue(() => mimeType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class glTFTexture : JsonSerializableBase
|
||||
{
|
||||
[JsonSchema(Minimum = 0)]
|
||||
public int sampler;
|
||||
|
||||
[JsonSchema(Minimum = 0)]
|
||||
public int source;
|
||||
|
||||
// empty schemas
|
||||
public object extensions;
|
||||
public object extras;
|
||||
public string name;
|
||||
|
||||
protected override void SerializeMembers(GLTFJsonFormatter f)
|
||||
{
|
||||
f.KeyValue(() => sampler);
|
||||
f.KeyValue(() => source);
|
||||
}
|
||||
}
|
||||
}
|
||||
13
UniGLTF/Core/Scripts/Format/glTFTexture.cs.meta
Normal file
13
UniGLTF/Core/Scripts/Format/glTFTexture.cs.meta
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5eabe858953de424787a21d633fedda0
|
||||
timeCreated: 1515832646
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
142
UniGLTF/Core/Scripts/Format/glbTypes.cs
Normal file
142
UniGLTF/Core/Scripts/Format/glbTypes.cs
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public enum GlbChunkType : UInt32
|
||||
{
|
||||
JSON = 0x4E4F534A,
|
||||
BIN = 0x004E4942,
|
||||
}
|
||||
|
||||
public struct GlbHeader
|
||||
{
|
||||
public static void WriteTo(Stream s)
|
||||
{
|
||||
s.WriteByte((Byte)'g');
|
||||
s.WriteByte((Byte)'l');
|
||||
s.WriteByte((Byte)'T');
|
||||
s.WriteByte((Byte)'F');
|
||||
var bytes = BitConverter.GetBytes((UInt32)2);
|
||||
s.Write(bytes, 0, bytes.Length);
|
||||
}
|
||||
}
|
||||
|
||||
public struct GlbChunk
|
||||
{
|
||||
public GlbChunkType ChunkType;
|
||||
public ArraySegment<Byte> Bytes;
|
||||
|
||||
public GlbChunk(string json) : this(
|
||||
GlbChunkType.JSON,
|
||||
new ArraySegment<byte>(Encoding.UTF8.GetBytes(json))
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
public GlbChunk(ArraySegment<Byte> bytes) : this(
|
||||
GlbChunkType.BIN,
|
||||
bytes
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
public GlbChunk(GlbChunkType type, ArraySegment<Byte> bytes)
|
||||
{
|
||||
ChunkType = type;
|
||||
Bytes = bytes;
|
||||
}
|
||||
|
||||
byte GetPaddingByte()
|
||||
{
|
||||
// chunk type
|
||||
switch (ChunkType)
|
||||
{
|
||||
case GlbChunkType.JSON:
|
||||
return 0x20;
|
||||
|
||||
case GlbChunkType.BIN:
|
||||
return 0x00;
|
||||
|
||||
default:
|
||||
throw new Exception("unknown chunk type: " + ChunkType);
|
||||
}
|
||||
}
|
||||
|
||||
public int WriteTo(Stream s)
|
||||
{
|
||||
// padding
|
||||
var paddingValue = Bytes.Count % 4;
|
||||
var padding = (paddingValue > 0) ? 4 - paddingValue : 0;
|
||||
|
||||
// size
|
||||
var bytes = BitConverter.GetBytes((int)(Bytes.Count + padding));
|
||||
s.Write(bytes, 0, bytes.Length);
|
||||
|
||||
// chunk type
|
||||
switch (ChunkType)
|
||||
{
|
||||
case GlbChunkType.JSON:
|
||||
s.WriteByte((byte)'J');
|
||||
s.WriteByte((byte)'S');
|
||||
s.WriteByte((byte)'O');
|
||||
s.WriteByte((byte)'N');
|
||||
break;
|
||||
|
||||
case GlbChunkType.BIN:
|
||||
s.WriteByte((byte)'B');
|
||||
s.WriteByte((byte)'I');
|
||||
s.WriteByte((byte)'N');
|
||||
s.WriteByte((byte)0);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception("unknown chunk type: " + ChunkType);
|
||||
}
|
||||
|
||||
// body
|
||||
s.Write(Bytes.Array, Bytes.Offset, Bytes.Count);
|
||||
|
||||
// 4byte align
|
||||
var pad = GetPaddingByte();
|
||||
for(int i=0; i<padding; ++i)
|
||||
{
|
||||
s.WriteByte(pad);
|
||||
}
|
||||
|
||||
return 4 + 4 + Bytes.Count + padding;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Glb
|
||||
{
|
||||
public static byte[] ToBytes(string json, ArraySegment<byte> body)
|
||||
{
|
||||
using (var s = new MemoryStream())
|
||||
{
|
||||
GlbHeader.WriteTo(s);
|
||||
|
||||
var pos = s.Position;
|
||||
s.Position += 4; // skip total size
|
||||
|
||||
int size = 12;
|
||||
|
||||
{
|
||||
var chunk = new GlbChunk(json);
|
||||
size += chunk.WriteTo(s);
|
||||
}
|
||||
{
|
||||
var chunk = new GlbChunk(body);
|
||||
size += chunk.WriteTo(s);
|
||||
}
|
||||
|
||||
s.Position = pos;
|
||||
var bytes = BitConverter.GetBytes(size);
|
||||
s.Write(bytes, 0, bytes.Length);
|
||||
|
||||
return s.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13
UniGLTF/Core/Scripts/Format/glbTypes.cs.meta
Normal file
13
UniGLTF/Core/Scripts/Format/glbTypes.cs.meta
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9238dc0c44df9484bbdca85d818c9b73
|
||||
timeCreated: 1516616871
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
10
UniGLTF/Core/Scripts/IO.meta
Normal file
10
UniGLTF/Core/Scripts/IO.meta
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a7addf524d7f84841b039527f0273bfa
|
||||
folderAsset: yes
|
||||
timeCreated: 1517139195
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
121
UniGLTF/Core/Scripts/IO/AnimationCurveData.cs
Normal file
121
UniGLTF/Core/Scripts/IO/AnimationCurveData.cs
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
class AnimationCurveData
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
public AnimationUtility.TangentMode TangentMode { get; private set; }
|
||||
public glTFAnimationTarget.AnimationPropertys AnimationProperty { get; private set; }
|
||||
public int SamplerIndex { get; private set; }
|
||||
public int ElementCount { get; private set; }
|
||||
public readonly List<AnimationKeyframeData> Keyframes = new List<AnimationKeyframeData>();
|
||||
|
||||
public AnimationCurveData(AnimationUtility.TangentMode tangentMode, glTFAnimationTarget.AnimationPropertys property, int samplerIndex, int elementCount)
|
||||
{
|
||||
TangentMode = tangentMode;
|
||||
AnimationProperty = property;
|
||||
SamplerIndex = samplerIndex;
|
||||
ElementCount = elementCount;
|
||||
}
|
||||
|
||||
public string GetInterpolation()
|
||||
{
|
||||
switch (TangentMode)
|
||||
{
|
||||
case AnimationUtility.TangentMode.Linear:
|
||||
return glTFAnimationTarget.Interpolations.LINEAR.ToString();
|
||||
case AnimationUtility.TangentMode.Constant:
|
||||
return glTFAnimationTarget.Interpolations.STEP.ToString();
|
||||
default:
|
||||
return glTFAnimationTarget.Interpolations.LINEAR.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// キーフレームのデータを入力する
|
||||
/// </summary>
|
||||
/// <param name="time"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="valueOffset"></param>
|
||||
public void SetKeyframeData(float time, float value, int valueOffset)
|
||||
{
|
||||
var existKeyframe = Keyframes.FirstOrDefault(x => x.Time == time);
|
||||
if (existKeyframe != null)
|
||||
{
|
||||
existKeyframe.SetValue(value, valueOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
var newKeyframe = GetKeyframeData(AnimationProperty, ElementCount);
|
||||
newKeyframe.Time = time;
|
||||
newKeyframe.SetValue(value, valueOffset);
|
||||
Keyframes.Add(newKeyframe);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// キー情報がなかった要素に対して直前のキーの値を入力する
|
||||
/// </summary>
|
||||
public void RecountEmptyKeyframe()
|
||||
{
|
||||
if (Keyframes.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Keyframes.Sort((x, y) => (x.Time < y.Time) ? -1 : 1);
|
||||
|
||||
for (int i = 1; i < Keyframes.Count; i++)
|
||||
{
|
||||
var current = Keyframes[i];
|
||||
var last = Keyframes[i - 1];
|
||||
for (int j = 0; j < current.EnterValues.Length; j++)
|
||||
{
|
||||
if (!current.EnterValues[j])
|
||||
{
|
||||
Keyframes[i].SetValue(last.Values[j], j);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// アニメーションプロパティに対応したキーフレームを挿入する
|
||||
/// </summary>
|
||||
/// <param name="property"></param>
|
||||
/// <returns></returns>
|
||||
private static AnimationKeyframeData GetKeyframeData(glTFAnimationTarget.AnimationPropertys property, int elementCount)
|
||||
{
|
||||
switch (property)
|
||||
{
|
||||
case glTFAnimationTarget.AnimationPropertys.Translation:
|
||||
return new AnimationKeyframeData(elementCount, (values) =>
|
||||
{
|
||||
var temp = new Vector3(values[0], values[1], values[2]);
|
||||
return temp.ReverseZ().ToArray();
|
||||
});
|
||||
case glTFAnimationTarget.AnimationPropertys.Rotation:
|
||||
return new AnimationKeyframeData(elementCount, (values) =>
|
||||
{
|
||||
var temp = new Quaternion(values[0], values[1], values[2], values[3]);
|
||||
return temp.ReverseZ().ToArray();
|
||||
});
|
||||
case glTFAnimationTarget.AnimationPropertys.Scale:
|
||||
return new AnimationKeyframeData(elementCount, null);
|
||||
case glTFAnimationTarget.AnimationPropertys.BlendShape:
|
||||
return new AnimationKeyframeData(elementCount, null);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
3
UniGLTF/Core/Scripts/IO/AnimationCurveData.cs.meta
Normal file
3
UniGLTF/Core/Scripts/IO/AnimationCurveData.cs.meta
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1af21722605d44f58deebfcfca642b32
|
||||
timeCreated: 1537442711
|
||||
222
UniGLTF/Core/Scripts/IO/AnimationExporter.cs
Normal file
222
UniGLTF/Core/Scripts/IO/AnimationExporter.cs
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
|
||||
public static class AnimationExporter
|
||||
{
|
||||
public class InputOutputValues
|
||||
{
|
||||
public float[] Input;
|
||||
public float[] Output;
|
||||
}
|
||||
|
||||
public class AnimationWithSampleCurves
|
||||
{
|
||||
public glTFAnimation Animation;
|
||||
public Dictionary<int, InputOutputValues> SamplerMap = new Dictionary<int, InputOutputValues>();
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public static List<AnimationClip> GetAnimationClips(Animation animation)
|
||||
{
|
||||
var clips = new List<AnimationClip>();
|
||||
foreach (AnimationState state in animation)
|
||||
{
|
||||
clips.Add(state.clip);
|
||||
}
|
||||
return clips;
|
||||
}
|
||||
|
||||
public static List<AnimationClip> GetAnimationClips(Animator animator)
|
||||
{
|
||||
var clips = new List<AnimationClip>();
|
||||
|
||||
RuntimeAnimatorController runtimeAnimatorController = animator.runtimeAnimatorController;
|
||||
UnityEditor.Animations.AnimatorController animationController = runtimeAnimatorController as UnityEditor.Animations.AnimatorController;
|
||||
|
||||
if (animationController == null)
|
||||
{
|
||||
return clips;
|
||||
}
|
||||
|
||||
foreach (var layer in animationController.layers)
|
||||
{
|
||||
foreach (var state in layer.stateMachine.states)
|
||||
{
|
||||
clips.Add(state.state.motion as AnimationClip);
|
||||
}
|
||||
}
|
||||
return clips;
|
||||
}
|
||||
|
||||
static int GetNodeIndex(Transform root, List<Transform> nodes, string path)
|
||||
{
|
||||
var descendant = root.GetFromPath(path);
|
||||
return nodes.IndexOf(descendant);
|
||||
}
|
||||
|
||||
public static glTFAnimationTarget.AnimationPropertys PropertyToTarget(string property)
|
||||
{
|
||||
if (property.StartsWith("m_LocalPosition."))
|
||||
{
|
||||
return glTFAnimationTarget.AnimationPropertys.Translation;
|
||||
}
|
||||
else if (property.StartsWith("localEulerAnglesRaw."))
|
||||
{
|
||||
return glTFAnimationTarget.AnimationPropertys.EulerRotation;
|
||||
}
|
||||
else if (property.StartsWith("m_LocalRotation."))
|
||||
{
|
||||
return glTFAnimationTarget.AnimationPropertys.Rotation;
|
||||
}
|
||||
else if (property.StartsWith("m_LocalScale."))
|
||||
{
|
||||
return glTFAnimationTarget.AnimationPropertys.Scale;
|
||||
}
|
||||
else if (property.StartsWith("blendShape."))
|
||||
{
|
||||
return glTFAnimationTarget.AnimationPropertys.BlendShape;
|
||||
}
|
||||
else
|
||||
{
|
||||
return glTFAnimationTarget.AnimationPropertys.NotImplemented;
|
||||
}
|
||||
}
|
||||
|
||||
public static int GetElementOffset(string property)
|
||||
{
|
||||
if (property.EndsWith(".x"))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (property.EndsWith(".y") || property.StartsWith("blendShape."))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (property.EndsWith(".z"))
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
if (property.EndsWith(".w"))
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public static AnimationWithSampleCurves Export(AnimationClip clip, Transform root, List<Transform> nodes)
|
||||
{
|
||||
var animation = new AnimationWithSampleCurves
|
||||
{
|
||||
Animation = new glTFAnimation(),
|
||||
};
|
||||
|
||||
#if UNITY_5_6_OR_NEWER
|
||||
List<AnimationCurveData> curveDatas = new List<AnimationCurveData>();
|
||||
|
||||
foreach (var binding in AnimationUtility.GetCurveBindings(clip))
|
||||
{
|
||||
var curve = AnimationUtility.GetEditorCurve(clip, binding);
|
||||
|
||||
var property = AnimationExporter.PropertyToTarget(binding.propertyName);
|
||||
if (property == glTFAnimationTarget.AnimationPropertys.NotImplemented)
|
||||
{
|
||||
Debug.LogWarning("Not Implemented keyframe property : " + binding.propertyName);
|
||||
continue;
|
||||
}
|
||||
if (property == glTFAnimationTarget.AnimationPropertys.EulerRotation)
|
||||
{
|
||||
Debug.LogWarning("Interpolation setting of AnimationClip should be Quaternion");
|
||||
continue;
|
||||
}
|
||||
|
||||
var nodeIndex = GetNodeIndex(root, nodes, binding.path);
|
||||
var samplerIndex = animation.Animation.AddChannelAndGetSampler(nodeIndex, property);
|
||||
var elementCount = 0;
|
||||
if (property == glTFAnimationTarget.AnimationPropertys.BlendShape)
|
||||
{
|
||||
var mesh = nodes[nodeIndex].GetComponent<SkinnedMeshRenderer>().sharedMesh;
|
||||
elementCount = mesh.blendShapeCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
elementCount = glTFAnimationTarget.GetElementCount(property);
|
||||
}
|
||||
|
||||
// 同一のsamplerIndexが割り当てられているcurveDataがある場合はそれを使用し、無ければ作る
|
||||
var curveData = curveDatas.FirstOrDefault(x => x.SamplerIndex == samplerIndex);
|
||||
if (curveData == null)
|
||||
{
|
||||
curveData = new AnimationCurveData(AnimationUtility.GetKeyRightTangentMode(curve, 0), property, samplerIndex, elementCount);
|
||||
curveDatas.Add(curveData);
|
||||
}
|
||||
|
||||
// 全てのキーフレームを回収
|
||||
int elementOffset = 0;
|
||||
float valueFactor = 1.0f;
|
||||
if (property == glTFAnimationTarget.AnimationPropertys.BlendShape)
|
||||
{
|
||||
var mesh = nodes[nodeIndex].GetComponent<SkinnedMeshRenderer>().sharedMesh;
|
||||
var blendShapeName = binding.propertyName.Replace("blendShape.", "");
|
||||
elementOffset = mesh.GetBlendShapeIndex(blendShapeName);
|
||||
valueFactor = 0.01f;
|
||||
}
|
||||
else
|
||||
{
|
||||
elementOffset = AnimationExporter.GetElementOffset(binding.propertyName);
|
||||
}
|
||||
|
||||
if (elementOffset >= 0 && elementOffset < elementCount)
|
||||
{
|
||||
for (int i = 0; i < curve.keys.Length; i++)
|
||||
{
|
||||
curveData.SetKeyframeData(curve.keys[i].time, curve.keys[i].value * valueFactor, elementOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//キー挿入
|
||||
foreach (var curve in curveDatas)
|
||||
{
|
||||
if (curve.Keyframes.Count == 0)
|
||||
continue;
|
||||
|
||||
curve.RecountEmptyKeyframe();
|
||||
|
||||
var elementNum = curve.Keyframes.First().Values.Length;
|
||||
var values = default(InputOutputValues);
|
||||
if (!animation.SamplerMap.TryGetValue(curve.SamplerIndex, out values))
|
||||
{
|
||||
values = new InputOutputValues();
|
||||
values.Input = new float[curve.Keyframes.Count];
|
||||
values.Output = new float[curve.Keyframes.Count * elementNum];
|
||||
animation.SamplerMap[curve.SamplerIndex] = values;
|
||||
animation.Animation.samplers[curve.SamplerIndex].interpolation = curve.GetInterpolation();
|
||||
}
|
||||
|
||||
int keyframeIndex = 0;
|
||||
foreach (var keyframe in curve.Keyframes)
|
||||
{
|
||||
values.Input[keyframeIndex] = keyframe.Time;
|
||||
Buffer.BlockCopy(keyframe.GetRightHandCoordinate(), 0, values.Output, keyframeIndex * elementNum * sizeof(float), elementNum * sizeof(float));
|
||||
keyframeIndex++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return animation;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
3
UniGLTF/Core/Scripts/IO/AnimationExporter.cs.meta
Normal file
3
UniGLTF/Core/Scripts/IO/AnimationExporter.cs.meta
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 015ae41bf6cb4428b8257ead79772908
|
||||
timeCreated: 1537443293
|
||||
330
UniGLTF/Core/Scripts/IO/AnimationImporter.cs
Normal file
330
UniGLTF/Core/Scripts/IO/AnimationImporter.cs
Normal file
|
|
@ -0,0 +1,330 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public static class AnimationImporter
|
||||
{
|
||||
private enum TangentMode
|
||||
{
|
||||
Linear,
|
||||
Constant,
|
||||
Cubicspline
|
||||
}
|
||||
|
||||
private static TangentMode GetTangentMode(string interpolation)
|
||||
{
|
||||
if (interpolation == glTFAnimationTarget.Interpolations.LINEAR.ToString())
|
||||
{
|
||||
return TangentMode.Linear;
|
||||
}
|
||||
else if (interpolation == glTFAnimationTarget.Interpolations.STEP.ToString())
|
||||
{
|
||||
return TangentMode.Constant;
|
||||
}
|
||||
else if (interpolation == glTFAnimationTarget.Interpolations.CUBICSPLINE.ToString())
|
||||
{
|
||||
return TangentMode.Cubicspline;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private static void CalculateTanget(List<Keyframe> keyframes, int current)
|
||||
{
|
||||
int back = current - 1;
|
||||
if (back < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (current < keyframes.Count)
|
||||
{
|
||||
var rightTangent = (keyframes[current].value - keyframes[back].value) / (keyframes[current].time - keyframes[back].time);
|
||||
keyframes[back] = new Keyframe(keyframes[back].time, keyframes[back].value, keyframes[back].inTangent, rightTangent);
|
||||
|
||||
var leftTangent = (keyframes[back].value - keyframes[current].value) / (keyframes[back].time - keyframes[current].time);
|
||||
keyframes[current] = new Keyframe(keyframes[current].time, keyframes[current].value, leftTangent, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static Quaternion GetShortest(Quaternion last, Quaternion rot)
|
||||
{
|
||||
if (Quaternion.Dot(last, rot) > 0.0)
|
||||
{
|
||||
return rot;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new Quaternion(-rot.x, -rot.y, -rot.z, -rot.w);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public delegate float[] ReverseZ(float[] current, float[] last);
|
||||
public static void SetAnimationCurve(
|
||||
AnimationClip targetClip,
|
||||
string relativePath,
|
||||
string[] propertyNames,
|
||||
float[] input,
|
||||
float[] output,
|
||||
string interpolation,
|
||||
Type curveType,
|
||||
ReverseZ reverse)
|
||||
{
|
||||
var tangentMode = GetTangentMode(interpolation);
|
||||
|
||||
var curveCount = propertyNames.Length;
|
||||
AnimationCurve[] curves = new AnimationCurve[curveCount];
|
||||
List<Keyframe>[] keyframes = new List<Keyframe>[curveCount];
|
||||
|
||||
int elementNum = curveCount;
|
||||
int inputIndex = 0;
|
||||
//Quaternion用
|
||||
float[] last = new float[curveCount];
|
||||
if (last.Length == 4)
|
||||
{
|
||||
last[3] = 1.0f;
|
||||
}
|
||||
for (inputIndex = 0; inputIndex < input.Length; ++inputIndex)
|
||||
{
|
||||
var time = input[inputIndex];
|
||||
var outputIndex = 0;
|
||||
if (tangentMode == TangentMode.Cubicspline)
|
||||
{
|
||||
outputIndex = inputIndex * elementNum * 3;
|
||||
var value = new float[curveCount];
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
value[i] = output[outputIndex + elementNum + i];
|
||||
}
|
||||
var reversed = reverse(value, last);
|
||||
last = reversed;
|
||||
for (int i = 0; i < keyframes.Length; i++)
|
||||
{
|
||||
if (keyframes[i] == null)
|
||||
keyframes[i] = new List<Keyframe>();
|
||||
keyframes[i].Add(new Keyframe(
|
||||
time,
|
||||
reversed[i],
|
||||
output[outputIndex + i],
|
||||
output[outputIndex + i + elementNum * 2]));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
outputIndex = inputIndex * elementNum;
|
||||
var value = new float[curveCount];
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
value[i] = output[outputIndex + i];
|
||||
}
|
||||
var reversed = reverse(value, last);
|
||||
last = reversed;
|
||||
|
||||
for (int i = 0; i < keyframes.Length; i++)
|
||||
{
|
||||
if (keyframes[i] == null)
|
||||
keyframes[i] = new List<Keyframe>();
|
||||
if (tangentMode == TangentMode.Linear)
|
||||
{
|
||||
keyframes[i].Add(new Keyframe(time, reversed[i], 0, 0));
|
||||
if (keyframes[i].Count > 0)
|
||||
{
|
||||
CalculateTanget(keyframes[i], keyframes[i].Count - 1);
|
||||
}
|
||||
}
|
||||
else if (tangentMode == TangentMode.Constant)
|
||||
keyframes[i].Add(new Keyframe(time, reversed[i], 0, float.PositiveInfinity));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < curves.Length; i++)
|
||||
{
|
||||
curves[i] = new AnimationCurve();
|
||||
for (int j = 0; j < keyframes[i].Count; j++)
|
||||
{
|
||||
curves[i].AddKey(keyframes[i][j]);
|
||||
}
|
||||
|
||||
targetClip.SetCurve(relativePath, curveType, propertyNames[i], curves[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<AnimationClip> ImportAnimationClip(ImporterContext ctx)
|
||||
{
|
||||
List<AnimationClip> animasionClips = new List<AnimationClip>();
|
||||
for (int i = 0; i < ctx.GLTF.animations.Count; ++i)
|
||||
{
|
||||
var clip = new AnimationClip();
|
||||
clip.ClearCurves();
|
||||
clip.legacy = true;
|
||||
clip.name = ctx.GLTF.animations[i].name;
|
||||
if (string.IsNullOrEmpty(clip.name))
|
||||
{
|
||||
clip.name = "legacy_" + i;
|
||||
}
|
||||
clip.wrapMode = WrapMode.Loop;
|
||||
|
||||
var animation = ctx.GLTF.animations[i];
|
||||
if (string.IsNullOrEmpty(animation.name))
|
||||
{
|
||||
animation.name = string.Format("animation:{0}", i);
|
||||
}
|
||||
|
||||
foreach (var channel in animation.channels)
|
||||
{
|
||||
var targetTransform = ctx.Nodes[channel.target.node];
|
||||
var relativePath = targetTransform.RelativePathFrom(ctx.Root.transform);
|
||||
switch (channel.target.path)
|
||||
{
|
||||
case glTFAnimationTarget.PATH_TRANSLATION:
|
||||
{
|
||||
var sampler = animation.samplers[channel.sampler];
|
||||
var input = ctx.GLTF.GetArrayFromAccessor<float>(sampler.input);
|
||||
var output = ctx.GLTF.GetArrayFromAccessorAsFloat(sampler.output);
|
||||
|
||||
AnimationImporter.SetAnimationCurve(
|
||||
clip,
|
||||
relativePath,
|
||||
new string[] { "localPosition.x", "localPosition.y", "localPosition.z" },
|
||||
input,
|
||||
output,
|
||||
sampler.interpolation,
|
||||
typeof(Transform),
|
||||
(values, last) =>
|
||||
{
|
||||
Vector3 temp = new Vector3(values[0], values[1], values[2]);
|
||||
return temp.ReverseZ().ToArray();
|
||||
}
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case glTFAnimationTarget.PATH_ROTATION:
|
||||
{
|
||||
var sampler = animation.samplers[channel.sampler];
|
||||
var input = ctx.GLTF.GetArrayFromAccessor<float>(sampler.input);
|
||||
var output = ctx.GLTF.GetArrayFromAccessorAsFloat(sampler.output);
|
||||
|
||||
AnimationImporter.SetAnimationCurve(
|
||||
clip,
|
||||
relativePath,
|
||||
new string[] { "localRotation.x", "localRotation.y", "localRotation.z", "localRotation.w" },
|
||||
input,
|
||||
output,
|
||||
sampler.interpolation,
|
||||
typeof(Transform),
|
||||
(values, last) =>
|
||||
{
|
||||
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 AnimationImporter.GetShortest(lastQuaternion, currentQuaternion.ReverseZ()).ToArray();
|
||||
}
|
||||
);
|
||||
|
||||
clip.EnsureQuaternionContinuity();
|
||||
}
|
||||
break;
|
||||
|
||||
case glTFAnimationTarget.PATH_SCALE:
|
||||
{
|
||||
var sampler = animation.samplers[channel.sampler];
|
||||
var input = ctx.GLTF.GetArrayFromAccessor<float>(sampler.input);
|
||||
var output = ctx.GLTF.GetArrayFromAccessorAsFloat(sampler.output);
|
||||
|
||||
AnimationImporter.SetAnimationCurve(
|
||||
clip,
|
||||
relativePath,
|
||||
new string[] { "localScale.x", "localScale.y", "localScale.z" },
|
||||
input,
|
||||
output,
|
||||
sampler.interpolation,
|
||||
typeof(Transform),
|
||||
(values, last) => values);
|
||||
}
|
||||
break;
|
||||
|
||||
case glTFAnimationTarget.PATH_WEIGHT:
|
||||
{
|
||||
var node = ctx.GLTF.nodes[channel.target.node];
|
||||
var mesh = ctx.GLTF.meshes[node.mesh];
|
||||
//var primitive = mesh.primitives.FirstOrDefault();
|
||||
//var targets = primitive.targets;
|
||||
|
||||
List<string> blendShapeNames = new List<string>();
|
||||
var transform = ctx.Nodes[channel.target.node];
|
||||
var skinnedMeshRenderer = transform.GetComponent<SkinnedMeshRenderer>();
|
||||
if (skinnedMeshRenderer == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int j = 0; j < skinnedMeshRenderer.sharedMesh.blendShapeCount; j++)
|
||||
{
|
||||
blendShapeNames.Add(skinnedMeshRenderer.sharedMesh.GetBlendShapeName(j));
|
||||
}
|
||||
|
||||
var keyNames = blendShapeNames
|
||||
.Where(x => !string.IsNullOrEmpty(x))
|
||||
.Select(x => "blendShape." + x)
|
||||
.ToArray();
|
||||
|
||||
var sampler = animation.samplers[channel.sampler];
|
||||
var input = ctx.GLTF.GetArrayFromAccessor<float>(sampler.input);
|
||||
var output = ctx.GLTF.GetArrayFromAccessor<float>(sampler.output);
|
||||
AnimationImporter.SetAnimationCurve(
|
||||
clip,
|
||||
relativePath,
|
||||
keyNames,
|
||||
input,
|
||||
output,
|
||||
sampler.interpolation,
|
||||
typeof(SkinnedMeshRenderer),
|
||||
(values, last) =>
|
||||
{
|
||||
for (int j = 0; j < values.Length; j++)
|
||||
{
|
||||
values[j] *= 100.0f;
|
||||
}
|
||||
return values;
|
||||
});
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Debug.LogWarningFormat("unknown path: {0}", channel.target.path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
animasionClips.Add(clip);
|
||||
}
|
||||
|
||||
return animasionClips;
|
||||
}
|
||||
|
||||
public static void ImportAnimation(ImporterContext ctx)
|
||||
{
|
||||
// animation
|
||||
if (ctx.GLTF.animations != null && ctx.GLTF.animations.Any())
|
||||
{
|
||||
var animation = ctx.Root.AddComponent<Animation>();
|
||||
ctx.AnimationClips = ImportAnimationClip(ctx);
|
||||
foreach (var clip in ctx.AnimationClips)
|
||||
{
|
||||
animation.AddClip(clip, clip.name);
|
||||
}
|
||||
if (ctx.AnimationClips.Count > 0)
|
||||
{
|
||||
animation.clip = ctx.AnimationClips.First();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
3
UniGLTF/Core/Scripts/IO/AnimationImporter.cs.meta
Normal file
3
UniGLTF/Core/Scripts/IO/AnimationImporter.cs.meta
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d602384685dd4f179350052013659720
|
||||
timeCreated: 1537445972
|
||||
54
UniGLTF/Core/Scripts/IO/AnimationKeyframeData.cs
Normal file
54
UniGLTF/Core/Scripts/IO/AnimationKeyframeData.cs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
namespace UniGLTF
|
||||
{
|
||||
class AnimationKeyframeData
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
public float Time { get; set; }
|
||||
public delegate float[] ConverterFunc(float[] values);
|
||||
private ConverterFunc _converter;
|
||||
private float[] _values;
|
||||
public float[] Values
|
||||
{
|
||||
get { return _values; }
|
||||
}
|
||||
|
||||
private bool[] _enterValues;
|
||||
public bool[] EnterValues
|
||||
{
|
||||
get { return _enterValues; }
|
||||
}
|
||||
|
||||
public AnimationKeyframeData(int elementCount, ConverterFunc converter)
|
||||
{
|
||||
_values = new float[elementCount];
|
||||
_enterValues = new bool[elementCount];
|
||||
for (int i = 0; i < _enterValues.Length; i++)
|
||||
{
|
||||
_enterValues[i] = false;
|
||||
}
|
||||
_converter = converter;
|
||||
}
|
||||
|
||||
public void SetValue(float src, int offset)
|
||||
{
|
||||
if (_values.Length > offset)
|
||||
{
|
||||
_values[offset] = src;
|
||||
_enterValues[offset] = true;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual float[] GetRightHandCoordinate()
|
||||
{
|
||||
if (_converter != null)
|
||||
{
|
||||
return _converter(_values);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _values;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
3
UniGLTF/Core/Scripts/IO/AnimationKeyframeData.cs.meta
Normal file
3
UniGLTF/Core/Scripts/IO/AnimationKeyframeData.cs.meta
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e27ef4fb768e49f591c2bb5eadd3b19b
|
||||
timeCreated: 1537442737
|
||||
81
UniGLTF/Core/Scripts/IO/BytesReader.cs
Normal file
81
UniGLTF/Core/Scripts/IO/BytesReader.cs
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public class BytesReader
|
||||
{
|
||||
Byte[] m_bytes;
|
||||
int m_pos;
|
||||
|
||||
public BytesReader(Byte[] bytes, int pos=0)
|
||||
{
|
||||
m_bytes = bytes;
|
||||
m_pos = pos;
|
||||
}
|
||||
|
||||
public string ReadString(int count, Encoding encoding)
|
||||
{
|
||||
var s = encoding.GetString(m_bytes, m_pos, count);
|
||||
m_pos += count;
|
||||
return s;
|
||||
}
|
||||
|
||||
public float ReadSingle()
|
||||
{
|
||||
var n = BitConverter.ToSingle(m_bytes, m_pos);
|
||||
m_pos += 4;
|
||||
return n;
|
||||
}
|
||||
|
||||
public byte ReadUInt8()
|
||||
{
|
||||
return m_bytes[m_pos++];
|
||||
}
|
||||
|
||||
public UInt16 ReadUInt16()
|
||||
{
|
||||
var n = BitConverter.ToUInt16(m_bytes, m_pos);
|
||||
m_pos += 2;
|
||||
return n;
|
||||
}
|
||||
|
||||
public sbyte ReadInt8()
|
||||
{
|
||||
return (sbyte)m_bytes[m_pos++];
|
||||
}
|
||||
|
||||
public Int16 ReadInt16()
|
||||
{
|
||||
var n = BitConverter.ToInt16(m_bytes, m_pos);
|
||||
m_pos += 2;
|
||||
return n;
|
||||
}
|
||||
|
||||
public int ReadInt32()
|
||||
{
|
||||
var n = BitConverter.ToInt32(m_bytes, m_pos);
|
||||
m_pos += 4;
|
||||
return n;
|
||||
}
|
||||
|
||||
public void ReadToArray<T>(T[] dst) where T : struct
|
||||
{
|
||||
var size = new ArraySegment<Byte>(m_bytes, m_pos, m_bytes.Length - m_pos).MarshalCoyTo(dst);
|
||||
m_pos += size;
|
||||
}
|
||||
|
||||
public T ReadStruct<T>() where T : struct
|
||||
{
|
||||
var size = Marshal.SizeOf(typeof(T));
|
||||
using (var pin = Pin.Create(new ArraySegment<Byte>(m_bytes, m_pos, m_bytes.Length - m_pos)))
|
||||
{
|
||||
var s = (T)Marshal.PtrToStructure(pin.Ptr, typeof(T));
|
||||
m_pos += size;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13
UniGLTF/Core/Scripts/IO/BytesReader.cs.meta
Normal file
13
UniGLTF/Core/Scripts/IO/BytesReader.cs.meta
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ecae7b7664269624ba8e03025a53819e
|
||||
timeCreated: 1514252306
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
74
UniGLTF/Core/Scripts/IO/IStorage.cs
Normal file
74
UniGLTF/Core/Scripts/IO/IStorage.cs
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public interface IStorage
|
||||
{
|
||||
ArraySegment<Byte> Get(string url);
|
||||
|
||||
/// <summary>
|
||||
/// Get original filepath if exists
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <returns></returns>
|
||||
string GetPath(string url);
|
||||
}
|
||||
|
||||
public class SimpleStorage : IStorage
|
||||
{
|
||||
ArraySegment<Byte> m_bytes;
|
||||
|
||||
public SimpleStorage():this(new ArraySegment<byte>())
|
||||
{
|
||||
}
|
||||
|
||||
public SimpleStorage(ArraySegment<Byte> bytes)
|
||||
{
|
||||
m_bytes = bytes;
|
||||
}
|
||||
|
||||
public ArraySegment<byte> Get(string url)
|
||||
{
|
||||
return m_bytes;
|
||||
}
|
||||
|
||||
public string GetPath(string url)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public class FileSystemStorage : IStorage
|
||||
{
|
||||
string m_root;
|
||||
|
||||
public FileSystemStorage(string root)
|
||||
{
|
||||
m_root = Path.GetFullPath(root);
|
||||
}
|
||||
|
||||
public ArraySegment<byte> Get(string url)
|
||||
{
|
||||
var bytes =
|
||||
(url.StartsWith("data:"))
|
||||
? UriByteBuffer.ReadEmbeded(url)
|
||||
: File.ReadAllBytes(Path.Combine(m_root, url))
|
||||
;
|
||||
return new ArraySegment<byte>(bytes);
|
||||
}
|
||||
|
||||
public string GetPath(string url)
|
||||
{
|
||||
if (url.StartsWith("data:"))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Path.Combine(m_root, url).Replace("\\", "/");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
UniGLTF/Core/Scripts/IO/IStorage.cs.meta
Normal file
12
UniGLTF/Core/Scripts/IO/IStorage.cs.meta
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9cb8b6f878e36a74f90d172daee60bed
|
||||
timeCreated: 1529327531
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
999
UniGLTF/Core/Scripts/IO/ImporterContext.cs
Normal file
999
UniGLTF/Core/Scripts/IO/ImporterContext.cs
Normal file
|
|
@ -0,0 +1,999 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Collections;
|
||||
using DepthFirstScheduler;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
#if ((NET_4_6 || NET_STANDARD_2_0) && UNITY_2017_1_OR_NEWER)
|
||||
using System.Threading.Tasks;
|
||||
#endif
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
/// <summary>
|
||||
/// GLTF importer
|
||||
/// </summary>
|
||||
public class ImporterContext: IDisposable
|
||||
{
|
||||
#region MeasureTime
|
||||
bool m_showSpeedLog
|
||||
#if UNIGLTF_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
|
||||
|
||||
IShaderStore m_shaderStore;
|
||||
public IShaderStore ShaderStore
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_shaderStore == null)
|
||||
{
|
||||
m_shaderStore = new ShaderStore(this);
|
||||
}
|
||||
return m_shaderStore;
|
||||
}
|
||||
}
|
||||
|
||||
IMaterialImporter m_materialImporter;
|
||||
protected void SetMaterialImporter(IMaterialImporter importer)
|
||||
{
|
||||
m_materialImporter = importer;
|
||||
}
|
||||
public IMaterialImporter MaterialImporter
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_materialImporter == null)
|
||||
{
|
||||
m_materialImporter = new MaterialImporter(ShaderStore, this);
|
||||
}
|
||||
return m_materialImporter;
|
||||
}
|
||||
}
|
||||
|
||||
public ImporterContext(IShaderStore shaderStore)
|
||||
{
|
||||
m_shaderStore = shaderStore;
|
||||
}
|
||||
|
||||
public ImporterContext(IMaterialImporter materialImporter)
|
||||
{
|
||||
m_materialImporter = materialImporter;
|
||||
}
|
||||
|
||||
public ImporterContext()
|
||||
{
|
||||
}
|
||||
|
||||
#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.StartsWith("UniGLTF-")) return false;
|
||||
|
||||
try
|
||||
{
|
||||
var index = generatorVersion.IndexOf('.');
|
||||
var generatorMajor = int.Parse(generatorVersion.Substring(8, index - 8));
|
||||
var generatorMinor = int.Parse(generatorVersion.Substring(index + 1));
|
||||
|
||||
if (generatorMajor < major)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
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;
|
||||
#endregion
|
||||
|
||||
#region Parse
|
||||
public void Parse(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;
|
||||
|
||||
case ".glb":
|
||||
ParseGlb(bytes);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
public void ParseGlb(Byte[] bytes)
|
||||
{
|
||||
var chunks = glbImporter.ParseGlbChanks(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");
|
||||
}
|
||||
|
||||
var jsonBytes = chunks[0].Bytes;
|
||||
ParseJson(Encoding.UTF8.GetString(jsonBytes.Array, jsonBytes.Offset, jsonBytes.Count),
|
||||
new SimpleStorage(chunks[1].Bytes));
|
||||
}
|
||||
|
||||
public virtual void ParseJson(string json, IStorage storage)
|
||||
{
|
||||
Json = json;
|
||||
Storage = storage;
|
||||
|
||||
GLTF = JsonUtility.FromJson<glTF>(Json);
|
||||
if (GLTF.asset.version != "2.0")
|
||||
{
|
||||
throw new UniGLTFException("unknown gltf version {0}", GLTF.asset.version);
|
||||
}
|
||||
|
||||
// Version Compatibility
|
||||
RestoreOlderVersionValues();
|
||||
|
||||
// parepare byte buffer
|
||||
//GLTF.baseDir = System.IO.Path.GetDirectoryName(Path);
|
||||
foreach (var buffer in GLTF.buffers)
|
||||
{
|
||||
buffer.OpenStorage(storage);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < GLTF.meshes.Count; ++i)
|
||||
{
|
||||
var mesh = GLTF.meshes[i];
|
||||
try
|
||||
{
|
||||
for (int j = 0; j < mesh.primitives.Count; ++j)
|
||||
{
|
||||
var primitive = mesh.primitives[j];
|
||||
for (int k = 0; k < primitive.targets.Count; ++k)
|
||||
{
|
||||
var extraName = parsed["meshes"][i]["primitives"][j]["targets"][k]["extra"]["name"].Value.GetString();
|
||||
//Debug.LogFormat("restore morphName: {0}", extraName);
|
||||
primitive.extras.targetNames.Add(extraName);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
#if false
|
||||
for (int i = 0; i < GLTF.nodes.Count; ++i)
|
||||
{
|
||||
var node = GLTF.nodes[i];
|
||||
try
|
||||
{
|
||||
var extra = parsed["nodes"][i]["extra"]["skinRootBone"].AsInt;
|
||||
//Debug.LogFormat("restore extra: {0}", extra);
|
||||
//node.extras.skinRootBone = extra;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Load. Build unity objects
|
||||
/// <summary>
|
||||
/// ReadAllBytes, Parse, Create GameObject
|
||||
/// </summary>
|
||||
/// <param name="path">allbytes</param>
|
||||
public void Load(string path)
|
||||
{
|
||||
var bytes = File.ReadAllBytes(path);
|
||||
Load(path, bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse, Create GameObject
|
||||
/// </summary>
|
||||
/// <param name="path">gltf or glb path</param>
|
||||
/// <param name="bytes">allbytes</param>
|
||||
public void Load(string path, byte[] bytes)
|
||||
{
|
||||
Parse(path, bytes);
|
||||
Load();
|
||||
Root.name = Path.GetFileNameWithoutExtension(path);
|
||||
}
|
||||
|
||||
public void CreateTextureItems(UnityPath imageBaseDir = default(UnityPath))
|
||||
{
|
||||
if (m_textures.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < GLTF.textures.Count; ++i)
|
||||
{
|
||||
var image = GLTF.GetImageFromTextureIndex(i);
|
||||
|
||||
TextureItem item = null;
|
||||
#if UNITY_EDITOR
|
||||
if (imageBaseDir.IsUnderAssetsFolder
|
||||
&& !string.IsNullOrEmpty(image.uri)
|
||||
&& !image.uri.StartsWith("data:")
|
||||
)
|
||||
{
|
||||
///
|
||||
/// required SaveTexturesAsPng or SetTextureBaseDir
|
||||
///
|
||||
var assetPath = imageBaseDir.Child(image.uri);
|
||||
var textureName = !string.IsNullOrEmpty(image.name) ? image.name : Path.GetFileNameWithoutExtension(image.uri);
|
||||
item = new TextureItem(i, assetPath, textureName);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
item = new TextureItem(i);
|
||||
}
|
||||
|
||||
AddTexture(item);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build unity objects from parsed gltf
|
||||
/// </summary>
|
||||
public void Load()
|
||||
{
|
||||
var schedulable = LoadAsync();
|
||||
schedulable.ExecuteAll();
|
||||
}
|
||||
|
||||
[Obsolete("Action<Unit> to Action")]
|
||||
public IEnumerator LoadCoroutine(Action<Unit> onLoaded, Action<Exception> onError = null)
|
||||
{
|
||||
return LoadCoroutine(() => onLoaded(Unit.Default), onError);
|
||||
}
|
||||
|
||||
public IEnumerator LoadCoroutine(Action<Exception> onError = null)
|
||||
{
|
||||
return LoadCoroutine(() => { }, onError);
|
||||
}
|
||||
|
||||
public IEnumerator LoadCoroutine(Action onLoaded, Action<Exception> onError = null)
|
||||
{
|
||||
if (onLoaded == null)
|
||||
{
|
||||
onLoaded = () => { };
|
||||
}
|
||||
|
||||
if (onError == null)
|
||||
{
|
||||
onError = Debug.LogError;
|
||||
}
|
||||
|
||||
var schedulable = LoadAsync();
|
||||
foreach (var x in schedulable.GetRoot().Traverse())
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var status = x.Execute();
|
||||
if (status != ExecutionStatus.Continue)
|
||||
{
|
||||
break;
|
||||
}
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
onLoaded();
|
||||
}
|
||||
|
||||
[Obsolete("Action<Unit> to Action")]
|
||||
public void LoadAsync(Action<Unit> onLoaded, Action<Exception> onError = null)
|
||||
{
|
||||
LoadAsync(() => onLoaded(Unit.Default), onError);
|
||||
}
|
||||
|
||||
public void LoadAsync(Action onLoaded, Action<Exception> onError = null)
|
||||
{
|
||||
if (onError == null)
|
||||
{
|
||||
onError = Debug.LogError;
|
||||
}
|
||||
|
||||
LoadAsync()
|
||||
.Subscribe(Scheduler.MainThread,
|
||||
_ => onLoaded(),
|
||||
onError
|
||||
);
|
||||
}
|
||||
|
||||
#if ((NET_4_6 || NET_STANDARD_2_0) && UNITY_2017_1_OR_NEWER)
|
||||
public async Task<GameObject> LoadAsyncTask()
|
||||
{
|
||||
await LoadAsync().ToTask();
|
||||
return Root;
|
||||
}
|
||||
#endif
|
||||
|
||||
protected virtual Schedulable<Unit> LoadAsync()
|
||||
{
|
||||
return
|
||||
Schedulable.Create()
|
||||
.AddTask(Scheduler.ThreadPool, () =>
|
||||
{
|
||||
if (m_textures.Count == 0)
|
||||
{
|
||||
//
|
||||
// runtime
|
||||
//
|
||||
CreateTextureItems();
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// already CreateTextures(by assetPostProcessor or editor menu)
|
||||
//
|
||||
}
|
||||
})
|
||||
.ContinueWithCoroutine(Scheduler.ThreadPool, TexturesProcessOnAnyThread)
|
||||
.ContinueWithCoroutine(Scheduler.MainThread, TexturesProcessOnMainThread)
|
||||
.ContinueWithCoroutine(Scheduler.MainThread, LoadMaterials)
|
||||
.OnExecute(Scheduler.ThreadPool, parent =>
|
||||
{
|
||||
if (GLTF.meshes
|
||||
.SelectMany(x => x.primitives)
|
||||
.Any(x => x.extensions.KHR_draco_mesh_compression != null))
|
||||
{
|
||||
throw new UniGLTFNotSupportedException("draco is not supported");
|
||||
}
|
||||
|
||||
// meshes
|
||||
var meshImporter = new MeshImporter();
|
||||
for (int i = 0; i < GLTF.meshes.Count; ++i)
|
||||
{
|
||||
var index = i;
|
||||
parent.AddTask(Scheduler.ThreadPool,
|
||||
() =>
|
||||
{
|
||||
using (MeasureTime("ReadMesh"))
|
||||
{
|
||||
return meshImporter.ReadMesh(this, index);
|
||||
}
|
||||
})
|
||||
.ContinueWith(Scheduler.MainThread, x =>
|
||||
{
|
||||
using (MeasureTime("BuildMesh"))
|
||||
{
|
||||
var meshWithMaterials = MeshImporter.BuildMesh(this, x);
|
||||
|
||||
var mesh = meshWithMaterials.Mesh;
|
||||
|
||||
// mesh name
|
||||
if (string.IsNullOrEmpty(mesh.name))
|
||||
{
|
||||
mesh.name = string.Format("UniGLTF import#{0}", i);
|
||||
}
|
||||
var originalName = mesh.name;
|
||||
for (int j = 1; Meshes.Any(y => y.Mesh.name == mesh.name); ++j)
|
||||
{
|
||||
mesh.name = string.Format("{0}({1})", originalName, j);
|
||||
}
|
||||
|
||||
return meshWithMaterials;
|
||||
}
|
||||
})
|
||||
.ContinueWith(Scheduler.ThreadPool, x => Meshes.Add(x))
|
||||
;
|
||||
}
|
||||
})
|
||||
.ContinueWithCoroutine(Scheduler.MainThread, LoadNodes)
|
||||
.ContinueWithCoroutine(Scheduler.MainThread, BuildHierarchy)
|
||||
.ContinueWith(Scheduler.MainThread, _ =>
|
||||
{
|
||||
using (MeasureTime("AnimationImporter"))
|
||||
{
|
||||
AnimationImporter.ImportAnimation(this);
|
||||
}
|
||||
})
|
||||
.ContinueWith(Scheduler.CurrentThread,
|
||||
_ =>
|
||||
{
|
||||
OnLoadModel();
|
||||
if (m_showSpeedLog)
|
||||
{
|
||||
Debug.Log(GetSpeedLog());
|
||||
}
|
||||
return Unit.Default;
|
||||
});
|
||||
}
|
||||
|
||||
protected virtual void OnLoadModel()
|
||||
{
|
||||
Root.name = "GLTF";
|
||||
}
|
||||
|
||||
IEnumerator TexturesProcessOnAnyThread()
|
||||
{
|
||||
using (MeasureTime("TexturesProcessOnAnyThread"))
|
||||
{
|
||||
foreach (var x in GetTextures())
|
||||
{
|
||||
x.ProcessOnAnyThread(GLTF, Storage);
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator TexturesProcessOnMainThread()
|
||||
{
|
||||
using (MeasureTime("TexturesProcessOnMainThread"))
|
||||
{
|
||||
foreach (var x in GetTextures())
|
||||
{
|
||||
yield return x.ProcessOnMainThreadCoroutine(GLTF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator LoadMaterials()
|
||||
{
|
||||
using (MeasureTime("LoadMaterials"))
|
||||
{
|
||||
if (GLTF.materials == null || !GLTF.materials.Any())
|
||||
{
|
||||
AddMaterial(MaterialImporter.CreateMaterial(0, null));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < GLTF.materials.Count; ++i)
|
||||
{
|
||||
AddMaterial(MaterialImporter.CreateMaterial(i, GLTF.materials[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
yield return null;
|
||||
}
|
||||
|
||||
IEnumerator LoadMeshes()
|
||||
{
|
||||
var meshImporter = new MeshImporter();
|
||||
for (int i = 0; i < GLTF.meshes.Count; ++i)
|
||||
{
|
||||
var meshContext = meshImporter.ReadMesh(this, i);
|
||||
var meshWithMaterials = MeshImporter.BuildMesh(this, meshContext);
|
||||
var mesh = meshWithMaterials.Mesh;
|
||||
if (string.IsNullOrEmpty(mesh.name))
|
||||
{
|
||||
mesh.name = string.Format("UniGLTF import#{0}", i);
|
||||
}
|
||||
Meshes.Add(meshWithMaterials);
|
||||
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator LoadNodes()
|
||||
{
|
||||
using (MeasureTime("LoadNodes"))
|
||||
{
|
||||
foreach (var x in GLTF.nodes)
|
||||
{
|
||||
Nodes.Add(NodeImporter.ImportNode(x).transform);
|
||||
}
|
||||
}
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
IEnumerator BuildHierarchy()
|
||||
{
|
||||
using (MeasureTime("BuildHierarchy"))
|
||||
{
|
||||
var nodes = new List<NodeImporter.TransformWithSkin>();
|
||||
for (int i = 0; i < Nodes.Count; ++i)
|
||||
{
|
||||
nodes.Add(NodeImporter.BuildHierarchy(this, i));
|
||||
}
|
||||
|
||||
NodeImporter.FixCoordinate(this, nodes);
|
||||
|
||||
// skinning
|
||||
for (int i = 0; i < nodes.Count; ++i)
|
||||
{
|
||||
NodeImporter.SetupSkinning(this, nodes, i);
|
||||
}
|
||||
|
||||
// connect root
|
||||
Root = new GameObject("_root_");
|
||||
foreach (var x in GLTF.rootnodes)
|
||||
{
|
||||
var t = nodes[x].Transform;
|
||||
t.SetParent(Root.transform, false);
|
||||
}
|
||||
}
|
||||
|
||||
yield return null;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Imported
|
||||
public GameObject Root;
|
||||
public List<Transform> Nodes = new List<Transform>();
|
||||
|
||||
List<TextureItem> m_textures = new List<TextureItem>();
|
||||
public IList<TextureItem> GetTextures()
|
||||
{
|
||||
return m_textures;
|
||||
}
|
||||
public TextureItem GetTexture(int i)
|
||||
{
|
||||
if (i < 0 || i >= m_textures.Count)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return m_textures[i];
|
||||
}
|
||||
public void AddTexture(TextureItem item)
|
||||
{
|
||||
m_textures.Add(item);
|
||||
}
|
||||
|
||||
List<Material> m_materials = new List<Material>();
|
||||
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 IList<Material> GetMaterials()
|
||||
{
|
||||
return m_materials;
|
||||
}
|
||||
public Material GetMaterial(int index)
|
||||
{
|
||||
if (index < 0) return null;
|
||||
if (index >= m_materials.Count) return null;
|
||||
return m_materials[index];
|
||||
}
|
||||
|
||||
public List<MeshWithMaterials> Meshes = new List<MeshWithMaterials>();
|
||||
public void ShowMeshes()
|
||||
{
|
||||
foreach (var x in Meshes)
|
||||
{
|
||||
foreach(var y in x.Renderers)
|
||||
{
|
||||
y.enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void EnableUpdateWhenOffscreen()
|
||||
{
|
||||
foreach (var x in Meshes)
|
||||
{
|
||||
foreach (var r in x.Renderers)
|
||||
{
|
||||
var skinnedMeshRenderer = r as SkinnedMeshRenderer;
|
||||
if (skinnedMeshRenderer != null)
|
||||
{
|
||||
skinnedMeshRenderer.updateWhenOffscreen = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<AnimationClip> AnimationClips = new List<AnimationClip>();
|
||||
#endregion
|
||||
|
||||
protected virtual IEnumerable<UnityEngine.Object> ObjectsForSubAsset()
|
||||
{
|
||||
HashSet<Texture2D> textures = new HashSet<Texture2D>();
|
||||
foreach (var x in m_textures.SelectMany(y => y.GetTexturesForSaveAssets()))
|
||||
{
|
||||
if (!textures.Contains(x))
|
||||
{
|
||||
textures.Add(x);
|
||||
}
|
||||
}
|
||||
foreach (var x in textures) { yield return x; }
|
||||
foreach (var x in m_materials) { yield return x; }
|
||||
foreach (var x in Meshes) { yield return x.Mesh; }
|
||||
foreach (var x in AnimationClips) { yield return x; }
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
#region Assets
|
||||
public bool MeshAsSubAsset = false;
|
||||
|
||||
protected virtual UnityPath GetAssetPath(UnityPath prefabPath, UnityEngine.Object o)
|
||||
{
|
||||
if (o is Material)
|
||||
{
|
||||
var materialDir = prefabPath.GetAssetFolder(".Materials");
|
||||
var materialPath = materialDir.Child(o.name.EscapeFilePath() + ".asset");
|
||||
return materialPath;
|
||||
}
|
||||
else if (o is Texture2D)
|
||||
{
|
||||
var textureDir = prefabPath.GetAssetFolder(".Textures");
|
||||
var texturePath = textureDir.Child(o.name.EscapeFilePath() + ".asset");
|
||||
return texturePath;
|
||||
}
|
||||
else if (o is Mesh && !MeshAsSubAsset)
|
||||
{
|
||||
var meshDir = prefabPath.GetAssetFolder(".Meshes");
|
||||
var meshPath = meshDir.Child(o.name.EscapeFilePath() + ".asset");
|
||||
return meshPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
return default(UnityPath);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool IsOverwrite(UnityEngine.Object o)
|
||||
{
|
||||
if(o is Material)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SaveAsAsset(UnityPath prefabPath)
|
||||
{
|
||||
ShowMeshes();
|
||||
|
||||
//var prefabPath = PrefabPath;
|
||||
if (prefabPath.IsFileExists)
|
||||
{
|
||||
// clear SubAssets
|
||||
foreach (var x in prefabPath.GetSubAssets().Where(x => !(x is GameObject) && !(x is Component)))
|
||||
{
|
||||
GameObject.DestroyImmediate(x, true);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// save sub assets
|
||||
//
|
||||
var paths = new List<UnityPath>(){
|
||||
prefabPath
|
||||
};
|
||||
foreach (var o in ObjectsForSubAsset())
|
||||
{
|
||||
if (o == null) continue;
|
||||
|
||||
var assetPath = GetAssetPath(prefabPath, o);
|
||||
if (!assetPath.IsNull)
|
||||
{
|
||||
if (assetPath.IsFileExists)
|
||||
{
|
||||
if (!IsOverwrite(o))
|
||||
{
|
||||
// 上書きしない
|
||||
Debug.LogWarningFormat("already exists. skip {0}", assetPath);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
assetPath.Parent.EnsureFolder();
|
||||
assetPath.CreateAsset(o);
|
||||
paths.Add(assetPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
// save as subasset
|
||||
prefabPath.AddObjectToAsset(o);
|
||||
}
|
||||
}
|
||||
|
||||
// Create or upate Main Asset
|
||||
if (prefabPath.IsFileExists)
|
||||
{
|
||||
Debug.LogFormat("replace prefab: {0}", prefabPath);
|
||||
var prefab = prefabPath.LoadAsset<GameObject>();
|
||||
PrefabUtility.ReplacePrefab(Root, prefab, ReplacePrefabOptions.ReplaceNameBased);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogFormat("create prefab: {0}", prefabPath);
|
||||
PrefabUtility.CreatePrefab(prefabPath.Value, Root);
|
||||
}
|
||||
foreach (var x in paths)
|
||||
{
|
||||
x.ImportAsset();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract images from glb or gltf out of Assets folder.
|
||||
/// </summary>
|
||||
/// <param name="prefabPath"></param>
|
||||
public void ExtranctImages(UnityPath prefabPath)
|
||||
{
|
||||
var prefabParentDir = prefabPath.Parent;
|
||||
|
||||
// glb buffer
|
||||
var folder = prefabPath.GetAssetFolder(".Textures");
|
||||
|
||||
//
|
||||
// https://answers.unity.com/questions/647615/how-to-update-import-settings-for-newly-created-as.html
|
||||
//
|
||||
int created = 0;
|
||||
//for (int i = 0; i < GLTF.textures.Count; ++i)
|
||||
for (int i = 0; i < GLTF.images.Count; ++i)
|
||||
{
|
||||
folder.EnsureFolder();
|
||||
|
||||
//var x = GLTF.textures[i];
|
||||
var image = GLTF.images[i];
|
||||
var src = Storage.GetPath(image.uri);
|
||||
if (UnityPath.FromFullpath(src).IsUnderAssetsFolder)
|
||||
{
|
||||
// asset is exists.
|
||||
}
|
||||
else
|
||||
{
|
||||
string textureName;
|
||||
var byteSegment = GLTF.GetImageBytes(Storage, i, out textureName);
|
||||
|
||||
// path
|
||||
var dst = folder.Child(textureName + image.GetExt());
|
||||
File.WriteAllBytes(dst.FullPath, byteSegment.ToArray());
|
||||
dst.ImportAsset();
|
||||
|
||||
// make relative path from PrefabParentDir
|
||||
image.uri = dst.Value.Substring(prefabParentDir.Value.Length + 1);
|
||||
++created;
|
||||
}
|
||||
}
|
||||
|
||||
if (created > 0)
|
||||
{
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
CreateTextureItems(prefabParentDir);
|
||||
}
|
||||
#endregion
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// This function is used for clean up after create assets.
|
||||
/// </summary>
|
||||
/// <param name="destroySubAssets">Ambiguous arguments</param>
|
||||
[Obsolete("Use Dispose for runtime loader resource management")]
|
||||
public void Destroy(bool destroySubAssets)
|
||||
{
|
||||
if (Root != null) GameObject.DestroyImmediate(Root);
|
||||
if (destroySubAssets)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
foreach (var o in ObjectsForSubAsset())
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(o, true);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DestroyRootAndResources();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destroy resources that created ImporterContext for runtime load.
|
||||
/// </summary>
|
||||
public void DestroyRootAndResources()
|
||||
{
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
Debug.LogWarningFormat("Dispose called in editor mode. This function is for runtime");
|
||||
}
|
||||
|
||||
// Remove hierarchy
|
||||
if (Root != null) GameObject.Destroy(Root);
|
||||
|
||||
// Remove resources. materials, textures meshes etc...
|
||||
foreach (var o in ObjectsForSubAsset())
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(o, true);
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
/// <summary>
|
||||
/// Destroy the GameObject that became the basis of Prefab
|
||||
/// </summary>
|
||||
public void EditorDestroyRoot()
|
||||
{
|
||||
if (Root != null) GameObject.DestroyImmediate(Root);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destroy assets that created ImporterContext. This function is clean up for imoprter error.
|
||||
/// </summary>
|
||||
public void EditorDestroyRootAndAssets()
|
||||
{
|
||||
// Remove hierarchy
|
||||
if (Root != null) GameObject.DestroyImmediate(Root);
|
||||
|
||||
// Remove resources. materials, textures meshes etc...
|
||||
foreach (var o in ObjectsForSubAsset())
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(o, true);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
13
UniGLTF/Core/Scripts/IO/ImporterContext.cs.meta
Normal file
13
UniGLTF/Core/Scripts/IO/ImporterContext.cs.meta
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8d2d07b67753fb0489decee9926ab2da
|
||||
timeCreated: 1517123236
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
268
UniGLTF/Core/Scripts/IO/MaterialExporter.cs
Normal file
268
UniGLTF/Core/Scripts/IO/MaterialExporter.cs
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UniGLTF.UniUnlit;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public enum glTFBlendMode
|
||||
{
|
||||
OPAQUE,
|
||||
MASK,
|
||||
BLEND
|
||||
}
|
||||
|
||||
public interface IMaterialExporter
|
||||
{
|
||||
glTFMaterial ExportMaterial(Material m, TextureExportManager textureManager);
|
||||
}
|
||||
|
||||
public class MaterialExporter : IMaterialExporter
|
||||
{
|
||||
public virtual glTFMaterial ExportMaterial(Material m, TextureExportManager textureManager)
|
||||
{
|
||||
var material = CreateMaterial(m);
|
||||
|
||||
// common params
|
||||
material.name = m.name;
|
||||
Export_Color(m, textureManager, material);
|
||||
Export_Metallic(m, textureManager, material);
|
||||
Export_Normal(m, textureManager, material);
|
||||
Export_Occlusion(m, textureManager, material);
|
||||
Export_Emission(m, textureManager, material);
|
||||
|
||||
return material;
|
||||
}
|
||||
|
||||
static void Export_Color(Material m, TextureExportManager textureManager, glTFMaterial material)
|
||||
{
|
||||
if (m.HasProperty("_Color"))
|
||||
{
|
||||
material.pbrMetallicRoughness.baseColorFactor = m.color.ToArray();
|
||||
}
|
||||
|
||||
if (m.HasProperty("_MainTex"))
|
||||
{
|
||||
var index = textureManager.CopyAndGetIndex(m.GetTexture("_MainTex"), RenderTextureReadWrite.sRGB);
|
||||
if (index != -1)
|
||||
{
|
||||
material.pbrMetallicRoughness.baseColorTexture = new glTFMaterialBaseColorTextureInfo()
|
||||
{
|
||||
index = index,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Export_Metallic(Material m, TextureExportManager textureManager, glTFMaterial material)
|
||||
{
|
||||
int index = -1;
|
||||
if (m.HasProperty("_MetallicGlossMap"))
|
||||
{
|
||||
index = textureManager.ConvertAndGetIndex(m.GetTexture("_MetallicGlossMap"), new MetallicRoughnessConverter());
|
||||
if (index != -1)
|
||||
{
|
||||
material.pbrMetallicRoughness.metallicRoughnessTexture = new glTFMaterialMetallicRoughnessTextureInfo()
|
||||
{
|
||||
index = index,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (index != -1 && m.HasProperty("_GlossMapScale"))
|
||||
{
|
||||
material.pbrMetallicRoughness.metallicFactor = 1.0f;
|
||||
material.pbrMetallicRoughness.roughnessFactor = 1.0f - m.GetFloat("_GlossMapScale");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m.HasProperty("_Metallic"))
|
||||
{
|
||||
material.pbrMetallicRoughness.metallicFactor = m.GetFloat("_Metallic");
|
||||
}
|
||||
if (m.HasProperty("_Glossiness"))
|
||||
{
|
||||
material.pbrMetallicRoughness.roughnessFactor = 1.0f - m.GetFloat("_Glossiness");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void Export_Normal(Material m, TextureExportManager textureManager, glTFMaterial material)
|
||||
{
|
||||
if (m.HasProperty("_BumpMap"))
|
||||
{
|
||||
var index = textureManager.ConvertAndGetIndex(m.GetTexture("_BumpMap"), new NormalConverter());
|
||||
if (index != -1)
|
||||
{
|
||||
material.normalTexture = new glTFMaterialNormalTextureInfo()
|
||||
{
|
||||
index = index,
|
||||
};
|
||||
}
|
||||
|
||||
if (index != -1 && m.HasProperty("_BumpScale"))
|
||||
{
|
||||
material.normalTexture.scale = m.GetFloat("_BumpScale");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Export_Occlusion(Material m, TextureExportManager textureManager, glTFMaterial material)
|
||||
{
|
||||
if (m.HasProperty("_OcclusionMap"))
|
||||
{
|
||||
var index = textureManager.ConvertAndGetIndex(m.GetTexture("_OcclusionMap"), new OcclusionConverter());
|
||||
if (index != -1)
|
||||
{
|
||||
material.occlusionTexture = new glTFMaterialOcclusionTextureInfo()
|
||||
{
|
||||
index = index,
|
||||
};
|
||||
}
|
||||
|
||||
if (index != -1 && m.HasProperty("_OcclusionStrength"))
|
||||
{
|
||||
material.occlusionTexture.strength = m.GetFloat("_OcclusionStrength");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Export_Emission(Material m, TextureExportManager textureManager, glTFMaterial material)
|
||||
{
|
||||
if (m.HasProperty("_EmissionColor"))
|
||||
{
|
||||
var color = m.GetColor("_EmissionColor");
|
||||
material.emissiveFactor = new float[] { color.r, color.g, color.b };
|
||||
}
|
||||
|
||||
if (m.HasProperty("_EmissionMap"))
|
||||
{
|
||||
var index = textureManager.CopyAndGetIndex(m.GetTexture("_EmissionMap"), RenderTextureReadWrite.sRGB);
|
||||
if (index != -1)
|
||||
{
|
||||
material.emissiveTexture = new glTFMaterialEmissiveTextureInfo()
|
||||
{
|
||||
index = index,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual glTFMaterial CreateMaterial(Material m)
|
||||
{
|
||||
switch (m.shader.name)
|
||||
{
|
||||
case "Unlit/Color":
|
||||
return Export_UnlitColor(m);
|
||||
|
||||
case "Unlit/Texture":
|
||||
return Export_UnlitTexture(m);
|
||||
|
||||
case "Unlit/Transparent":
|
||||
return Export_UnlitTransparent(m);
|
||||
|
||||
case "Unlit/Transparent Cutout":
|
||||
return Export_UnlitCutout(m);
|
||||
|
||||
case "UniGLTF/UniUnlit":
|
||||
return Export_UniUnlit(m);
|
||||
|
||||
default:
|
||||
return Export_Standard(m);
|
||||
}
|
||||
}
|
||||
|
||||
static glTFMaterial Export_UnlitColor(Material m)
|
||||
{
|
||||
var material = glTF_KHR_materials_unlit.CreateDefault();
|
||||
material.alphaMode = glTFBlendMode.OPAQUE.ToString();
|
||||
return material;
|
||||
}
|
||||
|
||||
static glTFMaterial Export_UnlitTexture(Material m)
|
||||
{
|
||||
var material = glTF_KHR_materials_unlit.CreateDefault();
|
||||
material.alphaMode = glTFBlendMode.OPAQUE.ToString();
|
||||
return material;
|
||||
}
|
||||
|
||||
static glTFMaterial Export_UnlitTransparent(Material m)
|
||||
{
|
||||
var material = glTF_KHR_materials_unlit.CreateDefault();
|
||||
material.alphaMode = glTFBlendMode.BLEND.ToString();
|
||||
return material;
|
||||
}
|
||||
|
||||
static glTFMaterial Export_UnlitCutout(Material m)
|
||||
{
|
||||
var material = glTF_KHR_materials_unlit.CreateDefault();
|
||||
material.alphaMode = glTFBlendMode.MASK.ToString();
|
||||
material.alphaCutoff = m.GetFloat("_Cutoff");
|
||||
return material;
|
||||
}
|
||||
|
||||
private glTFMaterial Export_UniUnlit(Material m)
|
||||
{
|
||||
var material = glTF_KHR_materials_unlit.CreateDefault();
|
||||
|
||||
var renderMode = UniUnlit.Utils.GetRenderMode(m);
|
||||
if (renderMode == UniUnlitRenderMode.Opaque)
|
||||
{
|
||||
material.alphaMode = glTFBlendMode.OPAQUE.ToString();
|
||||
}
|
||||
else if (renderMode == UniUnlitRenderMode.Transparent)
|
||||
{
|
||||
material.alphaMode = glTFBlendMode.BLEND.ToString();
|
||||
}
|
||||
else if (renderMode == UniUnlitRenderMode.Cutout)
|
||||
{
|
||||
material.alphaMode = glTFBlendMode.MASK.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
material.alphaMode = glTFBlendMode.OPAQUE.ToString();
|
||||
}
|
||||
|
||||
var cullMode = UniUnlit.Utils.GetCullMode(m);
|
||||
if (cullMode == UniUnlitCullMode.Off)
|
||||
{
|
||||
material.doubleSided = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
material.doubleSided = false;
|
||||
}
|
||||
|
||||
return material;
|
||||
}
|
||||
|
||||
static glTFMaterial Export_Standard(Material m)
|
||||
{
|
||||
var material = new glTFMaterial
|
||||
{
|
||||
pbrMetallicRoughness = new glTFPbrMetallicRoughness(),
|
||||
};
|
||||
|
||||
switch (m.GetTag("RenderType", true))
|
||||
{
|
||||
case "Transparent":
|
||||
material.alphaMode = glTFBlendMode.BLEND.ToString();
|
||||
break;
|
||||
|
||||
case "TransparentCutout":
|
||||
material.alphaMode = glTFBlendMode.MASK.ToString();
|
||||
material.alphaCutoff = m.GetFloat("_Cutoff");
|
||||
break;
|
||||
|
||||
default:
|
||||
material.alphaMode = glTFBlendMode.OPAQUE.ToString();
|
||||
break;
|
||||
}
|
||||
|
||||
return material;
|
||||
}
|
||||
}
|
||||
}
|
||||
12
UniGLTF/Core/Scripts/IO/MaterialExporter.cs.meta
Normal file
12
UniGLTF/Core/Scripts/IO/MaterialExporter.cs.meta
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 99662edfbf59e8f458bcba3b62b13050
|
||||
timeCreated: 1533622882
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
262
UniGLTF/Core/Scripts/IO/MaterialImporter.cs
Normal file
262
UniGLTF/Core/Scripts/IO/MaterialImporter.cs
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
using UnityEngine;
|
||||
using System;
|
||||
using UnityEngine.Rendering;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public interface IMaterialImporter
|
||||
{
|
||||
Material CreateMaterial(int i, glTFMaterial src);
|
||||
}
|
||||
|
||||
public class MaterialImporter : IMaterialImporter
|
||||
{
|
||||
IShaderStore m_shaderStore;
|
||||
|
||||
ImporterContext m_context;
|
||||
protected ImporterContext Context
|
||||
{
|
||||
get { return m_context; }
|
||||
}
|
||||
|
||||
public MaterialImporter(IShaderStore shaderStore, ImporterContext context)
|
||||
{
|
||||
m_shaderStore = shaderStore;
|
||||
m_context = context;
|
||||
}
|
||||
|
||||
private enum BlendMode
|
||||
{
|
||||
Opaque,
|
||||
Cutout,
|
||||
Fade,
|
||||
Transparent
|
||||
}
|
||||
|
||||
/// StandardShader vaiables
|
||||
///
|
||||
/// _Color
|
||||
/// _MainTex
|
||||
/// _Cutoff
|
||||
/// _Glossiness
|
||||
/// _Metallic
|
||||
/// _MetallicGlossMap
|
||||
/// _BumpScale
|
||||
/// _BumpMap
|
||||
/// _Parallax
|
||||
/// _ParallaxMap
|
||||
/// _OcclusionStrength
|
||||
/// _OcclusionMap
|
||||
/// _EmissionColor
|
||||
/// _EmissionMap
|
||||
/// _DetailMask
|
||||
/// _DetailAlbedoMap
|
||||
/// _DetailNormalMapScale
|
||||
/// _DetailNormalMap
|
||||
/// _UVSec
|
||||
/// _EmissionScaleUI
|
||||
/// _EmissionColorUI
|
||||
/// _Mode
|
||||
/// _SrcBlend
|
||||
/// _DstBlend
|
||||
/// _ZWrite
|
||||
public virtual Material CreateMaterial(int i, glTFMaterial x)
|
||||
{
|
||||
var shader = m_shaderStore.GetShader(x);
|
||||
//Debug.LogFormat("[{0}]{1}", i, shader.name);
|
||||
var material = new Material(shader);
|
||||
#if UNITY_EDITOR
|
||||
// textureImporter.SaveAndReimport(); may destory this material
|
||||
material.hideFlags = HideFlags.DontUnloadUnusedAsset;
|
||||
#endif
|
||||
|
||||
material.name = (x == null || string.IsNullOrEmpty(x.name))
|
||||
? string.Format("material_{0:00}", i)
|
||||
: x.name
|
||||
;
|
||||
|
||||
if (x == null)
|
||||
{
|
||||
Debug.LogWarning("glTFMaterial is empty");
|
||||
return material;
|
||||
}
|
||||
|
||||
// unlit material
|
||||
if (x.extensions != null && x.extensions.KHR_materials_unlit != null)
|
||||
{
|
||||
// texture
|
||||
var texture = m_context.GetTexture(x.pbrMetallicRoughness.baseColorTexture.index);
|
||||
if (texture != null)
|
||||
{
|
||||
material.mainTexture = texture.Texture;
|
||||
}
|
||||
|
||||
// color
|
||||
if (x.pbrMetallicRoughness.baseColorFactor != null && x.pbrMetallicRoughness.baseColorFactor.Length == 4)
|
||||
{
|
||||
var color = x.pbrMetallicRoughness.baseColorFactor;
|
||||
material.color = new Color(color[0], color[1], color[2], color[3]);
|
||||
}
|
||||
|
||||
//renderMode
|
||||
if (x.alphaMode == "OPAQUE")
|
||||
{
|
||||
UniUnlit.Utils.SetRenderMode(material, UniUnlit.UniUnlitRenderMode.Opaque);
|
||||
}
|
||||
else if (x.alphaMode == "BLEND")
|
||||
{
|
||||
UniUnlit.Utils.SetRenderMode(material, UniUnlit.UniUnlitRenderMode.Transparent);
|
||||
}
|
||||
else if(x.alphaMode == "MASK")
|
||||
{
|
||||
UniUnlit.Utils.SetRenderMode(material, UniUnlit.UniUnlitRenderMode.Cutout);
|
||||
}
|
||||
else
|
||||
{
|
||||
// default OPAQUE
|
||||
UniUnlit.Utils.SetRenderMode(material, UniUnlit.UniUnlitRenderMode.Opaque);
|
||||
}
|
||||
|
||||
// culling
|
||||
if (x.doubleSided)
|
||||
{
|
||||
UniUnlit.Utils.SetCullMode(material, UniUnlit.UniUnlitCullMode.Off);
|
||||
}
|
||||
else
|
||||
{
|
||||
UniUnlit.Utils.SetCullMode(material, UniUnlit.UniUnlitCullMode.Back);
|
||||
}
|
||||
|
||||
UniUnlit.Utils.ValidateProperties(material, true);
|
||||
|
||||
return material;
|
||||
}
|
||||
|
||||
// PBR material
|
||||
if (x.pbrMetallicRoughness != null)
|
||||
{
|
||||
if (x.pbrMetallicRoughness.baseColorFactor != null && x.pbrMetallicRoughness.baseColorFactor.Length == 4)
|
||||
{
|
||||
var color = x.pbrMetallicRoughness.baseColorFactor;
|
||||
material.color = new Color(color[0], color[1], color[2], color[3]);
|
||||
}
|
||||
|
||||
if (x.pbrMetallicRoughness.baseColorTexture != null && x.pbrMetallicRoughness.baseColorTexture.index != -1)
|
||||
{
|
||||
var texture = m_context.GetTexture(x.pbrMetallicRoughness.baseColorTexture.index);
|
||||
if (texture != null)
|
||||
{
|
||||
material.mainTexture = texture.Texture;
|
||||
}
|
||||
}
|
||||
|
||||
if (x.pbrMetallicRoughness.metallicRoughnessTexture != null && x.pbrMetallicRoughness.metallicRoughnessTexture.index != -1)
|
||||
{
|
||||
material.EnableKeyword("_METALLICGLOSSMAP");
|
||||
var texture = Context.GetTexture(x.pbrMetallicRoughness.metallicRoughnessTexture.index);
|
||||
if (texture != null)
|
||||
{
|
||||
var prop = "_MetallicGlossMap";
|
||||
material.SetTexture(prop, texture.ConvertTexture(prop));
|
||||
}
|
||||
}
|
||||
|
||||
material.SetFloat("_Metallic", x.pbrMetallicRoughness.metallicFactor);
|
||||
material.SetFloat("_Glossiness", 1.0f - x.pbrMetallicRoughness.roughnessFactor);
|
||||
}
|
||||
|
||||
if (x.normalTexture != null && x.normalTexture.index != -1)
|
||||
{
|
||||
material.EnableKeyword("_NORMALMAP");
|
||||
var texture = Context.GetTexture(x.normalTexture.index);
|
||||
if (texture != null)
|
||||
{
|
||||
var prop = "_BumpMap";
|
||||
material.SetTexture(prop, texture.ConvertTexture(prop));
|
||||
material.SetFloat("_BumpScale", x.normalTexture.scale);
|
||||
}
|
||||
}
|
||||
|
||||
if (x.occlusionTexture != null && x.occlusionTexture.index != -1)
|
||||
{
|
||||
var texture = Context.GetTexture(x.occlusionTexture.index);
|
||||
if (texture != null)
|
||||
{
|
||||
var prop = "_OcclusionMap";
|
||||
material.SetTexture(prop, texture.ConvertTexture(prop));
|
||||
material.SetFloat("_OcclusionStrength", x.occlusionTexture.strength);
|
||||
}
|
||||
}
|
||||
|
||||
if (x.emissiveFactor != null
|
||||
|| (x.emissiveTexture != null && x.emissiveTexture.index != -1))
|
||||
{
|
||||
material.EnableKeyword("_EMISSION");
|
||||
material.globalIlluminationFlags &= ~MaterialGlobalIlluminationFlags.EmissiveIsBlack;
|
||||
|
||||
if (x.emissiveFactor != null && x.emissiveFactor.Length == 3)
|
||||
{
|
||||
material.SetColor("_EmissionColor", new Color(x.emissiveFactor[0], x.emissiveFactor[1], x.emissiveFactor[2]));
|
||||
}
|
||||
|
||||
if (x.emissiveTexture.index != -1)
|
||||
{
|
||||
var texture = Context.GetTexture(x.emissiveTexture.index);
|
||||
if (texture != null)
|
||||
{
|
||||
material.SetTexture("_EmissionMap", texture.Texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BlendMode blendMode = BlendMode.Opaque;
|
||||
// https://forum.unity.com/threads/standard-material-shader-ignoring-setfloat-property-_mode.344557/#post-2229980
|
||||
switch (x.alphaMode)
|
||||
{
|
||||
case "BLEND":
|
||||
blendMode = BlendMode.Fade;
|
||||
material.SetOverrideTag("RenderType", "Transparent");
|
||||
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
|
||||
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
|
||||
material.SetInt("_ZWrite", 0);
|
||||
material.DisableKeyword("_ALPHATEST_ON");
|
||||
material.EnableKeyword("_ALPHABLEND_ON");
|
||||
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
|
||||
material.renderQueue = 3000;
|
||||
break;
|
||||
|
||||
case "MASK":
|
||||
blendMode = BlendMode.Cutout;
|
||||
material.SetOverrideTag("RenderType", "TransparentCutout");
|
||||
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
|
||||
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
|
||||
material.SetInt("_ZWrite", 1);
|
||||
material.EnableKeyword("_ALPHATEST_ON");
|
||||
material.DisableKeyword("_ALPHABLEND_ON");
|
||||
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
|
||||
material.renderQueue = 2450;
|
||||
|
||||
break;
|
||||
|
||||
default: // OPAQUE
|
||||
blendMode = BlendMode.Opaque;
|
||||
material.SetOverrideTag("RenderType", "");
|
||||
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
|
||||
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
|
||||
material.SetInt("_ZWrite", 1);
|
||||
material.DisableKeyword("_ALPHATEST_ON");
|
||||
material.DisableKeyword("_ALPHABLEND_ON");
|
||||
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
|
||||
material.renderQueue = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
material.SetFloat("_Mode", (float)blendMode);
|
||||
return material;
|
||||
}
|
||||
}
|
||||
}
|
||||
12
UniGLTF/Core/Scripts/IO/MaterialImporter.cs.meta
Normal file
12
UniGLTF/Core/Scripts/IO/MaterialImporter.cs.meta
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7ce22d10dc7cdc647b05c0449173ce71
|
||||
timeCreated: 1533624185
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
261
UniGLTF/Core/Scripts/IO/MeshExporter.cs
Normal file
261
UniGLTF/Core/Scripts/IO/MeshExporter.cs
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public struct MeshWithRenderer
|
||||
{
|
||||
public Mesh Mesh;
|
||||
public Renderer Rendererer;
|
||||
}
|
||||
|
||||
public static class MeshExporter
|
||||
{
|
||||
static glTFMesh ExportPrimitives(glTF gltf, int bufferIndex,
|
||||
string rendererName,
|
||||
Mesh mesh, Material[] materials,
|
||||
List<Material> unityMaterials)
|
||||
{
|
||||
var positions = mesh.vertices.Select(y => y.ReverseZ()).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.ReverseZ()).ToArray(), glBufferTarget.ARRAY_BUFFER);
|
||||
#if GLTF_EXPORT_TANGENTS
|
||||
var tangentAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, mesh.tangents.Select(y => y.ReverseZ()).ToArray(), glBufferTarget.ARRAY_BUFFER);
|
||||
#endif
|
||||
var uvAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, mesh.uv.Select(y => y.ReverseUV()).ToArray(), glBufferTarget.ARRAY_BUFFER);
|
||||
var colorAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, mesh.colors, glBufferTarget.ARRAY_BUFFER);
|
||||
|
||||
var boneweights = mesh.boneWeights;
|
||||
var weightAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, boneweights.Select(y => new Vector4(y.weight0, y.weight1, y.weight2, y.weight3)).ToArray(), glBufferTarget.ARRAY_BUFFER);
|
||||
var jointsAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, boneweights.Select(y => new UShort4((ushort)y.boneIndex0, (ushort)y.boneIndex1, (ushort)y.boneIndex2, (ushort)y.boneIndex3)).ToArray(), glBufferTarget.ARRAY_BUFFER);
|
||||
|
||||
var attributes = new glTFAttributes
|
||||
{
|
||||
POSITION = positionAccessorIndex,
|
||||
};
|
||||
if (normalAccessorIndex != -1)
|
||||
{
|
||||
attributes.NORMAL = normalAccessorIndex;
|
||||
}
|
||||
#if GLTF_EXPORT_TANGENTS
|
||||
if (tangentAccessorIndex != -1)
|
||||
{
|
||||
attributes.TANGENT = tangentAccessorIndex;
|
||||
}
|
||||
#endif
|
||||
if (uvAccessorIndex != -1)
|
||||
{
|
||||
attributes.TEXCOORD_0 = uvAccessorIndex;
|
||||
}
|
||||
if (colorAccessorIndex != -1)
|
||||
{
|
||||
attributes.COLOR_0 = colorAccessorIndex;
|
||||
}
|
||||
if (weightAccessorIndex != -1)
|
||||
{
|
||||
attributes.WEIGHTS_0 = weightAccessorIndex;
|
||||
}
|
||||
if (jointsAccessorIndex != -1)
|
||||
{
|
||||
attributes.JOINTS_0 = jointsAccessorIndex;
|
||||
}
|
||||
|
||||
var gltfMesh = new glTFMesh(mesh.name);
|
||||
for (int j = 0; j < mesh.subMeshCount; ++j)
|
||||
{
|
||||
var indices = TriangleUtil.FlipTriangle(mesh.GetIndices(j)).Select(y => (uint)y).ToArray();
|
||||
var indicesAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, indices, glBufferTarget.ELEMENT_ARRAY_BUFFER);
|
||||
|
||||
if (j >= materials.Length)
|
||||
{
|
||||
Debug.LogWarningFormat("{0}.materials is not enough", rendererName);
|
||||
break;
|
||||
}
|
||||
|
||||
gltfMesh.primitives.Add(new glTFPrimitives
|
||||
{
|
||||
attributes = attributes,
|
||||
indices = indicesAccessorIndex,
|
||||
mode = 4, // triangels ?
|
||||
material = unityMaterials.IndexOf(materials[j])
|
||||
});
|
||||
}
|
||||
return gltfMesh;
|
||||
}
|
||||
|
||||
static bool UseSparse(
|
||||
bool usePosition, Vector3 position,
|
||||
bool useNormal, Vector3 normal,
|
||||
bool useTangent, Vector3 tangent
|
||||
)
|
||||
{
|
||||
var useSparse =
|
||||
(usePosition && position != Vector3.zero)
|
||||
|| (useNormal && normal != Vector3.zero)
|
||||
|| (useTangent && tangent != Vector3.zero)
|
||||
;
|
||||
return useSparse;
|
||||
}
|
||||
|
||||
static gltfMorphTarget ExportMorphTarget(glTF gltf, int bufferIndex,
|
||||
Mesh mesh, int j,
|
||||
bool useSparseAccessorForMorphTarget)
|
||||
{
|
||||
var blendShapeVertices = mesh.vertices;
|
||||
var usePosition = blendShapeVertices != null && blendShapeVertices.Length > 0;
|
||||
|
||||
var blendShapeNormals = mesh.normals;
|
||||
var useNormal = usePosition && blendShapeNormals != null && blendShapeNormals.Length == blendShapeVertices.Length;
|
||||
|
||||
var blendShapeTangents = mesh.tangents.Select(y => (Vector3)y).ToArray();
|
||||
//var useTangent = usePosition && blendShapeTangents != null && blendShapeTangents.Length == blendShapeVertices.Length;
|
||||
var useTangent = false;
|
||||
|
||||
var frameCount = mesh.GetBlendShapeFrameCount(j);
|
||||
mesh.GetBlendShapeFrameVertices(j, frameCount - 1, blendShapeVertices, blendShapeNormals, null);
|
||||
|
||||
var blendShapePositionAccessorIndex = -1;
|
||||
var blendShapeNormalAccessorIndex = -1;
|
||||
var blendShapeTangentAccessorIndex = -1;
|
||||
if (useSparseAccessorForMorphTarget)
|
||||
{
|
||||
var accessorCount = blendShapeVertices.Length;
|
||||
var sparseIndices = Enumerable.Range(0, blendShapeVertices.Length)
|
||||
.Where(x => UseSparse(
|
||||
usePosition, blendShapeVertices[x],
|
||||
useNormal, blendShapeNormals[x],
|
||||
useTangent, blendShapeTangents[x]))
|
||||
.ToArray()
|
||||
;
|
||||
|
||||
if (sparseIndices.Length == 0)
|
||||
{
|
||||
usePosition = false;
|
||||
useNormal = false;
|
||||
useTangent = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogFormat("Sparse {0}/{1}", sparseIndices.Length, mesh.vertexCount);
|
||||
}
|
||||
/*
|
||||
var vertexSize = 12;
|
||||
if (useNormal) vertexSize += 12;
|
||||
if (useTangent) vertexSize += 24;
|
||||
var sparseBytes = (4 + vertexSize) * sparseIndices.Length;
|
||||
var fullBytes = (vertexSize) * blendShapeVertices.Length;
|
||||
Debug.LogFormat("Export sparse: {0}/{1}bytes({2}%)",
|
||||
sparseBytes, fullBytes, (int)((float)sparseBytes / fullBytes)
|
||||
);
|
||||
*/
|
||||
|
||||
var sparseIndicesViewIndex = -1;
|
||||
if (usePosition)
|
||||
{
|
||||
sparseIndicesViewIndex = gltf.ExtendBufferAndGetViewIndex(bufferIndex, sparseIndices);
|
||||
|
||||
blendShapeVertices = sparseIndices.Select(x => blendShapeVertices[x].ReverseZ()).ToArray();
|
||||
blendShapePositionAccessorIndex = gltf.ExtendSparseBufferAndGetAccessorIndex(bufferIndex, accessorCount,
|
||||
blendShapeVertices,
|
||||
sparseIndices, sparseIndicesViewIndex,
|
||||
glBufferTarget.ARRAY_BUFFER);
|
||||
}
|
||||
|
||||
if (useNormal)
|
||||
{
|
||||
blendShapeNormals = sparseIndices.Select(x => blendShapeNormals[x].ReverseZ()).ToArray();
|
||||
blendShapeNormalAccessorIndex = gltf.ExtendSparseBufferAndGetAccessorIndex(bufferIndex, accessorCount,
|
||||
blendShapeNormals,
|
||||
sparseIndices, sparseIndicesViewIndex,
|
||||
glBufferTarget.ARRAY_BUFFER);
|
||||
}
|
||||
|
||||
if (useTangent)
|
||||
{
|
||||
blendShapeTangents = sparseIndices.Select(x => blendShapeTangents[x].ReverseZ()).ToArray();
|
||||
blendShapeTangentAccessorIndex = gltf.ExtendSparseBufferAndGetAccessorIndex(bufferIndex, accessorCount,
|
||||
blendShapeTangents, sparseIndices, sparseIndicesViewIndex,
|
||||
glBufferTarget.ARRAY_BUFFER);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < blendShapeVertices.Length; ++i) blendShapeVertices[i] = blendShapeVertices[i].ReverseZ();
|
||||
if (usePosition)
|
||||
{
|
||||
blendShapePositionAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex,
|
||||
blendShapeVertices,
|
||||
glBufferTarget.ARRAY_BUFFER);
|
||||
}
|
||||
|
||||
if (useNormal)
|
||||
{
|
||||
for (int i = 0; i < blendShapeNormals.Length; ++i) blendShapeNormals[i] = blendShapeNormals[i].ReverseZ();
|
||||
blendShapeNormalAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex,
|
||||
blendShapeNormals,
|
||||
glBufferTarget.ARRAY_BUFFER);
|
||||
}
|
||||
|
||||
if (useTangent)
|
||||
{
|
||||
for (int i = 0; i < blendShapeTangents.Length; ++i) blendShapeTangents[i] = blendShapeTangents[i].ReverseZ();
|
||||
blendShapeTangentAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex,
|
||||
blendShapeTangents,
|
||||
glBufferTarget.ARRAY_BUFFER);
|
||||
}
|
||||
}
|
||||
|
||||
if (blendShapePositionAccessorIndex != -1)
|
||||
{
|
||||
gltf.accessors[blendShapePositionAccessorIndex].min = blendShapeVertices.Aggregate(blendShapeVertices[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[blendShapePositionAccessorIndex].max = blendShapeVertices.Aggregate(blendShapeVertices[0], (a, b) => new Vector3(Mathf.Max(a.x, b.x), Math.Max(a.y, b.y), Mathf.Max(a.z, b.z))).ToArray();
|
||||
}
|
||||
|
||||
return new gltfMorphTarget
|
||||
{
|
||||
POSITION = blendShapePositionAccessorIndex,
|
||||
NORMAL = blendShapeNormalAccessorIndex,
|
||||
TANGENT = blendShapeTangentAccessorIndex,
|
||||
};
|
||||
}
|
||||
|
||||
public static void ExportMeshes(glTF gltf, int bufferIndex,
|
||||
List<MeshWithRenderer> unityMeshes, List<Material> unityMaterials,
|
||||
bool useSparseAccessorForMorphTarget)
|
||||
{
|
||||
for (int i = 0; i < unityMeshes.Count; ++i)
|
||||
{
|
||||
var x = unityMeshes[i];
|
||||
var mesh = x.Mesh;
|
||||
var materials = x.Rendererer.sharedMaterials;
|
||||
|
||||
var gltfMesh = ExportPrimitives(gltf, bufferIndex,
|
||||
x.Rendererer.name,
|
||||
mesh, materials, unityMaterials);
|
||||
|
||||
for (int j = 0; j < mesh.blendShapeCount; ++j)
|
||||
{
|
||||
var morphTarget = ExportMorphTarget(gltf, bufferIndex,
|
||||
mesh, j,
|
||||
useSparseAccessorForMorphTarget);
|
||||
|
||||
//
|
||||
// all primitive has same blendShape
|
||||
//
|
||||
for (int k = 0; k < gltfMesh.primitives.Count; ++k)
|
||||
{
|
||||
gltfMesh.primitives[k].targets.Add(morphTarget);
|
||||
gltfMesh.primitives[k].extras.targetNames.Add(mesh.GetBlendShapeName(j));
|
||||
}
|
||||
}
|
||||
|
||||
gltf.meshes.Add(gltfMesh);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
UniGLTF/Core/Scripts/IO/MeshExporter.cs.meta
Normal file
12
UniGLTF/Core/Scripts/IO/MeshExporter.cs.meta
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9b5d31a97e5221e43b16cfb4213a4a79
|
||||
timeCreated: 1545139997
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
561
UniGLTF/Core/Scripts/IO/MeshImporter.cs
Normal file
561
UniGLTF/Core/Scripts/IO/MeshImporter.cs
Normal file
|
|
@ -0,0 +1,561 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public class MeshImporter
|
||||
{
|
||||
const float FRAME_WEIGHT = 100.0f;
|
||||
|
||||
|
||||
// multiple submMesh is not sharing a VertexBuffer.
|
||||
// each subMesh use a independent VertexBuffer.
|
||||
private static MeshContext _ImportMeshIndependentVertexBuffer(ImporterContext ctx, glTFMesh gltfMesh)
|
||||
{
|
||||
//Debug.LogWarning("_ImportMeshIndependentVertexBuffer");
|
||||
|
||||
var targets = gltfMesh.primitives[0].targets;
|
||||
for (int i = 1; i < gltfMesh.primitives.Count; ++i)
|
||||
{
|
||||
if (!gltfMesh.primitives[i].targets.SequenceEqual(targets))
|
||||
{
|
||||
throw new NotImplementedException(string.Format("diffirent targets: {0} with {1}",
|
||||
gltfMesh.primitives[i],
|
||||
targets));
|
||||
}
|
||||
}
|
||||
|
||||
var positions = new List<Vector3>();
|
||||
var normals = new List<Vector3>();
|
||||
var tangents = new List<Vector4>();
|
||||
var uv = new List<Vector2>();
|
||||
var colors = new List<Color>();
|
||||
var meshContext = new MeshContext();
|
||||
foreach (var prim in gltfMesh.primitives)
|
||||
{
|
||||
var indexOffset = positions.Count;
|
||||
var indexBuffer = prim.indices;
|
||||
|
||||
var positionCount = positions.Count;
|
||||
positions.AddRange(ctx.GLTF.GetArrayFromAccessor<Vector3>(prim.attributes.POSITION).Select(x => x.ReverseZ()));
|
||||
positionCount = positions.Count - positionCount;
|
||||
|
||||
// normal
|
||||
if (prim.attributes.NORMAL != -1)
|
||||
{
|
||||
normals.AddRange(ctx.GLTF.GetArrayFromAccessor<Vector3>(prim.attributes.NORMAL).Select(x => x.ReverseZ()));
|
||||
}
|
||||
|
||||
if (prim.attributes.TANGENT != -1)
|
||||
{
|
||||
tangents.AddRange(ctx.GLTF.GetArrayFromAccessor<Vector4>(prim.attributes.TANGENT).Select(x => x.ReverseZ()));
|
||||
}
|
||||
|
||||
// uv
|
||||
if (prim.attributes.TEXCOORD_0 != -1)
|
||||
{
|
||||
if (ctx.IsGeneratedUniGLTFAndOlder(1, 16))
|
||||
{
|
||||
#pragma warning disable 0612
|
||||
// backward compatibility
|
||||
uv.AddRange(ctx.GLTF.GetArrayFromAccessor<Vector2>(prim.attributes.TEXCOORD_0).Select(x => x.ReverseY()));
|
||||
#pragma warning restore 0612
|
||||
}
|
||||
else
|
||||
{
|
||||
uv.AddRange(ctx.GLTF.GetArrayFromAccessor<Vector2>(prim.attributes.TEXCOORD_0).Select(x => x.ReverseUV()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// for inconsistent attributes in primitives
|
||||
uv.AddRange(new Vector2[positionCount]);
|
||||
}
|
||||
|
||||
// color
|
||||
if (prim.attributes.COLOR_0 != -1)
|
||||
{
|
||||
colors.AddRange(ctx.GLTF.GetArrayFromAccessor<Color>(prim.attributes.COLOR_0));
|
||||
}
|
||||
|
||||
// skin
|
||||
if (prim.attributes.JOINTS_0 != -1 && prim.attributes.WEIGHTS_0 != -1)
|
||||
{
|
||||
var joints0 = ctx.GLTF.GetArrayFromAccessor<UShort4>(prim.attributes.JOINTS_0); // uint4
|
||||
var weights0 = ctx.GLTF.GetArrayFromAccessor<Float4>(prim.attributes.WEIGHTS_0).Select(x => x.One()).ToArray();
|
||||
|
||||
for (int j = 0; j < joints0.Length; ++j)
|
||||
{
|
||||
var bw = new BoneWeight();
|
||||
|
||||
bw.boneIndex0 = joints0[j].x;
|
||||
bw.weight0 = weights0[j].x;
|
||||
|
||||
bw.boneIndex1 = joints0[j].y;
|
||||
bw.weight1 = weights0[j].y;
|
||||
|
||||
bw.boneIndex2 = joints0[j].z;
|
||||
bw.weight2 = weights0[j].z;
|
||||
|
||||
bw.boneIndex3 = joints0[j].w;
|
||||
bw.weight3 = weights0[j].w;
|
||||
|
||||
meshContext.boneWeights.Add(bw);
|
||||
}
|
||||
}
|
||||
|
||||
// blendshape
|
||||
if (prim.targets != null && prim.targets.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < prim.targets.Count; ++i)
|
||||
{
|
||||
//var name = string.Format("target{0}", i++);
|
||||
var primTarget = prim.targets[i];
|
||||
var blendShape = new BlendShape(!string.IsNullOrEmpty(prim.extras.targetNames[i])
|
||||
? prim.extras.targetNames[i]
|
||||
: i.ToString())
|
||||
;
|
||||
if (primTarget.POSITION != -1)
|
||||
{
|
||||
blendShape.Positions.AddRange(
|
||||
ctx.GLTF.GetArrayFromAccessor<Vector3>(primTarget.POSITION).Select(x => x.ReverseZ()).ToArray());
|
||||
}
|
||||
if (primTarget.NORMAL != -1)
|
||||
{
|
||||
blendShape.Normals.AddRange(
|
||||
ctx.GLTF.GetArrayFromAccessor<Vector3>(primTarget.NORMAL).Select(x => x.ReverseZ()).ToArray());
|
||||
}
|
||||
if (primTarget.TANGENT != -1)
|
||||
{
|
||||
blendShape.Tangents.AddRange(
|
||||
ctx.GLTF.GetArrayFromAccessor<Vector3>(primTarget.TANGENT).Select(x => x.ReverseZ()).ToArray());
|
||||
}
|
||||
meshContext.blendShapes.Add(blendShape);
|
||||
}
|
||||
}
|
||||
|
||||
var indices =
|
||||
(indexBuffer >= 0)
|
||||
? ctx.GLTF.GetIndices(indexBuffer)
|
||||
: TriangleUtil.FlipTriangle(Enumerable.Range(0, meshContext.positions.Length)).ToArray() // without index array
|
||||
;
|
||||
for (int i = 0; i < indices.Length; ++i)
|
||||
{
|
||||
indices[i] += indexOffset;
|
||||
}
|
||||
|
||||
meshContext.subMeshes.Add(indices);
|
||||
|
||||
// material
|
||||
meshContext.materialIndices.Add(prim.material);
|
||||
}
|
||||
|
||||
meshContext.positions = positions.ToArray();
|
||||
meshContext.normals = normals.ToArray();
|
||||
meshContext.tangents = tangents.ToArray();
|
||||
meshContext.uv = uv.ToArray();
|
||||
|
||||
return meshContext;
|
||||
}
|
||||
|
||||
|
||||
// multiple submesh sharing same VertexBuffer
|
||||
private static MeshContext _ImportMeshSharingVertexBuffer(ImporterContext ctx, glTFMesh gltfMesh)
|
||||
{
|
||||
var context = new MeshContext();
|
||||
|
||||
{
|
||||
var prim = gltfMesh.primitives.First();
|
||||
context.positions = ctx.GLTF.GetArrayFromAccessor<Vector3>(prim.attributes.POSITION).SelectInplace(x => x.ReverseZ());
|
||||
|
||||
// normal
|
||||
if (prim.attributes.NORMAL != -1)
|
||||
{
|
||||
context.normals = ctx.GLTF.GetArrayFromAccessor<Vector3>(prim.attributes.NORMAL).SelectInplace(x => x.ReverseZ());
|
||||
}
|
||||
|
||||
// tangent
|
||||
if (prim.attributes.TANGENT != -1)
|
||||
{
|
||||
context.tangents = ctx.GLTF.GetArrayFromAccessor<Vector4>(prim.attributes.TANGENT).SelectInplace(x => x.ReverseZ());
|
||||
}
|
||||
|
||||
// uv
|
||||
if (prim.attributes.TEXCOORD_0 != -1)
|
||||
{
|
||||
if (ctx.IsGeneratedUniGLTFAndOlder(1, 16))
|
||||
{
|
||||
#pragma warning disable 0612
|
||||
// backward compatibility
|
||||
context.uv = ctx.GLTF.GetArrayFromAccessor<Vector2>(prim.attributes.TEXCOORD_0).SelectInplace(x => x.ReverseY());
|
||||
#pragma warning restore 0612
|
||||
}
|
||||
else
|
||||
{
|
||||
context.uv = ctx.GLTF.GetArrayFromAccessor<Vector2>(prim.attributes.TEXCOORD_0).SelectInplace(x => x.ReverseUV());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// for inconsistent attributes in primitives
|
||||
context.uv = new Vector2[context.positions.Length];
|
||||
}
|
||||
|
||||
// color
|
||||
if (prim.attributes.COLOR_0 != -1)
|
||||
{
|
||||
context.colors = ctx.GLTF.GetArrayFromAccessor<Color>(prim.attributes.COLOR_0);
|
||||
}
|
||||
|
||||
// skin
|
||||
if (prim.attributes.JOINTS_0 != -1 && prim.attributes.WEIGHTS_0 != -1)
|
||||
{
|
||||
var joints0 = ctx.GLTF.GetArrayFromAccessor<UShort4>(prim.attributes.JOINTS_0); // uint4
|
||||
var weights0 = ctx.GLTF.GetArrayFromAccessor<Float4>(prim.attributes.WEIGHTS_0);
|
||||
for (int i = 0; i < weights0.Length; ++i)
|
||||
{
|
||||
weights0[i] = weights0[i].One();
|
||||
}
|
||||
|
||||
for (int j = 0; j < joints0.Length; ++j)
|
||||
{
|
||||
var bw = new BoneWeight();
|
||||
|
||||
bw.boneIndex0 = joints0[j].x;
|
||||
bw.weight0 = weights0[j].x;
|
||||
|
||||
bw.boneIndex1 = joints0[j].y;
|
||||
bw.weight1 = weights0[j].y;
|
||||
|
||||
bw.boneIndex2 = joints0[j].z;
|
||||
bw.weight2 = weights0[j].z;
|
||||
|
||||
bw.boneIndex3 = joints0[j].w;
|
||||
bw.weight3 = weights0[j].w;
|
||||
|
||||
context.boneWeights.Add(bw);
|
||||
}
|
||||
}
|
||||
|
||||
// blendshape
|
||||
if (prim.targets != null && prim.targets.Count > 0)
|
||||
{
|
||||
context.blendShapes.AddRange(prim.targets.Select((x, i) => new BlendShape(
|
||||
i < prim.extras.targetNames.Count && !string.IsNullOrEmpty(prim.extras.targetNames[i])
|
||||
? prim.extras.targetNames[i]
|
||||
: i.ToString())));
|
||||
for (int i = 0; i < prim.targets.Count; ++i)
|
||||
{
|
||||
//var name = string.Format("target{0}", i++);
|
||||
var primTarget = prim.targets[i];
|
||||
var blendShape = context.blendShapes[i];
|
||||
|
||||
if (primTarget.POSITION != -1)
|
||||
{
|
||||
blendShape.Positions.Assign(
|
||||
ctx.GLTF.GetArrayFromAccessor<Vector3>(primTarget.POSITION), x => x.ReverseZ());
|
||||
}
|
||||
if (primTarget.NORMAL != -1)
|
||||
{
|
||||
blendShape.Normals.Assign(
|
||||
ctx.GLTF.GetArrayFromAccessor<Vector3>(primTarget.NORMAL), x => x.ReverseZ());
|
||||
}
|
||||
if (primTarget.TANGENT != -1)
|
||||
{
|
||||
blendShape.Tangents.Assign(
|
||||
ctx.GLTF.GetArrayFromAccessor<Vector3>(primTarget.TANGENT), x => x.ReverseZ());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var prim in gltfMesh.primitives)
|
||||
{
|
||||
if (prim.indices == -1)
|
||||
{
|
||||
context.subMeshes.Add(TriangleUtil.FlipTriangle(Enumerable.Range(0, context.positions.Length)).ToArray());
|
||||
}
|
||||
else
|
||||
{
|
||||
var indices = ctx.GLTF.GetIndices(prim.indices);
|
||||
context.subMeshes.Add(indices);
|
||||
}
|
||||
|
||||
// material
|
||||
context.materialIndices.Add(prim.material);
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
|
||||
[Serializable, StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
struct Float4
|
||||
{
|
||||
public float x;
|
||||
public float y;
|
||||
public float z;
|
||||
public float w;
|
||||
|
||||
public Float4 One()
|
||||
{
|
||||
var sum = x + y + z + w;
|
||||
var f = 1.0f / sum;
|
||||
return new Float4
|
||||
{
|
||||
x = x * f,
|
||||
y = y * f,
|
||||
z = z * f,
|
||||
w = w * f,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class MeshContext
|
||||
{
|
||||
public string name;
|
||||
public Vector3[] positions;
|
||||
public Vector3[] normals;
|
||||
public Vector4[] tangents;
|
||||
public Vector2[] uv;
|
||||
public Color[] colors;
|
||||
public List<BoneWeight> boneWeights = new List<BoneWeight>();
|
||||
public List<int[]> subMeshes = new List<int[]>();
|
||||
public List<int> materialIndices = new List<int>();
|
||||
public List<BlendShape> blendShapes = new List<BlendShape>();
|
||||
}
|
||||
|
||||
|
||||
public MeshContext ReadMesh(ImporterContext ctx, int meshIndex)
|
||||
{
|
||||
var gltfMesh = ctx.GLTF.meshes[meshIndex];
|
||||
glTFAttributes lastAttributes = null;
|
||||
var sharedAttributes = true;
|
||||
foreach (var prim in gltfMesh.primitives)
|
||||
{
|
||||
if (lastAttributes != null && !prim.attributes.Equals(lastAttributes))
|
||||
{
|
||||
sharedAttributes = false;
|
||||
break;
|
||||
}
|
||||
lastAttributes = prim.attributes;
|
||||
}
|
||||
|
||||
var meshContext = sharedAttributes
|
||||
? _ImportMeshSharingVertexBuffer(ctx, gltfMesh)
|
||||
: _ImportMeshIndependentVertexBuffer(ctx, gltfMesh)
|
||||
;
|
||||
meshContext.name = gltfMesh.name;
|
||||
if (string.IsNullOrEmpty(meshContext.name))
|
||||
{
|
||||
meshContext.name = string.Format("UniGLTF import#{0}", meshIndex);
|
||||
}
|
||||
|
||||
return meshContext;
|
||||
}
|
||||
|
||||
|
||||
public static MeshWithMaterials BuildMesh(ImporterContext ctx, MeshImporter.MeshContext meshContext)
|
||||
{
|
||||
if (!meshContext.materialIndices.Any())
|
||||
{
|
||||
meshContext.materialIndices.Add(0);
|
||||
}
|
||||
|
||||
//Debug.Log(prims.ToJson());
|
||||
var mesh = new Mesh();
|
||||
mesh.name = meshContext.name;
|
||||
|
||||
if (meshContext.positions.Length > UInt16.MaxValue)
|
||||
{
|
||||
#if UNITY_2017_3_OR_NEWER
|
||||
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
|
||||
#else
|
||||
Debug.LogWarningFormat("vertices {0} exceed 65535. not implemented. Unity2017.3 supports large mesh",
|
||||
meshContext.positions.Length);
|
||||
#endif
|
||||
}
|
||||
|
||||
mesh.vertices = meshContext.positions;
|
||||
bool recalculateNormals = false;
|
||||
if (meshContext.normals != null && meshContext.normals.Length > 0)
|
||||
{
|
||||
mesh.normals = meshContext.normals;
|
||||
}
|
||||
else
|
||||
{
|
||||
recalculateNormals = true;
|
||||
}
|
||||
|
||||
if (meshContext.uv != null && meshContext.uv.Length > 0)
|
||||
{
|
||||
mesh.uv = meshContext.uv;
|
||||
}
|
||||
|
||||
bool recalculateTangents = true;
|
||||
#if UNIGLTF_IMPORT_TANGENTS
|
||||
if (meshContext.tangents != null && meshContext.tangents.Length > 0)
|
||||
{
|
||||
mesh.tangents = meshContext.tangents;
|
||||
recalculateTangents = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (meshContext.colors != null && meshContext.colors.Length > 0)
|
||||
{
|
||||
mesh.colors = meshContext.colors;
|
||||
}
|
||||
if (meshContext.boneWeights != null && meshContext.boneWeights.Count > 0)
|
||||
{
|
||||
mesh.boneWeights = meshContext.boneWeights.ToArray();
|
||||
}
|
||||
mesh.subMeshCount = meshContext.subMeshes.Count;
|
||||
for (int i = 0; i < meshContext.subMeshes.Count; ++i)
|
||||
{
|
||||
mesh.SetTriangles(meshContext.subMeshes[i], i);
|
||||
}
|
||||
|
||||
if (recalculateNormals)
|
||||
{
|
||||
mesh.RecalculateNormals();
|
||||
}
|
||||
if (recalculateTangents)
|
||||
{
|
||||
#if UNITY_5_6_OR_NEWER
|
||||
mesh.RecalculateTangents();
|
||||
#else
|
||||
CalcTangents(mesh);
|
||||
#endif
|
||||
}
|
||||
|
||||
var result = new MeshWithMaterials
|
||||
{
|
||||
Mesh = mesh,
|
||||
Materials = meshContext.materialIndices.Select(x => ctx.GetMaterial(x)).ToArray()
|
||||
};
|
||||
|
||||
if (meshContext.blendShapes != null)
|
||||
{
|
||||
Vector3[] emptyVertices = null;
|
||||
foreach (var blendShape in meshContext.blendShapes)
|
||||
{
|
||||
if (blendShape.Positions.Count > 0)
|
||||
{
|
||||
if (blendShape.Positions.Count == mesh.vertexCount)
|
||||
{
|
||||
mesh.AddBlendShapeFrame(blendShape.Name, FRAME_WEIGHT,
|
||||
blendShape.Positions.ToArray(),
|
||||
(meshContext.normals != null && meshContext.normals.Length == mesh.vertexCount) ? blendShape.Normals.ToArray() : null,
|
||||
null
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarningFormat("May be partial primitive has blendShape. Rquire separete mesh or extend blend shape, but not implemented: {0}", blendShape.Name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (emptyVertices == null)
|
||||
{
|
||||
emptyVertices = new Vector3[mesh.vertexCount];
|
||||
}
|
||||
// Debug.LogFormat("empty blendshape: {0}.{1}", mesh.name, blendShape.Name);
|
||||
// add empty blend shape for keep blend shape index
|
||||
mesh.AddBlendShapeFrame(blendShape.Name, FRAME_WEIGHT,
|
||||
emptyVertices,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Meshの法線を元にタンジェントを計算する。
|
||||
/// </summary>
|
||||
/// <param name="mesh">メッシュ</param>
|
||||
/// <returns>タンジェント</returns>
|
||||
public static void CalcTangents(Mesh mesh)
|
||||
{
|
||||
int vertexCount = mesh.vertexCount;
|
||||
Vector3[] vertices = mesh.vertices;
|
||||
Vector3[] normals = mesh.normals;
|
||||
Vector2[] texcoords = mesh.uv;
|
||||
int[] triangles = mesh.triangles;
|
||||
int triangleCount = triangles.Length / 3;
|
||||
|
||||
Vector4[] tangents = new Vector4[vertexCount];
|
||||
Vector3[] tan1 = new Vector3[vertexCount];
|
||||
Vector3[] tan2 = new Vector3[vertexCount];
|
||||
|
||||
int tri = 0;
|
||||
|
||||
for (int i = 0; i < (triangleCount); i++)
|
||||
{
|
||||
int i1 = triangles[tri];
|
||||
int i2 = triangles[tri + 1];
|
||||
int i3 = triangles[tri + 2];
|
||||
|
||||
Vector3 v1 = vertices[i1];
|
||||
Vector3 v2 = vertices[i2];
|
||||
Vector3 v3 = vertices[i3];
|
||||
|
||||
Vector2 w1 = texcoords[i1];
|
||||
Vector2 w2 = texcoords[i2];
|
||||
Vector2 w3 = texcoords[i3];
|
||||
|
||||
float x1 = v2.x - v1.x;
|
||||
float x2 = v3.x - v1.x;
|
||||
float y1 = v2.y - v1.y;
|
||||
float y2 = v3.y - v1.y;
|
||||
float z1 = v2.z - v1.z;
|
||||
float z2 = v3.z - v1.z;
|
||||
|
||||
float s1 = w2.x - w1.x;
|
||||
float s2 = w3.x - w1.x;
|
||||
float t1 = w2.y - w1.y;
|
||||
float t2 = w3.y - w1.y;
|
||||
|
||||
float r = 1.0f / (s1 * t2 - s2 * t1);
|
||||
Vector3 sdir = new Vector3((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r);
|
||||
Vector3 tdir = new Vector3((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r);
|
||||
|
||||
tan1[i1] += sdir;
|
||||
tan1[i2] += sdir;
|
||||
tan1[i3] += sdir;
|
||||
|
||||
tan2[i1] += tdir;
|
||||
tan2[i2] += tdir;
|
||||
tan2[i3] += tdir;
|
||||
|
||||
tri += 3;
|
||||
}
|
||||
|
||||
for (int i = 0; i < (vertexCount); i++)
|
||||
{
|
||||
Vector3 n = normals[i];
|
||||
Vector3 t = tan1[i];
|
||||
|
||||
// Gram-Schmidt orthogonalize
|
||||
Vector3.OrthoNormalize(ref n, ref t);
|
||||
tangents[i].x = t.x;
|
||||
tangents[i].y = t.y;
|
||||
tangents[i].z = t.z;
|
||||
|
||||
// Calculate handedness
|
||||
tangents[i].w = (Vector3.Dot(Vector3.Cross(n, t), tan2[i]) < 0.0f) ? -1.0f : 1.0f;
|
||||
}
|
||||
|
||||
mesh.tangents = tangents;
|
||||
}
|
||||
}
|
||||
}
|
||||
12
UniGLTF/Core/Scripts/IO/MeshImporter.cs.meta
Normal file
12
UniGLTF/Core/Scripts/IO/MeshImporter.cs.meta
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cbd062b14ca5eba4da4a5653b85e2151
|
||||
timeCreated: 1535177327
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
13
UniGLTF/Core/Scripts/IO/MeshWithMaterials.cs
Normal file
13
UniGLTF/Core/Scripts/IO/MeshWithMaterials.cs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public class MeshWithMaterials
|
||||
{
|
||||
public Mesh Mesh;
|
||||
public Material[] Materials;
|
||||
public List<Renderer> Renderers=new List<Renderer>(); // SkinnedMeshRenderer or MeshRenderer
|
||||
}
|
||||
}
|
||||
12
UniGLTF/Core/Scripts/IO/MeshWithMaterials.cs.meta
Normal file
12
UniGLTF/Core/Scripts/IO/MeshWithMaterials.cs.meta
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 04f2758551c076e4e9060e3117817e29
|
||||
timeCreated: 1517335903
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
203
UniGLTF/Core/Scripts/IO/NodeImporter.cs
Normal file
203
UniGLTF/Core/Scripts/IO/NodeImporter.cs
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public static class NodeImporter
|
||||
{
|
||||
public static GameObject ImportNode(glTFNode node)
|
||||
{
|
||||
var nodeName = node.name;
|
||||
if (!string.IsNullOrEmpty(nodeName) && nodeName.Contains("/"))
|
||||
{
|
||||
Debug.LogWarningFormat("node {0} contains /. replace _", node.name);
|
||||
nodeName = nodeName.Replace("/", "_");
|
||||
}
|
||||
var go = new GameObject(nodeName);
|
||||
|
||||
//
|
||||
// transform
|
||||
//
|
||||
if (node.translation != null && node.translation.Length > 0)
|
||||
{
|
||||
go.transform.localPosition = new Vector3(
|
||||
node.translation[0],
|
||||
node.translation[1],
|
||||
node.translation[2]);
|
||||
}
|
||||
if (node.rotation != null && node.rotation.Length > 0)
|
||||
{
|
||||
go.transform.localRotation = new Quaternion(
|
||||
node.rotation[0],
|
||||
node.rotation[1],
|
||||
node.rotation[2],
|
||||
node.rotation[3]);
|
||||
}
|
||||
if (node.scale != null && node.scale.Length > 0)
|
||||
{
|
||||
go.transform.localScale = new Vector3(
|
||||
node.scale[0],
|
||||
node.scale[1],
|
||||
node.scale[2]);
|
||||
}
|
||||
if (node.matrix != null && node.matrix.Length > 0)
|
||||
{
|
||||
var m = UnityExtensions.MatrixFromArray(node.matrix);
|
||||
go.transform.localRotation = m.ExtractRotation();
|
||||
go.transform.localPosition = m.ExtractPosition();
|
||||
go.transform.localScale = m.ExtractScale();
|
||||
}
|
||||
return go;
|
||||
}
|
||||
|
||||
public class TransformWithSkin
|
||||
{
|
||||
public Transform Transform;
|
||||
public GameObject GameObject { get { return Transform.gameObject; } }
|
||||
public int? SkinIndex;
|
||||
}
|
||||
|
||||
public static TransformWithSkin BuildHierarchy(ImporterContext context, int i)
|
||||
{
|
||||
var go = context.Nodes[i].gameObject;
|
||||
if (string.IsNullOrEmpty(go.name))
|
||||
{
|
||||
go.name = string.Format("node{0:000}", i);
|
||||
}
|
||||
|
||||
var nodeWithSkin = new TransformWithSkin
|
||||
{
|
||||
Transform = go.transform,
|
||||
};
|
||||
|
||||
//
|
||||
// build hierachy
|
||||
//
|
||||
var node = context.GLTF.nodes[i];
|
||||
if (node.children != null)
|
||||
{
|
||||
foreach (var child in node.children)
|
||||
{
|
||||
context.Nodes[child].transform.SetParent(context.Nodes[i].transform,
|
||||
false // node has local transform
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// attach mesh
|
||||
//
|
||||
if (node.mesh != -1)
|
||||
{
|
||||
var mesh = context.Meshes[node.mesh];
|
||||
if (mesh.Mesh.blendShapeCount == 0 && node.skin == -1)
|
||||
{
|
||||
// without blendshape and bone skinning
|
||||
var filter = go.AddComponent<MeshFilter>();
|
||||
filter.sharedMesh = mesh.Mesh;
|
||||
var renderer = go.AddComponent<MeshRenderer>();
|
||||
renderer.sharedMaterials = mesh.Materials;
|
||||
// invisible in loading
|
||||
renderer.enabled = false;
|
||||
mesh.Renderers.Add(renderer);
|
||||
}
|
||||
else
|
||||
{
|
||||
var renderer = go.AddComponent<SkinnedMeshRenderer>();
|
||||
|
||||
if (node.skin != -1)
|
||||
{
|
||||
nodeWithSkin.SkinIndex = node.skin;
|
||||
}
|
||||
|
||||
renderer.sharedMesh = mesh.Mesh;
|
||||
renderer.sharedMaterials = mesh.Materials;
|
||||
// invisible in loading
|
||||
renderer.enabled = false;
|
||||
mesh.Renderers.Add(renderer);
|
||||
}
|
||||
}
|
||||
|
||||
return nodeWithSkin;
|
||||
}
|
||||
|
||||
//
|
||||
// fix node's coordinate. z-back to z-forward
|
||||
//
|
||||
public static void FixCoordinate(ImporterContext context, List<TransformWithSkin> nodes)
|
||||
{
|
||||
var globalTransformMap = nodes.ToDictionary(x => x.Transform, x => new PosRot
|
||||
{
|
||||
Position = x.Transform.position,
|
||||
Rotation = x.Transform.rotation,
|
||||
});
|
||||
foreach (var x in context.GLTF.rootnodes)
|
||||
{
|
||||
// fix nodes coordinate
|
||||
// reverse Z in global
|
||||
var t = nodes[x].Transform;
|
||||
//t.SetParent(root.transform, false);
|
||||
|
||||
foreach (var transform in t.Traverse())
|
||||
{
|
||||
var g = globalTransformMap[transform];
|
||||
transform.position = g.Position.ReverseZ();
|
||||
transform.rotation = g.Rotation.ReverseZ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetupSkinning(ImporterContext context, List<TransformWithSkin> nodes, int i)
|
||||
{
|
||||
var x = nodes[i];
|
||||
var skinnedMeshRenderer = x.Transform.GetComponent<SkinnedMeshRenderer>();
|
||||
if (skinnedMeshRenderer != null)
|
||||
{
|
||||
var mesh = skinnedMeshRenderer.sharedMesh;
|
||||
if (x.SkinIndex.HasValue)
|
||||
{
|
||||
if (mesh == null) throw new Exception();
|
||||
if (skinnedMeshRenderer == null) throw new Exception();
|
||||
|
||||
if (x.SkinIndex.Value < context.GLTF.skins.Count)
|
||||
{
|
||||
var skin = context.GLTF.skins[x.SkinIndex.Value];
|
||||
|
||||
skinnedMeshRenderer.sharedMesh = null;
|
||||
|
||||
var joints = skin.joints.Select(y => nodes[y].Transform).ToArray();
|
||||
skinnedMeshRenderer.bones = joints;
|
||||
|
||||
if (skin.skeleton >= 0 && skin.skeleton < nodes.Count)
|
||||
{
|
||||
skinnedMeshRenderer.rootBone = nodes[skin.skeleton].Transform;
|
||||
}
|
||||
|
||||
if (skin.inverseBindMatrices != -1)
|
||||
{
|
||||
// BlendShape only ?
|
||||
#if false
|
||||
// https://docs.unity3d.com/ScriptReference/Mesh-bindposes.html
|
||||
var hipsParent = nodes[0].Transform;
|
||||
var calculatedBindPoses = joints.Select(y => y.worldToLocalMatrix * hipsParent.localToWorldMatrix).ToArray();
|
||||
mesh.bindposes = calculatedBindPoses;
|
||||
#else
|
||||
var bindPoses = context.GLTF.GetArrayFromAccessor<Matrix4x4>(skin.inverseBindMatrices)
|
||||
.Select(y => y.ReverseZ())
|
||||
.ToArray()
|
||||
;
|
||||
mesh.bindposes = bindPoses;
|
||||
#endif
|
||||
}
|
||||
|
||||
skinnedMeshRenderer.sharedMesh = mesh;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
12
UniGLTF/Core/Scripts/IO/NodeImporter.cs.meta
Normal file
12
UniGLTF/Core/Scripts/IO/NodeImporter.cs.meta
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: dcc3725a84dcf7f49960c89f1f68534c
|
||||
timeCreated: 1537534443
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user