init
This commit is contained in:
BIN
Assets/GLTFUtility-master/Scripts/.DS_Store
vendored
Normal file
BIN
Assets/GLTFUtility-master/Scripts/.DS_Store
vendored
Normal file
Binary file not shown.
122
Assets/GLTFUtility-master/Scripts/BufferedBinaryReader.cs
Normal file
122
Assets/GLTFUtility-master/Scripts/BufferedBinaryReader.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
|
||||
// This is a modified version of the script from: https://jacksondunstan.com/articles/3568
|
||||
/// <summary> Much faster than BinaryReader </summary>
|
||||
public class BufferedBinaryReader : IDisposable {
|
||||
private readonly Stream stream;
|
||||
private readonly byte[] buffer;
|
||||
private readonly int bufferSize;
|
||||
private int bufferOffset;
|
||||
private int bufferedBytes;
|
||||
private int byteStride;
|
||||
|
||||
private Bit2Converter bit2Converter;
|
||||
private Bit4Converter bit4Converter;
|
||||
|
||||
public long Position { get { return stream.Position + bufferOffset; } set { stream.Position = value; bufferedBytes = 0; bufferOffset = 0; } }
|
||||
|
||||
public BufferedBinaryReader(Stream stream, int bufferSize) {
|
||||
this.stream = stream;
|
||||
this.bufferSize = bufferSize;
|
||||
buffer = new byte[bufferSize];
|
||||
bufferOffset = 0;
|
||||
bufferedBytes = 0;
|
||||
}
|
||||
|
||||
private void FillBuffer(int byteCount) {
|
||||
int unreadBytes = bufferedBytes - bufferOffset;
|
||||
|
||||
if (unreadBytes < byteCount) {
|
||||
// If not enough bytes left in buffer
|
||||
if (unreadBytes != 0) {
|
||||
// If buffer still has unread bytes, move them to start of buffer
|
||||
Buffer.BlockCopy(buffer, bufferOffset, buffer, 0, unreadBytes);
|
||||
}
|
||||
bufferedBytes = stream.Read(buffer, unreadBytes, bufferSize - unreadBytes) + unreadBytes;
|
||||
bufferOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public byte ReadByte() {
|
||||
FillBuffer(1);
|
||||
return buffer[bufferOffset++];
|
||||
}
|
||||
|
||||
public sbyte ReadSByte() {
|
||||
FillBuffer(1);
|
||||
return (sbyte) buffer[bufferOffset++];
|
||||
}
|
||||
|
||||
public ushort ReadUInt16() {
|
||||
FillBuffer(sizeof(ushort));
|
||||
return bit2Converter.Read(buffer, ref bufferOffset).@ushort;
|
||||
}
|
||||
|
||||
public short ReadInt16() {
|
||||
FillBuffer(sizeof(short));
|
||||
return bit2Converter.Read(buffer, ref bufferOffset).@short;
|
||||
}
|
||||
|
||||
public uint ReadUInt32() {
|
||||
FillBuffer(sizeof(uint));
|
||||
return bit4Converter.Read(buffer, ref bufferOffset).@uint;
|
||||
}
|
||||
|
||||
public int ReadInt32() {
|
||||
FillBuffer(sizeof(int));
|
||||
return bit4Converter.Read(buffer, ref bufferOffset).@int;
|
||||
}
|
||||
|
||||
public float ReadSingle() {
|
||||
FillBuffer(sizeof(float));
|
||||
return bit4Converter.Read(buffer, ref bufferOffset).@float;
|
||||
}
|
||||
|
||||
public void Skip(int bytes) {
|
||||
FillBuffer(bytes);
|
||||
bufferOffset += bytes;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct Bit2Converter {
|
||||
[FieldOffset(0)] public byte b0;
|
||||
[FieldOffset(1)] public byte b1;
|
||||
[FieldOffset(0)] public short @short;
|
||||
[FieldOffset(0)] public ushort @ushort;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Bit2Converter Read(byte[] buffer, ref int bufferOffset) {
|
||||
b0 = buffer[bufferOffset++];
|
||||
b1 = buffer[bufferOffset++];
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct Bit4Converter {
|
||||
[FieldOffset(0)] public byte b0;
|
||||
[FieldOffset(1)] public byte b1;
|
||||
[FieldOffset(2)] public byte b2;
|
||||
[FieldOffset(3)] public byte b3;
|
||||
[FieldOffset(0)] public float @float;
|
||||
[FieldOffset(0)] public int @int;
|
||||
[FieldOffset(0)] public uint @uint;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Bit4Converter Read(byte[] buffer, ref int bufferOffset) {
|
||||
b0 = buffer[bufferOffset++];
|
||||
b1 = buffer[bufferOffset++];
|
||||
b2 = buffer[bufferOffset++];
|
||||
b3 = buffer[bufferOffset++];
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
stream.Close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c0ec98d1cbd7324cbe265d9819a7b11
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/GLTFUtility-master/Scripts/Converters.meta
Normal file
8
Assets/GLTFUtility-master/Scripts/Converters.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f0a1eaa625f4834c98391d4025b2fa2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace Siccity.GLTFUtility.Converters {
|
||||
/// <summary> Converts from float array to Color during deserialization, and back </summary>
|
||||
[Preserve] public class ColorRGBConverter : JsonConverter {
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
|
||||
Color c = (Color) value;
|
||||
writer.WriteStartArray();
|
||||
writer.WriteValue(c.r);
|
||||
writer.WriteValue(c.g);
|
||||
writer.WriteValue(c.b);
|
||||
writer.WriteEndArray();
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
|
||||
float[] a = serializer.Deserialize<float[]>(reader);
|
||||
return new Color(a[0], a[1], a[2]);
|
||||
}
|
||||
|
||||
public override bool CanConvert(Type objectType) {
|
||||
return objectType == typeof(Color);
|
||||
}
|
||||
}
|
||||
|
||||
[Preserve] public class ColorRGBAConverter : JsonConverter {
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
|
||||
Color c = (Color) value;
|
||||
writer.WriteStartArray();
|
||||
writer.WriteValue(c.r);
|
||||
writer.WriteValue(c.g);
|
||||
writer.WriteValue(c.b);
|
||||
writer.WriteValue(c.a);
|
||||
writer.WriteEndArray();
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
|
||||
float[] a = serializer.Deserialize<float[]>(reader);
|
||||
return new Color(a[0], a[1], a[2], a[3]);
|
||||
}
|
||||
|
||||
public override bool CanConvert(Type objectType) {
|
||||
return objectType == typeof(Color);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6532df4837460e147922cbb51088800d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace Siccity.GLTFUtility.Converters {
|
||||
/// <summary> Converts from string to enum during deserialization, and back </summary>
|
||||
[Preserve] public class EnumConverter : JsonConverter {
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
|
||||
writer.WriteValue(value.ToString());
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
|
||||
string value = serializer.Deserialize<string>(reader);
|
||||
if (Enum.IsDefined(objectType, value)) return Enum.Parse(objectType, value);
|
||||
else return existingValue;
|
||||
}
|
||||
|
||||
public override bool CanConvert(Type objectType) {
|
||||
return typeof(Enum).IsAssignableFrom(objectType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e2d1df10b03ea5f43acd4f34d6150bf9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace Siccity.GLTFUtility.Converters {
|
||||
/// <summary> Converts from float array to Matrix4x4 during deserialization, and back </summary>
|
||||
[Preserve] public class Matrix4x4Converter : JsonConverter {
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
|
||||
Matrix4x4 m = (Matrix4x4) value;
|
||||
writer.WriteStartArray();
|
||||
for (int i = 0; i < 16; i++) {
|
||||
writer.WriteValue(m[i]);
|
||||
}
|
||||
writer.WriteEndArray();
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
|
||||
float[] floatArray = serializer.Deserialize<float[]>(reader);
|
||||
return new Matrix4x4(
|
||||
new Vector4(floatArray[0], floatArray[1], floatArray[2], floatArray[3]),
|
||||
new Vector4(floatArray[4], floatArray[5], floatArray[6], floatArray[7]),
|
||||
new Vector4(floatArray[8], floatArray[9], floatArray[10], floatArray[11]),
|
||||
new Vector4(floatArray[12], floatArray[13], floatArray[14], floatArray[15])
|
||||
);;
|
||||
}
|
||||
|
||||
public override bool CanConvert(Type objectType) {
|
||||
return objectType == typeof(Matrix4x4);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 948a6430ddecdb3409e998ba6e9a3704
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace Siccity.GLTFUtility.Converters {
|
||||
/// <summary>
|
||||
/// Converts from float array to Quaternion during deserialization, and back.
|
||||
/// Compensates for differing coordinate systems as well.
|
||||
/// </summary>
|
||||
[Preserve] public class QuaternionConverter : JsonConverter {
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
|
||||
Quaternion q = (Quaternion) value;
|
||||
writer.WriteStartArray();
|
||||
writer.WriteValue(q.x);
|
||||
writer.WriteValue(-q.y);
|
||||
writer.WriteValue(-q.z);
|
||||
writer.WriteValue(q.w);
|
||||
writer.WriteEndArray();
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
|
||||
float[] floatArray = serializer.Deserialize<float[]>(reader);
|
||||
return new Quaternion(floatArray[0], -floatArray[1], -floatArray[2], floatArray[3]);
|
||||
}
|
||||
|
||||
public override bool CanConvert(Type objectType) {
|
||||
return objectType == typeof(Quaternion);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2cbe765fb9857a44696eb441e0e0bb25
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace Siccity.GLTFUtility.Converters {
|
||||
/// <summary>
|
||||
/// Converts from float array to Vector3 during deserialization, and back.
|
||||
/// Compensates for differing coordinate systems as well.
|
||||
/// </summary>
|
||||
[Preserve] public class TranslationConverter : JsonConverter {
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
|
||||
Vector3 pos = (Vector3) value;
|
||||
writer.WriteStartArray();
|
||||
writer.WriteValue(-pos.x);
|
||||
writer.WriteValue(pos.y);
|
||||
writer.WriteValue(pos.z);
|
||||
writer.WriteEndArray();
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
|
||||
float[] floatArray = serializer.Deserialize<float[]>(reader);
|
||||
return new Vector3(-floatArray[0], floatArray[1], floatArray[2]);
|
||||
}
|
||||
|
||||
public override bool CanConvert(Type objectType) {
|
||||
return objectType == typeof(Vector3);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7c0b75c128b76e2488f9284096ada72d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace Siccity.GLTFUtility.Converters {
|
||||
/// <summary> Converts from float array to Vector2 during deserialization, and back </summary>
|
||||
[Preserve] public class Vector2Converter : JsonConverter {
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
|
||||
Vector2 pos = (Vector2) value;
|
||||
writer.WriteStartArray();
|
||||
writer.WriteValue(pos.x);
|
||||
writer.WriteValue(pos.y);
|
||||
writer.WriteEndArray();
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
|
||||
float[] floatArray = null;
|
||||
try {
|
||||
floatArray = serializer.Deserialize<float[]>(reader);
|
||||
} catch (System.Exception) {
|
||||
floatArray = new float[] { serializer.Deserialize<float>(reader) };
|
||||
}
|
||||
|
||||
switch (floatArray.Length) {
|
||||
case 1:
|
||||
return new Vector2(floatArray[0], floatArray[0]); // just copy float
|
||||
case 2:
|
||||
return new Vector2(floatArray[0], floatArray[1]);
|
||||
case 3:
|
||||
return new Vector2(floatArray[0], floatArray[1]); // we dont need third float
|
||||
default:
|
||||
return new Vector2(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanConvert(Type objectType) {
|
||||
return objectType == typeof(Vector2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3270bccdd3fef254d9cb10fd0fc8bf51
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace Siccity.GLTFUtility.Converters {
|
||||
/// <summary> Converts from float array to Vector3 during deserialization, and back </summary>
|
||||
[Preserve] public class Vector3Converter : JsonConverter {
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
|
||||
Vector3 pos = (Vector3) value;
|
||||
writer.WriteStartArray();
|
||||
writer.WriteValue(pos.x);
|
||||
writer.WriteValue(pos.y);
|
||||
writer.WriteValue(pos.z);
|
||||
writer.WriteEndArray();
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
|
||||
float[] floatArray = serializer.Deserialize<float[]>(reader);
|
||||
return new Vector3(floatArray[0], floatArray[1], floatArray[2]);
|
||||
}
|
||||
|
||||
public override bool CanConvert(Type objectType) {
|
||||
return objectType == typeof(Vector3);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 27048007fcb96f94f9c0af0877a49845
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
10
Assets/GLTFUtility-master/Scripts/Editor.meta
Normal file
10
Assets/GLTFUtility-master/Scripts/Editor.meta
Normal file
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 46478e359a7c2694db7532f3a07945f0
|
||||
folderAsset: yes
|
||||
timeCreated: 1538919615
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
36
Assets/GLTFUtility-master/Scripts/Editor/GLBImporter.cs
Normal file
36
Assets/GLTFUtility-master/Scripts/Editor/GLBImporter.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
#if HAVE_GLTFAST || HAVE_UNITYGLTF
|
||||
#define ANOTHER_IMPORTER_HAS_HIGHER_PRIORITY
|
||||
#endif
|
||||
|
||||
#if !ANOTHER_IMPORTER_HAS_HIGHER_PRIORITY && !GLTFUTILITY_FORCE_DEFAULT_IMPORTER_OFF
|
||||
#define ENABLE_DEFAULT_GLB_IMPORTER
|
||||
#endif
|
||||
#if GLTFUTILITY_FORCE_DEFAULT_IMPORTER_ON
|
||||
#define ENABLE_DEFAULT_GLB_IMPORTER
|
||||
#endif
|
||||
|
||||
using UnityEngine;
|
||||
#if !UNITY_2020_2_OR_NEWER
|
||||
using UnityEditor.Experimental.AssetImporters;
|
||||
#else
|
||||
using UnityEditor.AssetImporters;
|
||||
#endif
|
||||
|
||||
namespace Siccity.GLTFUtility {
|
||||
#if ENABLE_DEFAULT_GLB_IMPORTER
|
||||
[ScriptedImporter(1, "glb")]
|
||||
#else
|
||||
[ScriptedImporter(2, null, overrideExts: new[] { "glb" })]
|
||||
#endif
|
||||
public class GLBImporter : GLTFImporter {
|
||||
|
||||
public override void OnImportAsset(AssetImportContext ctx) {
|
||||
// Load asset
|
||||
AnimationClip[] animations;
|
||||
if (importSettings == null) importSettings = new ImportSettings();
|
||||
GameObject root = Importer.LoadFromFile(ctx.assetPath, importSettings, out animations, Format.GLB);
|
||||
// Save asset
|
||||
GLTFAssetUtility.SaveToAsset(root, animations, ctx, importSettings);
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Assets/GLTFUtility-master/Scripts/Editor/GLBImporter.cs.meta
Normal file
13
Assets/GLTFUtility-master/Scripts/Editor/GLBImporter.cs.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 768a5a18fd6eb914f980bf85dd7b34ec
|
||||
timeCreated: 1538919678
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
133
Assets/GLTFUtility-master/Scripts/Editor/GLTFAssetUtility.cs
Normal file
133
Assets/GLTFUtility-master/Scripts/Editor/GLTFAssetUtility.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
#if !UNITY_2020_2_OR_NEWER
|
||||
using UnityEditor.Experimental.AssetImporters;
|
||||
#else
|
||||
using UnityEditor.AssetImporters;
|
||||
#endif
|
||||
using UnityEngine;
|
||||
|
||||
namespace Siccity.GLTFUtility {
|
||||
/// <summary> Contains methods for saving a gameobject as an asset </summary>
|
||||
public static class GLTFAssetUtility {
|
||||
public static void SaveToAsset(GameObject root, AnimationClip[] animations, AssetImportContext ctx, ImportSettings settings) {
|
||||
#if UNITY_2018_2_OR_NEWER
|
||||
ctx.AddObjectToAsset("main", root);
|
||||
ctx.SetMainObject(root);
|
||||
#else
|
||||
ctx.SetMainAsset("main obj", root);
|
||||
#endif
|
||||
UnwrapParam? unwrapParams = new UnwrapParam()
|
||||
{
|
||||
angleError = settings.angleError,
|
||||
areaError = settings.areaError,
|
||||
hardAngle = settings.hardAngle,
|
||||
packMargin = settings.packMargin
|
||||
};
|
||||
|
||||
MeshRenderer[] renderers = root.GetComponentsInChildren<MeshRenderer>(true);
|
||||
SkinnedMeshRenderer[] skinnedRenderers = root.GetComponentsInChildren<SkinnedMeshRenderer>(true);
|
||||
MeshFilter[] filters = root.GetComponentsInChildren<MeshFilter>(true);
|
||||
AddMeshes(filters, skinnedRenderers, ctx, settings.generateLightmapUVs ? unwrapParams : null);
|
||||
AddMaterials(renderers, skinnedRenderers, ctx);
|
||||
AddAnimations(animations, ctx, settings.animationSettings);
|
||||
}
|
||||
|
||||
public static void AddMeshes(MeshFilter[] filters, SkinnedMeshRenderer[] skinnedRenderers, AssetImportContext ctx, UnwrapParam? lightmapUnwrapInfo) {
|
||||
HashSet<Mesh> visitedMeshes = new HashSet<Mesh>();
|
||||
for (int i = 0; i < filters.Length; i++) {
|
||||
Mesh mesh = filters[i].sharedMesh;
|
||||
if (lightmapUnwrapInfo.HasValue) Unwrapping.GenerateSecondaryUVSet(mesh, lightmapUnwrapInfo.Value);
|
||||
if (visitedMeshes.Contains(mesh)) continue;
|
||||
ctx.AddAsset(mesh.name, mesh);
|
||||
visitedMeshes.Add(mesh);
|
||||
}
|
||||
for (int i = 0; i < skinnedRenderers.Length; i++) {
|
||||
Mesh mesh = skinnedRenderers[i].sharedMesh;
|
||||
if (visitedMeshes.Contains(mesh)) continue;
|
||||
ctx.AddAsset(mesh.name, mesh);
|
||||
visitedMeshes.Add(mesh);
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddAnimations(AnimationClip[] animations, AssetImportContext ctx, AnimationSettings settings) {
|
||||
if (animations == null) return;
|
||||
|
||||
// Editor-only animation settings
|
||||
foreach (AnimationClip clip in animations) {
|
||||
AnimationClipSettings clipSettings = AnimationUtility.GetAnimationClipSettings(clip);
|
||||
clipSettings.loopTime = settings.looping;
|
||||
AnimationUtility.SetAnimationClipSettings(clip, clipSettings);
|
||||
}
|
||||
|
||||
HashSet<AnimationClip> visitedAnimations = new HashSet<AnimationClip>();
|
||||
for (int i = 0; i < animations.Length; i++) {
|
||||
AnimationClip clip = animations[i];
|
||||
if (visitedAnimations.Contains(clip)) continue;
|
||||
ctx.AddAsset(clip.name, clip);
|
||||
visitedAnimations.Add(clip);
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddMaterials(MeshRenderer[] renderers, SkinnedMeshRenderer[] skinnedRenderers, AssetImportContext ctx) {
|
||||
HashSet<Material> visitedMaterials = new HashSet<Material>();
|
||||
HashSet<Texture2D> visitedTextures = new HashSet<Texture2D>();
|
||||
for (int i = 0; i < renderers.Length; i++) {
|
||||
foreach (Material mat in renderers[i].sharedMaterials) {
|
||||
if (mat == GLTFMaterial.defaultMaterial) continue;
|
||||
if (visitedMaterials.Contains(mat)) continue;
|
||||
if (string.IsNullOrEmpty(mat.name)) mat.name = "material" + visitedMaterials.Count;
|
||||
ctx.AddAsset(mat.name, mat);
|
||||
visitedMaterials.Add(mat);
|
||||
|
||||
// Add textures
|
||||
foreach (Texture2D tex in mat.AllTextures()) {
|
||||
// Dont add asset textures
|
||||
//if (images[i].isAsset) continue;
|
||||
if (visitedTextures.Contains(tex)) continue;
|
||||
if (AssetDatabase.Contains(tex)) continue;
|
||||
if (string.IsNullOrEmpty(tex.name)) tex.name = "texture" + visitedTextures.Count;
|
||||
ctx.AddAsset(tex.name, tex);
|
||||
visitedTextures.Add(tex);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < skinnedRenderers.Length; i++) {
|
||||
foreach (Material mat in skinnedRenderers[i].sharedMaterials) {
|
||||
if (visitedMaterials.Contains(mat)) continue;
|
||||
if (string.IsNullOrEmpty(mat.name)) mat.name = "material" + visitedMaterials.Count;
|
||||
ctx.AddAsset(mat.name, mat);
|
||||
visitedMaterials.Add(mat);
|
||||
|
||||
// Add textures
|
||||
foreach (Texture2D tex in mat.AllTextures()) {
|
||||
// Dont add asset textures
|
||||
//if (images[i].isAsset) continue;
|
||||
if (visitedTextures.Contains(tex)) continue;
|
||||
if (AssetDatabase.Contains(tex)) continue;
|
||||
if (string.IsNullOrEmpty(tex.name)) tex.name = "texture" + visitedTextures.Count;
|
||||
ctx.AddAsset(tex.name, tex);
|
||||
visitedTextures.Add(tex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddAsset(this AssetImportContext ctx, string identifier, Object obj) {
|
||||
#if UNITY_2018_2_OR_NEWER
|
||||
ctx.AddObjectToAsset(identifier, obj);
|
||||
#else
|
||||
ctx.AddSubAsset(identifier, obj);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static IEnumerable<Texture2D> AllTextures(this Material mat) {
|
||||
int[] ids = mat.GetTexturePropertyNameIDs();
|
||||
for (int i = 0; i < ids.Length; i++) {
|
||||
Texture2D tex = mat.GetTexture(ids[i]) as Texture2D;
|
||||
if (tex != null) yield return tex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: abda7048a5d68e74281dc3833f5a7a22
|
||||
timeCreated: 1538919678
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
39
Assets/GLTFUtility-master/Scripts/Editor/GLTFImporter.cs
Normal file
39
Assets/GLTFUtility-master/Scripts/Editor/GLTFImporter.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
#if HAVE_GLTFAST || HAVE_UNITYGLTF
|
||||
#define ANOTHER_IMPORTER_HAS_HIGHER_PRIORITY
|
||||
#endif
|
||||
|
||||
#if !ANOTHER_IMPORTER_HAS_HIGHER_PRIORITY && !GLTFUTILITY_FORCE_DEFAULT_IMPORTER_OFF
|
||||
#define ENABLE_DEFAULT_GLB_IMPORTER
|
||||
#endif
|
||||
#if GLTFUTILITY_FORCE_DEFAULT_IMPORTER_ON
|
||||
#define ENABLE_DEFAULT_GLB_IMPORTER
|
||||
#endif
|
||||
|
||||
#if !UNITY_2020_2_OR_NEWER
|
||||
using UnityEditor.Experimental.AssetImporters;
|
||||
#else
|
||||
using UnityEditor.AssetImporters;
|
||||
#endif
|
||||
using UnityEngine;
|
||||
|
||||
namespace Siccity.GLTFUtility {
|
||||
#if ENABLE_DEFAULT_GLB_IMPORTER
|
||||
[ScriptedImporter(1, "gltf")]
|
||||
#else
|
||||
[ScriptedImporter(1, null, overrideExts: new[] { "gltf" })]
|
||||
#endif
|
||||
public class GLTFImporter : ScriptedImporter {
|
||||
|
||||
public ImportSettings importSettings;
|
||||
|
||||
public override void OnImportAsset(AssetImportContext ctx) {
|
||||
// Load asset
|
||||
AnimationClip[] animations;
|
||||
if (importSettings == null) importSettings = new ImportSettings();
|
||||
GameObject root = Importer.LoadFromFile(ctx.assetPath, importSettings, out animations, Format.GLTF);
|
||||
|
||||
// Save asset
|
||||
GLTFAssetUtility.SaveToAsset(root, animations, ctx, importSettings);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1fa4dafe23771ae498589f7f7c22c974
|
||||
timeCreated: 1538919678
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "Siccity.GLTFUtility.Editor",
|
||||
"references": [
|
||||
"Siccity.GLTFUtility"
|
||||
],
|
||||
"optionalUnityReferences": [],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"versionDefines": [
|
||||
{
|
||||
"name": "com.atteneder.gltfast",
|
||||
"expression": "3.0.0",
|
||||
"define": "HAVE_GLTFAST"
|
||||
},
|
||||
{
|
||||
"name": "org.khronos.unitygltf",
|
||||
"expression": "0.0.0",
|
||||
"define": "HAVE_UNITYGLTF"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 56f0af5eabd0f8244a313552ef7d963e
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,63 @@
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace Siccity.GLTFUtility {
|
||||
[CustomPropertyDrawer(typeof(ShaderSettings))]
|
||||
public class ShaderSettingsDrawer : PropertyDrawer {
|
||||
private static SerializedObject serializedGraphicsSettings;
|
||||
private bool hasShaders;
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
|
||||
EditorGUI.BeginProperty(position, label, property);
|
||||
|
||||
|
||||
// Get shader properties
|
||||
SerializedProperty metalicProp = property.FindPropertyRelative("metallic");
|
||||
SerializedProperty metallicBlendProp = property.FindPropertyRelative("metallicBlend");
|
||||
SerializedProperty specularProp = property.FindPropertyRelative("specular");
|
||||
SerializedProperty specularBlendProp = property.FindPropertyRelative("specularBlend");
|
||||
|
||||
hasShaders = HasShaders(metalicProp, metallicBlendProp, specularProp, specularBlendProp);
|
||||
EditorGUI.PropertyField(position, property, true);
|
||||
|
||||
// Show shader warning
|
||||
if (!hasShaders && property.isExpanded) {
|
||||
GUIContent content = new GUIContent("Shaders not found in 'GraphicSettings/Always Included Shaders'.\nShaders might not be available for runtime import.");
|
||||
float baseHeight = EditorGUI.GetPropertyHeight(property, true);
|
||||
Rect warningRect = new Rect(position.x, position.y + baseHeight + 2, position.width, 50);
|
||||
EditorGUI.HelpBox(warningRect, content.text, MessageType.Warning);
|
||||
/* Rect buttonRect = new Rect(warningRect.xMax - 150, warningRect.yMax + 2, 150, 18);
|
||||
if (GUI.Button(buttonRect, "Open GraphicsSettings")) {
|
||||
|
||||
} */
|
||||
}
|
||||
EditorGUI.EndProperty();
|
||||
}
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
|
||||
float height = EditorGUI.GetPropertyHeight(property, true);
|
||||
if (!hasShaders && property.isExpanded) height += 40;
|
||||
return height;
|
||||
}
|
||||
|
||||
public bool HasShaders(params SerializedProperty[] shaders) {
|
||||
|
||||
if (serializedGraphicsSettings == null) {
|
||||
GraphicsSettings graphicsSettings = AssetDatabase.LoadAssetAtPath<GraphicsSettings>("ProjectSettings/GraphicsSettings.asset");
|
||||
serializedGraphicsSettings = new SerializedObject(graphicsSettings);
|
||||
}
|
||||
|
||||
SerializedProperty arrayProp = serializedGraphicsSettings.FindProperty("m_AlwaysIncludedShaders");
|
||||
bool[] hasShaders = new bool[shaders.Length];
|
||||
for (int i = 0; i < arrayProp.arraySize; ++i) {
|
||||
SerializedProperty arrayElem = arrayProp.GetArrayElementAtIndex(i);
|
||||
for (int k = 0; k < shaders.Length; k++) {
|
||||
if (shaders[k].objectReferenceValue == arrayElem.objectReferenceValue) hasShaders[k] = true;
|
||||
}
|
||||
}
|
||||
return hasShaders.All(x => x);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d7fc255616f65554bad19a4eae4938bd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
55
Assets/GLTFUtility-master/Scripts/Enums.cs
Normal file
55
Assets/GLTFUtility-master/Scripts/Enums.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Siccity.GLTFUtility {
|
||||
public enum AlphaMode { OPAQUE, MASK, BLEND }
|
||||
public enum AccessorType { SCALAR, VEC2, VEC3, VEC4, MAT2, MAT3, MAT4 }
|
||||
public enum RenderingMode { POINTS = 0, LINES = 1, LINE_LOOP = 2, LINE_STRIP = 3, TRIANGLES = 4, TRIANGLE_STRIP = 5, TRIANGLE_FAN = 6 }
|
||||
public enum GLType { UNSET = -1, BYTE = 5120, UNSIGNED_BYTE = 5121, SHORT = 5122, UNSIGNED_SHORT = 5123, UNSIGNED_INT = 5125, FLOAT = 5126 }
|
||||
public enum Format { AUTO, GLTF, GLB }
|
||||
public enum CameraType { perspective, orthographic }
|
||||
public enum InterpolationMode { ImportFromFile = -1, LINEAR = 0, STEP = 1, CUBICSPLINE = 2 }
|
||||
|
||||
public static class EnumExtensions {
|
||||
public static int ByteSize(this GLType gltype) {
|
||||
switch (gltype) {
|
||||
case GLType.BYTE:
|
||||
return sizeof(sbyte);
|
||||
case GLType.UNSIGNED_BYTE:
|
||||
return sizeof(byte);
|
||||
case GLType.SHORT:
|
||||
return sizeof(short);
|
||||
case GLType.UNSIGNED_SHORT:
|
||||
return sizeof(ushort);
|
||||
case GLType.FLOAT:
|
||||
return sizeof(float);
|
||||
case GLType.UNSIGNED_INT:
|
||||
return sizeof(uint);
|
||||
default:
|
||||
Debug.LogError("GLType " + (int) gltype + " not supported!");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static int ComponentCount(this AccessorType accessorType) {
|
||||
switch (accessorType) {
|
||||
case AccessorType.SCALAR:
|
||||
return 1;
|
||||
case AccessorType.VEC2:
|
||||
return 2;
|
||||
case AccessorType.VEC3:
|
||||
return 3;
|
||||
case AccessorType.VEC4:
|
||||
return 4;
|
||||
case AccessorType.MAT2:
|
||||
return 4;
|
||||
case AccessorType.MAT3:
|
||||
return 9;
|
||||
case AccessorType.MAT4:
|
||||
return 16;
|
||||
default:
|
||||
Debug.LogError("AccessorType " + accessorType + " not supported!");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/GLTFUtility-master/Scripts/Enums.cs.meta
Normal file
11
Assets/GLTFUtility-master/Scripts/Enums.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: da9e406a98bd2df42b00ca699cf01c6b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
43
Assets/GLTFUtility-master/Scripts/Exporter.cs
Normal file
43
Assets/GLTFUtility-master/Scripts/Exporter.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Siccity.GLTFUtility {
|
||||
/// <summary> API used for exporting .gltf and .glb files </summary>
|
||||
public static class Exporter {
|
||||
#if UNITY_EDITOR
|
||||
[UnityEditor.MenuItem("File/Export Selection/.glb")]
|
||||
public static void ExportSelectedGLB() {
|
||||
ExportGLB(UnityEditor.Selection.activeGameObject);
|
||||
}
|
||||
#endif
|
||||
|
||||
public static void ExportGLB(GameObject root) {
|
||||
GLTFObject gltfObject = CreateGLTFObject(root.transform);
|
||||
Debug.Log(JsonConvert.SerializeObject(gltfObject, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore }));
|
||||
}
|
||||
|
||||
public static void ExportGLTF(GameObject root) {
|
||||
Debug.Log("Not implemented yet");
|
||||
}
|
||||
|
||||
public static GLTFObject CreateGLTFObject(Transform root) {
|
||||
GLTFObject gltfObject = new GLTFObject();
|
||||
GLTFAsset asset = new GLTFAsset() {
|
||||
generator = "GLTFUtility by Siccity https: //github.com/Siccity/GLTFUtility",
|
||||
version = "2.0"
|
||||
};
|
||||
//List<GLTFAccessor.ExportResult> accessors
|
||||
List<GLTFNode.ExportResult> nodes = GLTFNode.Export(root);
|
||||
List<GLTFMesh.ExportResult> meshes = GLTFMesh.Export(nodes);
|
||||
|
||||
gltfObject.scene = 0;
|
||||
gltfObject.asset = asset;
|
||||
gltfObject.nodes = nodes.Cast<GLTFNode>().ToList();
|
||||
gltfObject.meshes = meshes.Cast<GLTFMesh>().ToList();
|
||||
return gltfObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/GLTFUtility-master/Scripts/Exporter.cs.meta
Normal file
11
Assets/GLTFUtility-master/Scripts/Exporter.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f44abb798dea27c4eaa48bd050b1471f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
33
Assets/GLTFUtility-master/Scripts/Extensions.cs
Normal file
33
Assets/GLTFUtility-master/Scripts/Extensions.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Siccity.GLTFUtility {
|
||||
public static class Extensions {
|
||||
|
||||
public class CoroutineRunner : MonoBehaviour { }
|
||||
private static CoroutineRunner coroutineRunner;
|
||||
public static Coroutine RunCoroutine(this IEnumerator ienum) {
|
||||
if (coroutineRunner == null) {
|
||||
coroutineRunner = new GameObject("[CoroutineRunner]").AddComponent<CoroutineRunner>();
|
||||
coroutineRunner.hideFlags = HideFlags.DontSaveInEditor | HideFlags.HideInHierarchy | HideFlags.HideInInspector | HideFlags.NotEditable | HideFlags.DontSaveInBuild;
|
||||
coroutineRunner.gameObject.hideFlags = HideFlags.DontSaveInEditor | HideFlags.HideInHierarchy | HideFlags.HideInInspector | HideFlags.NotEditable | HideFlags.DontSaveInBuild;
|
||||
}
|
||||
return coroutineRunner.StartCoroutine(ienum);
|
||||
}
|
||||
|
||||
public static T[] SubArray<T>(this T[] data, int index, int length) {
|
||||
T[] result = new T[length];
|
||||
Array.Copy(data, index, result, 0, length);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void UnpackTRS(this Matrix4x4 trs, ref Vector3 position, ref Quaternion rotation, ref Vector3 scale) {
|
||||
position = trs.GetColumn(3);
|
||||
position.x = -position.x;
|
||||
rotation = trs.rotation;
|
||||
rotation = new Quaternion(rotation.x, -rotation.y, -rotation.z, rotation.w);
|
||||
scale = trs.lossyScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Assets/GLTFUtility-master/Scripts/Extensions.cs.meta
Normal file
13
Assets/GLTFUtility-master/Scripts/Extensions.cs.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b28e9070c77e9aa458e0629479371587
|
||||
timeCreated: 1539200073
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/GLTFUtility-master/Scripts/Extensions.meta
Normal file
8
Assets/GLTFUtility-master/Scripts/Extensions.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7d54e7c3f2ea6bd42a274cd36d5017f5
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,40 @@
|
||||
using Newtonsoft.Json;
|
||||
using Siccity.GLTFUtility.Converters;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace Siccity.GLTFUtility {
|
||||
/// <summary>
|
||||
/// KHR_texture_transform textureInfo extension
|
||||
/// glTF extension that enables shifting and scaling UV coordinates on a per-texture basis
|
||||
/// see: https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_texture_transform/schema/KHR_texture_transform.textureInfo.schema.json
|
||||
/// </summary>
|
||||
[Preserve] public class KHR_texture_transform : GLTFMaterial.TextureInfo.IExtension {
|
||||
/// <summary>
|
||||
/// The offset of the UV coordinate origin as a factor of the texture dimensions.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(Vector2Converter))] public Vector2 offset = Vector2.zero;
|
||||
|
||||
/// <summary>
|
||||
/// Rotate the UVs by this many radians counter-clockwise around the origin.
|
||||
/// </summary>
|
||||
public float rotation = 0.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The scale factor applied to the components of the UV coordinates.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(Vector2Converter))] public Vector2 scale = Vector2.one;
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the textureInfo texCoord value if supplied, and if this extension is supported.
|
||||
/// </summary>
|
||||
public int texCoord = 0;
|
||||
|
||||
public void Apply(GLTFMaterial.TextureInfo texInfo, Material material, string textureSamplerName) {
|
||||
material.SetTextureOffset(textureSamplerName, offset);
|
||||
// TODO rotation
|
||||
material.SetTextureScale(textureSamplerName, scale);
|
||||
// TODO texCoord
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 430c613bbb3e69145bf8e34015468fd6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
380
Assets/GLTFUtility-master/Scripts/Importer.cs
Normal file
380
Assets/GLTFUtility-master/Scripts/Importer.cs
Normal file
@@ -0,0 +1,380 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace Siccity.GLTFUtility {
|
||||
/// <summary> API used for importing .gltf and .glb files </summary>
|
||||
public static class Importer {
|
||||
public static GameObject LoadFromFile(string filepath, Format format = Format.AUTO) {
|
||||
AnimationClip[] animations;
|
||||
return LoadFromFile(filepath, new ImportSettings(), out animations, format);
|
||||
}
|
||||
|
||||
public static GameObject LoadFromFile(string filepath, ImportSettings importSettings, Format format = Format.AUTO) {
|
||||
AnimationClip[] animations;
|
||||
return LoadFromFile(filepath, importSettings, out animations, format);
|
||||
}
|
||||
|
||||
public static GameObject LoadFromFile(string filepath, ImportSettings importSettings, out AnimationClip[] animations, Format format = Format.AUTO) {
|
||||
if (format == Format.GLB) {
|
||||
return ImportGLB(filepath, importSettings, out animations);
|
||||
} else if (format == Format.GLTF) {
|
||||
return ImportGLTF(filepath, importSettings, out animations);
|
||||
} else {
|
||||
string extension = Path.GetExtension(filepath).ToLower();
|
||||
if (extension == ".glb") return ImportGLB(filepath, importSettings, out animations);
|
||||
else if (extension == ".gltf") return ImportGLTF(filepath, importSettings, out animations);
|
||||
else {
|
||||
Debug.Log("Extension '" + extension + "' not recognized in " + filepath);
|
||||
animations = null;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <param name="bytes">GLB file is supported</param>
|
||||
public static GameObject LoadFromBytes(byte[] bytes, ImportSettings importSettings = null) {
|
||||
AnimationClip[] animations;
|
||||
if (importSettings == null) importSettings = new ImportSettings();
|
||||
return ImportGLB(bytes, importSettings, out animations);
|
||||
}
|
||||
|
||||
/// <param name="bytes">GLB file is supported</param>
|
||||
public static GameObject LoadFromBytes(byte[] bytes, ImportSettings importSettings, out AnimationClip[] animations) {
|
||||
return ImportGLB(bytes, importSettings, out animations);
|
||||
}
|
||||
|
||||
public static void LoadFromFileAsync(string filepath, ImportSettings importSettings, Action<GameObject, AnimationClip[]> onFinished, Action<float> onProgress = null) {
|
||||
string extension = Path.GetExtension(filepath).ToLower();
|
||||
if (extension == ".glb") ImportGLBAsync(filepath, importSettings, onFinished, onProgress);
|
||||
else if (extension == ".gltf") ImportGLTFAsync(filepath, importSettings, onFinished, onProgress);
|
||||
else {
|
||||
Debug.Log("Extension '" + extension + "' not recognized in " + filepath);
|
||||
onFinished(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
#region GLB
|
||||
private static GameObject ImportGLB(string filepath, ImportSettings importSettings, out AnimationClip[] animations) {
|
||||
FileStream stream = File.OpenRead(filepath);
|
||||
long binChunkStart;
|
||||
string json = GetGLBJson(stream, out binChunkStart);
|
||||
GLTFObject gltfObject = JsonConvert.DeserializeObject<GLTFObject>(json);
|
||||
return gltfObject.LoadInternal(filepath, null, binChunkStart, importSettings, out animations);
|
||||
}
|
||||
|
||||
private static GameObject ImportGLB(byte[] bytes, ImportSettings importSettings, out AnimationClip[] animations) {
|
||||
Stream stream = new MemoryStream(bytes);
|
||||
long binChunkStart;
|
||||
string json = GetGLBJson(stream, out binChunkStart);
|
||||
GLTFObject gltfObject = JsonConvert.DeserializeObject<GLTFObject>(json);
|
||||
return gltfObject.LoadInternal(null, bytes, binChunkStart, importSettings, out animations);
|
||||
}
|
||||
|
||||
public static void ImportGLBAsync(string filepath, ImportSettings importSettings, Action<GameObject, AnimationClip[]> onFinished, Action<float> onProgress = null) {
|
||||
FileStream stream = File.OpenRead(filepath);
|
||||
long binChunkStart;
|
||||
string json = GetGLBJson(stream, out binChunkStart);
|
||||
LoadAsync(json, filepath, null, binChunkStart, importSettings, onFinished, onProgress).RunCoroutine();
|
||||
}
|
||||
|
||||
public static void ImportGLBAsync(byte[] bytes, ImportSettings importSettings, Action<GameObject, AnimationClip[]> onFinished, Action<float> onProgress = null) {
|
||||
Stream stream = new MemoryStream(bytes);
|
||||
long binChunkStart;
|
||||
string json = GetGLBJson(stream, out binChunkStart);
|
||||
LoadAsync(json, null, bytes, binChunkStart, importSettings, onFinished, onProgress).RunCoroutine();
|
||||
}
|
||||
|
||||
private static string GetGLBJson(Stream stream, out long binChunkStart) {
|
||||
byte[] buffer = new byte[12];
|
||||
stream.Read(buffer, 0, 12);
|
||||
// 12 byte header
|
||||
// 0-4 - magic = "glTF"
|
||||
// 4-8 - version = 2
|
||||
// 8-12 - length = total length of glb, including Header and all Chunks, in bytes.
|
||||
string magic = Encoding.Default.GetString(buffer, 0, 4);
|
||||
if (magic != "glTF") {
|
||||
Debug.LogWarning("Input does not look like a .glb file");
|
||||
binChunkStart = 0;
|
||||
return null;
|
||||
}
|
||||
uint version = System.BitConverter.ToUInt32(buffer, 4);
|
||||
if (version != 2) {
|
||||
Debug.LogWarning("Importer does not support gltf version " + version);
|
||||
binChunkStart = 0;
|
||||
return null;
|
||||
}
|
||||
// What do we even need the length for.
|
||||
//uint length = System.BitConverter.ToUInt32(bytes, 8);
|
||||
|
||||
// Chunk 0 (json)
|
||||
// 0-4 - chunkLength = total length of the chunkData
|
||||
// 4-8 - chunkType = "JSON"
|
||||
// 8-[chunkLength+8] - chunkData = json data.
|
||||
stream.Read(buffer, 0, 8);
|
||||
uint chunkLength = System.BitConverter.ToUInt32(buffer, 0);
|
||||
TextReader reader = new StreamReader(stream);
|
||||
char[] jsonChars = new char[chunkLength];
|
||||
reader.Read(jsonChars, 0, (int) chunkLength);
|
||||
string json = new string(jsonChars);
|
||||
|
||||
// Chunk
|
||||
binChunkStart = chunkLength + 20;
|
||||
stream.Close();
|
||||
|
||||
// Return json
|
||||
return json;
|
||||
}
|
||||
#endregion
|
||||
|
||||
private static GameObject ImportGLTF(string filepath, ImportSettings importSettings, out AnimationClip[] animations) {
|
||||
string json = File.ReadAllText(filepath);
|
||||
|
||||
// Parse json
|
||||
GLTFObject gltfObject = JsonConvert.DeserializeObject<GLTFObject>(json);
|
||||
return gltfObject.LoadInternal(filepath, null, 0, importSettings, out animations);
|
||||
}
|
||||
|
||||
public static void ImportGLTFAsync(string filepath, ImportSettings importSettings, Action<GameObject, AnimationClip[]> onFinished, Action<float> onProgress = null) {
|
||||
string json = File.ReadAllText(filepath);
|
||||
|
||||
// Parse json
|
||||
LoadAsync(json, filepath, null, 0, importSettings, onFinished, onProgress).RunCoroutine();
|
||||
}
|
||||
|
||||
public abstract class ImportTask<TReturn> : ImportTask {
|
||||
public TReturn Result;
|
||||
|
||||
/// <summary> Constructor. Sets waitFor which ensures ImportTasks are completed before running. </summary>
|
||||
public ImportTask(params ImportTask[] waitFor) : base(waitFor) { }
|
||||
|
||||
/// <summary> Runs task followed by OnCompleted </summary>
|
||||
public TReturn RunSynchronously() {
|
||||
if(task != null)
|
||||
{
|
||||
task.RunSynchronously();
|
||||
}
|
||||
IEnumerator en = OnCoroutine();
|
||||
while (en.MoveNext()) { };
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class ImportTask {
|
||||
public Task task;
|
||||
public readonly ImportTask[] waitFor;
|
||||
public bool IsReady { get { return waitFor.All(x => x.IsCompleted); } }
|
||||
public bool IsCompleted { get; protected set; }
|
||||
|
||||
/// <summary> Constructor. Sets waitFor which ensures ImportTasks are completed before running. </summary>
|
||||
public ImportTask(params ImportTask[] waitFor) {
|
||||
IsCompleted = false;
|
||||
this.waitFor = waitFor;
|
||||
}
|
||||
|
||||
public virtual IEnumerator OnCoroutine(Action<float> onProgress = null) {
|
||||
IsCompleted = true;
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
|
||||
#region Sync
|
||||
private static GameObject LoadInternal(this GLTFObject gltfObject, string filepath, byte[] bytefile, long binChunkStart, ImportSettings importSettings, out AnimationClip[] animations) {
|
||||
CheckExtensions(gltfObject);
|
||||
|
||||
// directory root is sometimes used for loading buffers from containing file, or local images
|
||||
string directoryRoot = filepath != null ? Directory.GetParent(filepath).ToString() + "/" : null;
|
||||
|
||||
importSettings.shaderOverrides.CacheDefaultShaders();
|
||||
|
||||
// Import tasks synchronously
|
||||
GLTFBuffer.ImportTask bufferTask = new GLTFBuffer.ImportTask(gltfObject.buffers, filepath, bytefile, binChunkStart);
|
||||
bufferTask.RunSynchronously();
|
||||
GLTFBufferView.ImportTask bufferViewTask = new GLTFBufferView.ImportTask(gltfObject.bufferViews, bufferTask);
|
||||
bufferViewTask.RunSynchronously();
|
||||
GLTFAccessor.ImportTask accessorTask = new GLTFAccessor.ImportTask(gltfObject.accessors, bufferViewTask);
|
||||
accessorTask.RunSynchronously();
|
||||
GLTFImage.ImportTask imageTask = new GLTFImage.ImportTask(gltfObject.images, directoryRoot, bufferViewTask);
|
||||
imageTask.RunSynchronously();
|
||||
GLTFTexture.ImportTask textureTask = new GLTFTexture.ImportTask(gltfObject.textures, imageTask);
|
||||
textureTask.RunSynchronously();
|
||||
GLTFMaterial.ImportTask materialTask = new GLTFMaterial.ImportTask(gltfObject.materials, textureTask, importSettings);
|
||||
materialTask.RunSynchronously();
|
||||
GLTFMesh.ImportTask meshTask = new GLTFMesh.ImportTask(gltfObject.meshes, accessorTask, bufferViewTask, materialTask, importSettings);
|
||||
meshTask.RunSynchronously();
|
||||
GLTFSkin.ImportTask skinTask = new GLTFSkin.ImportTask(gltfObject.skins, accessorTask);
|
||||
skinTask.RunSynchronously();
|
||||
GLTFNode.ImportTask nodeTask = new GLTFNode.ImportTask(gltfObject.nodes, meshTask, skinTask, gltfObject.cameras);
|
||||
nodeTask.RunSynchronously();
|
||||
GLTFAnimation.ImportResult[] animationResult = gltfObject.animations.Import(accessorTask.Result, nodeTask.Result, importSettings);
|
||||
if (animationResult != null) animations = animationResult.Select(x => x.clip).ToArray();
|
||||
else animations = new AnimationClip[0];
|
||||
|
||||
foreach (var item in bufferTask.Result) {
|
||||
item.Dispose();
|
||||
}
|
||||
|
||||
GameObject gameObject = nodeTask.Result.GetRoot();
|
||||
if (importSettings.extrasProcessor != null)
|
||||
{
|
||||
if(gltfObject.extras == null)
|
||||
{
|
||||
gltfObject.extras = new JObject();
|
||||
}
|
||||
|
||||
if(gltfObject.materials != null)
|
||||
{
|
||||
JArray materialExtras = new JArray();
|
||||
bool hasMaterialExtraData = false;
|
||||
foreach (GLTFMaterial material in gltfObject.materials)
|
||||
{
|
||||
if (material.extras != null)
|
||||
{
|
||||
materialExtras.Add(material.extras);
|
||||
hasMaterialExtraData = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
materialExtras.Add(new JObject());
|
||||
}
|
||||
}
|
||||
if (hasMaterialExtraData)
|
||||
{
|
||||
gltfObject.extras.Add("material", materialExtras);
|
||||
}
|
||||
}
|
||||
|
||||
if (gltfObject.animations != null)
|
||||
{
|
||||
JArray animationExtras = new JArray();
|
||||
bool hasAnimationExtraData = false;
|
||||
foreach (GLTFAnimation animation in gltfObject.animations)
|
||||
{
|
||||
if (animation.extras != null)
|
||||
{
|
||||
hasAnimationExtraData = true;
|
||||
animationExtras.Add(animation.extras);
|
||||
}
|
||||
else
|
||||
{
|
||||
animationExtras.Add(new JObject());
|
||||
}
|
||||
}
|
||||
if (hasAnimationExtraData)
|
||||
{
|
||||
gltfObject.extras.Add("animation", animationExtras);
|
||||
}
|
||||
}
|
||||
|
||||
importSettings.extrasProcessor.ProcessExtras(gameObject, animations, gltfObject.extras);
|
||||
}
|
||||
return gameObject;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Async
|
||||
private static IEnumerator LoadAsync(string json, string filepath, byte[] bytefile, long binChunkStart, ImportSettings importSettings, Action<GameObject, AnimationClip[]> onFinished, Action<float> onProgress = null) {
|
||||
// Threaded deserialization
|
||||
Task<GLTFObject> deserializeTask = new Task<GLTFObject>(() => JsonConvert.DeserializeObject<GLTFObject>(json));
|
||||
deserializeTask.Start();
|
||||
while (!deserializeTask.IsCompleted) yield return null;
|
||||
GLTFObject gltfObject = deserializeTask.Result;
|
||||
CheckExtensions(gltfObject);
|
||||
|
||||
// directory root is sometimes used for loading buffers from containing file, or local images
|
||||
string directoryRoot = filepath != null ? Directory.GetParent(filepath).ToString() + "/" : null;
|
||||
|
||||
importSettings.shaderOverrides.CacheDefaultShaders();
|
||||
|
||||
// Setup import tasks
|
||||
List<ImportTask> importTasks = new List<ImportTask>();
|
||||
|
||||
GLTFBuffer.ImportTask bufferTask = new GLTFBuffer.ImportTask(gltfObject.buffers, filepath, bytefile, binChunkStart);
|
||||
importTasks.Add(bufferTask);
|
||||
GLTFBufferView.ImportTask bufferViewTask = new GLTFBufferView.ImportTask(gltfObject.bufferViews, bufferTask);
|
||||
importTasks.Add(bufferViewTask);
|
||||
GLTFAccessor.ImportTask accessorTask = new GLTFAccessor.ImportTask(gltfObject.accessors, bufferViewTask);
|
||||
importTasks.Add(accessorTask);
|
||||
GLTFImage.ImportTask imageTask = new GLTFImage.ImportTask(gltfObject.images, directoryRoot, bufferViewTask);
|
||||
importTasks.Add(imageTask);
|
||||
GLTFTexture.ImportTask textureTask = new GLTFTexture.ImportTask(gltfObject.textures, imageTask);
|
||||
importTasks.Add(textureTask);
|
||||
GLTFMaterial.ImportTask materialTask = new GLTFMaterial.ImportTask(gltfObject.materials, textureTask, importSettings);
|
||||
importTasks.Add(materialTask);
|
||||
GLTFMesh.ImportTask meshTask = new GLTFMesh.ImportTask(gltfObject.meshes, accessorTask, bufferViewTask, materialTask, importSettings);
|
||||
importTasks.Add(meshTask);
|
||||
GLTFSkin.ImportTask skinTask = new GLTFSkin.ImportTask(gltfObject.skins, accessorTask);
|
||||
importTasks.Add(skinTask);
|
||||
GLTFNode.ImportTask nodeTask = new GLTFNode.ImportTask(gltfObject.nodes, meshTask, skinTask, gltfObject.cameras);
|
||||
importTasks.Add(nodeTask);
|
||||
|
||||
// Ignite
|
||||
for (int i = 0; i < importTasks.Count; i++) {
|
||||
TaskSupervisor(importTasks[i], onProgress).RunCoroutine();
|
||||
}
|
||||
|
||||
// Wait for all tasks to finish
|
||||
while (!importTasks.All(x => x.IsCompleted)) yield return null;
|
||||
|
||||
// Fire onFinished when all tasks have completed
|
||||
GameObject root = nodeTask.Result.GetRoot();
|
||||
GLTFAnimation.ImportResult[] animationResult = gltfObject.animations.Import(accessorTask.Result, nodeTask.Result, importSettings);
|
||||
AnimationClip[] animations = new AnimationClip[0];
|
||||
if (animationResult != null) animations = animationResult.Select(x => x.clip).ToArray();
|
||||
if (onFinished != null) onFinished(nodeTask.Result.GetRoot(), animations);
|
||||
|
||||
// Close file streams
|
||||
foreach (var item in bufferTask.Result) {
|
||||
item.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Keeps track of which threads to start when </summary>
|
||||
private static IEnumerator TaskSupervisor(ImportTask importTask, Action<float> onProgress = null) {
|
||||
// Wait for required results to complete before starting
|
||||
while (!importTask.IsReady) yield return null;
|
||||
// Prevent asynchronous data disorder
|
||||
yield return null;
|
||||
if(importTask.task != null)
|
||||
{
|
||||
// Start threaded task
|
||||
importTask.task.Start();
|
||||
// Wait for task to complete
|
||||
while (!importTask.task.IsCompleted) yield return null;
|
||||
// Prevent asynchronous data disorder
|
||||
yield return new WaitForSeconds(0.1f);
|
||||
}
|
||||
// Run additional unity code on main thread
|
||||
importTask.OnCoroutine(onProgress).RunCoroutine();
|
||||
//Wait for additional coroutines to complete
|
||||
while (!importTask.IsCompleted) { yield return null; }
|
||||
// Prevent asynchronous data disorder
|
||||
yield return new WaitForSeconds(0.1f);
|
||||
}
|
||||
#endregion
|
||||
|
||||
private static void CheckExtensions(GLTFObject gLTFObject) {
|
||||
if (gLTFObject.extensionsRequired != null) {
|
||||
for (int i = 0; i < gLTFObject.extensionsRequired.Count; i++) {
|
||||
switch (gLTFObject.extensionsRequired[i]) {
|
||||
case "KHR_materials_pbrSpecularGlossiness":
|
||||
break;
|
||||
case "KHR_draco_mesh_compression":
|
||||
break;
|
||||
default:
|
||||
Debug.LogWarning($"GLTFUtility: Required extension '{gLTFObject.extensionsRequired[i]}' not supported. Import process will proceed but results may vary.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/GLTFUtility-master/Scripts/Importer.cs.meta
Normal file
11
Assets/GLTFUtility-master/Scripts/Importer.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b3d8a47f9eb0b1c48b13e959fc075ada
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/GLTFUtility-master/Scripts/Settings.meta
Normal file
8
Assets/GLTFUtility-master/Scripts/Settings.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 124029330cc82b14aa51a2808ff4511c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace Siccity.GLTFUtility {
|
||||
/// <summary> Defines how animations are imported </summary>
|
||||
[Serializable]
|
||||
public class AnimationSettings {
|
||||
public bool looping;
|
||||
[Tooltip("Sample rate set on all imported animation clips.")]
|
||||
public float frameRate = 24;
|
||||
[Tooltip("Interpolation mode applied to all keyframe tangents. Use Import From File when mixing modes within an animation.")]
|
||||
public InterpolationMode interpolationMode = InterpolationMode.ImportFromFile;
|
||||
[Tooltip("When true, remove redundant keyframes from blend shape animations.")]
|
||||
public bool compressBlendShapeKeyFrames = true;
|
||||
[Tooltip("Load animations as legacy AnimationClips.")]
|
||||
public bool useLegacyClips;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 977131d8fdcfd7449b4c86946f77be9a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
34
Assets/GLTFUtility-master/Scripts/Settings/ImportSettings.cs
Normal file
34
Assets/GLTFUtility-master/Scripts/Settings/ImportSettings.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.Events;
|
||||
|
||||
|
||||
namespace Siccity.GLTFUtility {
|
||||
[Serializable]
|
||||
public class ImportSettings {
|
||||
|
||||
public bool materials = true;
|
||||
[FormerlySerializedAs("shaders")]
|
||||
public ShaderSettings shaderOverrides = new ShaderSettings();
|
||||
public AnimationSettings animationSettings = new AnimationSettings();
|
||||
public bool generateLightmapUVs;
|
||||
[Range(0, 180)]
|
||||
public float hardAngle = 88;
|
||||
|
||||
[Range(1, 75)]
|
||||
public float angleError = 8;
|
||||
|
||||
[Range(1, 75)]
|
||||
public float areaError = 15;
|
||||
|
||||
[Range(1, 64)]
|
||||
public float packMargin = 4;
|
||||
|
||||
[Tooltip("Script used to process extra data.")]
|
||||
public GLTFExtrasProcessor extrasProcessor;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b276c1b22c950af4583e0d432d9f11ea
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
61
Assets/GLTFUtility-master/Scripts/Settings/ShaderSettings.cs
Normal file
61
Assets/GLTFUtility-master/Scripts/Settings/ShaderSettings.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace Siccity.GLTFUtility {
|
||||
/// <summary> Defines which shaders to use in the gltf import process </summary>
|
||||
[Serializable]
|
||||
public class ShaderSettings {
|
||||
[SerializeField] private Shader metallic;
|
||||
public Shader Metallic { get { return metallic != null ? metallic : GetDefaultMetallic(); } }
|
||||
|
||||
[SerializeField] private Shader metallicBlend;
|
||||
public Shader MetallicBlend { get { return metallicBlend != null ? metallicBlend : GetDefaultMetallicBlend(); } }
|
||||
|
||||
[SerializeField] private Shader specular;
|
||||
public Shader Specular { get { return specular != null ? specular : GetDefaultSpecular(); } }
|
||||
|
||||
[SerializeField] private Shader specularBlend;
|
||||
public Shader SpecularBlend { get { return specularBlend != null ? specularBlend : GetDefaultSpecularBlend(); } }
|
||||
|
||||
/// <summary> Caches default shaders so that async import won't try to search for them while on a separate thread </summary>
|
||||
public void CacheDefaultShaders() {
|
||||
metallic = Metallic;
|
||||
metallicBlend = MetallicBlend;
|
||||
specular = Specular;
|
||||
specularBlend = SpecularBlend;
|
||||
}
|
||||
|
||||
public Shader GetDefaultMetallic() {
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
if (GraphicsSettings.renderPipelineAsset) return Shader.Find("GLTFUtility/URP/Standard (Metallic)");
|
||||
else
|
||||
#endif
|
||||
return Shader.Find("GLTFUtility/Standard (Metallic)");
|
||||
}
|
||||
|
||||
public Shader GetDefaultMetallicBlend() {
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
if (GraphicsSettings.renderPipelineAsset) return Shader.Find("GLTFUtility/URP/Standard Transparent (Metallic)");
|
||||
else
|
||||
#endif
|
||||
return Shader.Find("GLTFUtility/Standard Transparent (Metallic)");
|
||||
}
|
||||
|
||||
public Shader GetDefaultSpecular() {
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
if (GraphicsSettings.renderPipelineAsset) return Shader.Find("GLTFUtility/URP/Standard (Specular)");
|
||||
else
|
||||
#endif
|
||||
return Shader.Find("GLTFUtility/Standard (Specular)");
|
||||
}
|
||||
|
||||
public Shader GetDefaultSpecularBlend() {
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
if (GraphicsSettings.renderPipelineAsset) return Shader.Find("GLTFUtility/URP/Standard Transparent (Specular)");
|
||||
else
|
||||
#endif
|
||||
return Shader.Find("GLTFUtility/Standard Transparent (Specular)");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bc38d0f9a32cd0443ac479afd967ab81
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/GLTFUtility-master/Scripts/Spec.meta
Normal file
8
Assets/GLTFUtility-master/Scripts/Spec.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 893e3cbb80103e040aceb5dc3c05f135
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
508
Assets/GLTFUtility-master/Scripts/Spec/GLTFAccessor.cs
Normal file
508
Assets/GLTFUtility-master/Scripts/Spec/GLTFAccessor.cs
Normal file
@@ -0,0 +1,508 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Siccity.GLTFUtility.Converters;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace Siccity.GLTFUtility {
|
||||
// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#accessor
|
||||
/// <summary> Reads data from BufferViews </summary>
|
||||
[Preserve] public class GLTFAccessor {
|
||||
#region Serialized fields
|
||||
public int? bufferView;
|
||||
public int byteOffset = 0;
|
||||
[JsonProperty(Required = Required.Always), JsonConverter(typeof(EnumConverter))] public AccessorType type;
|
||||
[JsonProperty(Required = Required.Always)] public GLType componentType;
|
||||
[JsonProperty(Required = Required.Always)] public int count;
|
||||
public float[] min;
|
||||
public float[] max;
|
||||
public Sparse sparse;
|
||||
#endregion
|
||||
|
||||
// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#sparse
|
||||
[Preserve] public class Sparse {
|
||||
[JsonProperty(Required = Required.Always)] public int count;
|
||||
[JsonProperty(Required = Required.Always)] public Indices indices;
|
||||
[JsonProperty(Required = Required.Always)] public Values values;
|
||||
|
||||
// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#values
|
||||
[Preserve] public class Values {
|
||||
[JsonProperty(Required = Required.Always)] public int bufferView;
|
||||
public int byteOffset = 0;
|
||||
}
|
||||
|
||||
// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#indices
|
||||
[Preserve] public class Indices {
|
||||
[JsonProperty(Required = Required.Always)] public int bufferView;
|
||||
[JsonProperty(Required = Required.Always)] public GLType componentType;
|
||||
public int byteOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#region Import
|
||||
public class ImportResult {
|
||||
|
||||
private const float byteNormalize = 1f / byte.MaxValue;
|
||||
private const float shortNormalize = 1f / short.MaxValue;
|
||||
private const float ushortNormalize = 1f / ushort.MaxValue;
|
||||
private const float intNormalize = 1f / int.MaxValue;
|
||||
private const float uintNormalize = 1f / uint.MaxValue;
|
||||
|
||||
public GLTFBufferView.ImportResult bufferView;
|
||||
public int? byteStride;
|
||||
public int count;
|
||||
public GLType componentType;
|
||||
public AccessorType type;
|
||||
public int byteOffset;
|
||||
public Sparse sparse;
|
||||
|
||||
public class Sparse {
|
||||
public int count;
|
||||
public Indices indices;
|
||||
public Values values;
|
||||
|
||||
public class Values {
|
||||
public GLTFBufferView.ImportResult bufferView;
|
||||
public int byteOffset = 0;
|
||||
}
|
||||
|
||||
public class Indices {
|
||||
public GLTFBufferView.ImportResult bufferView;
|
||||
public GLType componentType;
|
||||
public int byteOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public Matrix4x4[] ReadMatrix4x4() {
|
||||
if (!ValidateAccessorType(type, AccessorType.MAT4)) return new Matrix4x4[count];
|
||||
|
||||
Func<BufferedBinaryReader, float> floatReader = GetFloatReader(componentType);
|
||||
|
||||
Matrix4x4[] m = new Matrix4x4[count];
|
||||
if (bufferView != null) {
|
||||
BufferedBinaryReader reader = new BufferedBinaryReader(bufferView.stream, 1024);
|
||||
reader.Position = bufferView.byteOffset + byteOffset;
|
||||
int byteSkip = byteStride.HasValue ? byteStride.Value - GetComponentSize() : 0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
m[i].m00 = floatReader(reader);
|
||||
m[i].m01 = floatReader(reader);
|
||||
m[i].m02 = floatReader(reader);
|
||||
m[i].m03 = floatReader(reader);
|
||||
m[i].m10 = floatReader(reader);
|
||||
m[i].m11 = floatReader(reader);
|
||||
m[i].m12 = floatReader(reader);
|
||||
m[i].m13 = floatReader(reader);
|
||||
m[i].m20 = floatReader(reader);
|
||||
m[i].m21 = floatReader(reader);
|
||||
m[i].m22 = floatReader(reader);
|
||||
m[i].m23 = floatReader(reader);
|
||||
m[i].m30 = floatReader(reader);
|
||||
m[i].m31 = floatReader(reader);
|
||||
m[i].m32 = floatReader(reader);
|
||||
m[i].m33 = floatReader(reader);
|
||||
reader.Skip(byteSkip);
|
||||
}
|
||||
}
|
||||
if (sparse != null) {
|
||||
Func<BufferedBinaryReader, int> indexIntReader = GetIntReader(sparse.indices.componentType);
|
||||
BufferedBinaryReader indexReader = new BufferedBinaryReader(sparse.indices.bufferView.stream, 1024);
|
||||
indexReader.Position = sparse.indices.bufferView.byteOffset + sparse.indices.byteOffset;
|
||||
int[] indices = new int[sparse.count];
|
||||
for (int i = 0; i < sparse.count; i++) {
|
||||
indices[i] = indexIntReader(indexReader);
|
||||
}
|
||||
BufferedBinaryReader valueReader = new BufferedBinaryReader(sparse.values.bufferView.stream, 1024);
|
||||
indexReader.Position = sparse.values.bufferView.byteOffset + sparse.values.byteOffset;
|
||||
for (int i = 0; i < sparse.count; i++) {
|
||||
int index = indices[i];
|
||||
m[index].m00 = floatReader(valueReader);
|
||||
m[index].m01 = floatReader(valueReader);
|
||||
m[index].m02 = floatReader(valueReader);
|
||||
m[index].m03 = floatReader(valueReader);
|
||||
m[index].m10 = floatReader(valueReader);
|
||||
m[index].m11 = floatReader(valueReader);
|
||||
m[index].m12 = floatReader(valueReader);
|
||||
m[index].m13 = floatReader(valueReader);
|
||||
m[index].m20 = floatReader(valueReader);
|
||||
m[index].m21 = floatReader(valueReader);
|
||||
m[index].m22 = floatReader(valueReader);
|
||||
m[index].m23 = floatReader(valueReader);
|
||||
m[index].m30 = floatReader(valueReader);
|
||||
m[index].m31 = floatReader(valueReader);
|
||||
m[index].m32 = floatReader(valueReader);
|
||||
m[index].m33 = floatReader(valueReader);
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
public Vector4[] ReadVec4(bool normalize = false) {
|
||||
if (!ValidateAccessorType(type, AccessorType.VEC4)) return new Vector4[count];
|
||||
|
||||
Func<BufferedBinaryReader, float> floatReader = normalize ? GetNormalizedFloatReader(componentType) : GetFloatReader(componentType);
|
||||
|
||||
Vector4[] v = new Vector4[count];
|
||||
if (bufferView != null) {
|
||||
BufferedBinaryReader reader = new BufferedBinaryReader(bufferView.stream, 1024);
|
||||
reader.Position = bufferView.byteOffset + byteOffset;
|
||||
int byteSkip = byteStride.HasValue ? byteStride.Value - GetComponentSize() : 0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
v[i].x = floatReader(reader);
|
||||
v[i].y = floatReader(reader);
|
||||
v[i].z = floatReader(reader);
|
||||
v[i].w = floatReader(reader);
|
||||
reader.Skip(byteSkip);
|
||||
}
|
||||
}
|
||||
if (sparse != null) {
|
||||
Func<BufferedBinaryReader, int> indexIntReader = GetIntReader(sparse.indices.componentType);
|
||||
BufferedBinaryReader indexReader = new BufferedBinaryReader(sparse.indices.bufferView.stream, 1024);
|
||||
indexReader.Position = sparse.indices.bufferView.byteOffset + sparse.indices.byteOffset;
|
||||
int[] indices = new int[sparse.count];
|
||||
for (int i = 0; i < sparse.count; i++) {
|
||||
indices[i] = indexIntReader(indexReader);
|
||||
}
|
||||
BufferedBinaryReader valueReader = new BufferedBinaryReader(sparse.values.bufferView.stream, 1024);
|
||||
indexReader.Position = sparse.values.bufferView.byteOffset + sparse.values.byteOffset;
|
||||
for (int i = 0; i < sparse.count; i++) {
|
||||
int index = indices[i];
|
||||
v[index].x = floatReader(valueReader);
|
||||
v[index].y = floatReader(valueReader);
|
||||
v[index].z = floatReader(valueReader);
|
||||
v[index].w = floatReader(valueReader);
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
public Color[] ReadColor() {
|
||||
if (!ValidateAccessorTypeAny(type, AccessorType.VEC3, AccessorType.VEC4)) return new Color[count];
|
||||
|
||||
Func<BufferedBinaryReader, float> floatReader = GetNormalizedFloatReader(componentType);
|
||||
|
||||
Color[] c = new Color[count];
|
||||
if (bufferView != null) {
|
||||
BufferedBinaryReader reader = new BufferedBinaryReader(bufferView.stream, 1024);
|
||||
reader.Position = bufferView.byteOffset + byteOffset;
|
||||
int byteSkip = byteStride.HasValue ? byteStride.Value - GetComponentSize() : 0;
|
||||
if (type == AccessorType.VEC3) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
c[i].r = floatReader(reader);
|
||||
c[i].g = floatReader(reader);
|
||||
c[i].b = floatReader(reader);
|
||||
reader.Skip(byteSkip);
|
||||
}
|
||||
} else if (type == AccessorType.VEC4) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
c[i].r = floatReader(reader);
|
||||
c[i].g = floatReader(reader);
|
||||
c[i].b = floatReader(reader);
|
||||
c[i].a = floatReader(reader);
|
||||
reader.Skip(byteSkip);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sparse != null) {
|
||||
Func<BufferedBinaryReader, int> indexIntReader = GetIntReader(sparse.indices.componentType);
|
||||
BufferedBinaryReader indexReader = new BufferedBinaryReader(sparse.indices.bufferView.stream, 1024);
|
||||
indexReader.Position = sparse.indices.bufferView.byteOffset + sparse.indices.byteOffset;
|
||||
int[] indices = new int[sparse.count];
|
||||
for (int i = 0; i < sparse.count; i++) {
|
||||
indices[i] = indexIntReader(indexReader);
|
||||
}
|
||||
BufferedBinaryReader valueReader = new BufferedBinaryReader(sparse.values.bufferView.stream, 1024);
|
||||
indexReader.Position = sparse.values.bufferView.byteOffset + sparse.values.byteOffset;
|
||||
if (type == AccessorType.VEC3) {
|
||||
for (int i = 0; i < sparse.count; i++) {
|
||||
int index = indices[i];
|
||||
c[index].r = floatReader(valueReader);
|
||||
c[index].g = floatReader(valueReader);
|
||||
c[index].b = floatReader(valueReader);
|
||||
}
|
||||
} else if (type == AccessorType.VEC4) {
|
||||
for (int i = 0; i < sparse.count; i++) {
|
||||
int index = indices[i];
|
||||
c[index].r = floatReader(valueReader);
|
||||
c[index].g = floatReader(valueReader);
|
||||
c[index].b = floatReader(valueReader);
|
||||
c[index].a = floatReader(valueReader);
|
||||
}
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
public Vector3[] ReadVec3(bool normalize = false) {
|
||||
if (!ValidateAccessorType(type, AccessorType.VEC3)) return new Vector3[count];
|
||||
|
||||
Func<BufferedBinaryReader, float> floatReader = normalize ? GetNormalizedFloatReader(componentType) : GetFloatReader(componentType);
|
||||
|
||||
Vector3[] v = new Vector3[count];
|
||||
if (bufferView != null) {
|
||||
BufferedBinaryReader reader = new BufferedBinaryReader(bufferView.stream, 1024);
|
||||
reader.Position = bufferView.byteOffset + byteOffset;
|
||||
int byteSkip = byteStride.HasValue ? byteStride.Value - GetComponentSize() : 0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
v[i].x = floatReader(reader);
|
||||
v[i].y = floatReader(reader);
|
||||
v[i].z = floatReader(reader);
|
||||
reader.Skip(byteSkip);
|
||||
}
|
||||
}
|
||||
if (sparse != null) {
|
||||
Func<BufferedBinaryReader, int> indexIntReader = GetIntReader(sparse.indices.componentType);
|
||||
BufferedBinaryReader indexReader = new BufferedBinaryReader(sparse.indices.bufferView.stream, 1024);
|
||||
indexReader.Position = sparse.indices.bufferView.byteOffset + sparse.indices.byteOffset;
|
||||
int[] indices = new int[sparse.count];
|
||||
for (int i = 0; i < sparse.count; i++) {
|
||||
indices[i] = indexIntReader(indexReader);
|
||||
}
|
||||
BufferedBinaryReader valueReader = new BufferedBinaryReader(sparse.values.bufferView.stream, 1024);
|
||||
valueReader.Position = sparse.values.bufferView.byteOffset + sparse.values.byteOffset;
|
||||
for (int i = 0; i < sparse.count; i++) {
|
||||
int index = indices[i];
|
||||
v[index].x = floatReader(valueReader);
|
||||
v[index].y = floatReader(valueReader);
|
||||
v[index].z = floatReader(valueReader);
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
public Vector2[] ReadVec2(bool normalize = false) {
|
||||
if (!ValidateAccessorType(type, AccessorType.VEC2)) return new Vector2[count];
|
||||
|
||||
Func<BufferedBinaryReader, float> floatReader = normalize ? GetNormalizedFloatReader(componentType) : GetFloatReader(componentType);
|
||||
|
||||
Vector2[] v = new Vector2[count];
|
||||
if (bufferView != null) {
|
||||
BufferedBinaryReader reader = new BufferedBinaryReader(bufferView.stream, 1024);
|
||||
reader.Position = bufferView.byteOffset + byteOffset;
|
||||
int byteSkip = byteStride.HasValue ? byteStride.Value - GetComponentSize() : 0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
v[i].x = floatReader(reader);
|
||||
v[i].y = floatReader(reader);
|
||||
reader.Skip(byteSkip);
|
||||
}
|
||||
}
|
||||
if (sparse != null) {
|
||||
Func<BufferedBinaryReader, int> indexIntReader = GetIntReader(sparse.indices.componentType);
|
||||
BufferedBinaryReader indexReader = new BufferedBinaryReader(sparse.indices.bufferView.stream, 1024);
|
||||
indexReader.Position = sparse.indices.bufferView.byteOffset + sparse.indices.byteOffset;
|
||||
int[] indices = new int[sparse.count];
|
||||
for (int i = 0; i < sparse.count; i++) {
|
||||
indices[i] = indexIntReader(indexReader);
|
||||
}
|
||||
BufferedBinaryReader valueReader = new BufferedBinaryReader(sparse.values.bufferView.stream, 1024);
|
||||
indexReader.Position = sparse.values.bufferView.byteOffset + sparse.values.byteOffset;
|
||||
for (int i = 0; i < sparse.count; i++) {
|
||||
int index = indices[i];
|
||||
v[index].x = floatReader(valueReader);
|
||||
v[index].y = floatReader(valueReader);
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
public float[] ReadFloat() {
|
||||
if (!ValidateAccessorType(type, AccessorType.SCALAR)) return new float[count];
|
||||
|
||||
Func<BufferedBinaryReader, float> floatReader = GetFloatReader(componentType);
|
||||
|
||||
float[] f = new float[count];
|
||||
if (bufferView != null) {
|
||||
BufferedBinaryReader reader = new BufferedBinaryReader(bufferView.stream, 1024);
|
||||
reader.Position = bufferView.byteOffset + byteOffset;
|
||||
int byteSkip = byteStride.HasValue ? byteStride.Value - GetComponentSize() : 0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
f[i] = floatReader(reader);
|
||||
reader.Skip(byteSkip);
|
||||
}
|
||||
}
|
||||
if (sparse != null) {
|
||||
Func<BufferedBinaryReader, int> indexIntReader = GetIntReader(sparse.indices.componentType);
|
||||
BufferedBinaryReader indexReader = new BufferedBinaryReader(sparse.indices.bufferView.stream, 1024);
|
||||
indexReader.Position = sparse.indices.bufferView.byteOffset + sparse.indices.byteOffset;
|
||||
int[] indices = new int[sparse.count];
|
||||
for (int i = 0; i < sparse.count; i++) {
|
||||
indices[i] = indexIntReader(indexReader);
|
||||
}
|
||||
BufferedBinaryReader valueReader = new BufferedBinaryReader(sparse.values.bufferView.stream, 1024);
|
||||
indexReader.Position = sparse.values.bufferView.byteOffset + sparse.values.byteOffset;
|
||||
for (int i = 0; i < sparse.count; i++) {
|
||||
int index = indices[i];
|
||||
f[index] = floatReader(valueReader);
|
||||
}
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
public int[] ReadInt() {
|
||||
if (!ValidateAccessorType(type, AccessorType.SCALAR)) return new int[count];
|
||||
|
||||
Func<BufferedBinaryReader, int> intReader = GetIntReader(componentType);
|
||||
|
||||
int[] v = new int[count];
|
||||
if (bufferView != null) {
|
||||
BufferedBinaryReader reader = new BufferedBinaryReader(bufferView.stream, 1024);
|
||||
reader.Position = bufferView.byteOffset + byteOffset;
|
||||
int byteSkip = byteStride.HasValue ? byteStride.Value - GetComponentSize() : 0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
v[i] = intReader(reader);
|
||||
reader.Skip(byteSkip);
|
||||
}
|
||||
}
|
||||
if (sparse != null) {
|
||||
Func<BufferedBinaryReader, int> indexIntReader = GetIntReader(sparse.indices.componentType);
|
||||
BufferedBinaryReader indexReader = new BufferedBinaryReader(sparse.indices.bufferView.stream, 1024);
|
||||
indexReader.Position = sparse.indices.bufferView.byteOffset + sparse.indices.byteOffset;
|
||||
int[] indices = new int[sparse.count];
|
||||
for (int i = 0; i < sparse.count; i++) {
|
||||
indices[i] = indexIntReader(indexReader);
|
||||
}
|
||||
BufferedBinaryReader valueReader = new BufferedBinaryReader(sparse.values.bufferView.stream, 1024);
|
||||
indexReader.Position = sparse.values.bufferView.byteOffset + sparse.values.byteOffset;
|
||||
for (int i = 0; i < sparse.count; i++) {
|
||||
int index = indices[i];
|
||||
v[index] = intReader(valueReader);
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
public Func<BufferedBinaryReader, int> GetIntReader(GLType componentType) {
|
||||
Func<BufferedBinaryReader, int> readMethod;
|
||||
switch (componentType) {
|
||||
case GLType.BYTE:
|
||||
return x => x.ReadSByte();
|
||||
case GLType.UNSIGNED_BYTE:
|
||||
return readMethod = x => x.ReadByte();
|
||||
case GLType.FLOAT:
|
||||
return readMethod = x => (int) x.ReadSingle();
|
||||
case GLType.SHORT:
|
||||
return readMethod = x => x.ReadInt16();
|
||||
case GLType.UNSIGNED_SHORT:
|
||||
return readMethod = x => x.ReadUInt16();
|
||||
case GLType.UNSIGNED_INT:
|
||||
return readMethod = x => (int) x.ReadUInt32();
|
||||
default:
|
||||
Debug.LogWarning("No componentType defined");
|
||||
return readMethod = x => x.ReadInt32();
|
||||
}
|
||||
}
|
||||
|
||||
public Func<BufferedBinaryReader, float> GetFloatReader(GLType componentType) {
|
||||
Func<BufferedBinaryReader, float> readMethod;
|
||||
switch (componentType) {
|
||||
case GLType.BYTE:
|
||||
return x => x.ReadSByte();
|
||||
case GLType.UNSIGNED_BYTE:
|
||||
return readMethod = x => x.ReadByte();
|
||||
case GLType.FLOAT:
|
||||
return readMethod = x => x.ReadSingle();
|
||||
case GLType.SHORT:
|
||||
return readMethod = x => x.ReadInt16();
|
||||
case GLType.UNSIGNED_SHORT:
|
||||
return readMethod = x => x.ReadUInt16();
|
||||
case GLType.UNSIGNED_INT:
|
||||
return readMethod = x => x.ReadUInt32();
|
||||
default:
|
||||
Debug.LogWarning("No componentType defined");
|
||||
return readMethod = x => x.ReadSingle();
|
||||
}
|
||||
}
|
||||
|
||||
public Func<BufferedBinaryReader, float> GetNormalizedFloatReader(GLType componentType)
|
||||
{
|
||||
Func<BufferedBinaryReader, float> readMethod;
|
||||
switch(componentType)
|
||||
{
|
||||
case GLType.BYTE:
|
||||
return x => x.ReadSByte();
|
||||
case GLType.UNSIGNED_BYTE:
|
||||
return readMethod = x => x.ReadByte() * byteNormalize;
|
||||
case GLType.FLOAT:
|
||||
return readMethod = x => x.ReadSingle();
|
||||
case GLType.SHORT:
|
||||
return readMethod = x => x.ReadInt16() * shortNormalize;
|
||||
case GLType.UNSIGNED_SHORT:
|
||||
return readMethod = x => x.ReadUInt16() * ushortNormalize;
|
||||
case GLType.UNSIGNED_INT:
|
||||
return readMethod = x => x.ReadUInt32() / uintNormalize;
|
||||
default:
|
||||
Debug.LogWarning("No componentType defined");
|
||||
return readMethod = x => x.ReadSingle();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Get the size of the attribute type, in bytes </summary>
|
||||
public int GetComponentSize() {
|
||||
return type.ComponentCount() * componentType.ByteSize();
|
||||
}
|
||||
|
||||
public static bool ValidateByteStride(int byteStride) {
|
||||
if (byteStride >= 4 && byteStride <= 252 && byteStride % 4 == 0) return true;
|
||||
Debug.Log("ByteStride of " + byteStride + " is invalid. Ignoring.");
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool ValidateAccessorType(AccessorType type, AccessorType expected) {
|
||||
if (type == expected) return true;
|
||||
else {
|
||||
Debug.LogError("Type mismatch! Expected " + expected + " got " + type);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ValidateAccessorTypeAny(AccessorType type, params AccessorType[] expected) {
|
||||
for (int i = 0; i < expected.Length; i++) {
|
||||
if (type == expected[i]) return true;
|
||||
}
|
||||
Debug.Log("Type mismatch! Expected " + string.Join("or ", expected) + ", got " + type);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public ImportResult Import(GLTFBufferView.ImportResult[] bufferViews) {
|
||||
|
||||
ImportResult result = new ImportResult();
|
||||
result.bufferView = bufferView.HasValue ? bufferViews[bufferView.Value] : null;
|
||||
result.componentType = componentType;
|
||||
result.type = type;
|
||||
result.count = count;
|
||||
result.byteOffset = byteOffset;
|
||||
result.byteStride = result.bufferView != null ? result.bufferView.byteStride : null;
|
||||
// Sparse accessor works by overwriting specified indices instead of defining a full data set. This can save space, especially for morph targets
|
||||
if (sparse != null) {
|
||||
result.sparse = new ImportResult.Sparse() {
|
||||
count = sparse.count,
|
||||
indices = new ImportResult.Sparse.Indices() {
|
||||
bufferView = bufferViews[sparse.indices.bufferView],
|
||||
componentType = sparse.indices.componentType,
|
||||
byteOffset = sparse.indices.byteOffset
|
||||
},
|
||||
values = new ImportResult.Sparse.Values() {
|
||||
bufferView = bufferViews[sparse.values.bufferView],
|
||||
byteOffset = sparse.values.byteOffset
|
||||
}
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public class ImportTask : Importer.ImportTask<ImportResult[]> {
|
||||
public ImportTask(List<GLTFAccessor> accessors, GLTFBufferView.ImportTask bufferViewTask) : base(bufferViewTask) {
|
||||
task = new Task(() => {
|
||||
Result = new ImportResult[accessors.Count];
|
||||
for (int i = 0; i < Result.Length; i++) {
|
||||
Result[i] = accessors[i].Import(bufferViewTask.Result);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
13
Assets/GLTFUtility-master/Scripts/Spec/GLTFAccessor.cs.meta
Normal file
13
Assets/GLTFUtility-master/Scripts/Spec/GLTFAccessor.cs.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2f27a48d4b05948419cd94312065cc96
|
||||
timeCreated: 1539029690
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
237
Assets/GLTFUtility-master/Scripts/Spec/GLTFAnimation.cs
Normal file
237
Assets/GLTFUtility-master/Scripts/Spec/GLTFAnimation.cs
Normal file
@@ -0,0 +1,237 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Siccity.GLTFUtility.Converters;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Scripting;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Siccity.GLTFUtility {
|
||||
// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#animation
|
||||
/// <summary> Contains info for a single animation clip </summary>
|
||||
[Preserve] public class GLTFAnimation {
|
||||
/// <summary> Connects the output values of the key frame animation to a specific node in the hierarchy </summary>
|
||||
[JsonProperty(Required = Required.Always)] public Channel[] channels;
|
||||
[JsonProperty(Required = Required.Always)] public Sampler[] samplers;
|
||||
public string name;
|
||||
public JObject extras;
|
||||
|
||||
#region Classes
|
||||
// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#animation-sampler
|
||||
[Preserve] public class Sampler {
|
||||
/// <summary> The index of an accessor containing keyframe input values, e.g., time. </summary>
|
||||
[JsonProperty(Required = Required.Always)] public int input;
|
||||
/// <summary> The index of an accessor containing keyframe output values. </summary>
|
||||
[JsonProperty(Required = Required.Always)] public int output;
|
||||
/// <summary> Valid names include: "LINEAR", "STEP", "CUBICSPLINE" </summary>
|
||||
[JsonConverter(typeof(EnumConverter))] public InterpolationMode interpolation = InterpolationMode.LINEAR;
|
||||
}
|
||||
|
||||
// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#channel
|
||||
/// <summary> Connects the output values of the key frame animation to a specific node in the hierarchy </summary>
|
||||
[Preserve] public class Channel {
|
||||
/// <summary> Target sampler index </summary>
|
||||
[JsonProperty(Required = Required.Always)] public int sampler;
|
||||
/// <summary> Target sampler index </summary>
|
||||
[JsonProperty(Required = Required.Always)] public Target target;
|
||||
}
|
||||
|
||||
// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#target
|
||||
/// <summary> Identifies which node and property to animate </summary>
|
||||
[Preserve] public class Target {
|
||||
/// <summary> Target node index.</summary>
|
||||
public int? node;
|
||||
/// <summary> Which property to animate. Valid names are: "translation", "rotation", "scale", "weights" </summary>
|
||||
[JsonProperty(Required = Required.Always)] public string path;
|
||||
}
|
||||
|
||||
[Preserve] public class ImportResult {
|
||||
public AnimationClip clip;
|
||||
}
|
||||
#endregion
|
||||
|
||||
public ImportResult Import(GLTFAccessor.ImportResult[] accessors, GLTFNode.ImportResult[] nodes, ImportSettings importSettings) {
|
||||
bool multiRoots = nodes.Where(x => x.IsRoot).Count() > 1;
|
||||
|
||||
ImportResult result = new ImportResult();
|
||||
result.clip = new AnimationClip();
|
||||
result.clip.name = name;
|
||||
result.clip.frameRate = importSettings.animationSettings.frameRate;
|
||||
|
||||
result.clip.legacy = importSettings.animationSettings.useLegacyClips;
|
||||
|
||||
if (result.clip.legacy && importSettings.animationSettings.looping)
|
||||
{
|
||||
result.clip.wrapMode = WrapMode.Loop;
|
||||
}
|
||||
|
||||
for (int i = 0; i < channels.Length; i++) {
|
||||
Channel channel = channels[i];
|
||||
if (samplers.Length <= channel.sampler) {
|
||||
Debug.LogWarning($"GLTFUtility: Animation channel points to sampler at index {channel.sampler} which doesn't exist. Skipping animation clip.");
|
||||
continue;
|
||||
}
|
||||
Sampler sampler = samplers[channel.sampler];
|
||||
|
||||
// Get interpolation mode
|
||||
InterpolationMode interpolationMode = importSettings.animationSettings.interpolationMode;
|
||||
if (interpolationMode == InterpolationMode.ImportFromFile) {
|
||||
interpolationMode = sampler.interpolation;
|
||||
}
|
||||
if (interpolationMode == InterpolationMode.CUBICSPLINE) Debug.LogWarning("Animation interpolation mode CUBICSPLINE not fully supported, result might look different.");
|
||||
|
||||
string relativePath = "";
|
||||
|
||||
GLTFNode.ImportResult node = nodes[channel.target.node.Value];
|
||||
while (node != null && !node.IsRoot) {
|
||||
if (string.IsNullOrEmpty(relativePath)) relativePath = node.transform.name;
|
||||
else relativePath = node.transform.name + "/" + relativePath;
|
||||
|
||||
if (node.parent.HasValue) node = nodes[node.parent.Value];
|
||||
else node = null;
|
||||
}
|
||||
|
||||
// If file has multiple root nodes, a new parent will be created for them as a final step of the import process. This parent fucks up the curve relative paths.
|
||||
// Add node.transform.name to path if there are multiple roots. This is not the most elegant fix but it works.
|
||||
// See GLTFNodeExtensions.GetRoot
|
||||
if (multiRoots) relativePath = node.transform.name + "/" + relativePath;
|
||||
|
||||
System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.InvariantCulture;
|
||||
float[] keyframeInput = accessors[sampler.input].ReadFloat().ToArray();
|
||||
switch (channel.target.path) {
|
||||
case "translation":
|
||||
Vector3[] pos = accessors[sampler.output].ReadVec3().ToArray();
|
||||
AnimationCurve posX = new AnimationCurve();
|
||||
AnimationCurve posY = new AnimationCurve();
|
||||
AnimationCurve posZ = new AnimationCurve();
|
||||
for (int k = 0; k < keyframeInput.Length; k++) {
|
||||
posX.AddKey(CreateKeyframe(k, keyframeInput, pos, x => -x.x, interpolationMode));
|
||||
posY.AddKey(CreateKeyframe(k, keyframeInput, pos, x => x.y, interpolationMode));
|
||||
posZ.AddKey(CreateKeyframe(k, keyframeInput, pos, x => x.z, interpolationMode));
|
||||
}
|
||||
result.clip.SetCurve(relativePath, typeof(Transform), "localPosition.x", posX);
|
||||
result.clip.SetCurve(relativePath, typeof(Transform), "localPosition.y", posY);
|
||||
result.clip.SetCurve(relativePath, typeof(Transform), "localPosition.z", posZ);
|
||||
break;
|
||||
case "rotation":
|
||||
Vector4[] rot = accessors[sampler.output].ReadVec4().ToArray();
|
||||
AnimationCurve rotX = new AnimationCurve();
|
||||
AnimationCurve rotY = new AnimationCurve();
|
||||
AnimationCurve rotZ = new AnimationCurve();
|
||||
AnimationCurve rotW = new AnimationCurve();
|
||||
for (int k = 0; k < keyframeInput.Length; k++) {
|
||||
// The Animation window in Unity shows keyframes incorrectly converted to euler. This is only to deceive you. The quaternions underneath work correctly
|
||||
rotX.AddKey(CreateKeyframe(k, keyframeInput, rot, x => x.x, interpolationMode));
|
||||
rotY.AddKey(CreateKeyframe(k, keyframeInput, rot, x => -x.y, interpolationMode));
|
||||
rotZ.AddKey(CreateKeyframe(k, keyframeInput, rot, x => -x.z, interpolationMode));
|
||||
rotW.AddKey(CreateKeyframe(k, keyframeInput, rot, x => x.w, interpolationMode));
|
||||
}
|
||||
result.clip.SetCurve(relativePath, typeof(Transform), "localRotation.x", rotX);
|
||||
result.clip.SetCurve(relativePath, typeof(Transform), "localRotation.y", rotY);
|
||||
result.clip.SetCurve(relativePath, typeof(Transform), "localRotation.z", rotZ);
|
||||
result.clip.SetCurve(relativePath, typeof(Transform), "localRotation.w", rotW);
|
||||
break;
|
||||
case "scale":
|
||||
Vector3[] scale = accessors[sampler.output].ReadVec3().ToArray();
|
||||
AnimationCurve scaleX = new AnimationCurve();
|
||||
AnimationCurve scaleY = new AnimationCurve();
|
||||
AnimationCurve scaleZ = new AnimationCurve();
|
||||
for (int k = 0; k < keyframeInput.Length; k++) {
|
||||
scaleX.AddKey(CreateKeyframe(k, keyframeInput, scale, x => x.x, interpolationMode));
|
||||
scaleY.AddKey(CreateKeyframe(k, keyframeInput, scale, x => x.y, interpolationMode));
|
||||
scaleZ.AddKey(CreateKeyframe(k, keyframeInput, scale, x => x.z, interpolationMode));
|
||||
}
|
||||
result.clip.SetCurve(relativePath, typeof(Transform), "localScale.x", scaleX);
|
||||
result.clip.SetCurve(relativePath, typeof(Transform), "localScale.y", scaleY);
|
||||
result.clip.SetCurve(relativePath, typeof(Transform), "localScale.z", scaleZ);
|
||||
break;
|
||||
case "weights":
|
||||
GLTFNode.ImportResult skinnedMeshNode = nodes[channel.target.node.Value];
|
||||
SkinnedMeshRenderer skinnedMeshRenderer = skinnedMeshNode.transform.GetComponent<SkinnedMeshRenderer>();
|
||||
|
||||
int numberOfBlendShapes = skinnedMeshRenderer.sharedMesh.blendShapeCount;
|
||||
AnimationCurve[] blendShapeCurves = new AnimationCurve[numberOfBlendShapes];
|
||||
for(int j = 0; j < numberOfBlendShapes; ++j) {
|
||||
blendShapeCurves[j] = new AnimationCurve();
|
||||
}
|
||||
|
||||
float[] weights = accessors[sampler.output].ReadFloat().ToArray();
|
||||
float[] weightValues = new float[keyframeInput.Length];
|
||||
|
||||
float[] previouslyKeyedValues = new float[numberOfBlendShapes];
|
||||
|
||||
// Reference for my future self:
|
||||
// keyframeInput.Length = number of keyframes
|
||||
// keyframeInput[ k ] = timestamp of keyframe
|
||||
// weights.Length = number of keyframes * number of blendshapes
|
||||
// weights[ j ] = actual animated weight of a specific blend shape
|
||||
// (index into weights[] array accounts for keyframe index and blend shape index)
|
||||
|
||||
for(int k = 0; k < keyframeInput.Length; ++k) {
|
||||
for(int j = 0; j < numberOfBlendShapes; ++j) {
|
||||
int weightIndex = (k * numberOfBlendShapes) + j;
|
||||
weightValues[k] = weights[weightIndex];
|
||||
|
||||
bool addKey = true;
|
||||
if(importSettings.animationSettings.compressBlendShapeKeyFrames) {
|
||||
if(k == 0 || !Mathf.Approximately(weightValues[k], previouslyKeyedValues[j])) {
|
||||
if(k > 0) {
|
||||
weightValues[k-1] = previouslyKeyedValues[j];
|
||||
blendShapeCurves[j].AddKey(CreateKeyframe(k-1, keyframeInput, weightValues, x => x, interpolationMode));
|
||||
}
|
||||
addKey = true;
|
||||
previouslyKeyedValues[j] = weightValues[k];
|
||||
} else {
|
||||
addKey = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(addKey) {
|
||||
blendShapeCurves[j].AddKey(CreateKeyframe(k, keyframeInput, weightValues, x => x, interpolationMode));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(int j = 0; j < numberOfBlendShapes; ++j) {
|
||||
string propertyName = "blendShape." + skinnedMeshRenderer.sharedMesh.GetBlendShapeName(j);
|
||||
result.clip.SetCurve(relativePath, typeof(SkinnedMeshRenderer), propertyName, blendShapeCurves[j]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Keyframe CreateKeyframe<T>(int index, float[] timeArray, T[] valueArray, Func<T, float> getValue, InterpolationMode interpolationMode) {
|
||||
float time = timeArray[index];
|
||||
Keyframe keyframe;
|
||||
#pragma warning disable CS0618
|
||||
if (interpolationMode == InterpolationMode.STEP) {
|
||||
keyframe = new Keyframe(time, getValue(valueArray[index]), float.PositiveInfinity, float.PositiveInfinity, 1, 1);
|
||||
} else if (interpolationMode == InterpolationMode.CUBICSPLINE) {
|
||||
// @TODO: Find out what the right math is to calculate the tangent/weight values.
|
||||
float inTangent = getValue(valueArray[index * 3]);
|
||||
float outTangent = getValue(valueArray[(index * 3) + 2]);
|
||||
keyframe = new Keyframe(time, getValue(valueArray[(index * 3) + 1]), inTangent, outTangent, 1, 1);
|
||||
} else { // LINEAR
|
||||
keyframe = new Keyframe(time, getValue(valueArray[index]), 0, 0, 0, 0);
|
||||
}
|
||||
#pragma warning restore CS0618
|
||||
return keyframe;
|
||||
}
|
||||
}
|
||||
|
||||
public static class GLTFAnimationExtensions {
|
||||
public static GLTFAnimation.ImportResult[] Import(this List<GLTFAnimation> animations, GLTFAccessor.ImportResult[] accessors, GLTFNode.ImportResult[] nodes, ImportSettings importSettings) {
|
||||
if (animations == null) return null;
|
||||
|
||||
GLTFAnimation.ImportResult[] results = new GLTFAnimation.ImportResult[animations.Count];
|
||||
for (int i = 0; i < results.Length; i++) {
|
||||
results[i] = animations[i].Import(accessors, nodes, importSettings);
|
||||
if (string.IsNullOrEmpty(results[i].clip.name)) results[i].clip.name = "animation" + i;
|
||||
}
|
||||
return results;
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Assets/GLTFUtility-master/Scripts/Spec/GLTFAnimation.cs.meta
Normal file
13
Assets/GLTFUtility-master/Scripts/Spec/GLTFAnimation.cs.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e5a3f1b564768444a879a11c4ae57c8c
|
||||
timeCreated: 1539024463
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
21
Assets/GLTFUtility-master/Scripts/Spec/GLTFAsset.cs
Normal file
21
Assets/GLTFUtility-master/Scripts/Spec/GLTFAsset.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace Siccity.GLTFUtility {
|
||||
// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#asset-1
|
||||
/// <summary> Metadata about the glTF asset </summary>
|
||||
[Preserve] public class GLTFAsset {
|
||||
/// <summary> A copyright message suitable for display to credit the content creator. </summary>
|
||||
public string copyright;
|
||||
/// <summary> Tool that generated this glTF model. Useful for debugging. </summary>
|
||||
public string generator;
|
||||
/// <summary> The glTF version that this asset targets. </summary>
|
||||
[JsonProperty(Required = Required.Always)] public string version;
|
||||
/// <summary> The minimum glTF version that this asset targets. </summary>
|
||||
public string minVersion;
|
||||
}
|
||||
}
|
||||
13
Assets/GLTFUtility-master/Scripts/Spec/GLTFAsset.cs.meta
Normal file
13
Assets/GLTFUtility-master/Scripts/Spec/GLTFAsset.cs.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5a649aef7d3e3c74aaa7be7d80ccda51
|
||||
timeCreated: 1539024463
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
75
Assets/GLTFUtility-master/Scripts/Spec/GLTFBuffer.cs
Normal file
75
Assets/GLTFUtility-master/Scripts/Spec/GLTFBuffer.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace Siccity.GLTFUtility {
|
||||
// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffer
|
||||
/// <summary> Contains raw binary data </summary>
|
||||
[Preserve] public class GLTFBuffer {
|
||||
[JsonProperty(Required = Required.Always)] public int byteLength;
|
||||
public string uri;
|
||||
public string name;
|
||||
|
||||
[JsonIgnore] private const string embeddedPrefix = "data:application/octet-stream;base64,";
|
||||
[JsonIgnore] private const string embeddedPrefix2 = "data:application/gltf-buffer;base64,";
|
||||
|
||||
public class ImportResult {
|
||||
public Stream stream;
|
||||
public long startOffset;
|
||||
|
||||
public void Dispose() {
|
||||
stream.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
#region Import
|
||||
/// <param name="filepath">Filepath if loading from a file</param>
|
||||
/// <param name="bytefile">bytes if loading from raw bytes</param>
|
||||
public ImportResult Import(string filepath, byte[] bytefile, long binChunkStart) {
|
||||
ImportResult result = new ImportResult();
|
||||
|
||||
if (uri == null) {
|
||||
// Load entire file
|
||||
if (string.IsNullOrEmpty(filepath)) result.stream = new MemoryStream(bytefile);
|
||||
else result.stream = File.OpenRead(filepath);
|
||||
result.startOffset = binChunkStart + 8;
|
||||
result.stream.Position = result.startOffset;
|
||||
} else if (uri.StartsWith(embeddedPrefix)) {
|
||||
// Load embedded
|
||||
string b64 = uri.Substring(embeddedPrefix.Length, uri.Length - embeddedPrefix.Length);
|
||||
byte[] bytes = Convert.FromBase64String(b64);
|
||||
result.stream = new MemoryStream(bytes);
|
||||
} else if (uri.StartsWith(embeddedPrefix2)) {
|
||||
// Load embedded
|
||||
string b64 = uri.Substring(embeddedPrefix2.Length, uri.Length - embeddedPrefix2.Length);
|
||||
byte[] bytes = Convert.FromBase64String(b64);
|
||||
result.stream = new MemoryStream(bytes);
|
||||
} else {
|
||||
// Load URI
|
||||
string directoryRoot = Directory.GetParent(filepath).ToString() + "/";
|
||||
result.stream = File.OpenRead(directoryRoot + uri);
|
||||
result.startOffset = result.stream.Length - byteLength;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public class ImportTask : Importer.ImportTask<ImportResult[]> {
|
||||
/// <param name="filepath">Filepath if loading from a file</param>
|
||||
/// <param name="bytefile">bytes if loading from raw bytes</param>
|
||||
public ImportTask(List<GLTFBuffer> buffers, string filepath, byte[] bytefile, long binChunkStart) : base() {
|
||||
task = new Task(() => {
|
||||
Result = new ImportResult[buffers.Count];
|
||||
for (int i = 0; i < Result.Length; i++) {
|
||||
Result[i] = buffers[i].Import(filepath, bytefile, binChunkStart);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
13
Assets/GLTFUtility-master/Scripts/Spec/GLTFBuffer.cs.meta
Normal file
13
Assets/GLTFUtility-master/Scripts/Spec/GLTFBuffer.cs.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8a0e8a023d948d340989746154fbe314
|
||||
timeCreated: 1539024463
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
47
Assets/GLTFUtility-master/Scripts/Spec/GLTFBufferView.cs
Normal file
47
Assets/GLTFUtility-master/Scripts/Spec/GLTFBufferView.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace Siccity.GLTFUtility {
|
||||
// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#bufferview
|
||||
/// <summary> Defines sections within the Buffer </summary>
|
||||
[Preserve] public class GLTFBufferView {
|
||||
[JsonProperty(Required = Required.Always)] public int buffer;
|
||||
[JsonProperty(Required = Required.Always)] public int byteLength;
|
||||
public int byteOffset = 0;
|
||||
public int? byteStride;
|
||||
/// <summary> OpenGL buffer target </summary>
|
||||
public int? target;
|
||||
public string name;
|
||||
|
||||
public class ImportResult {
|
||||
public Stream stream;
|
||||
public int byteOffset;
|
||||
public int byteLength;
|
||||
public int? byteStride;
|
||||
}
|
||||
|
||||
public class ImportTask : Importer.ImportTask<ImportResult[]> {
|
||||
public ImportTask(List<GLTFBufferView> bufferViews, GLTFBuffer.ImportTask bufferTask) : base(bufferTask) {
|
||||
task = new Task(() => {
|
||||
Result = new ImportResult[bufferViews.Count];
|
||||
for (int i = 0; i < Result.Length; i++) {
|
||||
GLTFBuffer.ImportResult buffer = bufferTask.Result[bufferViews[i].buffer];
|
||||
ImportResult result = new ImportResult();
|
||||
result.stream = buffer.stream;
|
||||
result.byteOffset = bufferViews[i].byteOffset;
|
||||
result.byteOffset += (int)buffer.startOffset;
|
||||
result.byteLength = bufferViews[i].byteLength;
|
||||
result.byteStride = bufferViews[i].byteStride;
|
||||
Result[i] = result;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f77c029d61e83644b812d926bc8a7706
|
||||
timeCreated: 1539029690
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
34
Assets/GLTFUtility-master/Scripts/Spec/GLTFCamera.cs
Normal file
34
Assets/GLTFUtility-master/Scripts/Spec/GLTFCamera.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Siccity.GLTFUtility.Converters;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace Siccity.GLTFUtility {
|
||||
// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#camera
|
||||
[Preserve] public class GLTFCamera {
|
||||
#region Serialization
|
||||
public Orthographic orthographic;
|
||||
public Perspective perspective;
|
||||
[JsonProperty(Required = Required.Always), JsonConverter(typeof(EnumConverter))] public CameraType type;
|
||||
public string name;
|
||||
|
||||
[Preserve] public class Orthographic {
|
||||
[JsonProperty(Required = Required.Always)] public float xmag;
|
||||
[JsonProperty(Required = Required.Always)] public float ymag;
|
||||
[JsonProperty(Required = Required.Always)] public float zfar;
|
||||
[JsonProperty(Required = Required.Always)] public float znear;
|
||||
}
|
||||
|
||||
[Preserve] public class Perspective {
|
||||
public float? aspectRatio;
|
||||
[JsonProperty(Required = Required.Always)] public float yfov;
|
||||
public float? zfar;
|
||||
[JsonProperty(Required = Required.Always)] public float znear;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
11
Assets/GLTFUtility-master/Scripts/Spec/GLTFCamera.cs.meta
Normal file
11
Assets/GLTFUtility-master/Scripts/Spec/GLTFCamera.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ecd6a04a0989ee34f9f188e57c464ac3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
15
Assets/GLTFUtility-master/Scripts/Spec/GLTFExtras.cs
Normal file
15
Assets/GLTFUtility-master/Scripts/Spec/GLTFExtras.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace Siccity.GLTFUtility {
|
||||
[Serializable]
|
||||
public class GLTFExtrasProcessor
|
||||
{
|
||||
public virtual void ProcessExtras(GameObject importedObject, AnimationClip[] animations, JObject extras)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/GLTFUtility-master/Scripts/Spec/GLTFExtras.cs.meta
Normal file
11
Assets/GLTFUtility-master/Scripts/Spec/GLTFExtras.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d1e414d9d11bb09448a2424d6ad278db
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
122
Assets/GLTFUtility-master/Scripts/Spec/GLTFImage.cs
Normal file
122
Assets/GLTFUtility-master/Scripts/Spec/GLTFImage.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace Siccity.GLTFUtility {
|
||||
// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#image
|
||||
[Preserve] public class GLTFImage {
|
||||
/// <summary>
|
||||
/// The uri of the image.
|
||||
/// Relative paths are relative to the .gltf file.
|
||||
/// Instead of referencing an external file, the uri can also be a data-uri.
|
||||
/// The image format must be jpg or png.
|
||||
/// </summary>
|
||||
public string uri;
|
||||
/// <summary> Either "image/jpeg" or "image/png" </summary>
|
||||
public string mimeType;
|
||||
public int? bufferView;
|
||||
public string name;
|
||||
|
||||
public class ImportResult {
|
||||
public byte[] bytes;
|
||||
public string path;
|
||||
|
||||
public ImportResult(byte[] bytes, string path = null) {
|
||||
this.bytes = bytes;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public IEnumerator CreateTextureAsync(bool linear, Action<Texture2D> onFinish, Action<float> onProgress = null) {
|
||||
if (!string.IsNullOrEmpty(path)) {
|
||||
#if UNITY_EDITOR
|
||||
// Load textures from asset database if we can
|
||||
Texture2D assetTexture = UnityEditor.AssetDatabase.LoadAssetAtPath(path, typeof(Texture2D)) as Texture2D;
|
||||
if (assetTexture != null) {
|
||||
onFinish(assetTexture);
|
||||
if (onProgress != null) onProgress(1f);
|
||||
yield break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !UNITY_EDITOR && ( UNITY_ANDROID || UNITY_IOS )
|
||||
path = "File://" + path;
|
||||
#endif
|
||||
// TODO: Support linear/sRGB textures
|
||||
using(UnityWebRequest uwr = UnityWebRequestTexture.GetTexture(path, true)) {
|
||||
UnityWebRequestAsyncOperation operation = uwr.SendWebRequest();
|
||||
float progress = 0;
|
||||
while (!operation.isDone) {
|
||||
if (progress != uwr.downloadProgress) {
|
||||
if (onProgress != null) onProgress(uwr.downloadProgress);
|
||||
}
|
||||
yield return null;
|
||||
}
|
||||
if (onProgress != null) onProgress(1f);
|
||||
|
||||
#if UNITY_2020_2_OR_NEWER
|
||||
if(uwr.result == UnityWebRequest.Result.ConnectionError ||
|
||||
uwr.result == UnityWebRequest.Result.ProtocolError)
|
||||
#else
|
||||
if(uwr.isNetworkError || uwr.isHttpError)
|
||||
#endif
|
||||
{
|
||||
Debug.LogError("GLTFImage.cs ToTexture2D() ERROR: " + uwr.error);
|
||||
} else {
|
||||
Texture2D tex = DownloadHandlerTexture.GetContent(uwr);
|
||||
tex.name = Path.GetFileNameWithoutExtension(path);
|
||||
onFinish(tex);
|
||||
}
|
||||
uwr.Dispose();
|
||||
}
|
||||
} else {
|
||||
Texture2D tex = new Texture2D(2, 2, TextureFormat.ARGB32, true, linear);
|
||||
if (!tex.LoadImage(bytes)) {
|
||||
Debug.Log("mimeType not supported");
|
||||
yield break;
|
||||
} else onFinish(tex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ImportTask : Importer.ImportTask<ImportResult[]> {
|
||||
public ImportTask(List<GLTFImage> images, string directoryRoot, GLTFBufferView.ImportTask bufferViewTask) : base(bufferViewTask) {
|
||||
task = new Task(() => {
|
||||
// No images
|
||||
if (images == null) return;
|
||||
|
||||
Result = new ImportResult[images.Count];
|
||||
for (int i = 0; i < images.Count; i++) {
|
||||
string fullUri = directoryRoot + images[i].uri;
|
||||
if (!string.IsNullOrEmpty(images[i].uri)) {
|
||||
if (File.Exists(fullUri)) {
|
||||
// If the file is found at fullUri, read it
|
||||
byte[] bytes = File.ReadAllBytes(fullUri);
|
||||
Result[i] = new ImportResult(bytes, fullUri);
|
||||
} else if (images[i].uri.StartsWith("data:")) {
|
||||
// If the image is embedded, find its Base64 content and save as byte array
|
||||
string content = images[i].uri.Split(',').Last();
|
||||
byte[] imageBytes = Convert.FromBase64String(content);
|
||||
Result[i] = new ImportResult(imageBytes);
|
||||
}
|
||||
} else if (images[i].bufferView.HasValue && !string.IsNullOrEmpty(images[i].mimeType)) {
|
||||
GLTFBufferView.ImportResult view = bufferViewTask.Result[images[i].bufferView.Value];
|
||||
byte[] bytes = new byte[view.byteLength];
|
||||
view.stream.Position = view.byteOffset;
|
||||
view.stream.Read(bytes, 0, view.byteLength);
|
||||
Result[i] = new ImportResult(bytes);
|
||||
} else {
|
||||
Debug.Log("Couldn't find texture at " + fullUri);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/GLTFUtility-master/Scripts/Spec/GLTFImage.cs.meta
Normal file
11
Assets/GLTFUtility-master/Scripts/Spec/GLTFImage.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5361c1ef422e7a9469e3fea3cb895c64
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
318
Assets/GLTFUtility-master/Scripts/Spec/GLTFMaterial.cs
Normal file
318
Assets/GLTFUtility-master/Scripts/Spec/GLTFMaterial.cs
Normal file
@@ -0,0 +1,318 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Siccity.GLTFUtility.Converters;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Scripting;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Siccity.GLTFUtility {
|
||||
// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#material
|
||||
[Preserve] public class GLTFMaterial {
|
||||
#if UNITY_EDITOR
|
||||
public static Material defaultMaterial { get { return _defaultMaterial != null ? _defaultMaterial : _defaultMaterial = UnityEditor.AssetDatabase.GetBuiltinExtraResource<Material>("Default-Material.mat"); } }
|
||||
private static Material _defaultMaterial;
|
||||
#else
|
||||
public static Material defaultMaterial { get { return null; } }
|
||||
#endif
|
||||
|
||||
public string name;
|
||||
public PbrMetalRoughness pbrMetallicRoughness;
|
||||
public TextureInfo normalTexture;
|
||||
public TextureInfo occlusionTexture;
|
||||
public TextureInfo emissiveTexture;
|
||||
[JsonConverter(typeof(ColorRGBConverter))] public Color emissiveFactor = Color.black;
|
||||
[JsonConverter(typeof(EnumConverter))] public AlphaMode alphaMode = AlphaMode.OPAQUE;
|
||||
public float alphaCutoff = 0.5f;
|
||||
public bool doubleSided = false;
|
||||
public Extensions extensions;
|
||||
public JObject extras;
|
||||
|
||||
public class ImportResult {
|
||||
public Material material;
|
||||
}
|
||||
|
||||
public IEnumerator CreateMaterial(GLTFTexture.ImportResult[] textures, ShaderSettings shaderSettings, Action<Material> onFinish) {
|
||||
Material mat = null;
|
||||
IEnumerator en = null;
|
||||
// Load metallic-roughness materials
|
||||
if (pbrMetallicRoughness != null) {
|
||||
en = pbrMetallicRoughness.CreateMaterial(textures, alphaMode, shaderSettings, x => mat = x);
|
||||
while (en.MoveNext()) { yield return null; };
|
||||
}
|
||||
// Load specular-glossiness materials
|
||||
else if (extensions != null && extensions.KHR_materials_pbrSpecularGlossiness != null) {
|
||||
en = extensions.KHR_materials_pbrSpecularGlossiness.CreateMaterial(textures, alphaMode, shaderSettings, x => mat = x);
|
||||
while (en.MoveNext()) { yield return null; };
|
||||
}
|
||||
// Load fallback material
|
||||
else mat = new Material(Shader.Find("Standard"));
|
||||
// Normal texture
|
||||
if (normalTexture != null) {
|
||||
en = TryGetTexture(textures, normalTexture, true, tex => {
|
||||
if (tex != null) {
|
||||
mat.SetTexture("_BumpMap", tex);
|
||||
mat.EnableKeyword("_NORMALMAP");
|
||||
mat.SetFloat("_BumpScale", normalTexture.scale);
|
||||
if (normalTexture.extensions != null) {
|
||||
normalTexture.extensions.Apply(normalTexture, mat, "_BumpMap");
|
||||
}
|
||||
}
|
||||
});
|
||||
while (en.MoveNext()) { yield return null; };
|
||||
}
|
||||
// Occlusion texture
|
||||
if (occlusionTexture != null) {
|
||||
en = TryGetTexture(textures, occlusionTexture, true, tex => {
|
||||
if (tex != null) {
|
||||
mat.SetTexture("_OcclusionMap", tex);
|
||||
if (occlusionTexture.extensions != null) {
|
||||
occlusionTexture.extensions.Apply(occlusionTexture, mat, "_OcclusionMap");
|
||||
}
|
||||
}
|
||||
});
|
||||
while (en.MoveNext()) { yield return null; };
|
||||
}
|
||||
// Emissive factor
|
||||
if (emissiveFactor != Color.black) {
|
||||
mat.SetColor("_EmissionColor", emissiveFactor);
|
||||
mat.EnableKeyword("_EMISSION");
|
||||
}
|
||||
// Emissive texture
|
||||
if (emissiveTexture != null) {
|
||||
en = TryGetTexture(textures, emissiveTexture, false, tex => {
|
||||
if (tex != null) {
|
||||
mat.SetTexture("_EmissionMap", tex);
|
||||
mat.EnableKeyword("_EMISSION");
|
||||
if (emissiveTexture.extensions != null) {
|
||||
emissiveTexture.extensions.Apply(emissiveTexture, mat, "_EmissionMap");
|
||||
}
|
||||
}
|
||||
});
|
||||
while (en.MoveNext()) { yield return null; };
|
||||
}
|
||||
|
||||
if (alphaMode == AlphaMode.MASK) {
|
||||
mat.SetFloat("_AlphaCutoff", alphaCutoff);
|
||||
}
|
||||
mat.name = name;
|
||||
onFinish(mat);
|
||||
}
|
||||
|
||||
public static IEnumerator TryGetTexture(GLTFTexture.ImportResult[] textures, TextureInfo texture, bool linear, Action<Texture2D> onFinish, Action<float> onProgress = null) {
|
||||
if (texture == null || texture.index < 0) {
|
||||
if (onProgress != null) onProgress(1f);
|
||||
onFinish(null);
|
||||
}
|
||||
if (textures == null) {
|
||||
if (onProgress != null) onProgress(1f);
|
||||
onFinish(null);
|
||||
}
|
||||
if (textures.Length <= texture.index) {
|
||||
Debug.LogWarning("Attempted to get texture index " + texture.index + " when only " + textures.Length + " exist");
|
||||
if (onProgress != null) onProgress(1f);
|
||||
onFinish(null);
|
||||
}
|
||||
IEnumerator en = textures[texture.index].GetTextureCached(linear, onFinish, onProgress);
|
||||
while (en.MoveNext()) { yield return null; };
|
||||
}
|
||||
|
||||
[Preserve] public class Extensions {
|
||||
public PbrSpecularGlossiness KHR_materials_pbrSpecularGlossiness = null;
|
||||
}
|
||||
|
||||
// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#pbrmetallicroughness
|
||||
[Preserve] public class PbrMetalRoughness {
|
||||
[JsonConverter(typeof(ColorRGBAConverter))] public Color baseColorFactor = Color.white;
|
||||
public TextureInfo baseColorTexture;
|
||||
public float metallicFactor = 1f;
|
||||
public float roughnessFactor = 1f;
|
||||
public TextureInfo metallicRoughnessTexture;
|
||||
|
||||
public IEnumerator CreateMaterial(GLTFTexture.ImportResult[] textures, AlphaMode alphaMode, ShaderSettings shaderSettings, Action<Material> onFinish) {
|
||||
// Shader
|
||||
Shader sh = null;
|
||||
if (alphaMode == AlphaMode.BLEND) sh = shaderSettings.MetallicBlend;
|
||||
else sh = shaderSettings.Metallic;
|
||||
|
||||
// Material
|
||||
Material mat = new Material(sh);
|
||||
mat.color = baseColorFactor;
|
||||
mat.SetFloat("_Metallic", metallicFactor);
|
||||
mat.SetFloat("_Roughness", roughnessFactor);
|
||||
|
||||
// Assign textures
|
||||
if (textures != null) {
|
||||
// Base color texture
|
||||
if (baseColorTexture != null && baseColorTexture.index >= 0) {
|
||||
if (textures.Length <= baseColorTexture.index) {
|
||||
Debug.LogWarning("Attempted to get basecolor texture index " + baseColorTexture.index + " when only " + textures.Length + " exist");
|
||||
} else {
|
||||
IEnumerator en = textures[baseColorTexture.index].GetTextureCached(false, tex => {
|
||||
if (tex != null) {
|
||||
mat.SetTexture("_MainTex", tex);
|
||||
if (baseColorTexture.extensions != null) {
|
||||
baseColorTexture.extensions.Apply(baseColorTexture, mat, "_MainTex");
|
||||
}
|
||||
}
|
||||
});
|
||||
while (en.MoveNext()) { yield return null; };
|
||||
}
|
||||
}
|
||||
// Metallic roughness texture
|
||||
if (metallicRoughnessTexture != null && metallicRoughnessTexture.index >= 0) {
|
||||
if (textures.Length <= metallicRoughnessTexture.index) {
|
||||
Debug.LogWarning("Attempted to get metallicRoughness texture index " + metallicRoughnessTexture.index + " when only " + textures.Length + " exist");
|
||||
} else {
|
||||
IEnumerator en = TryGetTexture(textures, metallicRoughnessTexture, true, tex => {
|
||||
if (tex != null) {
|
||||
mat.SetTexture("_MetallicGlossMap", tex);
|
||||
mat.EnableKeyword("_METALLICGLOSSMAP");
|
||||
if (metallicRoughnessTexture.extensions != null) {
|
||||
metallicRoughnessTexture.extensions.Apply(metallicRoughnessTexture, mat, "_MetallicGlossMap");
|
||||
}
|
||||
}
|
||||
});
|
||||
while (en.MoveNext()) { yield return null; };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// After the texture and color is extracted from the glTFObject
|
||||
if (mat.HasProperty("_BaseMap")) mat.SetTexture("_BaseMap", mat.mainTexture);
|
||||
if (mat.HasProperty("_BaseColor")) mat.SetColor("_BaseColor", baseColorFactor);
|
||||
onFinish(mat);
|
||||
}
|
||||
}
|
||||
|
||||
[Preserve] public class PbrSpecularGlossiness {
|
||||
/// <summary> The reflected diffuse factor of the material </summary>
|
||||
[JsonConverter(typeof(ColorRGBAConverter))] public Color diffuseFactor = Color.white;
|
||||
/// <summary> The diffuse texture </summary>
|
||||
public TextureInfo diffuseTexture;
|
||||
/// <summary> The reflected diffuse factor of the material </summary>
|
||||
[JsonConverter(typeof(ColorRGBConverter))] public Color specularFactor = Color.white;
|
||||
/// <summary> The glossiness or smoothness of the material </summary>
|
||||
public float glossinessFactor = 1f;
|
||||
/// <summary> The specular-glossiness texture </summary>
|
||||
public TextureInfo specularGlossinessTexture;
|
||||
|
||||
public IEnumerator CreateMaterial(GLTFTexture.ImportResult[] textures, AlphaMode alphaMode, ShaderSettings shaderSettings, Action<Material> onFinish) {
|
||||
// Shader
|
||||
Shader sh = null;
|
||||
if (alphaMode == AlphaMode.BLEND) sh = shaderSettings.SpecularBlend;
|
||||
else sh = shaderSettings.Specular;
|
||||
|
||||
// Material
|
||||
Material mat = new Material(sh);
|
||||
mat.color = diffuseFactor;
|
||||
mat.SetColor("_SpecColor", specularFactor);
|
||||
mat.SetFloat("_GlossyReflections", glossinessFactor);
|
||||
|
||||
// Assign textures
|
||||
if (textures != null) {
|
||||
// Diffuse texture
|
||||
if (diffuseTexture != null) {
|
||||
if (textures.Length <= diffuseTexture.index) {
|
||||
Debug.LogWarning("Attempted to get diffuseTexture texture index " + diffuseTexture.index + " when only " + textures.Length + " exist");
|
||||
} else {
|
||||
IEnumerator en = textures[diffuseTexture.index].GetTextureCached(false, tex => {
|
||||
if (tex != null) {
|
||||
mat.SetTexture("_MainTex", tex);
|
||||
if (diffuseTexture.extensions != null) {
|
||||
diffuseTexture.extensions.Apply(diffuseTexture, mat, "_MainTex");
|
||||
}
|
||||
}
|
||||
});
|
||||
while (en.MoveNext()) { yield return null; };
|
||||
}
|
||||
}
|
||||
// Specular texture
|
||||
if (specularGlossinessTexture != null) {
|
||||
if (textures.Length <= specularGlossinessTexture.index) {
|
||||
Debug.LogWarning("Attempted to get specularGlossinessTexture texture index " + specularGlossinessTexture.index + " when only " + textures.Length + " exist");
|
||||
} else {
|
||||
mat.EnableKeyword("_SPECGLOSSMAP");
|
||||
IEnumerator en = textures[specularGlossinessTexture.index].GetTextureCached(false, tex => {
|
||||
if (tex != null) {
|
||||
mat.SetTexture("_SpecGlossMap", tex);
|
||||
mat.EnableKeyword("_SPECGLOSSMAP");
|
||||
if (specularGlossinessTexture.extensions != null) {
|
||||
specularGlossinessTexture.extensions.Apply(specularGlossinessTexture, mat, "_SpecGlossMap");
|
||||
}
|
||||
}
|
||||
});
|
||||
while (en.MoveNext()) { yield return null; };
|
||||
}
|
||||
}
|
||||
}
|
||||
onFinish(mat);
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#normaltextureinfo
|
||||
[Preserve] public class TextureInfo {
|
||||
[JsonProperty(Required = Required.Always)] public int index;
|
||||
public int texCoord = 0;
|
||||
public float scale = 1;
|
||||
public Extensions extensions;
|
||||
|
||||
[Preserve] public class Extensions {
|
||||
public KHR_texture_transform KHR_texture_transform;
|
||||
|
||||
public void Apply(GLTFMaterial.TextureInfo texInfo, Material material, string textureSamplerName) {
|
||||
// TODO: check if GLTFObject has extensionUsed/extensionRequired for these extensions
|
||||
|
||||
if (KHR_texture_transform != null) {
|
||||
KHR_texture_transform.Apply(texInfo, material, textureSamplerName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface IExtension {
|
||||
void Apply(GLTFMaterial.TextureInfo texInfo, Material material, string textureSamplerName);
|
||||
}
|
||||
}
|
||||
|
||||
public class ImportTask : Importer.ImportTask<ImportResult[]> {
|
||||
private List<GLTFMaterial> materials;
|
||||
private GLTFTexture.ImportTask textureTask;
|
||||
private ImportSettings importSettings;
|
||||
|
||||
public ImportTask(List<GLTFMaterial> materials, GLTFTexture.ImportTask textureTask, ImportSettings importSettings) : base(textureTask) {
|
||||
this.materials = materials;
|
||||
this.textureTask = textureTask;
|
||||
this.importSettings = importSettings;
|
||||
|
||||
task = new Task(() => {
|
||||
if (materials == null) return;
|
||||
Result = new ImportResult[materials.Count];
|
||||
});
|
||||
}
|
||||
|
||||
public override IEnumerator OnCoroutine(Action<float> onProgress = null) {
|
||||
// No materials
|
||||
if (materials == null) {
|
||||
if (onProgress != null) onProgress.Invoke(1f);
|
||||
IsCompleted = true;
|
||||
yield break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < Result.Length; i++) {
|
||||
Result[i] = new ImportResult();
|
||||
|
||||
IEnumerator en = materials[i].CreateMaterial(textureTask.Result, importSettings.shaderOverrides, x => Result[i].material = x);
|
||||
while (en.MoveNext()) { yield return null; };
|
||||
|
||||
if (Result[i].material.name == null) Result[i].material.name = "material" + i;
|
||||
if (onProgress != null) onProgress.Invoke((float) (i + 1) / (float) Result.Length);
|
||||
yield return null;
|
||||
}
|
||||
IsCompleted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/GLTFUtility-master/Scripts/Spec/GLTFMaterial.cs.meta
Normal file
11
Assets/GLTFUtility-master/Scripts/Spec/GLTFMaterial.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9293e8f93131ab84f9bcf4cc5ab769a8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
421
Assets/GLTFUtility-master/Scripts/Spec/GLTFMesh.cs
Normal file
421
Assets/GLTFUtility-master/Scripts/Spec/GLTFMesh.cs
Normal file
@@ -0,0 +1,421 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace Siccity.GLTFUtility {
|
||||
// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#mesh
|
||||
[Preserve] public class GLTFMesh {
|
||||
#region Serialization
|
||||
[JsonProperty(Required = Required.Always)] public List<GLTFPrimitive> primitives;
|
||||
/// <summary> Morph target weights </summary>
|
||||
public List<float> weights;
|
||||
public string name;
|
||||
public Extras extras;
|
||||
|
||||
public class Extras {
|
||||
/// <summary>
|
||||
/// Morph target names. Not part of the official spec, but pretty much a standard.
|
||||
/// Discussed here https://github.com/KhronosGroup/glTF/issues/1036
|
||||
/// </summary>
|
||||
public string[] targetNames;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Import
|
||||
public class ImportResult {
|
||||
public Material[] materials;
|
||||
public Mesh mesh;
|
||||
}
|
||||
|
||||
public class ImportTask : Importer.ImportTask<ImportResult[]> {
|
||||
private class MeshData {
|
||||
string name;
|
||||
List<Vector3> normals = new List<Vector3>();
|
||||
List<List<int>> submeshTris = new List<List<int>>();
|
||||
List<RenderingMode> submeshTrisMode = new List<RenderingMode>();
|
||||
List<Vector3> verts = new List<Vector3>();
|
||||
List<Vector4> tangents = new List<Vector4>();
|
||||
List<Color> colors = new List<Color>();
|
||||
List<BoneWeight> weights = null;
|
||||
List<Vector2> uv1 = null;
|
||||
List<Vector2> uv2 = null;
|
||||
List<Vector2> uv3 = null;
|
||||
List<Vector2> uv4 = null;
|
||||
List<Vector2> uv5 = null;
|
||||
List<Vector2> uv6 = null;
|
||||
List<Vector2> uv7 = null;
|
||||
List<Vector2> uv8 = null;
|
||||
List<BlendShape> blendShapes = new List<BlendShape>();
|
||||
List<int> submeshVertexStart = new List<int>();
|
||||
|
||||
private class BlendShape {
|
||||
public string name;
|
||||
public Vector3[] pos, norm, tan;
|
||||
}
|
||||
|
||||
public MeshData(GLTFMesh gltfMesh, GLTFAccessor.ImportResult[] accessors, GLTFBufferView.ImportResult[] bufferViews) {
|
||||
name = gltfMesh.name;
|
||||
if (gltfMesh.primitives.Count == 0) {
|
||||
Debug.LogWarning("0 primitives in mesh");
|
||||
} else {
|
||||
for (int i = 0; i < gltfMesh.primitives.Count; i++) {
|
||||
GLTFPrimitive primitive = gltfMesh.primitives[i];
|
||||
// Load draco mesh
|
||||
if (primitive.extensions != null && primitive.extensions.KHR_draco_mesh_compression != null) {
|
||||
GLTFPrimitive.DracoMeshCompression draco = primitive.extensions.KHR_draco_mesh_compression;
|
||||
GLTFBufferView.ImportResult bufferView = bufferViews[draco.bufferView];
|
||||
GLTFUtilityDracoLoader loader = new GLTFUtilityDracoLoader();
|
||||
byte[] buffer = new byte[bufferView.byteLength];
|
||||
bufferView.stream.Seek(bufferView.byteOffset, System.IO.SeekOrigin.Begin);
|
||||
|
||||
bufferView.stream.Read(buffer, 0, bufferView.byteLength);
|
||||
|
||||
GLTFUtilityDracoLoader.MeshAttributes attribs = new GLTFUtilityDracoLoader.MeshAttributes(
|
||||
primitive.extensions.KHR_draco_mesh_compression.attributes.POSITION ?? -1,
|
||||
primitive.extensions.KHR_draco_mesh_compression.attributes.NORMAL ?? -1,
|
||||
primitive.extensions.KHR_draco_mesh_compression.attributes.TEXCOORD_0 ?? -1,
|
||||
primitive.extensions.KHR_draco_mesh_compression.attributes.JOINTS_0 ?? -1,
|
||||
primitive.extensions.KHR_draco_mesh_compression.attributes.WEIGHTS_0 ?? -1,
|
||||
primitive.extensions.KHR_draco_mesh_compression.attributes.COLOR_0 ?? -1
|
||||
);
|
||||
|
||||
//Mesh mesh = loader.LoadMesh(buffer, attribs);
|
||||
|
||||
GLTFUtilityDracoLoader.AsyncMesh asyncMesh = loader.LoadMesh(buffer, attribs);
|
||||
if (asyncMesh == null) Debug.LogWarning("Draco mesh couldn't be loaded");
|
||||
|
||||
submeshTrisMode.Add(primitive.mode);
|
||||
|
||||
// Tris
|
||||
int vertCount = verts.Count();
|
||||
submeshTris.Add(asyncMesh.tris.Reverse().Select(x => x + vertCount).ToList());
|
||||
|
||||
verts.AddRange(asyncMesh.verts.Select(x => new Vector3(-x.x, x.y, x.z)));
|
||||
|
||||
if (asyncMesh.norms != null) {
|
||||
normals.AddRange(asyncMesh.norms.Select(v => { v.x = -v.x; return v; }));
|
||||
}
|
||||
//tangents.AddRange(asyncMesh.tangents.Select(v => { v.y = -v.y; v.z = -v.z; return v; }));
|
||||
|
||||
// Weights
|
||||
if (asyncMesh.boneWeights != null) {
|
||||
if (weights == null) weights = new List<BoneWeight>();
|
||||
weights.AddRange(asyncMesh.boneWeights);
|
||||
}
|
||||
|
||||
// BlendShapes not supported yet
|
||||
/* for (int k = 0; k < mesh.blendShapeCount; k++) {
|
||||
int frameCount = mesh.GetBlendShapeFrameCount(k);
|
||||
BlendShape blendShape = new BlendShape();
|
||||
blendShape.pos = new Vector3[frameCount];
|
||||
blendShape.norm = new Vector3[frameCount];
|
||||
blendShape.tan = new Vector3[frameCount];
|
||||
for (int o = 0; o < frameCount; o++) {
|
||||
mesh.GetBlendShapeFrameVertices(k, o, blendShape.pos, blendShape.norm, blendShape.tan);
|
||||
}
|
||||
blendShapes.Add(blendShape);
|
||||
} */
|
||||
|
||||
// UVs
|
||||
if (asyncMesh.uv != null) {
|
||||
if (uv1 == null) uv1 = new List<Vector2>();
|
||||
uv1.AddRange(asyncMesh.uv.Select(x => new Vector2(x.x, -x.y)));
|
||||
}
|
||||
}
|
||||
// Load normal mesh
|
||||
else {
|
||||
int vertStartIndex = verts.Count;
|
||||
submeshVertexStart.Add(vertStartIndex);
|
||||
|
||||
// Verts - (X points left in GLTF)
|
||||
if (primitive.attributes.POSITION.HasValue) {
|
||||
IEnumerable<Vector3> newVerts = accessors[primitive.attributes.POSITION.Value].ReadVec3(true).Select(v => { v.x = -v.x; return v; });
|
||||
verts.AddRange(newVerts);
|
||||
}
|
||||
|
||||
int vertCount = verts.Count;
|
||||
|
||||
// Tris - (Invert all triangles. Instead of flipping each triangle, just flip the entire array. Much easier)
|
||||
if (primitive.indices.HasValue) {
|
||||
submeshTris.Add(new List<int>(accessors[primitive.indices.Value].ReadInt().Reverse().Select(x => x + vertStartIndex)));
|
||||
submeshTrisMode.Add(primitive.mode);
|
||||
}
|
||||
|
||||
/// Normals - (X points left in GLTF)
|
||||
if (primitive.attributes.NORMAL.HasValue) {
|
||||
normals.AddRange(accessors[primitive.attributes.NORMAL.Value].ReadVec3(true).Select(v => { v.x = -v.x; return v; }));
|
||||
}
|
||||
|
||||
// Tangents - (X points left in GLTF)
|
||||
if (primitive.attributes.TANGENT.HasValue) {
|
||||
tangents.AddRange(accessors[primitive.attributes.TANGENT.Value].ReadVec4(true).Select(v => { v.y = -v.y; v.z = -v.z; return v; }));
|
||||
}
|
||||
|
||||
// Vertex colors
|
||||
if (primitive.attributes.COLOR_0.HasValue) {
|
||||
colors.AddRange(accessors[primitive.attributes.COLOR_0.Value].ReadColor());
|
||||
}
|
||||
|
||||
// Weights
|
||||
if (primitive.attributes.WEIGHTS_0.HasValue && primitive.attributes.JOINTS_0.HasValue) {
|
||||
Vector4[] weights0 = accessors[primitive.attributes.WEIGHTS_0.Value].ReadVec4(true);
|
||||
Vector4[] joints0 = accessors[primitive.attributes.JOINTS_0.Value].ReadVec4();
|
||||
if (joints0.Length == weights0.Length) {
|
||||
BoneWeight[] boneWeights = new BoneWeight[weights0.Length];
|
||||
for (int k = 0; k < boneWeights.Length; k++) {
|
||||
NormalizeWeights(ref weights0[k]);
|
||||
boneWeights[k].weight0 = weights0[k].x;
|
||||
boneWeights[k].weight1 = weights0[k].y;
|
||||
boneWeights[k].weight2 = weights0[k].z;
|
||||
boneWeights[k].weight3 = weights0[k].w;
|
||||
boneWeights[k].boneIndex0 = Mathf.RoundToInt(joints0[k].x);
|
||||
boneWeights[k].boneIndex1 = Mathf.RoundToInt(joints0[k].y);
|
||||
boneWeights[k].boneIndex2 = Mathf.RoundToInt(joints0[k].z);
|
||||
boneWeights[k].boneIndex3 = Mathf.RoundToInt(joints0[k].w);
|
||||
}
|
||||
if (weights == null) weights = new List<BoneWeight>(new BoneWeight[vertCount - boneWeights.Length]);
|
||||
weights.AddRange(boneWeights);
|
||||
} else Debug.LogWarning("WEIGHTS_0 and JOINTS_0 not same length. Skipped");
|
||||
} else {
|
||||
if (weights != null) weights.AddRange(new BoneWeight[vertCount - weights.Count]);
|
||||
}
|
||||
|
||||
// UVs
|
||||
ReadUVs(ref uv1, accessors, primitive.attributes.TEXCOORD_0, vertCount);
|
||||
ReadUVs(ref uv2, accessors, primitive.attributes.TEXCOORD_1, vertCount);
|
||||
ReadUVs(ref uv3, accessors, primitive.attributes.TEXCOORD_2, vertCount);
|
||||
ReadUVs(ref uv4, accessors, primitive.attributes.TEXCOORD_3, vertCount);
|
||||
ReadUVs(ref uv5, accessors, primitive.attributes.TEXCOORD_4, vertCount);
|
||||
ReadUVs(ref uv6, accessors, primitive.attributes.TEXCOORD_5, vertCount);
|
||||
ReadUVs(ref uv7, accessors, primitive.attributes.TEXCOORD_6, vertCount);
|
||||
ReadUVs(ref uv8, accessors, primitive.attributes.TEXCOORD_7, vertCount);
|
||||
}
|
||||
}
|
||||
|
||||
bool hasTargetNames = gltfMesh.extras != null && gltfMesh.extras.targetNames != null;
|
||||
if (hasTargetNames) {
|
||||
if (gltfMesh.primitives.All(x => x.targets.Count != gltfMesh.extras.targetNames.Length)) {
|
||||
Debug.LogWarning("Morph target names found in mesh " + name + " but array length does not match primitive morph target array length");
|
||||
hasTargetNames = false;
|
||||
}
|
||||
}
|
||||
// Read blend shapes after knowing final vertex count
|
||||
int finalVertCount = verts.Count;
|
||||
|
||||
for (int i = 0; i < gltfMesh.primitives.Count; i++) {
|
||||
GLTFPrimitive primitive = gltfMesh.primitives[i];
|
||||
if (primitive.targets != null) {
|
||||
for (int k = 0; k < primitive.targets.Count; k++) {
|
||||
BlendShape blendShape = new BlendShape();
|
||||
blendShape.pos = GetMorphWeights(primitive.targets[k].POSITION, submeshVertexStart[i], finalVertCount, accessors);
|
||||
blendShape.norm = GetMorphWeights(primitive.targets[k].NORMAL, submeshVertexStart[i], finalVertCount, accessors);
|
||||
blendShape.tan = GetMorphWeights(primitive.targets[k].TANGENT, submeshVertexStart[i], finalVertCount, accessors);
|
||||
if (hasTargetNames) blendShape.name = gltfMesh.extras.targetNames[k];
|
||||
else blendShape.name = "morph-" + blendShapes.Count;
|
||||
blendShapes.Add(blendShape);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Vector3[] GetMorphWeights(int? accessor, int vertStartIndex, int vertCount, GLTFAccessor.ImportResult[] accessors) {
|
||||
if (accessor.HasValue) {
|
||||
if (accessors[accessor.Value] == null) {
|
||||
Debug.LogWarning("Accessor is null");
|
||||
return new Vector3[vertCount];
|
||||
}
|
||||
Vector3[] accessorData = accessors[accessor.Value].ReadVec3(true).Select(v => { v.x = -v.x; return v; }).ToArray();
|
||||
if (accessorData.Length != vertCount) {
|
||||
Vector3[] resized = new Vector3[vertCount];
|
||||
Array.Copy(accessorData, 0, resized, vertStartIndex, accessorData.Length);
|
||||
return resized;
|
||||
} else return accessorData;
|
||||
} else return new Vector3[vertCount];
|
||||
}
|
||||
|
||||
public Mesh ToMesh() {
|
||||
Mesh mesh = new Mesh();
|
||||
if (verts.Count >= ushort.MaxValue) mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
|
||||
mesh.vertices = verts.ToArray();
|
||||
mesh.subMeshCount = submeshTris.Count;
|
||||
var onlyTriangles = true;
|
||||
for (int i = 0; i < submeshTris.Count; i++) {
|
||||
switch (submeshTrisMode[i]) {
|
||||
case RenderingMode.POINTS:
|
||||
mesh.SetIndices(submeshTris[i].ToArray(), MeshTopology.Points, i);
|
||||
onlyTriangles = false;
|
||||
break;
|
||||
case RenderingMode.LINES:
|
||||
mesh.SetIndices(submeshTris[i].ToArray(), MeshTopology.Lines, i);
|
||||
onlyTriangles = false;
|
||||
break;
|
||||
case RenderingMode.LINE_STRIP:
|
||||
mesh.SetIndices(submeshTris[i].ToArray(), MeshTopology.LineStrip, i);
|
||||
onlyTriangles = false;
|
||||
break;
|
||||
case RenderingMode.TRIANGLES:
|
||||
mesh.SetTriangles(submeshTris[i].ToArray(), i);
|
||||
break;
|
||||
default:
|
||||
Debug.LogWarning("GLTF rendering mode " + submeshTrisMode[i] + " not supported.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
mesh.colors = colors.ToArray();
|
||||
if (uv1 != null) mesh.uv = uv1.ToArray();
|
||||
if (uv2 != null) mesh.uv2 = uv2.ToArray();
|
||||
if (uv3 != null) mesh.uv3 = uv3.ToArray();
|
||||
if (uv4 != null) mesh.uv4 = uv4.ToArray();
|
||||
if (uv5 != null) mesh.uv5 = uv5.ToArray();
|
||||
if (uv6 != null) mesh.uv6 = uv6.ToArray();
|
||||
if (uv7 != null) mesh.uv7 = uv7.ToArray();
|
||||
if (uv8 != null) mesh.uv8 = uv8.ToArray();
|
||||
if (weights != null) mesh.boneWeights = weights.ToArray();
|
||||
|
||||
mesh.RecalculateBounds();
|
||||
|
||||
// Blend shapes
|
||||
for (int i = 0; i < blendShapes.Count; i++) {
|
||||
mesh.AddBlendShapeFrame(blendShapes[i].name, 1f, blendShapes[i].pos, blendShapes[i].norm, blendShapes[i].tan);
|
||||
}
|
||||
|
||||
if (normals.Count == 0 && onlyTriangles)
|
||||
mesh.RecalculateNormals();
|
||||
else
|
||||
mesh.normals = normals.ToArray();
|
||||
|
||||
if (tangents.Count == 0 && onlyTriangles)
|
||||
mesh.RecalculateTangents();
|
||||
else
|
||||
mesh.tangents = tangents.ToArray();
|
||||
|
||||
mesh.name = name;
|
||||
return mesh;
|
||||
}
|
||||
|
||||
public void NormalizeWeights(ref Vector4 weights) {
|
||||
float total = weights.x + weights.y + weights.z + weights.w;
|
||||
float mult = 1f / total;
|
||||
weights.x *= mult;
|
||||
weights.y *= mult;
|
||||
weights.z *= mult;
|
||||
weights.w *= mult;
|
||||
}
|
||||
|
||||
private void ReadUVs(ref List<Vector2> uvs, GLTFAccessor.ImportResult[] accessors, int? texcoord, int vertCount) {
|
||||
// If there are no valid texcoords
|
||||
if (!texcoord.HasValue) {
|
||||
// If there are already uvs, add some empty filler uvs so it still matches the vertex array
|
||||
if (uvs != null) uvs.AddRange(new Vector2[vertCount - uvs.Count]);
|
||||
return;
|
||||
}
|
||||
Vector2[] _uvs = accessors[texcoord.Value].ReadVec2(true);
|
||||
FlipY(ref _uvs);
|
||||
if (uvs == null) uvs = new List<Vector2>(_uvs);
|
||||
else uvs.AddRange(_uvs);
|
||||
}
|
||||
|
||||
public void FlipY(ref Vector2[] uv) {
|
||||
for (int i = 0; i < uv.Length; i++) {
|
||||
uv[i].y = 1 - uv[i].y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private MeshData[] meshData;
|
||||
private List<GLTFMesh> meshes;
|
||||
private GLTFMaterial.ImportTask materialTask;
|
||||
|
||||
public ImportTask(List<GLTFMesh> meshes, GLTFAccessor.ImportTask accessorTask, GLTFBufferView.ImportTask bufferViewTask, GLTFMaterial.ImportTask materialTask, ImportSettings importSettings) : base(accessorTask, materialTask) {
|
||||
this.meshes = meshes;
|
||||
this.materialTask = materialTask;
|
||||
|
||||
task = new Task(() => {
|
||||
if (meshes == null) return;
|
||||
|
||||
meshData = new MeshData[meshes.Count];
|
||||
for (int i = 0; i < meshData.Length; i++) {
|
||||
meshData[i] = new MeshData(meshes[i], accessorTask.Result, bufferViewTask.Result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public override IEnumerator OnCoroutine(Action<float> onProgress = null) {
|
||||
// No mesh
|
||||
if (meshData == null) {
|
||||
if (onProgress != null) onProgress.Invoke(1f);
|
||||
IsCompleted = true;
|
||||
yield break;
|
||||
}
|
||||
|
||||
Result = new ImportResult[meshData.Length];
|
||||
for (int i = 0; i < meshData.Length; i++) {
|
||||
if (meshData[i] == null) {
|
||||
Debug.LogWarning("Mesh " + i + " import error");
|
||||
continue;
|
||||
}
|
||||
|
||||
Result[i] = new ImportResult();
|
||||
Result[i].mesh = meshData[i].ToMesh();
|
||||
Result[i].materials = new Material[meshes[i].primitives.Count];
|
||||
for (int k = 0; k < meshes[i].primitives.Count; k++) {
|
||||
int? matIndex = meshes[i].primitives[k].material;
|
||||
if (matIndex.HasValue && materialTask.Result != null && materialTask.Result.Count() > matIndex.Value) {
|
||||
GLTFMaterial.ImportResult matImport = materialTask.Result[matIndex.Value];
|
||||
if (matImport != null) Result[i].materials[k] = matImport.material;
|
||||
else {
|
||||
Debug.LogWarning("Mesh[" + i + "].matIndex points to null material (index " + matIndex.Value + ")");
|
||||
Result[i].materials[k] = GLTFMaterial.defaultMaterial;
|
||||
}
|
||||
} else {
|
||||
Result[i].materials[k] = GLTFMaterial.defaultMaterial;
|
||||
}
|
||||
}
|
||||
if (string.IsNullOrEmpty(Result[i].mesh.name)) Result[i].mesh.name = "mesh" + i;
|
||||
if (onProgress != null) onProgress.Invoke((float) (i + 1) / (float) meshData.Length);
|
||||
yield return null;
|
||||
}
|
||||
IsCompleted = true;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Export
|
||||
public class ExportResult : GLTFMesh {
|
||||
[JsonIgnore] public Mesh mesh;
|
||||
}
|
||||
|
||||
public static List<ExportResult> Export(List<GLTFNode.ExportResult> nodes) {
|
||||
List<ExportResult> results = new List<ExportResult>();
|
||||
for (int i = 0; i < nodes.Count; i++) {
|
||||
if (nodes[i].filter) {
|
||||
Mesh mesh = nodes[i].filter.sharedMesh;
|
||||
if (mesh) {
|
||||
nodes[i].mesh = results.Count;
|
||||
results.Add(Export(mesh));
|
||||
}
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
public static ExportResult Export(Mesh mesh) {
|
||||
ExportResult result = new ExportResult();
|
||||
result.name = mesh.name;
|
||||
result.primitives = new List<GLTFPrimitive>();
|
||||
for (int i = 0; i < mesh.subMeshCount; i++) {
|
||||
GLTFPrimitive primitive = new GLTFPrimitive();
|
||||
result.primitives.Add(primitive);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
13
Assets/GLTFUtility-master/Scripts/Spec/GLTFMesh.cs.meta
Normal file
13
Assets/GLTFUtility-master/Scripts/Spec/GLTFMesh.cs.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1ddbab1e140e1234fae1dc56d0d0f479
|
||||
timeCreated: 1539024463
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
209
Assets/GLTFUtility-master/Scripts/Spec/GLTFNode.cs
Normal file
209
Assets/GLTFUtility-master/Scripts/Spec/GLTFNode.cs
Normal file
@@ -0,0 +1,209 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Siccity.GLTFUtility.Converters;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace Siccity.GLTFUtility {
|
||||
// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#node
|
||||
[Preserve] public class GLTFNode {
|
||||
#region Serialization
|
||||
public string name;
|
||||
/// <summary> Indices of child nodes </summary>
|
||||
public int[] children;
|
||||
/// <summary> Local TRS </summary>
|
||||
[JsonConverter(typeof(Matrix4x4Converter))] public Matrix4x4 matrix = Matrix4x4.identity;
|
||||
/// <summary> Local position </summary>
|
||||
[JsonConverter(typeof(TranslationConverter))] public Vector3 translation = Vector3.zero;
|
||||
/// <summary> Local rotation </summary>
|
||||
[JsonConverter(typeof(QuaternionConverter))] public Quaternion rotation = Quaternion.identity;
|
||||
/// <summary> Local scale </summary>
|
||||
[JsonConverter(typeof(Vector3Converter))] public Vector3 scale = Vector3.one;
|
||||
public int? mesh;
|
||||
public int? skin;
|
||||
public int? camera;
|
||||
public int? weights;
|
||||
|
||||
public bool ShouldSerializetranslation() { return translation != Vector3.zero; }
|
||||
public bool ShouldSerializerotation() { return rotation != Quaternion.identity; }
|
||||
public bool ShouldSerializescale() { return scale != Vector3.one; }
|
||||
#endregion
|
||||
|
||||
#region Import
|
||||
public class ImportResult {
|
||||
public int? parent;
|
||||
public int[] children;
|
||||
public Transform transform;
|
||||
|
||||
public bool IsRoot { get { return !parent.HasValue; } }
|
||||
}
|
||||
|
||||
/// <summary> Set local position, rotation and scale </summary>
|
||||
public void ApplyTRS(Transform transform) {
|
||||
if(matrix!=Matrix4x4.identity)
|
||||
matrix.UnpackTRS(ref translation, ref rotation, ref scale);
|
||||
transform.localPosition = translation;
|
||||
transform.localRotation = rotation;
|
||||
transform.localScale = scale;
|
||||
}
|
||||
|
||||
public class ImportTask : Importer.ImportTask<ImportResult[]> {
|
||||
List<GLTFNode> nodes;
|
||||
GLTFMesh.ImportTask meshTask;
|
||||
GLTFSkin.ImportTask skinTask;
|
||||
List<GLTFCamera> cameras;
|
||||
|
||||
public ImportTask(List<GLTFNode> nodes, GLTFMesh.ImportTask meshTask, GLTFSkin.ImportTask skinTask, List<GLTFCamera> cameras) : base(meshTask, skinTask) {
|
||||
this.nodes = nodes;
|
||||
this.meshTask = meshTask;
|
||||
this.skinTask = skinTask;
|
||||
this.cameras = cameras;
|
||||
//task = new Task(() => { });
|
||||
}
|
||||
|
||||
public override IEnumerator OnCoroutine(Action<float> onProgress = null) {
|
||||
// No nodes
|
||||
if (nodes == null) {
|
||||
if (onProgress != null) onProgress.Invoke(1f);
|
||||
IsCompleted = true;
|
||||
yield break;
|
||||
}
|
||||
|
||||
Result = new ImportResult[nodes.Count];
|
||||
|
||||
|
||||
// Initialize transforms
|
||||
for (int i = 0; i < Result.Length; i++) {
|
||||
Result[i] = new GLTFNode.ImportResult();
|
||||
Result[i].transform = new GameObject().transform;
|
||||
Result[i].transform.name = nodes[i].name;
|
||||
}
|
||||
// Set up hierarchy
|
||||
for (int i = 0; i < Result.Length; i++) {
|
||||
if (nodes[i].children != null) {
|
||||
int[] children = nodes[i].children;
|
||||
Result[i].children = children;
|
||||
for (int k = 0; k < children.Length; k++) {
|
||||
int childIndex = children[k];
|
||||
Result[childIndex].parent = i;
|
||||
Result[childIndex].transform.parent = Result[i].transform;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Apply TRS
|
||||
for (int i = 0; i < Result.Length; i++) {
|
||||
nodes[i].ApplyTRS(Result[i].transform);
|
||||
}
|
||||
// Setup components
|
||||
for (int i = 0; i < Result.Length; i++) {
|
||||
// Setup mesh
|
||||
if (nodes[i].mesh.HasValue) {
|
||||
GLTFMesh.ImportResult meshResult = meshTask.Result[nodes[i].mesh.Value];
|
||||
if (meshResult == null) continue;
|
||||
|
||||
Mesh mesh = meshResult.mesh;
|
||||
Renderer renderer;
|
||||
if (nodes[i].skin.HasValue) {
|
||||
GLTFSkin.ImportResult skin = skinTask.Result[nodes[i].skin.Value];
|
||||
renderer = skin.SetupSkinnedRenderer(Result[i].transform.gameObject, mesh, Result);
|
||||
} else if (mesh.blendShapeCount > 0) {
|
||||
// Blend shapes require skinned mesh renderer
|
||||
SkinnedMeshRenderer mr = Result[i].transform.gameObject.AddComponent<SkinnedMeshRenderer>();
|
||||
mr.sharedMesh = mesh;
|
||||
renderer = mr;
|
||||
} else {
|
||||
MeshRenderer mr = Result[i].transform.gameObject.AddComponent<MeshRenderer>();
|
||||
MeshFilter mf = Result[i].transform.gameObject.AddComponent<MeshFilter>();
|
||||
renderer = mr;
|
||||
mf.sharedMesh = mesh;
|
||||
}
|
||||
//Materials
|
||||
renderer.materials = meshResult.materials;
|
||||
if (string.IsNullOrEmpty(Result[i].transform.name)) Result[i].transform.name = "node" + i;
|
||||
} else {
|
||||
if (string.IsNullOrEmpty(Result[i].transform.name)) Result[i].transform.name = "node" + i;
|
||||
}
|
||||
|
||||
// Setup camera
|
||||
if (nodes[i].camera.HasValue) {
|
||||
GLTFCamera cameraData = cameras[nodes[i].camera.Value];
|
||||
Camera camera = Result[i].transform.gameObject.AddComponent<Camera>();
|
||||
Result[i].transform.localRotation = Result[i].transform.localRotation * Quaternion.Euler(0, 180, 0);
|
||||
if (cameraData.type == CameraType.orthographic) {
|
||||
camera.orthographic = true;
|
||||
camera.nearClipPlane = cameraData.orthographic.znear;
|
||||
camera.farClipPlane = cameraData.orthographic.zfar;
|
||||
camera.orthographicSize = cameraData.orthographic.ymag;
|
||||
} else {
|
||||
camera.orthographic = false;
|
||||
camera.nearClipPlane = cameraData.perspective.znear;
|
||||
if (cameraData.perspective.zfar.HasValue) camera.farClipPlane = cameraData.perspective.zfar.Value;
|
||||
if (cameraData.perspective.aspectRatio.HasValue) camera.aspect = cameraData.perspective.aspectRatio.Value;
|
||||
camera.fieldOfView = Mathf.Rad2Deg * cameraData.perspective.yfov;
|
||||
}
|
||||
}
|
||||
}
|
||||
IsCompleted = true;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Export
|
||||
public class ExportResult : GLTFNode {
|
||||
[JsonIgnore] public MeshRenderer renderer;
|
||||
[JsonIgnore] public MeshFilter filter;
|
||||
[JsonIgnore] public SkinnedMeshRenderer skinnedRenderer;
|
||||
}
|
||||
|
||||
public static List<ExportResult> Export(Transform root) {
|
||||
List<ExportResult> nodes = new List<ExportResult>();
|
||||
CreateNodeListRecursive(root, nodes);
|
||||
return nodes;
|
||||
}
|
||||
|
||||
private static void CreateNodeListRecursive(Transform transform, List<ExportResult> nodes) {
|
||||
ExportResult node = new ExportResult();
|
||||
node.name = transform.name;
|
||||
node.translation = transform.localPosition;
|
||||
node.rotation = transform.localRotation;
|
||||
node.scale = transform.localScale;
|
||||
node.renderer = transform.gameObject.GetComponent<MeshRenderer>();
|
||||
node.filter = transform.gameObject.GetComponent<MeshFilter>();
|
||||
node.skinnedRenderer = transform.gameObject.GetComponent<SkinnedMeshRenderer>();
|
||||
nodes.Add(node);
|
||||
if (transform.childCount > 0) {
|
||||
if (transform.childCount > 0) {
|
||||
node.children = new int[transform.childCount];
|
||||
for (int i = 0; i < node.children.Length; i++) {
|
||||
Transform child = transform.GetChild(i);
|
||||
node.children[i] = nodes.Count;
|
||||
CreateNodeListRecursive(child, nodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
public static class GLTFNodeExtensions {
|
||||
#region Import
|
||||
/// <summary> Returns the root if there is one, otherwise creates a new empty root </summary>
|
||||
public static GameObject GetRoot(this GLTFNode.ImportResult[] nodes) {
|
||||
GLTFNode.ImportResult[] roots = nodes.Where(x => x.IsRoot).ToArray();
|
||||
|
||||
if (roots.Length == 1) return roots[0].transform.gameObject;
|
||||
else {
|
||||
GameObject root = new GameObject("Root");
|
||||
for (int i = 0; i < roots.Length; i++) {
|
||||
roots[i].transform.parent = root.transform;
|
||||
}
|
||||
return root;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
13
Assets/GLTFUtility-master/Scripts/Spec/GLTFNode.cs.meta
Normal file
13
Assets/GLTFUtility-master/Scripts/Spec/GLTFNode.cs.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 276c161f2b15c0a48b89210deb9a6e24
|
||||
timeCreated: 1539024181
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
28
Assets/GLTFUtility-master/Scripts/Spec/GLTFObject.cs
Normal file
28
Assets/GLTFUtility-master/Scripts/Spec/GLTFObject.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace Siccity.GLTFUtility {
|
||||
[Preserve] public class GLTFObject {
|
||||
public int? scene;
|
||||
[JsonProperty(Required = Required.Always)] public GLTFAsset asset;
|
||||
public List<GLTFScene> scenes;
|
||||
public List<GLTFNode> nodes;
|
||||
public List<GLTFMesh> meshes;
|
||||
public List<GLTFAnimation> animations;
|
||||
public List<GLTFBuffer> buffers;
|
||||
public List<GLTFBufferView> bufferViews;
|
||||
public List<GLTFAccessor> accessors;
|
||||
public List<GLTFSkin> skins;
|
||||
public List<GLTFTexture> textures;
|
||||
public List<GLTFImage> images;
|
||||
public List<GLTFMaterial> materials;
|
||||
public List<GLTFCamera> cameras;
|
||||
public List<string> extensionsUsed;
|
||||
public List<string> extensionsRequired;
|
||||
public JObject extras;
|
||||
}
|
||||
}
|
||||
13
Assets/GLTFUtility-master/Scripts/Spec/GLTFObject.cs.meta
Normal file
13
Assets/GLTFUtility-master/Scripts/Spec/GLTFObject.cs.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 84e30c2ce8f61b443800564b827d7395
|
||||
timeCreated: 1539024463
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
50
Assets/GLTFUtility-master/Scripts/Spec/GLTFPrimitive.cs
Normal file
50
Assets/GLTFUtility-master/Scripts/Spec/GLTFPrimitive.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace Siccity.GLTFUtility {
|
||||
// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#primitive
|
||||
[Preserve] public class GLTFPrimitive {
|
||||
[JsonProperty(Required = Required.Always)] public GLTFAttributes attributes;
|
||||
/// <summary> Rendering mode</summary>
|
||||
public RenderingMode mode = RenderingMode.TRIANGLES;
|
||||
public int? indices;
|
||||
public int? material;
|
||||
/// <summary> Morph targets </summary>
|
||||
public List<GLTFAttributes> targets;
|
||||
public Extensions extensions;
|
||||
|
||||
[Preserve] public class GLTFAttributes {
|
||||
public int? POSITION;
|
||||
public int? NORMAL;
|
||||
public int? TANGENT;
|
||||
public int? COLOR_0;
|
||||
public int? TEXCOORD_0;
|
||||
public int? TEXCOORD_1;
|
||||
public int? TEXCOORD_2;
|
||||
public int? TEXCOORD_3;
|
||||
public int? TEXCOORD_4;
|
||||
public int? TEXCOORD_5;
|
||||
public int? TEXCOORD_6;
|
||||
public int? TEXCOORD_7;
|
||||
public int? JOINTS_0;
|
||||
public int? JOINTS_1;
|
||||
public int? JOINTS_2;
|
||||
public int? JOINTS_3;
|
||||
public int? WEIGHTS_0;
|
||||
public int? WEIGHTS_1;
|
||||
public int? WEIGHTS_2;
|
||||
public int? WEIGHTS_3;
|
||||
}
|
||||
|
||||
[Preserve] public class Extensions {
|
||||
public DracoMeshCompression KHR_draco_mesh_compression;
|
||||
}
|
||||
|
||||
[Preserve] public class DracoMeshCompression {
|
||||
public int bufferView = 0;
|
||||
public GLTFAttributes attributes;
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Assets/GLTFUtility-master/Scripts/Spec/GLTFPrimitive.cs.meta
Normal file
13
Assets/GLTFUtility-master/Scripts/Spec/GLTFPrimitive.cs.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f9acfe0fd7cc80849ad51216685cb042
|
||||
timeCreated: 1539024463
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
15
Assets/GLTFUtility-master/Scripts/Spec/GLTFProperty.cs
Normal file
15
Assets/GLTFUtility-master/Scripts/Spec/GLTFProperty.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace Siccity.GLTFUtility {
|
||||
// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#specifying-extensions
|
||||
// https://github.com/KhronosGroup/glTF/issues/1628
|
||||
/// <summary> Will eventually become the base class of all gltf classes to enable extensions, but this isn't supported yet </summary>
|
||||
[Preserve] public class GLTFProperty {
|
||||
public object extensions;
|
||||
public object extras;
|
||||
}
|
||||
}
|
||||
11
Assets/GLTFUtility-master/Scripts/Spec/GLTFProperty.cs.meta
Normal file
11
Assets/GLTFUtility-master/Scripts/Spec/GLTFProperty.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1cd28e55dfa3bf142b439eaaa1014a79
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
13
Assets/GLTFUtility-master/Scripts/Spec/GLTFScene.cs
Normal file
13
Assets/GLTFUtility-master/Scripts/Spec/GLTFScene.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace Siccity.GLTFUtility {
|
||||
// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#scene
|
||||
[Preserve] public class GLTFScene {
|
||||
/// <summary> Indices of nodes </summary>
|
||||
public List<int> nodes;
|
||||
public string name;
|
||||
}
|
||||
}
|
||||
13
Assets/GLTFUtility-master/Scripts/Spec/GLTFScene.cs.meta
Normal file
13
Assets/GLTFUtility-master/Scripts/Spec/GLTFScene.cs.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1328a8789f9666842985198e60afb2ad
|
||||
timeCreated: 1539024181
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
104
Assets/GLTFUtility-master/Scripts/Spec/GLTFSkin.cs
Normal file
104
Assets/GLTFUtility-master/Scripts/Spec/GLTFSkin.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace Siccity.GLTFUtility {
|
||||
// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#skin
|
||||
[Preserve] public class GLTFSkin {
|
||||
/// <summary> Index of accessor containing inverse bind shape matrices </summary>
|
||||
public int? inverseBindMatrices;
|
||||
public int[] joints;
|
||||
public int? skeleton;
|
||||
public string name;
|
||||
|
||||
public class ImportResult {
|
||||
public Matrix4x4[] inverseBindMatrices;
|
||||
public int[] joints;
|
||||
|
||||
#region Import
|
||||
public SkinnedMeshRenderer SetupSkinnedRenderer(GameObject go, Mesh mesh, GLTFNode.ImportResult[] nodes) {
|
||||
SkinnedMeshRenderer smr = go.AddComponent<SkinnedMeshRenderer>();
|
||||
Transform[] bones = new Transform[joints.Length];
|
||||
for (int i = 0; i < bones.Length; i++) {
|
||||
int jointNodeIndex = joints[i];
|
||||
GLTFNode.ImportResult jointNode = nodes[jointNodeIndex];
|
||||
bones[i] = jointNode.transform;
|
||||
if (string.IsNullOrEmpty(jointNode.transform.name)) jointNode.transform.name = "joint" + i;
|
||||
}
|
||||
smr.bones = bones;
|
||||
smr.rootBone = bones[0];
|
||||
|
||||
// Bindposes
|
||||
if (inverseBindMatrices != null) {
|
||||
if (inverseBindMatrices.Length != joints.Length) Debug.LogWarning("InverseBindMatrices count and joints count not the same");
|
||||
Matrix4x4 m = nodes[0].transform.localToWorldMatrix;
|
||||
Matrix4x4[] bindPoses = new Matrix4x4[joints.Length];
|
||||
for (int i = 0; i < joints.Length; i++) {
|
||||
bindPoses[i] = inverseBindMatrices[i];
|
||||
}
|
||||
mesh.bindposes = bindPoses;
|
||||
} else {
|
||||
Matrix4x4 m = nodes[0].transform.localToWorldMatrix;
|
||||
Matrix4x4[] bindPoses = new Matrix4x4[joints.Length];
|
||||
for (int i = 0; i < joints.Length; i++) {
|
||||
bindPoses[i] = nodes[joints[i]].transform.worldToLocalMatrix * m;
|
||||
}
|
||||
mesh.bindposes = bindPoses;
|
||||
}
|
||||
smr.sharedMesh = mesh;
|
||||
return smr;
|
||||
}
|
||||
}
|
||||
|
||||
public ImportResult Import(GLTFAccessor.ImportResult[] accessors) {
|
||||
ImportResult result = new ImportResult();
|
||||
result.joints = joints;
|
||||
|
||||
// Inverse bind matrices
|
||||
if (inverseBindMatrices.HasValue) {
|
||||
result.inverseBindMatrices = accessors[inverseBindMatrices.Value].ReadMatrix4x4();
|
||||
for (int i = 0; i < result.inverseBindMatrices.Length; i++) {
|
||||
// Flip the matrix from GLTF (right handed) to Unity (left handed) format.
|
||||
// This was done through comparing the GLTF matrix to
|
||||
// the correctly imported matrix from the source model,
|
||||
// and flipping the values where needed.
|
||||
// Notice how the rows become collumns
|
||||
Matrix4x4 m = result.inverseBindMatrices[i];
|
||||
|
||||
Vector4 row0 = m.GetRow(0);
|
||||
row0.y = -row0.y;
|
||||
row0.z = -row0.z;
|
||||
Vector4 row1 = m.GetRow(1);
|
||||
row1.x = -row1.x;
|
||||
Vector4 row2 = m.GetRow(2);
|
||||
row2.x = -row2.x;
|
||||
Vector4 row3 = m.GetRow(3);
|
||||
row3.x = -row3.x;
|
||||
m.SetColumn(0, row0);
|
||||
m.SetColumn(1, row1);
|
||||
m.SetColumn(2, row2);
|
||||
m.SetColumn(3, row3);
|
||||
result.inverseBindMatrices[i] = m;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public class ImportTask : Importer.ImportTask<ImportResult[]> {
|
||||
public ImportTask(List<GLTFSkin> skins, GLTFAccessor.ImportTask accessorTask) : base(accessorTask) {
|
||||
task = new Task(() => {
|
||||
if (skins == null) return;
|
||||
|
||||
Result = new ImportResult[skins.Count];
|
||||
for (int i = 0; i < Result.Length; i++) {
|
||||
Result[i] = skins[i].Import(accessorTask.Result);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
13
Assets/GLTFUtility-master/Scripts/Spec/GLTFSkin.cs.meta
Normal file
13
Assets/GLTFUtility-master/Scripts/Spec/GLTFSkin.cs.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 861c8afb28d0d2946aeef4c5091068a5
|
||||
timeCreated: 1539024463
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
56
Assets/GLTFUtility-master/Scripts/Spec/GLTFTexture.cs
Normal file
56
Assets/GLTFUtility-master/Scripts/Spec/GLTFTexture.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace Siccity.GLTFUtility {
|
||||
// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#texture
|
||||
[Preserve] public class GLTFTexture {
|
||||
public int? sampler;
|
||||
public int? source;
|
||||
public string name;
|
||||
|
||||
public class ImportResult {
|
||||
private GLTFImage.ImportResult image;
|
||||
private Texture2D cache;
|
||||
|
||||
/// <summary> Constructor </summary>
|
||||
public ImportResult(GLTFImage.ImportResult image) {
|
||||
this.image = image;
|
||||
}
|
||||
|
||||
/// <summary> Create or return cached texture </summary>
|
||||
public IEnumerator GetTextureCached(bool linear, Action<Texture2D> onFinish, Action<float> onProgress = null) {
|
||||
if (cache == null) {
|
||||
IEnumerator en = image.CreateTextureAsync(linear, x => cache = x, onProgress);
|
||||
while (en.MoveNext()) { yield return null; };
|
||||
}
|
||||
onFinish(cache);
|
||||
}
|
||||
}
|
||||
|
||||
public ImportResult Import(GLTFImage.ImportResult[] images) {
|
||||
if (source.HasValue) {
|
||||
ImportResult result = new ImportResult(images[source.Value]);
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public class ImportTask : Importer.ImportTask<ImportResult[]> {
|
||||
public ImportTask(List<GLTFTexture> textures, GLTFImage.ImportTask imageTask) : base(imageTask) {
|
||||
task = new Task(() => {
|
||||
if (textures == null) return;
|
||||
|
||||
Result = new ImportResult[textures.Count];
|
||||
for (int i = 0; i < Result.Length; i++) {
|
||||
Result[i] = textures[i].Import(imageTask.Result);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/GLTFUtility-master/Scripts/Spec/GLTFTexture.cs.meta
Normal file
11
Assets/GLTFUtility-master/Scripts/Spec/GLTFTexture.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 80d754e1551a4ee43a1e649d964d6610
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user