diff --git a/GLTFSerialization/GLTFSerialization/Schema/Accessor.cs b/GLTFSerialization/GLTFSerialization/Schema/Accessor.cs index 9d2696c42..08107e00b 100644 --- a/GLTFSerialization/GLTFSerialization/Schema/Accessor.cs +++ b/GLTFSerialization/GLTFSerialization/Schema/Accessor.cs @@ -102,10 +102,10 @@ public Accessor() public Accessor(Accessor accessor, GLTFRoot gltfRoot) : base(accessor, gltfRoot) { - if (accessor == null) - { - return; - } + if (accessor == null) + { + return; + } if (accessor.BufferView != null) { @@ -322,15 +322,15 @@ private static void GetTypeDetails( public uint[] AsUIntArray(ref NumericArray contents, byte[] bufferViewData, int offset) { - if (contents.AsUInts != null) - { - return contents.AsUInts; - } + if (contents.AsUInts != null) + { + return contents.AsUInts; + } - if (Type != GLTFAccessorAttributeType.SCALAR) - { - return null; - } + if (Type != GLTFAccessorAttributeType.SCALAR) + { + return null; + } var arr = new uint[Count]; var totalByteOffset = ByteOffset + offset; @@ -355,15 +355,15 @@ public uint[] AsUIntArray(ref NumericArray contents, byte[] bufferViewData, int public float[] AsFloatArray(ref NumericArray contents, byte[] bufferViewData, int offset) { - if (contents.AsUInts != null) - { - return contents.AsFloats; - } + if (contents.AsUInts != null) + { + return contents.AsFloats; + } - if (Type != GLTFAccessorAttributeType.SCALAR) - { - return null; - } + if (Type != GLTFAccessorAttributeType.SCALAR) + { + return null; + } var arr = new float[Count]; var totalByteOffset = ByteOffset + offset; @@ -388,20 +388,20 @@ public float[] AsFloatArray(ref NumericArray contents, byte[] bufferViewData, in public Vector2[] AsVector2Array(ref NumericArray contents, byte[] bufferViewData, int offset, bool normalizeIntValues = true) { - if (contents.AsVec2s != null) - { - return contents.AsVec2s; - } + if (contents.AsVec2s != null) + { + return contents.AsVec2s; + } - if (Type != GLTFAccessorAttributeType.VEC2) - { - return null; - } + if (Type != GLTFAccessorAttributeType.VEC2) + { + return null; + } - if (ComponentType == GLTFComponentType.UnsignedInt) - { - return null; - } + if (ComponentType == GLTFComponentType.UnsignedInt) + { + return null; + } var arr = new Vector2[Count]; var totalByteOffset = ByteOffset + offset; @@ -433,20 +433,20 @@ public Vector2[] AsVector2Array(ref NumericArray contents, byte[] bufferViewData public Vector3[] AsVector3Array(ref NumericArray contents, byte[] bufferViewData, int offset, bool normalizeIntValues = true) { - if (contents.AsVec3s != null) - { - return contents.AsVec3s; - } + if (contents.AsVec3s != null) + { + return contents.AsVec3s; + } - if (Type != GLTFAccessorAttributeType.VEC3) - { - return null; - } + if (Type != GLTFAccessorAttributeType.VEC3) + { + return null; + } - if (ComponentType == GLTFComponentType.UnsignedInt) - { - return null; - } + if (ComponentType == GLTFComponentType.UnsignedInt) + { + return null; + } var arr = new Vector3[Count]; var totalByteOffset = ByteOffset + offset; @@ -478,22 +478,22 @@ public Vector3[] AsVector3Array(ref NumericArray contents, byte[] bufferViewData return arr; } - public Vector4[] AsVector4Array(ref NumericArray contents, byte[] bufferViewData, int offset, bool normalizeIntValues = true) - { - if (contents.AsVec4s != null) - { - return contents.AsVec4s; - } - - if (Type != GLTFAccessorAttributeType.VEC4) - { - return null; - } - - if (ComponentType == GLTFComponentType.UnsignedInt) - { - return null; - } + public Vector4[] AsVector4Array(ref NumericArray contents, byte[] bufferViewData, int offset, bool normalizeIntValues = true) + { + if (contents.AsVec4s != null) + { + return contents.AsVec4s; + } + + if (Type != GLTFAccessorAttributeType.VEC4) + { + return null; + } + + if (ComponentType == GLTFComponentType.UnsignedInt) + { + return null; + } var arr = new Vector4[Count]; var totalByteOffset = ByteOffset + offset; @@ -529,20 +529,20 @@ public Vector4[] AsVector4Array(ref NumericArray contents, byte[] bufferViewData public Color[] AsColorArray(ref NumericArray contents, byte[] bufferViewData, int offset) { - if (contents.AsColors != null) - { - return contents.AsColors; - } - - if (Type != GLTFAccessorAttributeType.VEC3 && Type != GLTFAccessorAttributeType.VEC4) - { - return null; - } - - if (ComponentType == GLTFComponentType.UnsignedInt) - { - return null; - } + if (contents.AsColors != null) + { + return contents.AsColors; + } + + if (Type != GLTFAccessorAttributeType.VEC3 && Type != GLTFAccessorAttributeType.VEC4) + { + return null; + } + + if (ComponentType == GLTFComponentType.UnsignedInt) + { + return null; + } var arr = new Color[Count]; var totalByteOffset = ByteOffset + offset; @@ -584,10 +584,10 @@ public Color[] AsColorArray(ref NumericArray contents, byte[] bufferViewData, in public Vector2[] AsTexcoordArray(ref NumericArray contents, byte[] bufferViewData, int offset) { - if (contents.AsTexcoords != null) - { - return contents.AsTexcoords; - } + if (contents.AsTexcoords != null) + { + return contents.AsTexcoords; + } contents.AsTexcoords = AsVector2Array(ref contents, bufferViewData, offset); @@ -596,10 +596,10 @@ public Vector2[] AsTexcoordArray(ref NumericArray contents, byte[] bufferViewDat public Vector3[] AsVertexArray(ref NumericArray contents, byte[] bufferViewData, int offset) { - if (contents.AsVertices != null) - { - return contents.AsVertices; - } + if (contents.AsVertices != null) + { + return contents.AsVertices; + } contents.AsVertices = AsVector3Array(ref contents, bufferViewData, offset); @@ -608,10 +608,10 @@ public Vector3[] AsVertexArray(ref NumericArray contents, byte[] bufferViewData, public Vector3[] AsNormalArray(ref NumericArray contents, byte[] bufferViewData, int offset) { - if (contents.AsNormals != null) - { - return contents.AsNormals; - } + if (contents.AsNormals != null) + { + return contents.AsNormals; + } contents.AsNormals = AsVector3Array(ref contents, bufferViewData, offset); @@ -620,10 +620,10 @@ public Vector3[] AsNormalArray(ref NumericArray contents, byte[] bufferViewData, public Vector4[] AsTangentArray(ref NumericArray contents, byte[] bufferViewData, int offset) { - if (contents.AsTangents != null) - { - return contents.AsTangents; - } + if (contents.AsTangents != null) + { + return contents.AsTangents; + } contents.AsTangents = AsVector4Array(ref contents, bufferViewData, offset); @@ -632,10 +632,10 @@ public Vector4[] AsTangentArray(ref NumericArray contents, byte[] bufferViewData public uint[] AsTriangles(ref NumericArray contents, byte[] bufferViewData, int offset) { - if (contents.AsTriangles != null) - { - return contents.AsTriangles; - } + if (contents.AsTriangles != null) + { + return contents.AsTriangles; + } contents.AsTriangles = AsUIntArray(ref contents, bufferViewData, offset); @@ -703,15 +703,15 @@ private static uint GetUnsignedDiscreteElement(byte[] bufferViewData, int offset public Matrix4x4[] AsMatrix4x4Array(ref NumericArray contents, byte[] bufferViewData, int offset, bool normalizeIntValues = true) { - if (contents.AsMatrix4x4s != null) - { - return contents.AsMatrix4x4s; - } + if (contents.AsMatrix4x4s != null) + { + return contents.AsMatrix4x4s; + } - if (Type != GLTFAccessorAttributeType.MAT4) - { - return null; - } + if (Type != GLTFAccessorAttributeType.MAT4) + { + return null; + } Matrix4x4[] arr = new Matrix4x4[Count]; var totalByteOffset = ByteOffset + offset; @@ -720,12 +720,12 @@ public Matrix4x4[] AsMatrix4x4Array(ref NumericArray contents, byte[] bufferView float maxValue; GetTypeDetails(ComponentType, out componentSize, out maxValue); - if (normalizeIntValues) - { - maxValue = 1; - } + if (normalizeIntValues) + { + maxValue = 1; + } - var stride = BufferView.Value.ByteStride > 0 ? BufferView.Value.ByteStride : componentSize * 16; + var stride = BufferView.Value.ByteStride > 0 ? BufferView.Value.ByteStride : componentSize * 16; for (var idx = 0; idx < Count; idx++) { @@ -775,9 +775,9 @@ public enum GLTFAccessorAttributeType MAT4 } - /// - /// This struct is a union, and should be used as such - /// + /// + /// This struct is a union, and should be used as such + /// [StructLayout(LayoutKind.Explicit)] public struct NumericArray { diff --git a/GLTFSerialization/GLTFSerialization/Schema/Image.cs b/GLTFSerialization/GLTFSerialization/Schema/Image.cs index 233211356..7144919de 100644 --- a/GLTFSerialization/GLTFSerialization/Schema/Image.cs +++ b/GLTFSerialization/GLTFSerialization/Schema/Image.cs @@ -91,7 +91,7 @@ public override void Serialize(JsonWriter writer) if (BufferView != null) { writer.WritePropertyName("bufferView"); - writer.WriteValue(BufferView); + writer.WriteValue(BufferView.Id); } base.Serialize(writer); diff --git a/UnityGLTF/Assets/UnityGLTF/Scripts/Editor/GLTFExportMenu.cs b/UnityGLTF/Assets/UnityGLTF/Scripts/Editor/GLTFExportMenu.cs index 8243eace9..b61942f69 100644 --- a/UnityGLTF/Assets/UnityGLTF/Scripts/Editor/GLTFExportMenu.cs +++ b/UnityGLTF/Assets/UnityGLTF/Scripts/Editor/GLTFExportMenu.cs @@ -6,32 +6,32 @@ public class GLTFExportMenu : EditorWindow { - public static string RetrieveTexturePath(UnityEngine.Texture texture) - { - return AssetDatabase.GetAssetPath(texture); - } + public static string RetrieveTexturePath(UnityEngine.Texture texture) + { + return AssetDatabase.GetAssetPath(texture); + } - [MenuItem("GLTF/Settings")] - static void Init() - { - GLTFExportMenu window = (GLTFExportMenu)EditorWindow.GetWindow(typeof(GLTFExportMenu), false, "GLTF Settings"); - window.Show(); - } + [MenuItem("GLTF/Settings")] + static void Init() + { + GLTFExportMenu window = (GLTFExportMenu)EditorWindow.GetWindow(typeof(GLTFExportMenu), false, "GLTF Settings"); + window.Show(); + } - void OnGUI() - { - EditorGUILayout.LabelField("Exporter", EditorStyles.boldLabel); - GLTFSceneExporter.ExportFullPath = EditorGUILayout.Toggle("Export using original path", GLTFSceneExporter.ExportFullPath); - GLTFSceneExporter.ExportNames = EditorGUILayout.Toggle("Export names of nodes", GLTFSceneExporter.ExportNames); - GLTFSceneExporter.RequireExtensions= EditorGUILayout.Toggle("Require extensions", GLTFSceneExporter.RequireExtensions); - EditorGUILayout.Separator(); - EditorGUILayout.LabelField("Importer", EditorStyles.boldLabel); - EditorGUILayout.Separator(); - EditorGUILayout.HelpBox("UnityGLTF version 0.1", MessageType.Info); - EditorGUILayout.HelpBox("Supported extensions: KHR_material_pbrSpecularGlossiness, ExtTextureTransform", MessageType.Info); - } + void OnGUI() + { + EditorGUILayout.LabelField("Exporter", EditorStyles.boldLabel); + GLTFSceneExporter.ExportFullPath = EditorGUILayout.Toggle("Export using original path", GLTFSceneExporter.ExportFullPath); + GLTFSceneExporter.ExportNames = EditorGUILayout.Toggle("Export names of nodes", GLTFSceneExporter.ExportNames); + GLTFSceneExporter.RequireExtensions= EditorGUILayout.Toggle("Require extensions", GLTFSceneExporter.RequireExtensions); + EditorGUILayout.Separator(); + EditorGUILayout.LabelField("Importer", EditorStyles.boldLabel); + EditorGUILayout.Separator(); + EditorGUILayout.HelpBox("UnityGLTF version 0.1", MessageType.Info); + EditorGUILayout.HelpBox("Supported extensions: KHR_material_pbrSpecularGlossiness, ExtTextureTransform", MessageType.Info); + } - [MenuItem("GLTF/Export Selected")] + [MenuItem("GLTF/Export Selected")] static void ExportSelected() { string name; diff --git a/UnityGLTF/Assets/UnityGLTF/Scripts/Editor/GLTFImporter.cs b/UnityGLTF/Assets/UnityGLTF/Scripts/Editor/GLTFImporter.cs index cda4dae32..a1fb35eed 100644 --- a/UnityGLTF/Assets/UnityGLTF/Scripts/Editor/GLTFImporter.cs +++ b/UnityGLTF/Assets/UnityGLTF/Scripts/Editor/GLTFImporter.cs @@ -14,299 +14,299 @@ namespace UnityGLTF { - [ScriptedImporter(1, new[] { "glb" })] - public class GLTFImporter : ScriptedImporter - { - [SerializeField] private bool _removeEmptyRootObjects = true; - [SerializeField] private float _scaleFactor = 1.0f; - [SerializeField] private int _maximumLod = 300; - [SerializeField] private bool _readWriteEnabled = true; - [SerializeField] private bool _generateColliders = false; - [SerializeField] private bool _swapUvs = false; - [SerializeField] private GLTFImporterNormals _importNormals = GLTFImporterNormals.Import; - [SerializeField] private bool _importMaterials = true; - [SerializeField] private bool _useJpgTextures = false; - - public override void OnImportAsset(AssetImportContext ctx) - { - string sceneName = null; - GameObject gltfScene = null; - UnityEngine.Mesh[] meshes = null; - try - { - sceneName = Path.GetFileNameWithoutExtension(ctx.assetPath); - gltfScene = CreateGLTFScene(ctx.assetPath); - - // Remove empty roots - if (_removeEmptyRootObjects) - { - var t = gltfScene.transform; - while ( - gltfScene.transform.childCount == 1 && - gltfScene.GetComponents().Length == 1) - { - var parent = gltfScene; - gltfScene = gltfScene.transform.GetChild(0).gameObject; - t = gltfScene.transform; - t.parent = null; // To keep transform information in the new parent - Object.DestroyImmediate(parent); // Get rid of the parent - } - } - - // Ensure there are no hide flags present (will cause problems when saving) - gltfScene.hideFlags &= ~(HideFlags.HideAndDontSave); - foreach (Transform child in gltfScene.transform) - { - child.gameObject.hideFlags &= ~(HideFlags.HideAndDontSave); - } - - // Zero position - gltfScene.transform.position = Vector3.zero; - - // Get meshes - var meshNames = new List(); - var meshHash = new HashSet(); - var meshFilters = gltfScene.GetComponentsInChildren(); - var vertexBuffer = new List(); - meshes = meshFilters.Select(mf => - { - var mesh = mf.sharedMesh; - vertexBuffer.Clear(); - mesh.GetVertices(vertexBuffer); - for (var i = 0; i < vertexBuffer.Count; ++i) - { - vertexBuffer[i] *= _scaleFactor; - } - mesh.SetVertices(vertexBuffer); - if (_swapUvs) - { - var uv = mesh.uv; - var uv2 = mesh.uv2; - mesh.uv = uv2; - mesh.uv2 = uv2; - } - if (_importNormals == GLTFImporterNormals.None) - { - mesh.normals = new Vector3[0]; - } - if (_importNormals == GLTFImporterNormals.Calculate) - { - mesh.RecalculateNormals(); - } - mesh.UploadMeshData(!_readWriteEnabled); - - if (_generateColliders) - { - var collider = mf.gameObject.AddComponent(); - collider.sharedMesh = mesh; - } - - if (meshHash.Add(mesh)) - { - var meshName = string.IsNullOrEmpty(mesh.name) ? mf.gameObject.name : mesh.name; - mesh.name = ObjectNames.GetUniqueName(meshNames.ToArray(), meshName); - meshNames.Add(mesh.name); - } - - return mesh; - }).ToArray(); - - var renderers = gltfScene.GetComponentsInChildren(); - - if (_importMaterials) - { - // Get materials - var materialNames = new List(); - var materialHash = new HashSet(); - var materials = renderers.SelectMany(r => - { - return r.sharedMaterials.Select(mat => - { - if (materialHash.Add(mat)) - { - var matName = string.IsNullOrEmpty(mat.name) ? mat.shader.name : mat.name; - if (matName == mat.shader.name) - { - matName = matName.Substring(Mathf.Min(matName.LastIndexOf("/") + 1, matName.Length - 1)); - } - - // Ensure name is unique - matName = string.Format("{0} {1}", sceneName, ObjectNames.NicifyVariableName(matName)); - matName = ObjectNames.GetUniqueName(materialNames.ToArray(), matName); - - mat.name = matName; - materialNames.Add(matName); - } - - return mat; - }); - }).ToArray(); - - // Get textures - var textureNames = new List(); - var textureHash = new HashSet(); - var texMaterialMap = new Dictionary>(); - var textures = materials.SelectMany(mat => - { - var shader = mat.shader; - if (!shader) return Enumerable.Empty(); - - var matTextures = new List(); - for (var i = 0; i < ShaderUtil.GetPropertyCount(shader); ++i) - { - if (ShaderUtil.GetPropertyType(shader, i) == ShaderUtil.ShaderPropertyType.TexEnv) - { - var propertyName = ShaderUtil.GetPropertyName(shader, i); - var tex = mat.GetTexture(propertyName) as Texture2D; - if (tex) - { - if (textureHash.Add(tex)) - { - var texName = tex.name; - if (string.IsNullOrEmpty(texName)) - { - if (propertyName.StartsWith("_")) texName = propertyName.Substring(Mathf.Min(1, propertyName.Length - 1)); - } - - // Ensure name is unique - texName = string.Format("{0} {1}", sceneName, ObjectNames.NicifyVariableName(texName)); - texName = ObjectNames.GetUniqueName(textureNames.ToArray(), texName); - - tex.name = texName; - textureNames.Add(texName); - matTextures.Add(tex); - } - - List materialMaps; - if (!texMaterialMap.TryGetValue(tex, out materialMaps)) - { - materialMaps = new List(); - texMaterialMap.Add(tex, materialMaps); - } - - materialMaps.Add(new TexMaterialMap(mat, propertyName, propertyName == "_BumpMap")); - } - } - } - return matTextures; - }).ToArray(); - - var folderName = Path.GetDirectoryName(ctx.assetPath); - - // Save textures as separate assets and rewrite refs - // TODO: Support for other texture types - if (textures.Length > 0) - { - var texturesRoot = string.Concat(folderName, "/", "Textures/"); - Directory.CreateDirectory(texturesRoot); - - foreach (var tex in textures) - { - var ext = _useJpgTextures ? ".jpg" : ".png"; - var texPath = string.Concat(texturesRoot, tex.name, ext); - File.WriteAllBytes(texPath, _useJpgTextures ? tex.EncodeToJPG() : tex.EncodeToPNG()); - - AssetDatabase.ImportAsset(texPath); - } - } - - // Save materials as separate assets and rewrite refs - if (materials.Length > 0) - { - var materialRoot = string.Concat(folderName, "/", "Materials/"); - Directory.CreateDirectory(materialRoot); - - foreach (var mat in materials) - { - var materialPath = string.Concat(materialRoot, mat.name, ".mat"); - var newMat = mat; - CopyOrNew(mat, materialPath, m => - { - // Fix references - newMat = m; - foreach (var r in renderers) - { - var sharedMaterials = r.sharedMaterials; - for (var i = 0; i < sharedMaterials.Length; ++i) - { - var sharedMaterial = sharedMaterials[i]; - if (sharedMaterial.name == mat.name) sharedMaterials[i] = m; - } - sharedMaterials = sharedMaterials.Where(sm => sm).ToArray(); - r.sharedMaterials = sharedMaterials; - } - }); - // Fix textures - // HACK: This needs to be a delayed call. - // Unity needs a frame to kick off the texture import so we can rewrite the ref - if (textures.Length > 0) - { - EditorApplication.delayCall += () => - { - for (var i = 0; i < textures.Length; ++i) - { - var tex = textures[i]; - var texturesRoot = string.Concat(folderName, "/", "Textures/"); - var ext = _useJpgTextures ? ".jpg" : ".png"; - var texPath = string.Concat(texturesRoot, tex.name, ext); - - // Grab new imported texture - var materialMaps = texMaterialMap[tex]; - var importer = (TextureImporter)TextureImporter.GetAtPath(texPath); - var importedTex = AssetDatabase.LoadAssetAtPath(texPath); - if (importer != null) - { - var isNormalMap = false; - foreach (var materialMap in materialMaps) - { - if (materialMap.Material == mat) - { - isNormalMap |= materialMap.IsNormalMap; - newMat.SetTexture(materialMap.Property, importedTex); - } - }; - - if (isNormalMap) - { - // Try to auto-detect normal maps - importer.textureType = TextureImporterType.NormalMap; - } - else if (importer.textureType == TextureImporterType.Sprite) - { - // Force disable sprite mode, even for 2D projects - importer.textureType = TextureImporterType.Default; - } - - importer.SaveAndReimport(); - } - else - { - Debug.LogWarning(string.Format("GLTFImporter: Unable to import texture at path: {0}", texPath)); - } - } - }; - } - } - } - } - else - { - var temp = GameObject.CreatePrimitive(PrimitiveType.Plane); - temp.SetActive(false); - var defaultMat = new[] { temp.GetComponent().sharedMaterial }; - DestroyImmediate(temp); - - foreach (var rend in renderers) - { - rend.sharedMaterials = defaultMat; - } - } - } - catch - { - if (gltfScene) DestroyImmediate(gltfScene); - throw; - } + [ScriptedImporter(1, new[] { "glb" })] + public class GLTFImporter : ScriptedImporter + { + [SerializeField] private bool _removeEmptyRootObjects = true; + [SerializeField] private float _scaleFactor = 1.0f; + [SerializeField] private int _maximumLod = 300; + [SerializeField] private bool _readWriteEnabled = true; + [SerializeField] private bool _generateColliders = false; + [SerializeField] private bool _swapUvs = false; + [SerializeField] private GLTFImporterNormals _importNormals = GLTFImporterNormals.Import; + [SerializeField] private bool _importMaterials = true; + [SerializeField] private bool _useJpgTextures = false; + + public override void OnImportAsset(AssetImportContext ctx) + { + string sceneName = null; + GameObject gltfScene = null; + UnityEngine.Mesh[] meshes = null; + try + { + sceneName = Path.GetFileNameWithoutExtension(ctx.assetPath); + gltfScene = CreateGLTFScene(ctx.assetPath); + + // Remove empty roots + if (_removeEmptyRootObjects) + { + var t = gltfScene.transform; + while ( + gltfScene.transform.childCount == 1 && + gltfScene.GetComponents().Length == 1) + { + var parent = gltfScene; + gltfScene = gltfScene.transform.GetChild(0).gameObject; + t = gltfScene.transform; + t.parent = null; // To keep transform information in the new parent + Object.DestroyImmediate(parent); // Get rid of the parent + } + } + + // Ensure there are no hide flags present (will cause problems when saving) + gltfScene.hideFlags &= ~(HideFlags.HideAndDontSave); + foreach (Transform child in gltfScene.transform) + { + child.gameObject.hideFlags &= ~(HideFlags.HideAndDontSave); + } + + // Zero position + gltfScene.transform.position = Vector3.zero; + + // Get meshes + var meshNames = new List(); + var meshHash = new HashSet(); + var meshFilters = gltfScene.GetComponentsInChildren(); + var vertexBuffer = new List(); + meshes = meshFilters.Select(mf => + { + var mesh = mf.sharedMesh; + vertexBuffer.Clear(); + mesh.GetVertices(vertexBuffer); + for (var i = 0; i < vertexBuffer.Count; ++i) + { + vertexBuffer[i] *= _scaleFactor; + } + mesh.SetVertices(vertexBuffer); + if (_swapUvs) + { + var uv = mesh.uv; + var uv2 = mesh.uv2; + mesh.uv = uv2; + mesh.uv2 = uv2; + } + if (_importNormals == GLTFImporterNormals.None) + { + mesh.normals = new Vector3[0]; + } + if (_importNormals == GLTFImporterNormals.Calculate) + { + mesh.RecalculateNormals(); + } + mesh.UploadMeshData(!_readWriteEnabled); + + if (_generateColliders) + { + var collider = mf.gameObject.AddComponent(); + collider.sharedMesh = mesh; + } + + if (meshHash.Add(mesh)) + { + var meshName = string.IsNullOrEmpty(mesh.name) ? mf.gameObject.name : mesh.name; + mesh.name = ObjectNames.GetUniqueName(meshNames.ToArray(), meshName); + meshNames.Add(mesh.name); + } + + return mesh; + }).ToArray(); + + var renderers = gltfScene.GetComponentsInChildren(); + + if (_importMaterials) + { + // Get materials + var materialNames = new List(); + var materialHash = new HashSet(); + var materials = renderers.SelectMany(r => + { + return r.sharedMaterials.Select(mat => + { + if (materialHash.Add(mat)) + { + var matName = string.IsNullOrEmpty(mat.name) ? mat.shader.name : mat.name; + if (matName == mat.shader.name) + { + matName = matName.Substring(Mathf.Min(matName.LastIndexOf("/") + 1, matName.Length - 1)); + } + + // Ensure name is unique + matName = string.Format("{0} {1}", sceneName, ObjectNames.NicifyVariableName(matName)); + matName = ObjectNames.GetUniqueName(materialNames.ToArray(), matName); + + mat.name = matName; + materialNames.Add(matName); + } + + return mat; + }); + }).ToArray(); + + // Get textures + var textureNames = new List(); + var textureHash = new HashSet(); + var texMaterialMap = new Dictionary>(); + var textures = materials.SelectMany(mat => + { + var shader = mat.shader; + if (!shader) return Enumerable.Empty(); + + var matTextures = new List(); + for (var i = 0; i < ShaderUtil.GetPropertyCount(shader); ++i) + { + if (ShaderUtil.GetPropertyType(shader, i) == ShaderUtil.ShaderPropertyType.TexEnv) + { + var propertyName = ShaderUtil.GetPropertyName(shader, i); + var tex = mat.GetTexture(propertyName) as Texture2D; + if (tex) + { + if (textureHash.Add(tex)) + { + var texName = tex.name; + if (string.IsNullOrEmpty(texName)) + { + if (propertyName.StartsWith("_")) texName = propertyName.Substring(Mathf.Min(1, propertyName.Length - 1)); + } + + // Ensure name is unique + texName = string.Format("{0} {1}", sceneName, ObjectNames.NicifyVariableName(texName)); + texName = ObjectNames.GetUniqueName(textureNames.ToArray(), texName); + + tex.name = texName; + textureNames.Add(texName); + matTextures.Add(tex); + } + + List materialMaps; + if (!texMaterialMap.TryGetValue(tex, out materialMaps)) + { + materialMaps = new List(); + texMaterialMap.Add(tex, materialMaps); + } + + materialMaps.Add(new TexMaterialMap(mat, propertyName, propertyName == "_BumpMap")); + } + } + } + return matTextures; + }).ToArray(); + + var folderName = Path.GetDirectoryName(ctx.assetPath); + + // Save textures as separate assets and rewrite refs + // TODO: Support for other texture types + if (textures.Length > 0) + { + var texturesRoot = string.Concat(folderName, "/", "Textures/"); + Directory.CreateDirectory(texturesRoot); + + foreach (var tex in textures) + { + var ext = _useJpgTextures ? ".jpg" : ".png"; + var texPath = string.Concat(texturesRoot, tex.name, ext); + File.WriteAllBytes(texPath, _useJpgTextures ? tex.EncodeToJPG() : tex.EncodeToPNG()); + + AssetDatabase.ImportAsset(texPath); + } + } + + // Save materials as separate assets and rewrite refs + if (materials.Length > 0) + { + var materialRoot = string.Concat(folderName, "/", "Materials/"); + Directory.CreateDirectory(materialRoot); + + foreach (var mat in materials) + { + var materialPath = string.Concat(materialRoot, mat.name, ".mat"); + var newMat = mat; + CopyOrNew(mat, materialPath, m => + { + // Fix references + newMat = m; + foreach (var r in renderers) + { + var sharedMaterials = r.sharedMaterials; + for (var i = 0; i < sharedMaterials.Length; ++i) + { + var sharedMaterial = sharedMaterials[i]; + if (sharedMaterial.name == mat.name) sharedMaterials[i] = m; + } + sharedMaterials = sharedMaterials.Where(sm => sm).ToArray(); + r.sharedMaterials = sharedMaterials; + } + }); + // Fix textures + // HACK: This needs to be a delayed call. + // Unity needs a frame to kick off the texture import so we can rewrite the ref + if (textures.Length > 0) + { + EditorApplication.delayCall += () => + { + for (var i = 0; i < textures.Length; ++i) + { + var tex = textures[i]; + var texturesRoot = string.Concat(folderName, "/", "Textures/"); + var ext = _useJpgTextures ? ".jpg" : ".png"; + var texPath = string.Concat(texturesRoot, tex.name, ext); + + // Grab new imported texture + var materialMaps = texMaterialMap[tex]; + var importer = (TextureImporter)TextureImporter.GetAtPath(texPath); + var importedTex = AssetDatabase.LoadAssetAtPath(texPath); + if (importer != null) + { + var isNormalMap = false; + foreach (var materialMap in materialMaps) + { + if (materialMap.Material == mat) + { + isNormalMap |= materialMap.IsNormalMap; + newMat.SetTexture(materialMap.Property, importedTex); + } + }; + + if (isNormalMap) + { + // Try to auto-detect normal maps + importer.textureType = TextureImporterType.NormalMap; + } + else if (importer.textureType == TextureImporterType.Sprite) + { + // Force disable sprite mode, even for 2D projects + importer.textureType = TextureImporterType.Default; + } + + importer.SaveAndReimport(); + } + else + { + Debug.LogWarning(string.Format("GLTFImporter: Unable to import texture at path: {0}", texPath)); + } + } + }; + } + } + } + } + else + { + var temp = GameObject.CreatePrimitive(PrimitiveType.Plane); + temp.SetActive(false); + var defaultMat = new[] { temp.GetComponent().sharedMaterial }; + DestroyImmediate(temp); + + foreach (var rend in renderers) + { + rend.sharedMaterials = defaultMat; + } + } + } + catch + { + if (gltfScene) DestroyImmediate(gltfScene); + throw; + } #if UNITY_2017_3_OR_NEWER // Set main asset @@ -320,74 +320,74 @@ public override void OnImportAsset(AssetImportContext ctx) ctx.SetMainObject(gltfScene); #else - // Set main asset - ctx.SetMainAsset("main asset", gltfScene); - - // Add meshes - foreach (var mesh in meshes) - { - ctx.AddSubAsset("mesh " + mesh.name, mesh); - } + // Set main asset + ctx.SetMainAsset("main asset", gltfScene); + + // Add meshes + foreach (var mesh in meshes) + { + ctx.AddSubAsset("mesh " + mesh.name, mesh); + } #endif - } - - private GameObject CreateGLTFScene(string projectFilePath) - { - ILoader fileLoader = new FileLoader(Path.GetDirectoryName(projectFilePath)); - using (var stream = File.OpenRead(projectFilePath)) - { - GLTFRoot gLTFRoot = GLTFParser.ParseJson(stream); - var loader = new GLTFSceneImporter(gLTFRoot, fileLoader, stream); - - loader.MaximumLod = _maximumLod; - - // HACK: Force the coroutine to run synchronously in the editor - var stack = new Stack(); - stack.Push(loader.LoadScene(isMultithreaded: true)); - - while (stack.Count > 0) - { - var enumerator = stack.Pop(); - if (enumerator.MoveNext()) - { - stack.Push(enumerator); - var subEnumerator = enumerator.Current as IEnumerator; - if (subEnumerator != null) - { - stack.Push(subEnumerator); - } - } - } - return loader.LastLoadedScene; - } - } - - private void CopyOrNew(T asset, string assetPath, Action replaceReferences) where T : Object - { - var existingAsset = AssetDatabase.LoadAssetAtPath(assetPath); - if (existingAsset) - { - EditorUtility.CopySerialized(asset, existingAsset); - replaceReferences(existingAsset); - return; - } - - AssetDatabase.CreateAsset(asset, assetPath); - } - - private class TexMaterialMap - { - public UnityEngine.Material Material { get; set; } - public string Property { get; set; } - public bool IsNormalMap { get; set; } - - public TexMaterialMap(UnityEngine.Material material, string property, bool isNormalMap) - { - Material = material; - Property = property; - IsNormalMap = isNormalMap; - } - } - } + } + + private GameObject CreateGLTFScene(string projectFilePath) + { + ILoader fileLoader = new FileLoader(Path.GetDirectoryName(projectFilePath)); + using (var stream = File.OpenRead(projectFilePath)) + { + GLTFRoot gLTFRoot = GLTFParser.ParseJson(stream); + var loader = new GLTFSceneImporter(gLTFRoot, fileLoader, stream); + + loader.MaximumLod = _maximumLod; + + // HACK: Force the coroutine to run synchronously in the editor + var stack = new Stack(); + stack.Push(loader.LoadScene(isMultithreaded: true)); + + while (stack.Count > 0) + { + var enumerator = stack.Pop(); + if (enumerator.MoveNext()) + { + stack.Push(enumerator); + var subEnumerator = enumerator.Current as IEnumerator; + if (subEnumerator != null) + { + stack.Push(subEnumerator); + } + } + } + return loader.LastLoadedScene; + } + } + + private void CopyOrNew(T asset, string assetPath, Action replaceReferences) where T : Object + { + var existingAsset = AssetDatabase.LoadAssetAtPath(assetPath); + if (existingAsset) + { + EditorUtility.CopySerialized(asset, existingAsset); + replaceReferences(existingAsset); + return; + } + + AssetDatabase.CreateAsset(asset, assetPath); + } + + private class TexMaterialMap + { + public UnityEngine.Material Material { get; set; } + public string Property { get; set; } + public bool IsNormalMap { get; set; } + + public TexMaterialMap(UnityEngine.Material material, string property, bool isNormalMap) + { + Material = material; + Property = property; + IsNormalMap = isNormalMap; + } + } + } } #endif diff --git a/UnityGLTF/Assets/UnityGLTF/Scripts/Editor/GLTFImporterNormals.cs b/UnityGLTF/Assets/UnityGLTF/Scripts/Editor/GLTFImporterNormals.cs index 31f4b9a65..7d43b7b26 100644 --- a/UnityGLTF/Assets/UnityGLTF/Scripts/Editor/GLTFImporterNormals.cs +++ b/UnityGLTF/Assets/UnityGLTF/Scripts/Editor/GLTFImporterNormals.cs @@ -1,11 +1,11 @@ #if UNITY_2017_1_OR_NEWER namespace UnityGLTF { - public enum GLTFImporterNormals - { - Import, - Calculate, - None - } + public enum GLTFImporterNormals + { + Import, + Calculate, + None + } } #endif diff --git a/UnityGLTF/Assets/UnityGLTF/Scripts/Editor/PbrShaderGUI.cs b/UnityGLTF/Assets/UnityGLTF/Scripts/Editor/PbrShaderGUI.cs index 0130eee6b..cf6935dc1 100644 --- a/UnityGLTF/Assets/UnityGLTF/Scripts/Editor/PbrShaderGUI.cs +++ b/UnityGLTF/Assets/UnityGLTF/Scripts/Editor/PbrShaderGUI.cs @@ -5,269 +5,269 @@ namespace UnityEditor { - internal class PbrShaderGUI : ShaderGUI - { - private enum WorkflowMode - { - SpecularGlossiness, - MetallicRoughness, - Unlit - } - - public enum BlendMode - { - Opaque, - Mask, - Blend - } - - private static class Styles - { - public static GUIContent albedoText = new GUIContent("Base Color", "Albedo (RGB) and Transparency (A)"); - public static GUIContent alphaCutoffText = new GUIContent("Alpha Cutoff", "Threshold for alpha cutoff"); - public static GUIContent specularMapText = new GUIContent("Spec Gloss", "Specular (RGB) and Glossiness (A)"); - public static GUIContent metallicMapText = new GUIContent("Metal Rough", "Metallic (B) and Roughness (G)"); - public static GUIContent metallicText = new GUIContent("Metallic", "Metallic value"); - public static GUIContent metallicScaleText = new GUIContent("Metallic", "Metallic scale factor"); - public static GUIContent roughnessText = new GUIContent("Roughness", "Roughness value"); - - public static GUIContent roughnessScaleText = new GUIContent("Roughness", "Roughness scale factor"); - public static GUIContent glossinessText = new GUIContent("Glossiness", "Glossiness value"); - public static GUIContent glossinessScaleText = new GUIContent("Glossiness", "Glossiness scale factor"); - public static GUIContent normalMapText = new GUIContent("Normal Map", "Normal Map"); - public static GUIContent occlusionText = new GUIContent("Occlusion", "Occlusion (R)"); - public static GUIContent emissionText = new GUIContent("Emissive", "Emissive (RGB)"); - - public static string primaryMapsText = "Main Maps"; - public static string renderingMode = "Rendering Mode"; - public static GUIContent emissiveWarning = new GUIContent("Emissive value is animated but the material has not been configured to support emissive. Please make sure the material itself has some amount of emissive."); - public static readonly string[] blendNames = Enum.GetNames(typeof(BlendMode)); - } - - MaterialProperty blendMode = null; - MaterialProperty albedoMap = null; - MaterialProperty albedoColor = null; - MaterialProperty alphaCutoff = null; - MaterialProperty specularMap = null; - MaterialProperty specularColor = null; - MaterialProperty metallicMap = null; - MaterialProperty metallic = null; - MaterialProperty smoothness = null; - MaterialProperty bumpScale = null; - MaterialProperty bumpMap = null; - MaterialProperty occlusionStrength = null; - MaterialProperty occlusionMap = null; - MaterialProperty emissionColor = null; - MaterialProperty emissionMap = null; - - MaterialEditor m_MaterialEditor; - WorkflowMode m_WorkflowMode = WorkflowMode.MetallicRoughness; - - bool m_FirstTimeApply = true; - - public void FindProperties(MaterialProperty[] props) - { - blendMode = FindProperty("_Mode", props); - albedoMap = FindProperty("_MainTex", props); - albedoColor = FindProperty("_Color", props); - alphaCutoff = FindProperty("_Cutoff", props); - specularMap = FindProperty("_SpecGlossMap", props, false); - specularColor = FindProperty("_SpecColor", props, false); - metallicMap = FindProperty("_MetallicGlossMap", props, false); - metallic = FindProperty("_Metallic", props, false); - if (specularMap != null && specularColor != null) - m_WorkflowMode = WorkflowMode.SpecularGlossiness; - else if (metallicMap != null && metallic != null) - m_WorkflowMode = WorkflowMode.MetallicRoughness; - else - m_WorkflowMode = WorkflowMode.Unlit; - smoothness = FindProperty("_Glossiness", props); - bumpScale = FindProperty("_BumpScale", props); - bumpMap = FindProperty("_BumpMap", props); - occlusionStrength = FindProperty("_OcclusionStrength", props); - occlusionMap = FindProperty("_OcclusionMap", props); - emissionColor = FindProperty("_EmissionColor", props); - emissionMap = FindProperty("_EmissionMap", props); - } - - public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] props) - { - FindProperties(props); // MaterialProperties can be animated so we do not cache them but fetch them every event to ensure animated values are updated correctly - m_MaterialEditor = materialEditor; - Material material = materialEditor.target as Material; - - // Make sure that needed setup (ie keywords/renderqueue) are set up if we're switching some existing - // material to a standard shader. - // Do this before any GUI code has been issued to prevent layout issues in subsequent GUILayout statements (case 780071) - if (m_FirstTimeApply) - { - MaterialChanged(material, m_WorkflowMode); - m_FirstTimeApply = false; - } - - ShaderPropertiesGUI(material); - } - - public void ShaderPropertiesGUI(Material material) - { - // Use default labelWidth - EditorGUIUtility.labelWidth = 0f; - - // Detect any changes to the material - EditorGUI.BeginChangeCheck(); - { - BlendModePopup(); - - // Primary properties - GUILayout.Label(Styles.primaryMapsText, EditorStyles.boldLabel); - DoAlbedoArea(material); - DoSpecularMetallicArea(); - m_MaterialEditor.TexturePropertySingleLine(Styles.normalMapText, bumpMap, bumpMap.textureValue != null ? bumpScale : null); - m_MaterialEditor.TexturePropertySingleLine(Styles.occlusionText, occlusionMap, occlusionMap.textureValue != null ? occlusionStrength : null); - DoEmissionArea(material); - - EditorGUILayout.Space(); - } - if (EditorGUI.EndChangeCheck()) - { - foreach (var obj in blendMode.targets) - MaterialChanged((Material)obj, m_WorkflowMode); - } - } - - internal void DetermineWorkflow(MaterialProperty[] props) - { - if (FindProperty("_SpecGlossMap", props, false) != null && FindProperty("_SpecColor", props, false) != null) - m_WorkflowMode = WorkflowMode.SpecularGlossiness; - else if (FindProperty("_MetallicGlossMap", props, false) != null && FindProperty("_Metallic", props, false) != null) - m_WorkflowMode = WorkflowMode.MetallicRoughness; - else - m_WorkflowMode = WorkflowMode.Unlit; - } - - void BlendModePopup() - { - EditorGUI.showMixedValue = blendMode.hasMixedValue; - var mode = (BlendMode)blendMode.floatValue; - - EditorGUI.BeginChangeCheck(); - mode = (BlendMode)EditorGUILayout.Popup(Styles.renderingMode, (int)mode, Styles.blendNames); - if (EditorGUI.EndChangeCheck()) - { - m_MaterialEditor.RegisterPropertyChangeUndo("Rendering Mode"); - blendMode.floatValue = (float)mode; - } - - EditorGUI.showMixedValue = false; - } - - void DoAlbedoArea(Material material) - { - m_MaterialEditor.TexturePropertySingleLine(Styles.albedoText, albedoMap, albedoColor); - if (((BlendMode)material.GetFloat("_Mode") == BlendMode.Mask)) - { - m_MaterialEditor.ShaderProperty(alphaCutoff, Styles.alphaCutoffText.text, MaterialEditor.kMiniTextureFieldLabelIndentLevel + 1); - } - } - - void DoEmissionArea(Material material) - { - bool hadEmissionTexture = emissionMap.textureValue != null; - - // Texture and HDR color controls - m_MaterialEditor.TexturePropertySingleLine(Styles.emissionText, emissionMap, emissionColor); - - // If texture was assigned and color was black set color to white - float brightness = emissionColor.colorValue.maxColorComponent; - if (emissionMap.textureValue != null && !hadEmissionTexture && brightness <= 0f) - emissionColor.colorValue = Color.white; - } - - void DoSpecularMetallicArea() - { - bool hasGlossMap = false; - if (m_WorkflowMode == WorkflowMode.SpecularGlossiness) - { - hasGlossMap = specularMap.textureValue != null; - m_MaterialEditor.TexturePropertySingleLine(Styles.specularMapText, specularMap, hasGlossMap ? null : specularColor); - m_MaterialEditor.ShaderProperty(smoothness, hasGlossMap ? Styles.glossinessScaleText : Styles.glossinessText, 2); - } - else if (m_WorkflowMode == WorkflowMode.MetallicRoughness) - { - hasGlossMap = metallicMap.textureValue != null; - m_MaterialEditor.TexturePropertySingleLine(Styles.metallicMapText, metallicMap); - m_MaterialEditor.ShaderProperty(metallic, hasGlossMap ? Styles.metallicScaleText : Styles.metallicText, 2); - m_MaterialEditor.ShaderProperty(smoothness, hasGlossMap ? Styles.roughnessScaleText : Styles.roughnessText, 2); - } - } - - public static void SetupMaterialWithBlendMode(Material material, BlendMode blendMode, float alphaCutoff) - { - switch (blendMode) - { - case BlendMode.Opaque: - material.SetOverrideTag("RenderType", "Opaque"); - material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One); - material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero); - material.SetInt("_ZWrite", 1); - material.DisableKeyword("_ALPHATEST_ON"); - material.DisableKeyword("_ALPHABLEND_ON"); - material.DisableKeyword("_ALPHAPREMULTIPLY_ON"); - material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Geometry; - break; - case BlendMode.Mask: - material.SetOverrideTag("RenderType", "TransparentCutout"); - material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One); - material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero); - material.SetInt("_ZWrite", 1); - material.EnableKeyword("_ALPHATEST_ON"); - material.DisableKeyword("_ALPHABLEND_ON"); - material.DisableKeyword("_ALPHAPREMULTIPLY_ON"); - material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.AlphaTest; - material.SetFloat("_Cutoff", alphaCutoff); - break; - case BlendMode.Blend: - material.SetOverrideTag("RenderType", "Transparent"); - material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha); - material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); - material.SetInt("_ZWrite", 0); - material.DisableKeyword("_ALPHATEST_ON"); - material.EnableKeyword("_ALPHABLEND_ON"); - material.DisableKeyword("_ALPHAPREMULTIPLY_ON"); - material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent; - break; - } - - material.SetFloat("_Mode", (float)blendMode); - } - - static void SetMaterialKeywords(Material material, WorkflowMode workflowMode) - { - // Note: keywords must be based on Material value not on MaterialProperty due to multi-edit & material animation - // (MaterialProperty value might come from renderer material property block) - SetKeyword(material, "_NORMALMAP", material.GetTexture("_BumpMap")); - if (workflowMode == WorkflowMode.SpecularGlossiness) - SetKeyword(material, "_SPECGLOSSMAP", material.GetTexture("_SpecGlossMap")); - else if (workflowMode == WorkflowMode.MetallicRoughness) - SetKeyword(material, "_METALLICGLOSSMAP", material.GetTexture("_MetallicGlossMap")); - - bool shouldEmissionBeEnabled = material.GetColor("_EmissionColor") != Color.black; - SetKeyword(material, "_EMISSION", shouldEmissionBeEnabled); - } - - static void MaterialChanged(Material material, WorkflowMode workflowMode) - { - SetupMaterialWithBlendMode(material, (BlendMode)material.GetFloat("_Mode"), material.GetFloat("_Cutoff")); - - SetMaterialKeywords(material, workflowMode); - } - - static void SetKeyword(Material m, string keyword, bool state) - { - if (state) - m.EnableKeyword(keyword); - else - m.DisableKeyword(keyword); - } - } + internal class PbrShaderGUI : ShaderGUI + { + private enum WorkflowMode + { + SpecularGlossiness, + MetallicRoughness, + Unlit + } + + public enum BlendMode + { + Opaque, + Mask, + Blend + } + + private static class Styles + { + public static GUIContent albedoText = new GUIContent("Base Color", "Albedo (RGB) and Transparency (A)"); + public static GUIContent alphaCutoffText = new GUIContent("Alpha Cutoff", "Threshold for alpha cutoff"); + public static GUIContent specularMapText = new GUIContent("Spec Gloss", "Specular (RGB) and Glossiness (A)"); + public static GUIContent metallicMapText = new GUIContent("Metal Rough", "Metallic (B) and Roughness (G)"); + public static GUIContent metallicText = new GUIContent("Metallic", "Metallic value"); + public static GUIContent metallicScaleText = new GUIContent("Metallic", "Metallic scale factor"); + public static GUIContent roughnessText = new GUIContent("Roughness", "Roughness value"); + + public static GUIContent roughnessScaleText = new GUIContent("Roughness", "Roughness scale factor"); + public static GUIContent glossinessText = new GUIContent("Glossiness", "Glossiness value"); + public static GUIContent glossinessScaleText = new GUIContent("Glossiness", "Glossiness scale factor"); + public static GUIContent normalMapText = new GUIContent("Normal Map", "Normal Map"); + public static GUIContent occlusionText = new GUIContent("Occlusion", "Occlusion (R)"); + public static GUIContent emissionText = new GUIContent("Emissive", "Emissive (RGB)"); + + public static string primaryMapsText = "Main Maps"; + public static string renderingMode = "Rendering Mode"; + public static GUIContent emissiveWarning = new GUIContent("Emissive value is animated but the material has not been configured to support emissive. Please make sure the material itself has some amount of emissive."); + public static readonly string[] blendNames = Enum.GetNames(typeof(BlendMode)); + } + + MaterialProperty blendMode = null; + MaterialProperty albedoMap = null; + MaterialProperty albedoColor = null; + MaterialProperty alphaCutoff = null; + MaterialProperty specularMap = null; + MaterialProperty specularColor = null; + MaterialProperty metallicMap = null; + MaterialProperty metallic = null; + MaterialProperty smoothness = null; + MaterialProperty bumpScale = null; + MaterialProperty bumpMap = null; + MaterialProperty occlusionStrength = null; + MaterialProperty occlusionMap = null; + MaterialProperty emissionColor = null; + MaterialProperty emissionMap = null; + + MaterialEditor m_MaterialEditor; + WorkflowMode m_WorkflowMode = WorkflowMode.MetallicRoughness; + + bool m_FirstTimeApply = true; + + public void FindProperties(MaterialProperty[] props) + { + blendMode = FindProperty("_Mode", props); + albedoMap = FindProperty("_MainTex", props); + albedoColor = FindProperty("_Color", props); + alphaCutoff = FindProperty("_Cutoff", props); + specularMap = FindProperty("_SpecGlossMap", props, false); + specularColor = FindProperty("_SpecColor", props, false); + metallicMap = FindProperty("_MetallicGlossMap", props, false); + metallic = FindProperty("_Metallic", props, false); + if (specularMap != null && specularColor != null) + m_WorkflowMode = WorkflowMode.SpecularGlossiness; + else if (metallicMap != null && metallic != null) + m_WorkflowMode = WorkflowMode.MetallicRoughness; + else + m_WorkflowMode = WorkflowMode.Unlit; + smoothness = FindProperty("_Glossiness", props); + bumpScale = FindProperty("_BumpScale", props); + bumpMap = FindProperty("_BumpMap", props); + occlusionStrength = FindProperty("_OcclusionStrength", props); + occlusionMap = FindProperty("_OcclusionMap", props); + emissionColor = FindProperty("_EmissionColor", props); + emissionMap = FindProperty("_EmissionMap", props); + } + + public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] props) + { + FindProperties(props); // MaterialProperties can be animated so we do not cache them but fetch them every event to ensure animated values are updated correctly + m_MaterialEditor = materialEditor; + Material material = materialEditor.target as Material; + + // Make sure that needed setup (ie keywords/renderqueue) are set up if we're switching some existing + // material to a standard shader. + // Do this before any GUI code has been issued to prevent layout issues in subsequent GUILayout statements (case 780071) + if (m_FirstTimeApply) + { + MaterialChanged(material, m_WorkflowMode); + m_FirstTimeApply = false; + } + + ShaderPropertiesGUI(material); + } + + public void ShaderPropertiesGUI(Material material) + { + // Use default labelWidth + EditorGUIUtility.labelWidth = 0f; + + // Detect any changes to the material + EditorGUI.BeginChangeCheck(); + { + BlendModePopup(); + + // Primary properties + GUILayout.Label(Styles.primaryMapsText, EditorStyles.boldLabel); + DoAlbedoArea(material); + DoSpecularMetallicArea(); + m_MaterialEditor.TexturePropertySingleLine(Styles.normalMapText, bumpMap, bumpMap.textureValue != null ? bumpScale : null); + m_MaterialEditor.TexturePropertySingleLine(Styles.occlusionText, occlusionMap, occlusionMap.textureValue != null ? occlusionStrength : null); + DoEmissionArea(material); + + EditorGUILayout.Space(); + } + if (EditorGUI.EndChangeCheck()) + { + foreach (var obj in blendMode.targets) + MaterialChanged((Material)obj, m_WorkflowMode); + } + } + + internal void DetermineWorkflow(MaterialProperty[] props) + { + if (FindProperty("_SpecGlossMap", props, false) != null && FindProperty("_SpecColor", props, false) != null) + m_WorkflowMode = WorkflowMode.SpecularGlossiness; + else if (FindProperty("_MetallicGlossMap", props, false) != null && FindProperty("_Metallic", props, false) != null) + m_WorkflowMode = WorkflowMode.MetallicRoughness; + else + m_WorkflowMode = WorkflowMode.Unlit; + } + + void BlendModePopup() + { + EditorGUI.showMixedValue = blendMode.hasMixedValue; + var mode = (BlendMode)blendMode.floatValue; + + EditorGUI.BeginChangeCheck(); + mode = (BlendMode)EditorGUILayout.Popup(Styles.renderingMode, (int)mode, Styles.blendNames); + if (EditorGUI.EndChangeCheck()) + { + m_MaterialEditor.RegisterPropertyChangeUndo("Rendering Mode"); + blendMode.floatValue = (float)mode; + } + + EditorGUI.showMixedValue = false; + } + + void DoAlbedoArea(Material material) + { + m_MaterialEditor.TexturePropertySingleLine(Styles.albedoText, albedoMap, albedoColor); + if (((BlendMode)material.GetFloat("_Mode") == BlendMode.Mask)) + { + m_MaterialEditor.ShaderProperty(alphaCutoff, Styles.alphaCutoffText.text, MaterialEditor.kMiniTextureFieldLabelIndentLevel + 1); + } + } + + void DoEmissionArea(Material material) + { + bool hadEmissionTexture = emissionMap.textureValue != null; + + // Texture and HDR color controls + m_MaterialEditor.TexturePropertySingleLine(Styles.emissionText, emissionMap, emissionColor); + + // If texture was assigned and color was black set color to white + float brightness = emissionColor.colorValue.maxColorComponent; + if (emissionMap.textureValue != null && !hadEmissionTexture && brightness <= 0f) + emissionColor.colorValue = Color.white; + } + + void DoSpecularMetallicArea() + { + bool hasGlossMap = false; + if (m_WorkflowMode == WorkflowMode.SpecularGlossiness) + { + hasGlossMap = specularMap.textureValue != null; + m_MaterialEditor.TexturePropertySingleLine(Styles.specularMapText, specularMap, hasGlossMap ? null : specularColor); + m_MaterialEditor.ShaderProperty(smoothness, hasGlossMap ? Styles.glossinessScaleText : Styles.glossinessText, 2); + } + else if (m_WorkflowMode == WorkflowMode.MetallicRoughness) + { + hasGlossMap = metallicMap.textureValue != null; + m_MaterialEditor.TexturePropertySingleLine(Styles.metallicMapText, metallicMap); + m_MaterialEditor.ShaderProperty(metallic, hasGlossMap ? Styles.metallicScaleText : Styles.metallicText, 2); + m_MaterialEditor.ShaderProperty(smoothness, hasGlossMap ? Styles.roughnessScaleText : Styles.roughnessText, 2); + } + } + + public static void SetupMaterialWithBlendMode(Material material, BlendMode blendMode, float alphaCutoff) + { + switch (blendMode) + { + case BlendMode.Opaque: + material.SetOverrideTag("RenderType", "Opaque"); + material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One); + material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero); + material.SetInt("_ZWrite", 1); + material.DisableKeyword("_ALPHATEST_ON"); + material.DisableKeyword("_ALPHABLEND_ON"); + material.DisableKeyword("_ALPHAPREMULTIPLY_ON"); + material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Geometry; + break; + case BlendMode.Mask: + material.SetOverrideTag("RenderType", "TransparentCutout"); + material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One); + material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero); + material.SetInt("_ZWrite", 1); + material.EnableKeyword("_ALPHATEST_ON"); + material.DisableKeyword("_ALPHABLEND_ON"); + material.DisableKeyword("_ALPHAPREMULTIPLY_ON"); + material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.AlphaTest; + material.SetFloat("_Cutoff", alphaCutoff); + break; + case BlendMode.Blend: + material.SetOverrideTag("RenderType", "Transparent"); + material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha); + material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); + material.SetInt("_ZWrite", 0); + material.DisableKeyword("_ALPHATEST_ON"); + material.EnableKeyword("_ALPHABLEND_ON"); + material.DisableKeyword("_ALPHAPREMULTIPLY_ON"); + material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent; + break; + } + + material.SetFloat("_Mode", (float)blendMode); + } + + static void SetMaterialKeywords(Material material, WorkflowMode workflowMode) + { + // Note: keywords must be based on Material value not on MaterialProperty due to multi-edit & material animation + // (MaterialProperty value might come from renderer material property block) + SetKeyword(material, "_NORMALMAP", material.GetTexture("_BumpMap")); + if (workflowMode == WorkflowMode.SpecularGlossiness) + SetKeyword(material, "_SPECGLOSSMAP", material.GetTexture("_SpecGlossMap")); + else if (workflowMode == WorkflowMode.MetallicRoughness) + SetKeyword(material, "_METALLICGLOSSMAP", material.GetTexture("_MetallicGlossMap")); + + bool shouldEmissionBeEnabled = material.GetColor("_EmissionColor") != Color.black; + SetKeyword(material, "_EMISSION", shouldEmissionBeEnabled); + } + + static void MaterialChanged(Material material, WorkflowMode workflowMode) + { + SetupMaterialWithBlendMode(material, (BlendMode)material.GetFloat("_Mode"), material.GetFloat("_Cutoff")); + + SetMaterialKeywords(material, workflowMode); + } + + static void SetKeyword(Material m, string keyword, bool state) + { + if (state) + m.EnableKeyword(keyword); + else + m.DisableKeyword(keyword); + } + } } // namespace UnityEditor diff --git a/UnityGLTF/Assets/UnityGLTF/Scripts/GLTFSceneExporter.cs b/UnityGLTF/Assets/UnityGLTF/Scripts/GLTFSceneExporter.cs index b224bcde3..ed75d4a0f 100644 --- a/UnityGLTF/Assets/UnityGLTF/Scripts/GLTFSceneExporter.cs +++ b/UnityGLTF/Assets/UnityGLTF/Scripts/GLTFSceneExporter.cs @@ -30,7 +30,8 @@ private enum TextureMapType Emission, MetallicGloss, Light, - Occlusion} + Occlusion + } ; @@ -48,19 +49,20 @@ private struct ImageInfo private List _imageInfos; private List _textures; private List _materials; + private bool _imagesInInternalBuffer; private RetrieveTexturePathDelegate _retrieveTexturePathDelegate; private UnityEngine.Material _metalGlossChannelSwapMaterial; private UnityEngine.Material _normalChannelMaterial; - private const uint MagicGLTF = 0x46546C67; - private const uint Version = 2; - private const uint MagicJson = 0x4E4F534A; - private const uint MagicBin = 0x004E4942; + private const uint MagicGLTF = 0x46546C67; + private const uint Version = 2; + private const uint MagicJson = 0x4E4F534A; + private const uint MagicBin = 0x004E4942; private const int GLTFHeaderSize = 12; private const int SectionHeaderSize = 8; - + protected struct PrimKey { public UnityEngine.Mesh Mesh; @@ -74,6 +76,7 @@ protected struct PrimKey public static bool ExportFullPath = true; public static bool RequireExtensions = false; + /// /// Create a GLTFExporter that exports out a transform /// @@ -82,11 +85,11 @@ public GLTFSceneExporter(Transform[] rootTransforms, RetrieveTexturePathDelegate { _retrieveTexturePathDelegate = retrieveTexturePathDelegate; - var metalGlossChannelSwapShader = Resources.Load ("MetalGlossChannelSwap", typeof(UnityEngine.Shader)) as UnityEngine.Shader; - _metalGlossChannelSwapMaterial = new UnityEngine.Material (metalGlossChannelSwapShader); + var metalGlossChannelSwapShader = Resources.Load("MetalGlossChannelSwap", typeof(UnityEngine.Shader)) as UnityEngine.Shader; + _metalGlossChannelSwapMaterial = new UnityEngine.Material(metalGlossChannelSwapShader); - var normalChannelShader = Resources.Load ("NormalChannel", typeof(UnityEngine.Shader)) as UnityEngine.Shader; - _normalChannelMaterial = new UnityEngine.Material (normalChannelShader); + var normalChannelShader = Resources.Load("NormalChannel", typeof(UnityEngine.Shader)) as UnityEngine.Shader; + _normalChannelMaterial = new UnityEngine.Material(normalChannelShader); _rootTransforms = rootTransforms; _root = new GLTFRoot @@ -108,9 +111,9 @@ public GLTFSceneExporter(Transform[] rootTransforms, RetrieveTexturePathDelegate Textures = new List(), }; - _imageInfos = new List (); - _materials = new List (); - _textures = new List (); + _imageInfos = new List(); + _materials = new List(); + _textures = new List(); _buffer = new GLTF.Schema.Buffer(); _bufferId = new BufferId @@ -118,7 +121,7 @@ public GLTFSceneExporter(Transform[] rootTransforms, RetrieveTexturePathDelegate Id = _root.Buffers.Count, Root = _root }; - _root.Buffers.Add (_buffer); + _root.Buffers.Add(_buffer); } /// @@ -137,6 +140,7 @@ public GLTFRoot GetRoot() /// The name of the GLTF file public void SaveGLB(string path, string fileName) { + _imagesInInternalBuffer = true; Stream binStream = new MemoryStream(); Stream jsonStream = new MemoryStream(); @@ -188,8 +192,10 @@ public void SaveGLB(string path, string fileName) writer.Flush(); } - - ExportImages(path); + if (!_imagesInInternalBuffer) + { + ExportImages(path); + } } /// @@ -220,7 +226,7 @@ private static void CopyStream(Stream input, BinaryWriter output) /// private static void AlignToBoundary(Stream stream, byte pad = (byte)' ', uint boundary = 4) { - uint currentLength = (uint) stream.Length; + uint currentLength = (uint)stream.Length; uint newLength = CalculateAlignment(currentLength, boundary); for (int i = 0; i < newLength - currentLength; i++) { @@ -248,6 +254,8 @@ public static uint CalculateAlignment(uint currentSize, uint byteAlignment) /// The name of the GLTF file public void SaveGLTFandBin(string path, string fileName) { + _imagesInInternalBuffer = false; + var binFile = File.Create(Path.Combine(path, fileName + ".bin")); _bufferWriter = new BinaryWriter(binFile); @@ -263,30 +271,32 @@ public void SaveGLTFandBin(string path, string fileName) gltfFile.Dispose(); binFile.Dispose(); #else - gltfFile.Close (); - binFile.Close (); + gltfFile.Close(); + binFile.Close(); #endif - ExportImages (path); - + ExportImages(path); + } - private void ExportImages (string outputPath) + private void ExportImages(string outputPath) { - for (int t = 0; t < _imageInfos.Count; ++t) { - var image = _imageInfos [t].texture; + for (int t = 0; t < _imageInfos.Count; ++t) + { + var image = _imageInfos[t].texture; int height = image.height; int width = image.width; - switch (_imageInfos [t].textureMapType) { - case TextureMapType.MetallicGloss: - ExportMetallicGlossTexture (image, outputPath); - break; - case TextureMapType.Bump: - ExportNormalTexture (image, outputPath); - break; - default: - ExportTexture (image, outputPath); - break; + switch (_imageInfos[t].textureMapType) + { + case TextureMapType.MetallicGloss: + ExportMetallicGlossTexture(image, outputPath); + break; + case TextureMapType.Bump: + ExportNormalTexture(image, outputPath); + break; + default: + ExportTexture(image, outputPath); + break; } } } @@ -298,19 +308,19 @@ private void ExportImages (string outputPath) /// /// Unity's metallic-gloss texture to be exported /// The location to export the texture - private void ExportMetallicGlossTexture (Texture2D texture, string outputPath) + private void ExportMetallicGlossTexture(Texture2D texture, string outputPath) { var destRenderTexture = RenderTexture.GetTemporary(texture.width, texture.height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear); - Graphics.Blit (texture, destRenderTexture, _metalGlossChannelSwapMaterial); - - var exportTexture = new Texture2D (texture.width, texture.height); - exportTexture.ReadPixels (new Rect (0, 0, destRenderTexture.width, destRenderTexture.height), 0, 0); - exportTexture.Apply (); + Graphics.Blit(texture, destRenderTexture, _metalGlossChannelSwapMaterial); - var finalFilenamePath = ConstructImageFilenamePath (texture, outputPath); - File.WriteAllBytes (finalFilenamePath, exportTexture.EncodeToPNG ()); + var exportTexture = new Texture2D(texture.width, texture.height); + exportTexture.ReadPixels(new Rect(0, 0, destRenderTexture.width, destRenderTexture.height), 0, 0); + exportTexture.Apply(); + var finalFilenamePath = ConstructImageFilenamePath(texture, outputPath); + File.WriteAllBytes(finalFilenamePath, exportTexture.EncodeToPNG()); + RenderTexture.active = null; destRenderTexture.Release(); if (Application.isEditor) { @@ -328,19 +338,19 @@ private void ExportMetallicGlossTexture (Texture2D texture, string outputPath) /// /// Unity's normal texture to be exported /// The location to export the texture - private void ExportNormalTexture (Texture2D texture, string outputPath) + private void ExportNormalTexture(Texture2D texture, string outputPath) { var destRenderTexture = RenderTexture.GetTemporary(texture.width, texture.height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear); - Graphics.Blit (texture, destRenderTexture, _normalChannelMaterial); + Graphics.Blit(texture, destRenderTexture, _normalChannelMaterial); - var exportTexture = new Texture2D (texture.width, texture.height); - exportTexture.ReadPixels (new Rect (0, 0, destRenderTexture.width, destRenderTexture.height), 0, 0); - exportTexture.Apply (); - - var finalFilenamePath = ConstructImageFilenamePath (texture, outputPath); - File.WriteAllBytes (finalFilenamePath, exportTexture.EncodeToPNG ()); + var exportTexture = new Texture2D(texture.width, texture.height); + exportTexture.ReadPixels(new Rect(0, 0, destRenderTexture.width, destRenderTexture.height), 0, 0); + exportTexture.Apply(); + var finalFilenamePath = ConstructImageFilenamePath(texture, outputPath); + File.WriteAllBytes(finalFilenamePath, exportTexture.EncodeToPNG()); + RenderTexture.active = null; destRenderTexture.Release(); if (Application.isEditor) @@ -353,19 +363,19 @@ private void ExportNormalTexture (Texture2D texture, string outputPath) } } - private void ExportTexture (Texture2D texture, string outputPath) + private void ExportTexture(Texture2D texture, string outputPath) { var destRenderTexture = RenderTexture.GetTemporary(texture.width, texture.height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB); - Graphics.Blit (texture, destRenderTexture); - - var exportTexture = new Texture2D (texture.width, texture.height); - exportTexture.ReadPixels (new Rect (0, 0, destRenderTexture.width, destRenderTexture.height), 0, 0); - exportTexture.Apply (); + Graphics.Blit(texture, destRenderTexture); - var finalFilenamePath = ConstructImageFilenamePath (texture, outputPath); - File.WriteAllBytes (finalFilenamePath, exportTexture.EncodeToPNG ()); + var exportTexture = new Texture2D(texture.width, texture.height); + exportTexture.ReadPixels(new Rect(0, 0, destRenderTexture.width, destRenderTexture.height), 0, 0); + exportTexture.Apply(); + var finalFilenamePath = ConstructImageFilenamePath(texture, outputPath); + File.WriteAllBytes(finalFilenamePath, exportTexture.EncodeToPNG()); + RenderTexture.active = null; destRenderTexture.Release(); if (Application.isEditor) { @@ -377,35 +387,35 @@ private void ExportTexture (Texture2D texture, string outputPath) } } - private string ConstructImageFilenamePath (Texture2D texture, string outputPath) + private string ConstructImageFilenamePath(Texture2D texture, string outputPath) { var imagePath = _retrieveTexturePathDelegate(texture); - var filenamePath = Path.Combine (outputPath, imagePath); + var filenamePath = Path.Combine(outputPath, imagePath); if (!ExportFullPath) { filenamePath = outputPath + "/" + texture.name; } - var file = new System.IO.FileInfo (filenamePath); - file.Directory.Create (); - return Path.ChangeExtension (filenamePath, ".png"); + var file = new System.IO.FileInfo(filenamePath); + file.Directory.Create(); + return Path.ChangeExtension(filenamePath, ".png"); } - private SceneId ExportScene (string name, Transform[] rootObjTransforms) + private SceneId ExportScene(string name, Transform[] rootObjTransforms) { - var scene = new Scene (); + var scene = new Scene(); - if (ExportNames) + if (ExportNames) { scene.Name = name; } - scene.Nodes = new List (rootObjTransforms.Length); - foreach (var transform in rootObjTransforms) + scene.Nodes = new List(rootObjTransforms.Length); + foreach (var transform in rootObjTransforms) { - scene.Nodes.Add (ExportNode (transform)); + scene.Nodes.Add(ExportNode(transform)); } - _root.Scenes.Add (scene); + _root.Scenes.Add(scene); return new SceneId { @@ -502,12 +512,13 @@ private CameraId ExportCamera(UnityEngine.Camera unityCamera) perspective.YFov = fov; perspective.AspectRatio = aspectRatio; - if (matrix[2,2] == -1) + if (matrix[2, 2] == -1) { //infinite projection matrix float nearClip = matrix[2, 3] * -0.5f; perspective.ZNear = nearClip; - } else + } + else { //finite projection matrix float farClip = matrix[2, 3] / (matrix[2, 2] + 1); @@ -788,13 +799,13 @@ private MaterialId ExportMaterial(UnityEngine.Material materialObj) } } - if (IsPBRMetallicRoughness (materialObj)) + if (IsPBRMetallicRoughness(materialObj)) { - material.PbrMetallicRoughness = ExportPBRMetallicRoughness (materialObj); - } - else if (IsCommonConstant (materialObj)) + material.PbrMetallicRoughness = ExportPBRMetallicRoughness(materialObj); + } + else if (IsCommonConstant(materialObj)) { - material.CommonConstant = ExportCommonConstant (materialObj); + material.CommonConstant = ExportCommonConstant(materialObj); } _materials.Add(materialObj); @@ -809,16 +820,16 @@ private MaterialId ExportMaterial(UnityEngine.Material materialObj) return id; } - private bool IsPBRMetallicRoughness (UnityEngine.Material material) + private bool IsPBRMetallicRoughness(UnityEngine.Material material) { - return material.HasProperty ("_Metallic") && material.HasProperty ("_MetallicGlossMap"); + return material.HasProperty("_Metallic") && material.HasProperty("_MetallicGlossMap"); } - private bool IsCommonConstant (UnityEngine.Material material) + private bool IsCommonConstant(UnityEngine.Material material) { - return material.HasProperty ("_AmbientFactor") && - material.HasProperty ("_LightMap") && - material.HasProperty ("_LightFactor"); + return material.HasProperty("_AmbientFactor") && + material.HasProperty("_LightMap") && + material.HasProperty("_LightFactor"); } private void ExportTextureTransform(TextureInfo def, UnityEngine.Material mat, string texName) @@ -863,33 +874,35 @@ private void ExportTextureTransform(TextureInfo def, UnityEngine.Material mat, s ); } - private NormalTextureInfo ExportNormalTextureInfo ( - UnityEngine.Texture texture, - TextureMapType textureMapType, + private NormalTextureInfo ExportNormalTextureInfo( + UnityEngine.Texture texture, + TextureMapType textureMapType, UnityEngine.Material material) { - var info = new NormalTextureInfo (); + var info = new NormalTextureInfo(); - info.Index = ExportTexture (texture, textureMapType); + info.Index = ExportTexture(texture, textureMapType); - if (material.HasProperty ("_BumpScale")) { - info.Scale = material.GetFloat ("_BumpScale"); + if (material.HasProperty("_BumpScale")) + { + info.Scale = material.GetFloat("_BumpScale"); } return info; } - private OcclusionTextureInfo ExportOcclusionTextureInfo ( - UnityEngine.Texture texture, + private OcclusionTextureInfo ExportOcclusionTextureInfo( + UnityEngine.Texture texture, TextureMapType textureMapType, UnityEngine.Material material) { - var info = new OcclusionTextureInfo (); + var info = new OcclusionTextureInfo(); - info.Index = ExportTexture (texture, textureMapType); + info.Index = ExportTexture(texture, textureMapType); - if (material.HasProperty ("_OcclusionStrength")) { - info.Strength = material.GetFloat ("_OcclusionStrength"); + if (material.HasProperty("_OcclusionStrength")) + { + info.Strength = material.GetFloat("_OcclusionStrength"); } return info; @@ -915,16 +928,16 @@ private PbrMetallicRoughness ExportPBRMetallicRoughness(UnityEngine.Material mat } } - if (material.HasProperty ("_Metallic")) + if (material.HasProperty("_Metallic")) { - var metallicGlossMap = material.GetTexture ("_MetallicGlossMap"); - pbr.MetallicFactor = (metallicGlossMap != null) ? 1.0 : material.GetFloat ("_Metallic"); + var metallicGlossMap = material.GetTexture("_MetallicGlossMap"); + pbr.MetallicFactor = (metallicGlossMap != null) ? 1.0 : material.GetFloat("_Metallic"); } - if (material.HasProperty ("_Glossiness")) + if (material.HasProperty("_Glossiness")) { - var metallicGlossMap = material.GetTexture ("_MetallicGlossMap"); - pbr.RoughnessFactor = (metallicGlossMap != null) ? 1.0 : material.GetFloat ("_Glossiness"); + var metallicGlossMap = material.GetTexture("_MetallicGlossMap"); + pbr.RoughnessFactor = (metallicGlossMap != null) ? 1.0 : material.GetFloat("_Glossiness"); } if (material.HasProperty("_MetallicGlossMap")) @@ -1031,7 +1044,14 @@ private TextureId ExportTexture(UnityEngine.Texture textureObj, TextureMapType t texture.Name = textureObj.name; } - texture.Source = ExportImage(textureObj, textureMapType); + if (_imagesInInternalBuffer) + { + texture.Source = ExportImageInternalBuffer(textureObj); + } + else + { + texture.Source = ExportImage(textureObj, textureMapType); + } texture.Sampler = ExportSampler(textureObj); _textures.Add(textureObj); @@ -1062,7 +1082,8 @@ private ImageId ExportImage(UnityEngine.Texture texture, TextureMapType texturMa image.Name = texture.name; } - _imageInfos.Add (new ImageInfo { + _imageInfos.Add(new ImageInfo + { texture = texture as Texture2D, textureMapType = texturMapType }); @@ -1086,6 +1107,60 @@ private ImageId ExportImage(UnityEngine.Texture texture, TextureMapType texturMa return id; } + private ImageId ExportImageInternalBuffer(UnityEngine.Texture texture) + { + + if (texture == null) + { + throw new Exception("texture can not be NULL."); + } + + var image = new Image(); + image.MimeType = "image/png"; + + var byteOffset = _bufferWriter.BaseStream.Position; + + {// + var destRenderTexture = RenderTexture.GetTemporary(texture.width, texture.height, 24, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB); + GL.sRGBWrite = true; + Graphics.Blit(texture, destRenderTexture); + + var exportTexture = new Texture2D(texture.width, texture.height, TextureFormat.ARGB32, false); + exportTexture.ReadPixels(new Rect(0, 0, destRenderTexture.width, destRenderTexture.height), 0, 0); + exportTexture.Apply(); + + var pngImageData = exportTexture.EncodeToPNG(); + _bufferWriter.Write(pngImageData); + RenderTexture.active = null; + destRenderTexture.Release(); + GL.sRGBWrite = false; + if (Application.isEditor) + { + UnityEngine.Object.DestroyImmediate(exportTexture); + } + else + { + UnityEngine.Object.Destroy(exportTexture); + } + } + + var byteLength = _bufferWriter.BaseStream.Position - byteOffset; + + byteLength = AppendToBufferMultiplyOf4(byteOffset, byteLength); + + image.BufferView = ExportBufferView((int)byteOffset, (int)byteLength); + + + var id = new ImageId + { + Id = _root.Images.Count, + Root = _root + }; + _root.Images.Add(image); + + return id; + } + private SamplerId ExportSampler(UnityEngine.Texture texture) { var samplerId = GetSamplerId(_root, texture); @@ -1164,6 +1239,8 @@ private AccessorId ExportAccessor(int[] arr, bool isIndices = false) var byteOffset = _bufferWriter.BaseStream.Position; + + if (max <= byte.MaxValue && min >= byte.MinValue) { accessor.ComponentType = GLTFComponentType.UnsignedByte; @@ -1221,9 +1298,12 @@ private AccessorId ExportAccessor(int[] arr, bool isIndices = false) accessor.Min = new List { min }; accessor.Max = new List { max }; + //byteOffset = AppendToBufferSizeTo4(accessor, byteOffset); var byteLength = _bufferWriter.BaseStream.Position - byteOffset; + byteLength = AppendToBufferMultiplyOf4(byteOffset, byteLength); + accessor.BufferView = ExportBufferView((int)byteOffset, (int)byteLength); var id = new AccessorId @@ -1236,6 +1316,21 @@ private AccessorId ExportAccessor(int[] arr, bool isIndices = false) return id; } + private long AppendToBufferMultiplyOf4(long byteOffset, long byteLength) + { + var moduloOffset = byteLength % 4; + if (moduloOffset > 0) + { + for (int i = 0; i < (4 - moduloOffset); i++) + { + _bufferWriter.Write((byte)0x00); + } + byteLength = _bufferWriter.BaseStream.Position - byteOffset; + } + + return byteLength; + } + private AccessorId ExportAccessor(Vector2[] arr) { var count = arr.Length; @@ -1288,8 +1383,11 @@ private AccessorId ExportAccessor(Vector2[] arr) _bufferWriter.Write(vec.y); } + var byteLength = _bufferWriter.BaseStream.Position - byteOffset; + byteLength = AppendToBufferMultiplyOf4(byteOffset, byteLength); + accessor.BufferView = ExportBufferView((int)byteOffset, (int)byteLength); var id = new AccessorId @@ -1367,6 +1465,8 @@ private AccessorId ExportAccessor(Vector3[] arr) var byteLength = _bufferWriter.BaseStream.Position - byteOffset; + byteLength = AppendToBufferMultiplyOf4(byteOffset, byteLength); + accessor.BufferView = ExportBufferView((int)byteOffset, (int)byteLength); var id = new AccessorId @@ -1452,9 +1552,10 @@ private AccessorId ExportAccessor(Vector4[] arr) _bufferWriter.Write(vec.z); _bufferWriter.Write(vec.w); } - var byteLength = _bufferWriter.BaseStream.Position - byteOffset; + byteLength = AppendToBufferMultiplyOf4(byteOffset, byteLength); + accessor.BufferView = ExportBufferView((int)byteOffset, (int)byteLength); var id = new AccessorId @@ -1540,9 +1641,10 @@ private AccessorId ExportAccessor(UnityEngine.Color[] arr) _bufferWriter.Write(color.b); _bufferWriter.Write(color.a); } - var byteLength = _bufferWriter.BaseStream.Position - byteOffset; + byteLength = AppendToBufferMultiplyOf4(byteOffset, byteLength); + accessor.BufferView = ExportBufferView((int)byteOffset, (int)byteLength); var id = new AccessorId @@ -1555,8 +1657,11 @@ private AccessorId ExportAccessor(UnityEngine.Color[] arr) return id; } + + private BufferViewId ExportBufferView(int byteOffset, int byteLength) { + var bufferView = new BufferView { Buffer = _bufferId, @@ -1570,6 +1675,7 @@ private BufferViewId ExportBufferView(int byteOffset, int byteLength) Root = _root }; + _root.BufferViews.Add(bufferView); return id; diff --git a/UnityGLTF/Assets/UnityGLTF/Scripts/GLTFSceneImporter.cs b/UnityGLTF/Assets/UnityGLTF/Scripts/GLTFSceneImporter.cs index bd1cff220..8228b77b8 100644 --- a/UnityGLTF/Assets/UnityGLTF/Scripts/GLTFSceneImporter.cs +++ b/UnityGLTF/Assets/UnityGLTF/Scripts/GLTFSceneImporter.cs @@ -99,8 +99,8 @@ private GLTFSceneImporter(ILoader externalDataLoader) { _loader = externalDataLoader; _asyncAction = new AsyncAction(); - } - + } + public void Dispose() { if (_assetCache != null) @@ -1388,18 +1388,18 @@ protected virtual IEnumerator ConstructTexture(GLTF.Schema.Texture texture, int var sampler = texture.Sampler.Value; switch (sampler.MinFilter) { - case MinFilterMode.Nearest: - case MinFilterMode.NearestMipmapNearest: - case MinFilterMode.NearestMipmapLinear: + case MinFilterMode.Nearest: + case MinFilterMode.NearestMipmapNearest: + case MinFilterMode.NearestMipmapLinear: desiredFilterMode = FilterMode.Point; break; - case MinFilterMode.Linear: - case MinFilterMode.LinearMipmapNearest: - case MinFilterMode.LinearMipmapLinear: - desiredFilterMode = FilterMode.Bilinear; - break; - default: - Debug.LogWarning("Unsupported Sampler.MinFilter: " + sampler.MinFilter); + case MinFilterMode.Linear: + case MinFilterMode.LinearMipmapNearest: + case MinFilterMode.LinearMipmapLinear: + desiredFilterMode = FilterMode.Bilinear; + break; + default: + Debug.LogWarning("Unsupported Sampler.MinFilter: " + sampler.MinFilter); break; } diff --git a/UnityGLTF/Assets/UnityGLTF/Scripts/InstantiatedGLTFObject.cs b/UnityGLTF/Assets/UnityGLTF/Scripts/InstantiatedGLTFObject.cs index cf92f31ab..aec907f9d 100644 --- a/UnityGLTF/Assets/UnityGLTF/Scripts/InstantiatedGLTFObject.cs +++ b/UnityGLTF/Assets/UnityGLTF/Scripts/InstantiatedGLTFObject.cs @@ -3,63 +3,63 @@ namespace UnityGLTF { - /// - /// Instantiated GLTF Object component that gets added to the root of every GLTF game object created by a scene importer. - /// - public class InstantiatedGLTFObject : MonoBehaviour - { - /// - /// Ref-counted cache data for this object. - /// The same instance of this cached data will be used for all copies of this GLTF object, - /// and the data gets cleaned up when the ref counts goes to 0. - /// - private RefCountedCacheData cachedData; - public RefCountedCacheData CachedData - { - get - { - return cachedData; - } + /// + /// Instantiated GLTF Object component that gets added to the root of every GLTF game object created by a scene importer. + /// + public class InstantiatedGLTFObject : MonoBehaviour + { + /// + /// Ref-counted cache data for this object. + /// The same instance of this cached data will be used for all copies of this GLTF object, + /// and the data gets cleaned up when the ref counts goes to 0. + /// + private RefCountedCacheData cachedData; + public RefCountedCacheData CachedData + { + get + { + return cachedData; + } - set - { - if (cachedData != value) - { - if (cachedData != null) - { - cachedData.DecreaseRefCount(); - } + set + { + if (cachedData != value) + { + if (cachedData != null) + { + cachedData.DecreaseRefCount(); + } - cachedData = value; + cachedData = value; - if (cachedData != null) - { - cachedData.IncreaseRefCount(); - } - } - } - } + if (cachedData != null) + { + cachedData.IncreaseRefCount(); + } + } + } + } - /// - /// Duplicates the instantiated GLTF object. - /// Note that this should always be called if you intend to create a new instance of a GLTF object, - /// in order to properly preserve the ref count of the dynamically loaded mesh data, otherwise - /// you will run into a memory leak due to non-destroyed meshes, textures and materials. - /// - /// - public InstantiatedGLTFObject Duplicate() - { - GameObject duplicatedObject = Instantiate(gameObject); + /// + /// Duplicates the instantiated GLTF object. + /// Note that this should always be called if you intend to create a new instance of a GLTF object, + /// in order to properly preserve the ref count of the dynamically loaded mesh data, otherwise + /// you will run into a memory leak due to non-destroyed meshes, textures and materials. + /// + /// + public InstantiatedGLTFObject Duplicate() + { + GameObject duplicatedObject = Instantiate(gameObject); - InstantiatedGLTFObject newGltfObjectComponent = duplicatedObject.GetComponent(); - newGltfObjectComponent.CachedData = CachedData; + InstantiatedGLTFObject newGltfObjectComponent = duplicatedObject.GetComponent(); + newGltfObjectComponent.CachedData = CachedData; - return newGltfObjectComponent; - } + return newGltfObjectComponent; + } - private void OnDestroy() - { - CachedData = null; - } - } + private void OnDestroy() + { + CachedData = null; + } + } }